在 Raku 中制作命令行工具
— 焉知非鱼准备 #
zef install App::Mi6 # 安装模块骨架工具
mi6 new FakeStreaming # 创建一个模块骨架
使用 tree
看一下目录结构:
├── Changes
├── LICENSE
├── META6.json
├── README.md
├── bin
│ └── fake-stream
├── dist.ini
├── lib
│ └── FakeStreaming.pm6
└── t
└── 01-basic.t
我们主要看 META6.json
, bin
目录, lib
目录。
META6.json
{
"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
秒, 然后继续这个过程)。功能很简单。
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<raku>
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
, 运行的时候传不同的参数就可以调用对应的函数了:
#!/usr/bin/env raku
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();
}
安装命令行工具 #
这样一个命令行工具的准备工作就完成了, 因为这是自己用的, 所以我们安装在本地即可:
rm -rf .precomp # 删除预编译文件
zef install . --force-install # 为方便调试, 强制安装
安装后的可执行文件在 ~/.raku/bin/
下面:
which fake-stream
~/.raku/bin/fake-stream
它其实还是个文本文件, 我们打开看一下:
#!/usr/bin/env raku
sub MAIN(:$name, :$auth, :$ver, *@, *%) {
CompUnit::RepositoryRegistry.run-script("fake-stream", :$name, :$auth, :$ver);
}
这段代码可能是仓库注册相关的, 官网没有详细解释, 只给出了源代码地址。
调用命令行 #
运行很简单拉,使用命名参数即可:
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)创建 Raku 模块(.pm6)放在
lib
- 2)创建命令行程序(不带后缀.pl6)放在
bin
- 3)使用
zef install .
安装命令行程序
如果你在第二步带了后缀, 例如 .pl6
那么你在执行的时候, 就需要加上后缀了, 这很不 Rakuish! App::Task 就是忘记去掉 .pl6
了。哈哈哈哈, 我自己在本地安装不带后缀的, 很好用!