基于c++和基于python的服务器后台的一些比较

基于c++和基于python的服务器后台的一些比较,第1张

基于c++和基于python的服务器后台的一些比较 1、写在开始

这一年以来,在服务器后台做了大量工作。到11月中旬,我们用python重写的c++服务器终于正式发版了,也算是对这一年的交代。从以后的工作规划来看,几无接触到c++/python服务器后台的可能了。我们的代码主要采用的框架是c++/boost vs python/Django/REST。c++的代码应该是10年前的,能在那个时候使用boost库来写生产环境的服务器代码,实在令人敬佩。不久前无意中了解到,boost网络编程库还是没有被纳入c++标准库,这不得不说是一种遗憾。而反观Django社区,则出现了蓬勃发展的态势,知名网站如国外的Instagram、Mozilla,国内的诸如搜狐、豆瓣、头条等。Django下REST framework是一个用于构建Web API的功能强大且灵活的工具包,在Django下使用REST能极大的方便web开发。

2、轮子

开发服务器后台有许多框架层的工作,诸如:日志模块、json序列化模块、国际化模块等等。以json序列化模块为例,c++就不得不自己造轮子。

2.1 c++的json序列化

在我们的c++代码中,使用一个ruby脚本来做json数据的序列化和反序列化。需要遵循项目自定义的一些规则及步骤:

2.1.1 事先定义好数据及格式
struct MasterSrvUserInfoAndPhone {
	// @json_serialization_object;
	std::string id; // @json_serialization_field ;
	std::string user_name; // @json_serialization_field ;
	std::string email; // @json_serialization_field ;
	std::string phone_number; // @json_serialization_field ;
	int device_number; // @json_serialization_field ;
	time_t last_invite_time; // @json_serialization_field ;
};

	bool Serialize(const tmms_json::MasterSrvUserInfoAndPhone& obj_val, Json::Value& json_val);
	bool Deserialize(const Json::Value& json_val, tmms_json::MasterSrvUserInfoAndPhone& obj_val);
2.1.2 调用ruby脚本生成具体序列化及反序列化的代码
bool Serialize(const MasterSrvUserInfoAndPhone& obj_val, Json::Value& json_val) {
    std::vector ret_vector;

    ret_vector.push_back(Serialize(obj_val.id, "id", json_val));
    ret_vector.push_back(Serialize(obj_val.user_name, "user_name", json_val));
    ret_vector.push_back(Serialize(obj_val.email, "email", json_val));
    ret_vector.push_back(Serialize(obj_val.phone_number, "phone_number", json_val));
    ret_vector.push_back(Serialize(obj_val.device_number, "device_number", json_val));
    ret_vector.push_back(Serialize(obj_val.last_invite_time, "last_invite_time", json_val));

    return tmms_json::Passed("tmms_json::Serialize", typeid(obj_val).name(), ret_vector);
}

bool Deserialize(const Json::Value& json_val, MasterSrvUserInfoAndPhone& obj_val) {
    std::vector ret_vector;

    ret_vector.push_back(Deserialize(json_val, "id", obj_val.id));
    ret_vector.push_back(Deserialize(json_val, "user_name", obj_val.user_name));
    ret_vector.push_back(Deserialize(json_val, "email", obj_val.email));
    ret_vector.push_back(Deserialize(json_val, "phone_number", obj_val.phone_number));
    ret_vector.push_back(Deserialize(json_val, "device_number", obj_val.device_number));
    ret_vector.push_back(Deserialize(json_val, "last_invite_time", obj_val.last_invite_time));

    return tmms_json::Passed("tmms_json::Deserialize", typeid(obj_val).name(), ret_vector);
}

这样,json数据序列化及反序列化就做好了,使用的时候如下:

	std::vector users_vec;
	tmms_json::MasterSrvUserInfoAndPhone user;
	……
2.2 Django的json序列化

Django下采用REST framework的话,json序列化就变得极其简单起来。

class DeviceLocationSerializer(serializers.ModelSerializer):
    class meta:
        model = Device
        fields = ['Latitude', 'Longitude', 'LocationUpdateTime']']

按照REST文档,model对应于数据库表名,fields 表明了需要进行序列化的字段,使用的时候如下:

……
serialized_device = DeviceLocationSerializer(device)
……

这样就得到了经过序列化之后的数据,通常是list类型的数据。

2.3 c++的请求接收

c++是如何来接收请求的呢,还是得自己造轮子。要自己创建服务器进程,监听相应端口来实现响应。

2.4 Django的请求接收

根据文档,Django框架层已经做了请求接收,只需直接处理请求即可。具体来说,在url.py文件中定义正则表达式,请求就会被转发到相应接口。

urlpatterns = [
    url(r'^login/$', LoginView.as_view(), name='user-login'),
    url(r'^logout/$', LogoutView.as_view(), name='user-logout'),
……
]

相应的类方法LoginView就可以处理类似http:/login这样的请求。

