make-commandline-tools-in-perl6

准备

1
2
zef install App::Mi6  # 安装模块骨架工具
mi6 new FakeStreaming # 创建一个模块骨架

使用 tree 看一下目录结构:

1
2
3
4
5
6
7
8
9
10
11
├── Changes
├── LICENSE
├── META6.json
├── README.md
├── bin
│   └── fake-stream
├── dist.ini
├── lib
│   └── FakeStreaming.pm6
└── t
└── 01-basic.t

我们主要看 META6.json, bin 目录, lib 目录。

  • META6.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"authors" : [
""
],
"build-depends" : [ ],
"depends" : ["PKafka"],
"description" : "a commandline tool to simulate socket streraming mock and kafka producer",
"license" : "Artistic-2.0",
"name" : "FakeStreaming",
"perl" : "6.c",
"provides" : {
"FakeStreaming" : "lib/FakeStreaming.pm6"
},
"resources" : [ ],
"source-url" : "",
"tags" : [ ],
"test-depends" : [ ],
"version" : "0.0.1"
}

这个文件是 zef 安装工具所需的, depends 一栏填写该模块所需要的依赖, name 为模块名, provides 为模块的 lib 路径。其余可选填。

创建模块

  • lib

存放 .pm6 模块的地方。这个模块我定义了俩个方法, 一个是模拟 socket 实时流, 一个是模拟 Kafka producer(每隔 1s 发一条数据到 Kafka, 然后等待 sleeping 秒, 然后继续这个过程)。功能很简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use v6.c;
use PKafka::Producer;

unit class FakeStreaming:ver<0.0.1>;

class Data {
has $.host is rw = '0.0.0.0';
has $.port is rw = 3333;
has $.vin is rw = 'LSJA0000000000091';
has $.last_meter is rw = 0;
has $.sleeping is rw = 5;
has $.rate is rw = 1;
has Str $.brokers is rw = "127.0.0.1";
has Str $.topics is rw = "xs6-nation-test";
has Int $.partitions is rw = 6;

method feed() {
react {
whenever IO::Socket::Async.listen($!host, $!port) -> $conn {
react {
my Bool:D $ignore = True;

whenever Supply.interval($!sleeping).rotor(1, 1 => 1) {
$ignore = !$ignore;
}

whenever Supply.interval($!rate) {
next if $ignore;
print sprintf("\{'vin':'%s','createTime':%s,'mileage':%s}\n", $!vin, DateTime.now.posix, $!last_meter);
$conn.print: sprintf("\{'vin':'%s','createTime':%s,'mileage':%s}\n", $!vin, DateTime.now.posix, $!last_meter++);
}

whenever signal(SIGINT) {
say "Done.";
done;
}
}
}

CATCH {
default {
say .^name, ': ', .Str;
say "handled in $?LINE";
}
}
}
}

method producer() {
react {
my $producer = PKafka::Producer.new(topic => $!topics, brokers => $!brokers);
my Bool:D $ignore = True;
whenever Supply.interval($!sleeping).rotor(1, 1 => 1) {
$ignore = !$ignore;
}

whenever Supply.interval($!rate) {
next if $ignore;
print sprintf("\{'vin':'%s','createTime':%s,'mileage':%s}\n", $!vin, DateTime.now.posix, $!last_meter);
my Int $partition = (0..^$!partitions).pick;
my Str $payload = sprintf("\{'vin':'%s','createTime':%s,'mileage':%s}", $!vin, DateTime.now.posix, $!last_meter++);
my Str $key = $!vin;
$producer.put(partition => $partition, payload => $payload, key => $key);
}

whenever signal(SIGINT) {
say "Done.";
done;
}
}
}

}

=begin pod

=head1 NAME

FakeStreaming - blah blah blah

=head1 SYNOPSIS

=begin code :lang<perl6>

use FakeStreaming;

=end code

=head1 DESCRIPTION

FakeStreaming is ...

=head1 AUTHOR

<>

=head1 COPYRIGHT AND LICENSE

Copyright 2019

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.

=end pod

创建命令行工具

  • bin 存放可执行文件的地方

这里我定义了两个 multi sub MAIN, 运行的时候传不同的参数就可以调用对应的函数了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/usr/bin/env perl6

use FakeStreaming;

multi sub MAIN(
:$host = '0.0.0.0',
:$port = 3333,
:$vin = 'LSJA0000000000091',
:$last_meter = 0,
:$sleeping = 10,
:$rate = 1
) {
my $app = FakeStreaming::Data.new(
:$host,
:$port,
:$vin,
:$last_meter,
:$sleeping,
:$rate
);
$app.feed();
}

multi sub MAIN(
:$vin = 'LSJA0000000000091',
:$last_meter = 0,
:$sleeping = 10,
:$rate = 1,
:$brokers = "127.0.0.1",
:$topics = "xs6-nation-test",
:$partitions = 6
) {
my $app = FakeStreaming::Data.new(
:$vin,
:$last_meter,
:$sleeping,
:$rate,
:$brokers,
:$topics,
:$partitions
);
$app.producer();
}

安装命令行工具

这样一个命令行工具的准备工作就完成了, 因为这是自己用的, 所以我们安装在本地即可:

1
2
rm -rf .precomp # 删除预编译文件
zef install . --force-install # 为方便调试, 强制安装

安装后的可执行文件在 ~/.perl6/bin/ 下面:

1
2
which fake-stream
~/.perl6/bin/fake-stream

它其实还是个文本文件, 我们打开看一下:

1
2
3
4
#!/usr/bin/env perl6
sub MAIN(:$name, :$auth, :$ver, *@, *%) {
CompUnit::RepositoryRegistry.run-script("fake-stream", :$name, :$auth, :$ver);
}

这段代码可能是仓库注册相关的, 官网没有详细解释, 只给出了源代码地址

调用命令行

运行很简单拉,使用命名参数即可:

1
fake-stream  --vin='LSJA0000000000091'  --last_meter=0 --sleeping=5 --rate=1 --brokers='127.0.0.1' --topics='xs6-nation-test' --partitions=6

这会调用 producer, 给 kafka 发数据。

总结

简单来说, 就是三步:

  • 1)创建 Perl 6 模块(.pm6)放在 lib
  • 2)创建命令行程序(不带后缀.pl6)放在 bin
  • 3)使用 zef install . 安装命令行程序

如果你在第二步带了后缀, 例如 .pl6 那么你在执行的时候, 就需要加上后缀了, 这很不 Perl6ish! App::Task 就是忘记去掉 .pl6 了。哈哈哈哈, 我自己在本地安装不带后缀的, 很好用!