Thrift 框架下Python做Server,C++做Client的流程我在这篇blog:https://blog.youkuaiyun.com/lingyunxianhe/article/details/95766787
已经通过一个简单加法示例做了介绍在此不再详细说明了。这个示例只能在本地机器上模拟使用,不可以通过网页访问,现在就简单实现一下通过网页访问服务器的方式来远程Client请求Server的流程。
首先说下我的大致要做的事:通过网页远程Client一个请求加法,服务器Server收到请求后,启动计算函数计算,然后把结果通过json格式通过网页返还给并呈现在网页上。好了下面来看一下大致的流程把。
1 定义thrift接口文件
service AddOpt {string Add(1:i32 Num1,2:i32 Num2)}
在这里和前面有些不一样,我这是要返回json格式的字符串,所以定义为string,这里只是一个简单示例,使用json格式只是为方便网页传送数据。
2 生成gen-cpp 和gen-py具体接口函数文件
在终端执行下面语句:
thrift --gen py Calculator.thrift
thrift --gen cpp Calculator.thrift
下面这张图是gen-py文件夹下的文件
下面这张图是Calculator文件夹下的文件
下面这张图是gen-cpp文件夹下的文件
3 搭建Server端Python程序
Server端Python程序为Server.py,其代码如下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#2019/05/15 by DQ
import json
import sys
sys.path.append('gen-py') #加入接口文件
from Calculator import AddOpt
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer
class AddOptHandler(object):
def Add(self,Num1,Num2):#接口加法函数的最终实现
print 'Server receive Add request from Python'
Msg='Server Adding {}+{}\n'.format(Num1,Num2)
print (Msg)
return json.dumps(Num1+Num2)
if __name__ == '__main__':
handler = AddOptHandler()#服务端最终处理类
processor = AddOpt.Processor(handler)
transport = TSocket.TServerSocket(host='127.0.0.1', port=9090)#本地地址
tfactory = TTransport.TBufferedTransportFactory()
pfactory = TBinaryProtocol.TBinaryProtocolFactory()
server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)
# You could do one of these for a multithreaded server
# server = TServer.TThreadedServer(
# processor, transport, tfactory, pfactory)
# server = TServer.TThreadPoolServer(
# processor, transport, tfactory, pfactory)
print('Starting the server...')
server.serve()
print('done.')
4 搭建nginx 外部模块cpp程序(含有gen-cpp中的一些文件)
在当前目录下创建一个文件夹名为:AddOptNginx
在AddOptNginx文件夹下创建AddoptClient.cpp,为了方便管理文件,把上述产生的gen-cpp中的除AddOpt_server.skeleton.cpp其余都复制过来,最终AddOptNginx文件夹下的文件如图(注意conf现在还没生成,为了截图先这样把):
先上一段完整的cpp代码:
//2019/07/12 by DQ
//含有nginx的头文件要放在extern “C”{ }所在的{ }中
extern "C"
{
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
}
//thrift 库文件
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TTransportUtils.h>
//C++的一些库
#include <iostream>
#include <string>
//刚才生成的gen-cpp文件中的文件
#include "AddOpt.h"
//命名控件
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace std;
//预先申明一个字符数组
u_char ngx_AddOptResult_string[1000];
//最终实际处理请求的函数
static ngx_int_t ngx_http_AddOpt_handler(ngx_http_request_t *r){
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t out;
/* set the 'Content-type' header */
r->headers_out.content_type.len = sizeof("text/html") - 1;
r->headers_out.content_type.data = (u_char *) "text/html";
/* allocate a buffer */
b = (ngx_buf_t*)ngx_pcalloc(r->pool, sizeof(ngx_buf_t));//Me 注意前面的(ngx_buf_t*),c语言是可以不加的,但是c++不加在编译时会报错
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* attach buffer to the buffer chain */
out.buf = b;
out.next = NULL;
/*TCP/IP通信的一些接口 */
stdcxx::shared_ptr<TSocket> socket(new TSocket("127.0.0.1", 9090));
stdcxx::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
stdcxx::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
AddOptClient client(protocol);//我们定义的类,初始化一个对象
transport->open();
cout << "Client sent AddOpt request from Nginx C++"<<endl;
string AddOptResult;
//请求服务器操作,获取计算结果
client.Add(AddOptResult,10, 1);//这里需要特别注意,在Calculator.thrift文件中我们定义的是service AddOpt {string Add(1:i32 Num1,2:i32 Num2)}
transport->close(); //按理函数定义应该这样AddOptResult=client.Add(10, 1)才行,但是你去gen-cpp/AddOpt.cpp.cpp中看到却不是这样的, //好像性返回字符串的都是这样做的
cout<<"AddOptResult="<<endl<<AddOptResult<<endl;
ngx_memcpy(ngx_AddOptResult_string,AddOptResult.c_str(),AddOptResult.length());//要使用copy方法,不能直接赋值
/* adjust the pointers of the buffer */
b->pos = ngx_AddOptResult_string; /* the begin offset of the buffer */
b->last = ngx_AddOptResult_string + sizeof(ngx_AddOptResult_string) - 1; /* the end offset of the buffer */
b->memory = 1; /* this buffer is in memory */
b->last_buf = 1; /* this is the last buffer in the buffer chain */
/* set the status line */
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = sizeof(ngx_AddOptResult_string) - 1;
/* send the headers of your response */
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
/* send the buffer chain of your response */
return ngx_http_output_filter(r, &out);
}
char* ngx_http_AddOpt_setup(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){
ngx_http_core_loc_conf_t *clcf;
clcf = (ngx_http_core_loc_conf_t *)ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);//Me 注意前面的(ngx_http_core_loc_conf_t *),c语言是可以不加的,但是c++不加在编译时会报错
clcf->handler = ngx_http_AddOpt_handler; /* handler to process the 'CNNServer' directive */
return NGX_CONF_OK;
}
static ngx_command_t ngx_http_AddOpt_commands[] = {
{
ngx_string("NginxProjectForAddOpt"),//安装后,配置nginx.conf需要用到
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_AddOpt_setup,
0,
0,
NULL
},
ngx_null_command
};
static ngx_http_module_t ngx_http_AddOpt_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
//这个相当于C/C++中的main函数
ngx_module_t AddOptClientMain = {
NGX_MODULE_V1,
&ngx_http_AddOpt_module_ctx, /* module context */
ngx_http_AddOpt_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
char* ngx_http_AddOpt_setup(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){
ngx_http_core_loc_conf_t *clcf;
clcf = (ngx_http_core_loc_conf_t *)ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);//Me
clcf->handler = ngx_http_AddOpt_handler; /* handler to process the 'CNNServer' directive */
return NGX_CONF_OK;
}
static ngx_command_t ngx_http_AddOpt_commands[] = {
{
ngx_string("NginxProjectForAddOpt"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_AddOpt_setup,
0,
0,
NULL
},
ngx_null_command
};
static ngx_http_module_t ngx_http_AddOpt_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t AddOptClientMain = {
NGX_MODULE_V1,
&ngx_http_AddOpt_module_ctx, /* module context */
ngx_http_AddOpt_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
上述代码主要参考了4.2中的2),在编辑cpp文件时需要注意以下事项,这个我在代码中也有所注解。
4.1 Nginx的数据结构和运行流程可以参考nginx官网:
4.2 Nginx的c程序的流程可以参考:
1) https://kb.cnblogs.com/page/98352/
比较详细的讲解了nginx的配置,对其结构和理论做了总体阐述
2) http://www.hoverlees.com/blog/?p=352
比较详细的讲解了nginx的配置,添加一个模块需要的组成部分和作用,列 举了三个示例
4.3 Nginx的c++程序(含有gen-cpp中的一些文件)
1)Nginx的c++程序和上述Nginx的c程序无本质区别。
2)含有nginx的头文件要放在extern “C”{ }所在的{ }中,如:
extern "C"
{
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
}
3)可能需要的强制转化操作,如编译:
ngx_buf_t *b;
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
会错现:不能从ngx_buf_t类型到ngx_buf_t*,此时需要做下面操作强制转化:b = (ngx_buf_t*)ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
5 创建外部模块cpp程序conf文件
conf文件和cpp程序位于同一个目录,其格式比较固定,在此如下:
ngx_addon_name=AddOptClientModule #可以任意取
HTTP_MODULES="$HTTP_MODULES AddOptClientMain"#CPP文件中类似main函数的函数名
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/AddoptClient.cpp" #CPP文件
最终当前目录下我的文件结构如下图:
6 配置nginx(在此我使用nginx-1.16.0)的configure文件(主要是nginx安装目录和外加模块),生成objs文件夹
一般把nginx安装在/usr/local下,因此执行:
cd nginx-1.16.0
./configure --prefix=/usr/local/nginx --add-module=../AddOptNginx
7 修改objs文件夹下的Makefile文件使其同时能够编译、链接cpp文件和thrift生成的gen-cpp中的一些文件
7.1 修改Makefile文件的约前6行
CC = cc
CFLAGS = -pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -g #-Werror
CPP = cc -E
#LINK = $(CC)
CXX=g++ #新增C++编译器
CXXFLAGS=-pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -fpermissive -std=c++11 -L/usr/local/lib #新增C++编译选项,启用C++11
LINK=g++
为:
CC = cc
CFLAGS = -pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -g #-Werror
CPP = cc -E
#LINK = $(CC)
CXX=g++ #新增C++编译器
CXXFLAGS=-pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -fpermissive #新增C++编译选项,启用C++11
LINK=g++ #新增C++链接器
7.2 ALL_INCS部分末尾添加:
-I /usr/local/include/ \
-I /usr/local/include/thrift
7.3 objs/nginx 部分末尾找到objs/addon/你的外加模块文件夹名/AddoptClient.o 那一行,在其下面添加:
objs/addon/AddOptNginx/AddOpt.o \
objs/addon/AddOptNginx/Calculator_constants.o \
objs/addon/AddOptNginx/Calculator_types.o \
注意这部分 \ 后面不能有空格,我刚开始就是因为这个问题弄了好几次才找到编译通过了,如果有空格如我的问题:
objs/addon/AddOptNginx/Calculator_types.o \后面有空格,造成编译时出现:
make[1]: *** No rule to make target '\', needed by 'objs/nginx'。 停止
7.4 $(LINK)部分
1)在最开头添加:
-L/usr/local/lib
2)在末尾找到objs/addon/你的外加模块文件夹名/AddoptClient.o 那一行,在其下面添加:
objs/addon/AddOptNginx/AddOpt.o \
objs/addon/AddOptNginx/Calculator_constants.o \
objs/addon/AddOptNginx/Calculator_types.o \
3)替换末尾的(倒数第二行):
-ldl -lpthread -lcrypt -lpcre -lz
为:
-ldl -lpthread -lcrypt -lpcre -lz -lssl -lcrypto -lthrift
7.5 修改modules部分
在末尾找到objs/addon/你的外加模块文件夹名/AddoptClient.o : $(ADDON_DEPS) \ 这一行及其命令块,按照这个命令块的格式把上述6.3和7.4中类似这样的文件
objs/addon/AddOptNginx/AddOpt.o: $(ADDON_DEPS) \
../AddOptNginx/AddOpt.cpp
$(CXX) -c $(CXXFLAGS) $(ALL_INCS) \
-o objs/addon/AddOptNginx/AddOpt.o \
../AddOptNginx/AddOpt.cpp
objs/addon/AddOptNginx/Calculator_constants.o: $(ADDON_DEPS) \
../AddOptNginx/Calculator_constants.cpp
$(CXX) -c $(CXXFLAGS) $(ALL_INCS) \
-o objs/addon/AddOptNginx/Calculator_constants.o \
../AddOptNginx/Calculator_constants.cpp
objs/addon/AddOptNginx/Calculator_types.o: $(ADDON_DEPS) \
../AddOptNginx/Calculator_types.cpp
$(CXX) -c $(CXXFLAGS) $(ALL_INCS) \
-o objs/addon/AddOptNginx/Calculator_types.o \
../AddOptNginx/Calculator_types.cpp
至此,编译包含thrift文件的cpp的Makefile文件修改完毕
8 执行make命令编译、链接,生成nginx执行文件;make install 安装nginx
make
执行完毕一般会出现:
当看到这张图的类似情况时,说明nginx及外接模块已经编译好了,此时在objs下会生成一个nginx可执行文件
接下来就是安装一下,执行:
sudo make install
9 配置nginx安装位置下的nginx/conf/nginx.conf 文件及启动
9.1 配置nginx安装位置下的nginx/conf/nginx.conf 文件
进入/usr/local/nginx/conf
首先执行:
sudo chmod 777 ./nginx.conf
找到如下server中的location位置:
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
在原始的location上添加一个location块,具体为:
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
#我的块,和外加块的配置文件conf中的ngx_addon_name=AddOptClientModule
location /AddOptClientModule {
NginxProjectForAddOpt;#cpp程序中对应的
}
location / {
root html;
index index.html index.htm;
}
好了至此就完成nginx的编译和配置了,保存一下文件
9.2 启动配置的nginx
sudo /usr/local/nginx/sbin/nginx
10 网页端访问nginx,测试
最后就是通过网页来访问刚才配置的服务器及请求吧
10.1 启动Server端程序:
python AddOptServer.py
10.2 启动网页端请求程序
打开网页,输入:
http://192.168.2.160/AddOptClientModule
按enter键执行,然后页面上会出现:
而服务端则会出现:
好了一个完整的配置一个Thrift 框架下Python做Server,C++做Client,Nginx做服务器的简单流程大致如此,中间走过很多弯路,但终究是过来了。
整个工程的原始文件我打包放置在这里:https://download.youkuaiyun.com/download/lingyunxianhe/11421623
csdn现在好像不能自己设置资源积分,好像默认为5积分,不然设置为1积分就好了,有些坑,希望官网能更正。