3、业务处理

列举几个接口来比较两种语言下web后台是如何处理请求的。

3.1 一个删除接口 3.1.1 c++的处理
int my_universal_mdm::DeleteDevices(void *ctx, std::string& is, std::string& os)
{
    std::vector in_data;
    my_json::DeleteDevicesResponse out_data;
    try
    {
    	//转换为json字符串
        FROM_JSON_STRING(is, in_data);
		//遍历数据
        BOOST_FOREACH(my_dal::Device& device, in_data)
        {
        	//查询数据库得到指定device
            if (CODE_SUCCESS != device_manager.GetDevice(ctx, device.id, device))
            {
                RETURN(out_data, os, CODE_ERR_ACCESS_DB_FAIL);
            }
			//删除指定device
            if (CODE_SUCCESS != device_manager.DeleteDevice(ctx, device))
            {
                RETURN(out_data, os, CODE_ERR_ACCESS_DB_FAIL);
            }
        }
        TO_JSON_STRING(out_data, os);
    }
    catch(...)
    {
        RETURN(out_data, os, CODE_ERR_FAIL);
    }
    return out_data.error_code;
}
3.1.2 python的处理
class DeleteDevices(APIView):
    def post(self, request):
        device_ids = self.request.data['data']
        #直接获取数据库对象删除数据
        Device.objects.filter(Id__in=device_ids).delete()
		……
        return general_response(status.HTTP_200_OK, ErrorCode.SUCCESS)

c++代码还有一层一层的封装,固然和架构有些关系,这当然不能说明全部问题;不可否认,python代码比c++代码简洁了很多数量级,区区几行代码就搞定了c++代码上百行代码才能完成的功能。

3.2 一个搜索接口 3.2.1 C++的实现

首先给出主体代码,搜索符合条件的设备,序列化之后返回结果。

//返回搜索到符合条件的设备
int DeviceManager::SearchDevices(void *ctx,my_dal::SearchDevicesCondition& search_condition,
                                                                        int& total_count,
                                                                        std::vector& devices)
{
	……
    std::vector db_devices;
    if (!device_service->SearchDevices(search_condition, total_count, db_devices))
    {
        return    ERR_ACCESS_DB_FAIL;
    }
    
    BOOST_FOREACH(my_dal::Device& db_device, db_devices)
    {
        my_json::MasterSrvDeviceInfo device;
        device.id = to_utf8(db_device.id);
        device.device_name = to_utf8(db_device.name);
        device.phone_number = to_utf8(db_device.phone_number);
        device.email = to_utf8(db_device.user.email);
        device.description = to_utf8(db_device.description);

        devices.push_back(device);
    }
    return MDM_SUCCESS;
}

搜索的详细过程,

