使用Python WSGI编写Web服务器前端,并利用Boost.Python在同一进程内连接到C++服务后端实现
2017-10-16 21:47
1461 查看
首先,是WSGI application文件:(这里假设请求是JSON字符串,而响应则是二进制数据流)
其次,是C++ Server类代码:
这里略去"routeserver.h"头文件,现在是boost.python wrapper文件:
注意这里有2个问题:Python的bytes对象传递到C++,映射到参数std::string类型没有问题。但是C++的结果类型std::string传递到Python就变成了Unicode编码的str。这是Boost.Python-1.55 + Python-3.6下默认数据类型绑定的一个局限。这里的解决办法就是把C++的std::vector<byte>映射为Python的bytes类型。
OK,接下来是构建文件,这里使用SConstruct:
这里费了一些功夫,坑:
(1)Boost.Python-1.55的官方二进制编译版本默认是链接到Python-2.7的,需要从源代码编译(链接到Python-3.6 32位版)
(2)敲scons命令之前,先用VS2013的x86 command tools进命令行环境,然后注意设置TARGET_ARCH='x86', 这才能保证2个cpp文件编译后的obj文件是x86架构的,否则在Windows 64位上,默认是x64,从而导致后续link时报错;
(3)CPPDEFINES=['BOOST_PYTHON_STATIC_LIB'], 指的是使用boost.python静态库,——不知道为什么默认是动态库链接,并且boost会报错,见鬼
OK,接下来,就大同小异了:
把编译出来的dll文件后缀名改为pyd。代码就可以走通了。加上一个main.py来驱动:
遗留未解决问题:
即使Python WSGI的application的每个请求处理都是运行在threading多线程模式,但是由于GIL,是不是说实际上没有并发?这样可不行。为了高性能的话,就只能把C++代码包装在gRPC的rpc server框架下了
import json import routeserver RS = routeserver.RouteServer(b'not used') #全局python对象(对接到C++对象实例) def routeserver_application(environ, start_response): #首先,取出request body里的JSON字符串:REQUEST_METHOD method = environ['REQUEST_METHOD'] path = environ['PATH_INFO'] qs = environ['QUERY_STRING'] content_type = environ['CONTENT_TYPE'] content_length = environ['CONTENT_LENGTH'] print("method={} path={} qs={} content_type={} content_length={}".format(method, path, qs, content_type, content_length)) #for debug only; request_body = b'{"a":1, "b":"str", "c":[1,2,3,4]}' #test if content_length and int(content_length)>0: request_body = environ['wsgi.input'].read(int(content_length)) print("request_body={}".format(request_body)) request_json = json.loads(request_body) print("request_json=", json.dumps(request_json, sort_keys=True, indent=4)) #TODO: pass request_body to C++ side via Boost.Python and get response data back result = RS.routeRequest(request_body) print(repr(result)) #OK, 数据正确传出 print(result.__class__) #class 'str' #将它dump到文件(调试查看): status = '200 OK' headers = [('Content-type', 'application/octet-stream'),('Content-type', str(len(result)))] write = start_response(status, headers) write(result) return [] #额外的字符串数组,如果是输出template格式化的html,这个特性比较有用
其次,是C++ Server类代码:
#include "routeserver.h" #include<string> #include<iostream> byte_array RouteServer::routeRequest(std::string json_input) { std::cout<<"RouteServer::routeRequest: json_input="<<json_input<<std::endl; byte_array result; for(const char* p = "test"; *p; ++p) result.push_back(*p); //逐个字节复制? return result; }
这里略去"routeserver.h"头文件,现在是boost.python wrapper文件:
#include "boost/python.hpp" #include "routeserver.h" using namespace boost::python; PyObject* byte_array_to_PyBytes_convert( byte_array const&ba ) { const char* src = ba.empty() ? "" : reinterpret_cast<const char*>( &ba.front() ); return PyBytes_FromStringAndSize( src, ba.size() ); } struct ByteArray2PyBytesConverter { static PyObject* convert(const byte_array &ba) { return byte_array_to_PyBytes_convert(ba); } }; BOOST_PYTHON_MODULE(routeserver) { to_python_converter<byte_array, ByteArray2PyBytesConverter>(); class_<RouteServer>("RouteServer", init<std::string>()) .def("routeRequest", &RouteServer::routeRequest) ; }
注意这里有2个问题:Python的bytes对象传递到C++,映射到参数std::string类型没有问题。但是C++的结果类型std::string传递到Python就变成了Unicode编码的str。这是Boost.Python-1.55 + Python-3.6下默认数据类型绑定的一个局限。这里的解决办法就是把C++的std::vector<byte>映射为Python的bytes类型。
OK,接下来是构建文件,这里使用SConstruct:
BASE_PYTHON_DIR = 'C:/Boost/include/boost-1_55' env = Environment(TARGET_ARCH='x86', MSVC_VERSION='12.0', #VS2013 CPPPATH='.;'+BASE_PYTHON_DIR+';C:/Python36-32/include', CPPDEFINES=['BOOST_PYTHON_STATIC_LIB'], CXXFLAGS=['/EHsc', '/MD'], LIBPATH='C:/Boost/lib;C:/Python36-32/libs', LIBS=[''], # Boost会在.h代码中用#pragma指令指定要连接的lib名称; LINKFLAGS=['/machine:X86'] ) env.SharedLibrary('routeserver', ['routeserver.cpp', 'routeserver_wrapper.cpp'])
这里费了一些功夫,坑:
(1)Boost.Python-1.55的官方二进制编译版本默认是链接到Python-2.7的,需要从源代码编译(链接到Python-3.6 32位版)
(2)敲scons命令之前,先用VS2013的x86 command tools进命令行环境,然后注意设置TARGET_ARCH='x86', 这才能保证2个cpp文件编译后的obj文件是x86架构的,否则在Windows 64位上,默认是x64,从而导致后续link时报错;
(3)CPPDEFINES=['BOOST_PYTHON_STATIC_LIB'], 指的是使用boost.python静态库,——不知道为什么默认是动态库链接,并且boost会报错,见鬼
OK,接下来,就大同小异了:
把编译出来的dll文件后缀名改为pyd。代码就可以走通了。加上一个main.py来驱动:
x# 使用Python3内置的WSGIServer实现,产品环境可以使用uWSGI, Nginx+mod_wsgi之类高性能的部署环境 from wsgiref.simple_server import make_server import routeserver_application with make_server('', 8000, routeserver_application.routeserver_application) as httpd: print("Serving Route Server on port 8000...") httpd.serve_forever()
遗留未解决问题:
即使Python WSGI的application的每个请求处理都是运行在threading多线程模式,但是由于GIL,是不是说实际上没有并发?这样可不行。为了高性能的话,就只能把C++代码包装在gRPC的rpc server框架下了
相关文章推荐
- 使用Python编写免安装运行时、以Windows后台服务形式运行的WEB服务器
- 利用C++ Boost编写扩展Python模块
- 利用python2.7自带的简单的web服务器SimpleHTTPServer实现web页面的访问
- 构建简单的 C++ 服务组件,第 2 部分: 通过服务组件体系结构使用 Python、Ruby 和 Web 服务
- 使用Python或Node创建简单web服务器和FTP服务器实现文件共享
- 使用nginx搭建前端项目web服务器以及利用反向代理调试远程后台接口
- 利用Python 1分钟搭建测试Web服务器,可实现linux目录文件共享
- 使用sshtunnel实现python公网连接阿里云mongo服务器
- Python(27)使用python的http,cgi模块实现一个简单的web服务
- 使用boost实现c++与python的相互调用
- python:使用web.py实现最简易的web服务器
- python利用面向对象,协程开发web后端服务器
- 使用Python 2.7中pycurl模块编写探测多节点Web服务质量脚本
- 不使用Web服务器编写SOAP服务程序相关联接
- 使用boost实现python调用c++
- 利用Boost.Python实现Python C/C++混合编程
- 在Mac OS上使用mod_wsgi连接Python与Apache服务器
- 使用uWSGI Web服务器和Nginx部署Python WSGI应用
- 在Mac OS上使用mod_wsgi连接Python与Apache服务器