您的位置:首页 > 编程语言 > C语言/C++

使用Python WSGI编写Web服务器前端,并利用Boost.Python在同一进程内连接到C++服务后端实现

2017-10-16 21:47 1461 查看
首先,是WSGI application文件:(这里假设请求是JSON字符串,而响应则是二进制数据流)

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框架下了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息