-
Notifications
You must be signed in to change notification settings - Fork 393
srpc小工具:快速构建脚手架
一个帮你快速生成Workflow和SRPC项目的脚手架小工具。
这个小工具可以帮你快速构建你的Workflow和SRPC项目,目的是为了让开发者可以快速构建出一个简单而完整的项目,降低编译和运行起来的门槛,让开发者可以伴随着修改
->执行
->验证效果
,一步步深入了解Workflow和SRPC项目的基本功能,是一个极佳的学习工具。
构建出的项目内包括:
- 源代码:可以直接编译,编译出来的可执行文件可以直接跑起来使用。我们也可以进行简单地修改,从而快速验证我们想要的功能。
- 配置文件:json格式,用于指定项目的基本配置,我们也可以快速通过配置文件了解到项目相关的信息。
- CMakeLists.txt:写好了依赖库位置,编译与链接方式等,供开发者用cmake进行编译。内部有简单的注释,我们可以根据注释一点点学习如何写基本的cmake。
- GNUmakefile:只是为了方便开发者可以直接执行make命令进行编译的文件,内部封装了cmake命令。
- config目录:内部有读取配置文件的代码,以及一些开发者暂时无需关心的工具代码比如util.h。
整个项目构建出来之后,可以拷走拿到别的地方使用。
我们把上述的项目clone下来,并打开tools目录,就可以编译出我们的srpc小工具。工具名字也叫srpc,但是是小写的~
git clone https://github.com/sogou/srpc.git
cd srpc/tools && make
这个小工具和SRPC框架目前还没有关系,所以即使本地没有安装SRPC所需要的protobuf或者没有--recursive拉submodule下来,也依然可以编译。唯一需要的是cmake 3.6及以上的版本。
我们先把这个srpc小工具运行起来,可以看到它第二个参数COMMAND:表示支持什么命令。
./srpc
Description:
Simple generator for building Workflow and SRPC projects.
Usage:
./srpc <COMMAND> <PROJECT_NAME> [FLAGS]
Available Commands:
http - create project with both client and server
redis - create project with both client and server
rpc - create project with both client and server
api - create protobuf or thrift IDL api
proxy - create proxy for some client and server protocol
file - create project with asynchronous file service
compute - create project with asynchronous computing service
这些COMMAND包括了我们最常用的场景,适合入门了解服务器编程最简单的内容。
第三个参数是项目名,我们先用一行简单的命令,构建出一个Http服务器与客户端。
./srpc http my_project
Success:
make project path " my_project/ " done.
Commands:
cd my_project/
make -j
Execute:
./server
./client
可以看到提示Success!
我们按照上述的提示看到:my_project目录已经在本地目录下创建,并给出了:
- 编译的命令:make
- 运行的命令:分别在两个终端上执行./server 和 ./client
cd my_project
make
执行ls -all
一下可以看到,两个可执行文件已经编译出来了。我们分别在两个终端运行./server
和./client
:
./server
Http server started, port 8080
http server get request_uri: /client_request
peer address: 127.0.0.1:65313, seq: 0.
client运行起来后会给server发一个请求,然后server会打印出上面显示的最后两行,然后client收到回复之后也会打印下面的两行:
./client
Http client state = 0 error = 0
<html>Hello from server!</html>
即使刚才git clone项目时没有加--recursive拉取依赖的submodule,或者srpc的lib没有编译(按上述的步骤的话,是还没有的~),那么工具会自动做一些初始化的工作。
当然,目前C++跟GO等其他语言比起来在构建方面还是薄弱了一点。如果大家还没有安装protobuf,或者系统的版本太旧、导致编译SRPC时所依赖的protobuf版本与链接时不一样,那么可以先使用源码编译protobuf。
这里找了一个不太新也不太旧的版本,大家有需要可以按照以下步骤先源码编译和安装protobuf:
git clone -b 3.20.x https://github.com/protocolbuffers/protobuf.git protobuf.3.20
cd protobuf.3.20
sh autogen.sh
./configure
make -j4
make install
然后我们就可以愉快再试一下上述步骤了~
小工具整体需要执行的命令与需要安装的依赖提前一览:
我们执行tree
命令,查看这个项目里的文件结构。
需要我们关注的有这些:
脚手架小工具目前还是使用cmake进行编译,后续计划支持bazel和xmake。
GNUmakefile
包了一层cmake命令,让我们可以执行make
就编译出项目,这个文件我们不需要关心。
打开CMakeLists.txt
,可以看到一共32行,包括了:
- 寻找依赖路径的写法
- include和link的写法
- 编出执行文件
开发者可以根据里边的注释自行修改,即使不常用C++的开发者也可以边试边学。
client会读取client.conf
作为它的配置文件,主要是指定要访问的目标是什么。
我们打开client_main.cc
,可以看到脚手架默认生成的client只有60多行,一共做了3件事:
int main()
{
// 1. 初始化,这个实现也在源码中,主要是调用config.load("./client.conf")
init();
std::string url = std::string("http://") + config.client_host() +
std::string(":") + std::to_string(config.client_port());
// 2. 构造一个http task并且填回调函数
WFHttpTask *task = WFTaskFactory::create_http_task(url,
config.redirect_max(),
config.retry_max(),
callback);
// 3. 把task运行起来
task->start();
wait_group.wait();
return 0;
}
可以看到,这个和workflow的tutorial中的例子是一样的,需要填的callback函数也在文件中。
我们打开server_main.cc
,50多行的代码,也是做了3件事,可以看到和上面的client是非常对称的:
int main()
{
// 1. 初始化,这个实现也在源码中,主要是调用config.load(“./server.conf")
init();
// 2. 造一个server,填好处理函数
WFHttpServer server(process);
// 3. 把server运行起来
if (server.start(config.server_port()) == 0)
{
fprintf(stderr, "Http server started, port %u\n", config.server_port());
wait_group.wait();
server.stop();
}
else
perror("server start");
return 0;
}
process函数也在源码中,开发者可以尝试修改,进行不同的行为处理。示例中的行为就是回复一个 " Hello from server! "
配置解析并不是Workflow和SRPC项目自带的,但是脚手架项目增加了这个功能。
我们目前使用的配置文件都是json格式,和配置解析相关的都放到了config目录中。除了client.conf和server.conf以外,我们还多加了一份full.conf,用来指引Workflow和SRPC目前支持的配置项,开发者可以通配置文件,快速了解我们还可以用什么功能。
比如框架的全局配置:
{
"server":
{
"port": 8080
},
"client":
{
"remote_host": "127.0.0.1",
"remote_port": 8080,
"retry_max": 1,
},
"global":
{
"poller_threads": 4,
"handler_threads": 20
}
}
熟悉的开发者可能接触过Workflow的Upstream,以及trace和metrics等监控数据的上报插件,这些都可以在配置文件中指定并一键加载,帮开发者接管外部生态,真正实现脚手架的能力。
经过以上介绍,应该可以基本掌握怎么快速构建和运行一个自己的小项目。接下来我们进一步解锁这个srpc小工具,每个COMMAND都是一个二级命令。
创建HTTP项目的用法如上已经展示,可以创建http协议的server和client。其中server和client里的示例代码都可以自行改动,配置文件server.conf
和client.conf
里也可以指定基本的配置项,cmake编译文件都已经生成好了,整个项目可以直接拿走使用。
./srpc http
Missing: PROJECT_NAME
Usage:
./srpc http <PROJECT_NAME> [FLAGS]
Example:
./srpc http my_http_project
Available Flags:
-o : project output path (default: CURRENT_PATH)
-d : path of dependencies (default: COMPILE_PATH)
构建一个以protobuf
或者thrift
作为IDL的多协议RPC项目。
我们的client和server只需要保证以同样的协议进行通信,而其余的东西交给SPRC框架帮你处理就好,最终开发者接触到的就是我们的IDL所约定的接口。其中,支持的RPC协议包括:SRPC、SRPCHttp、BRPC、TRPC、TRPCHttp、Thrift、ThriftHttp。这些东西都可以在构建时通过参数指定。
我们执行./srpc rpc
,就可以看到rpc命令支持的参数:
我们尝试以默认方式构建一个RPC项目。也可以使用-f
指定IDL文件进行构建,这会使用srpc_generator
去进行代码生成。
./srpc rpc rpc_project
打开之后,可以看到和http命令相比,有如下区别:
- 多了一个
rpc_project.proto
- server_main.cc和client_main.cc分别变成了SRPCServer和SRPCClient;
- CMakeLists.txt也变复杂了,因为需要依赖protobuf和snappy等压缩库;
我们同样可以通过make
把项目编译出来,运行方式也与前面类似。
其他常用的参数中,值得一提的是-f
:指定要创建的RPC项目所依赖的proto文件。示例如下:
./srpc rpc rpc_example -f test_proto/test.proto
然后就可以看到一些生成代码多信息,这和原先使用srpc_genrator
时所看到的是类似的。
Info: srpc generator begin.
proto file: [/root/srpc/tools/rpc_example/test.proto]
Successfully parse service block [message] : EchoRequest
Successfully parse service block [message] : EchoResponse
Successfully parse service block [service] : new_test
Successfully parse method:Echo req:EchoRequest resp:EchoResponse
finish parsing proto file: [/root/srpc/tools/rpc_example/test.proto]
[Generator] generate srpc files: /root/srpc/tools/rpc_example/test.srpc.h
[Generator Done]
[Generator] generate server files: /root/srpc/tools/rpc_example/server_main.cc, client files: /root/srpc/tools/rpc_example/client_main.cc
Info: srpc generator done.
Success:
make project path rpc_example/ done.
Commands:
cd rpc_example/
make -j
Execute:
./server
./client
如果对IDL文件不熟悉的小伙伴,可以在执行rpc命令之前先生成一个默认的IDL,我们按如下命令,就可以生成默认为protobuf格式的my_proto_file.proto
:
./srpc api my_proto_file
Success:
Create api file my_proto_file.proto at path /root/srpc/tools/ done.
Suggestions:
Modify the api file as you needed.
And make rpc project base on this file with the following command:
./srpc rpc my_rpc_project -f my_proto_file.proto -p /root/srpc/tools/my_proto_file/
可以看到my_proto_file.proto
已经成功创建,Suggestions给出了命令可以让我们基于这个文件进一步构造我们的rpc项目,当然也可以先进行修改再构建项目。打开文件看到默认的rpc接口如下:
syntax="proto2";
message EchoRequest {
required string message = 1;
};
message EchoResponse {
required string message = 1;
optional int32 state = 2;
optional int32 error = 3;
};
service my_proto_file {
rpc Echo(EchoRequest) returns (EchoResponse);
};
这个命令主要用于构建redis协议的server和client,由于Workflow的协议对server和client来说都是对等的,因此基于Workflow实现的redis server依然非常简洁高效。
这个例子中,client发出的请求是set k1 v1
,server收到任何内容都回复一个OK
。并且client.conf中增加了用户名和密码的项,开发者可以通过修改配置,用这个client访问其他任意的redis server。
根据指引我们创建了一个项目后,就可以得到最简单的redis server和client。client就简单地实现了发送SET k1 v1
命令,而server无论收到什么都会简单地回复一个OK
。我们可以用这简单的示例,改造一个可以请求任何redis协议服务的client,也可以构造一个简单的redis服务器。
./server
Redis server start, port 6379
redis server get cmd: [SET] from peer address: 127.0.0.1:60665, seq: 0.
./client
Redis client state = 0 error = 0
response: OK
如果client有填写用户名和密码的需求,可以填到client.conf
中。我们打开这个配置文件看看:
1 {
2 "client":
3 {
4 "remote_host": "127.0.0.1",
5 "remote_port": 6379,
6 "retry_max": 2,
7 "user_name": "root",
8 "password": ""
9 }
10 }
代理服务器顾名思义,就是可以多构建一个proxy,我们可以用client去访问proxy,并由proxy去转发给server,这中间proxy就可以做很多事情,包括:更改协议、内容校验等等。
一个常见的场景是,我们的现有业务是客户端发出TRPC协议,而需要访问SRPC协议的服务器时,则可以构建出一个TRPC-SRPC的proxy,并且让大家使用统一的proto文件约定好请求,则proxy就可以直接做转发。
./srpc proxy
执行上述命令,我们可以看到proxy命令的指引:
Missing: PROJECT_NAME
Usage:
./srpc proxy <PROJECT_NAME> [FLAGS]
Example:
./srpc redis my_proxy_project
Available Flags:
-c : client type for proxy [ Http | Redis | SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http)
-s : server type for proxy [ Http | Redis | SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http)
-o : project output path (default: CURRENT_PATH)
-d : path of dependencies (default: COMPILE_PATH)
我们执行如下命令,用-c
指定client端的协议,用-s
指定server端的协议:
./srpc proxy srpc_trpc_proxy_example -c SRPC -s TRPC
Success:
make project path srpc_trpc_proxy_example/ " done.
Commands:
cd srpc_trpc_proxy_example/
make -j
Execute:
./server
./proxy
./client
查看新创建的项目中有什么文件:
cd srpc_trpc_proxy_example && tree
.
├── CMakeLists.txt
├── GNUmakefile
├── client.conf
├── client_main.cc
├── config
│ ├── Json.cc
│ ├── Json.h
│ ├── config.cc
│ └── config.h
├── proxy.conf
├── proxy_main.cc
├── server.conf
├── server_main.cc
└── srpc_trpc_proxy_example.proto
2 directories, 13 files
分别在三个终端执行./server
./proxy
和 ./client
,我们可以看到,client发送了trpc协议的请求"Hello, this is sync request!" 和一个异步请求Hello, this is async request!"给proxy,而proxy收到之后把请求用srpc协议发给了server。SRPC server填了回复"Hi back"并通过刚才的proxy路线转回给了client,期间转发纯异步,不会阻塞任何线程。
./server
srpc_trpc_proxy_example TRPC server start, port 1412
get req: message: "Hello, this is sync request!"
get req. message: "Hello, this is async request!"
./proxy
srpc_trpc_proxy_example [SRPC]-[TRPC] proxy start, port 1411
srpc_trpc_proxy_example proxy get request from client. ip : 127.0.0.1
message: "Hello, this is sync request!"
srpc_trpc_proxy_example proxy get request from client. ip : 127.0.0.1
message: "Hello, this is async request!"
./client
sync resp. message: "Hi back"
async resp. message: "Hi back"
其中proxy_main.cc
的实现,是起了一个TRPCServer,并使用SRPCClient去转发请求。感兴趣的小伙伴可以围观一下,其实SRPC项目的tutorial里也已经有这样的例子了:tutorial-15-srpc_pb_proxy.cc
文件服务器通过异步IO读取,也是我们常用的功能,这里不再赘述,对实现感兴趣的小伙伴欢迎查看原先的一篇文章:《Workflow编程小示例4: 转发服务器与series上下文的使用》
我们通过./srpc file file_project
构建一下项目,我们可以通过curl命令去读取想要的文件,例如curl localhost:8080/index.html
就可以读取到指定root目录下的index.html文件。
./srpc file file_project
Success:
make project path file_project/ done.
Commands:
cd ./file_project/
make -j
Execute:
./server
Try file service:
curl localhost:8080/index.html
curl -i localhost:8080/a/b/
可以看到,多了一个html
的目录,里边放了index.html
, 404.hmtl
和50x.html
。如果使用过其他Http服务器的小伙伴应该不陌生,这是常见的用法。
.
├── CMakeLists.txt
├── GNUmakefile
├── config
│ ├── Json.cc
│ ├── Json.h
│ ├── config.cc
│ └── config.h
├── file_service.cc
├── file_service.h
├── html
│ ├── 404.html
│ ├── 50x.html
│ └── index.html
├── server.conf
└── server_main.cc
3 directories, 13 files
打开server.conf
,就可以看到我们为文件服务器添加的具体配置项:root
和error_page
。我们可以通过root去指定打开文件的根目录,以及通过error_page去关联具体的错误码和它们所要返回作body的页面名称。而错误页面和其他文件一样,都是通过异步IO的方式读取,不会阻塞server当前的处理线程。
熟悉的error_page,它来了:
{
"server":
{
"port": 8080,
"root": "./html/",
"error_page" : [
{
"error" : [ 404 ],
"page" : "404.html"
},
{
"error" : [ 500, 502, 503, 504],
"page" : "50x.html"
}
]
}
}
我们执行make
进行编译,然后执行./server
把文件服务器跑起来,然后用curl
进行测试:
示例1:在根目录./html/
下读取文件index.html
(即使请求localhost:8080,默认也是读index.html)。
curl localhost:8080/index.html
<html>Hello from workflow and srpc file server!</html>
示例2:读文件/a/b/
,这个文件不存在,所以我们根据上面配置文件server.conf
中所指定的,填入404
错误码会返回页面404.html
的内容。
curl -i localhost:8080/a/b/
HTTP/1.1 404 Not Found
Server: SRPC HTTP File Server
Content-Length: 59
Connection: Keep-Alive
<html>This is not the web page you are looking for.</html>
以下信息在server端可以看到:
./server
http file service start, port 8080
file service get request: /a/b/
计算服务器也是通过go_task发起计算任务,不会阻塞当前线程。实现原理欢迎参考:《WF编程小示例6: 计算型服务器与计算任务》
./srpc compute compute_test
通过以上命令,我们可以创建一个小项目,项目默认接收url请求作为参数n,并进行斐波那契计算。
Success:
make project path compute_test/ done.
Commands:
cd compute_test/
make -j
Execute:
./server
Try compute with n=8:
curl localhost:8080/8
进入compute_test/
目录执行make
,并执行./server
运行起来。然后我们就可以使用curl或者浏览器输入localhost:8080/8
,计算第8个斐波那契数是多少。这里以curl
为例:
curl localhost:8080/8
<html><p>0 + 1 = 1.</p><p>1 + 1 = 2.</p><p>1 + 2 = 3.</p><p>2 + 3 = 5.</p><p>3 + 5 = 8.</p><p>5 + 8 = 13.</p><p>The No. 8 Fibonacci number is: 13.</p></html>
我们看到了server内部的计算步骤。server的计算例子使用了go_task去封装一个计算函数,欢迎尝试更多的计算调度。