配置一个Thrift 框架下Python做Server,C++做Client,Nginx做服务器的简单流程

       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官网:

   http://nginx.org/

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积分就好了,有些坑,希望官网能更正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值