//序列化搜索到的设备
bool DeviceRepository::SearchDevices(my_dal::SearchDevicesCondition& search_condition,
                                     int& total_count,
                                     std::vector& devices)
{
    try
    {
        total_count = 0;
        std::wostringstream sql = GetSearchDevicesSqlString(search_condition);

        CADORecordSet recordset;
        recordset.Open(connection_, sql.str(), adOpenForwardOnly, adLockReadOnly);

        CADORecordMemo record;
        devices.clear();
        while (!recordset.IsEOF())
        {
	        recordset.GetCurrent(record);
	        my_dal::Device device;
	        MappingDevice::Deserialize(record, device, L"Device_Description");
	        MappingDevice::Deserialize(record, device.agent, L"Device_Name");
	        MappingDevice::Deserialize(record, device.user, L"Device_Email");
	        MappingDevice::Deserialize(record, device, L"Device_PhoneNumber");

            if (IsValidAgent(search_condition, device.agent))
            {
                devices.push_back(device);     
            }      
		    recordset.MoveNext();
        }
		……
        return true;
    }
	catch (const sql_exception& ex) {
		MY_ERROR(L"DeviceRepository::SearchDevices -> illegal parameter: " << *boost::get_error_info(ex));
	}
    catch (exception& e) {
        MY_INFO(L"DeviceRepository::SearchDevices -> get exception:"< 

搜索主语句,主分支进到if语句,这里搜索的结果是返回符合查询条件的结果,并将搜索结果分页,返回指定页码的数据。这里的分页技巧有赖于SQL实现,如果对SQL高级语句不太熟悉的话,其实理解这段搜索语句还是存在一定困难。

//搜索主语句
std::wostringstream DeviceRepository::GetSearchDevicesSqlString(my_dal::SearchDevicesCondition& search_condition)
{
         size_t start_pos = search_condition.paging_info.page_index * search_condition.paging_info.page_size;
         size_t end_pos = start_pos + search_condition.paging_info.page_size;
         wstring order_str = L"ORDER by Device_DeviceName";
         std::wostringstream sql;
          if (!search_condition.paging_info.Empty())
          {
                sql << L"SELECT * from ("< 

搜索场景分为简单搜索和高级搜索。简单搜索只支持名字及电话号码,高级搜索支持各个维度的搜索。给出了代码示例,其实这部分代码有600多行。

//拼接搜索条件
std::wostringstream DeviceRepository::GetSearchDevicesWhereString(my_dal::SearchDevicesCondition& search_condition)
{
        std::wostringstream  where_str;
        where_str << L"WHERe 1=1 "< 

这样,就通过c++实现了搜索接口,这个接口整合了简单及高级搜索。

3.2.2 python实现

笔者将简单搜索和高级搜索拆分成了两个接口。这样代码的可读性会强一些。

#分页的实现类,有赖于Django框架的Paginator
class CustomSearchDevicePagination(pagination.PageNumberPagination):
    def get_paginated_response(self, data):
        return Response({
            'links': {
                'next': self.get_next_link(),
                'previous': self.get_previous_link()
            },
            'count': self.page.paginator.count,
            'results': data
        })
       
#简单搜索的实现
class SearchDevices(generics.ListAPIView):
    serializer_class = PagedDeviceSerializer
    pagination_class = CustomSearchDevicePagination

    def get_queryset(self):
        group_id = self.request.query_params.get('group_id', None)
        page_index = self.request.query_params.get('page', None)
        page_size = self.request.query_params.get('page_size', None)
        devices = Device.objects.filter(GroupId=group_id)
        out_put_devices = []
        for db_device in devices:
        	#直接组装返回数据,避免了繁复的序列化
            out_put_device = {
                'description': db_device.Description,
                'device_name': db_device.Name,
                'email': User.objects.get(Id=db_device.UserId).Email,
                'phone_number': db_device.PhoneNumber,
            }
            out_put_devices.append(out_put_device)
        paginator = Paginator(out_put_devices, page_size)
        try:
            paged_devices = paginator.page(page_index)
        except InvalidPage:
            logger.debug(InvalidPage)
            return general_response(status.HTTP_400_BAD_REQUEST, ErrorCode.FAILURE)
            
        if devices is None:
            logger.debug("error get devices")
            
        return paged_devices

可以看到,虽然只是简单搜索的实现,python代码也简洁了很多。这里,email结果的返回直接查询了数据库。如果设置了主外键关系,在rest framework下还有更简单的实现。

#高级搜索的实现
class AdvancedSearch(generics.ListAPIView):
    serializer_class = PagedDeviceSerializer

    def get_queryset(self):
        condition = self.request.query_params.get('data', None)
        page_index = condition.get('paging_info').get('page_index')
        page_size = condition.get('paging_info').get('page_size')
        sql = ''
        #拼接查询条件
        if 'device_name' in condition:
            sql += "AND Name LIKE " + "'" + condition.get('device_name') + "'"

        if 'phone_number' in condition:
            sql += " AND PhoneNumber LIKE " + "'" + condition.get('phone_number') + "'"

        if 'description' in condition:
            sql += " AND Description LIKE " + "'" + condition.get('description') + "'"
		……
        devices_set = DeviceManager().advanced_search(sql)
        out_put_devices = []
        for db_device in devices_set:
            out_put_device = {
                'description': db_device['Device_DeviceDescription'],
                'device_name': db_device['Device_DeviceName'],
                'phone_number': db_device['Device_DevicePhoneNumber'],
            }
            out_put_devices.append(out_put_device)
        return out_put_devices

Django不支持数据库view,这里强行“构造”了view:

    def advanced_search(self, condition):
        sql = view_device_sql()
        sql += " WHERe 1=1 " + condition
        with connection.cursor() as cursor:
            cursor.execute(sql)
            rows = cursor.fetchall()
            col_names = [desc[0] for desc in cursor.description]
            result = []
            # dump raw data to dict
            for row in rows:
                objDict = {}
                for index, value in enumerate(row):
                    objDict[col_names[index]] = value

                result.append(objDict)

            return result
def view_device_sql():
    sql = "SELECt     device_device.Id AS Device_DeviceId, 
            device_device.Name AS Device_DeviceName,
            device_device.PhoneNumber AS Device_DevicePhoneNumber, 
            device_device.Description AS Device_DeviceDescription,
           ……
          FROM   device_deviceandroidextension RIGHT OUTER JOIN 
                      device_device LEFT OUTER JOIN 
                      device_user ON device_device.UserId = device_user.Id LEFT OUTER JOIN 
                      ……"
    return sql
4、总结

用c++来做服务器后台确实有诸多不便,可移植性较差。且为了实现灵活部署,原有的代码还做了好多模块化开发,安装路径下有许多.dll文件,追踪这些.dll文件间传递的数据对没有Windows开发的程序员来说也不容易。python先天就具有跨平台的特性,语言本身还有许多令人振奋的特性,诸如list切片等,Django社区发展也很蓬勃,对服务器初级开发人员来说非常友好。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5703196.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存