当前位置:首页 > CN2资讯 > 正文内容

python 服务器

3天前CN2资讯


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<bool> 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<bool> 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<tmms_json::MasterSrvUserInfoAndPhone> 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++是如何来接收请求的呢,还是得自己造轮子。先给出一张时序图:

这个轮子造起来就显得比较费时费力了,需要对boost库及常用Windows库函数比较熟悉才行。重要过程在时序图中已经作了说明。

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<my_dal::Device> 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<my_json::MasterSrvDeviceInfo>& devices) { …… std::vector<my_dal::Device> 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<my_dal::Device>& 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<err_str>(ex)); } catch (exception& e) { MY_INFO(L"DeviceRepository::SearchDevices -> get exception:"<<e.what()); } return false; }

搜索主语句,主分支进到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 ("<<endl << L" SELECT TOP "<<end_pos<< L" *, "<<endl << L" ROW_NUMBER() OVER( "<<order_str<<" ) as 'RowNo' "<<endl << L" FROM View_Device "<<endl << GetSearchDevicesWhereString(search_condition).str() << endl <<L") as A"<<endl <<L"where A.RowNo BETWEEN "<<start_pos + 1<<" AND "<<end_pos<<endl <<order_str<<endl; } else { sql << L"SELECT * "<<endl << L"FROM View_Device "<<endl << GetSearchDevicesWhereString(search_condition).str()<<endl <<order_str<<endl; } return sql; }

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

//拼接搜索条件 std::wostringstream DeviceRepository::GetSearchDevicesWhereString(my_dal::SearchDevicesCondition& search_condition) { std::wostringstream where_str; where_str << L"WHERE 1=1 "<<endl; if (!search_condition.device_name.empty()) { where_str<< L" AND Device_DeviceName LIKE N'%"<< SQLParameter::escapeSQL(search_condition.device_name, true) <<"%' "<<endl; } if (!search_condition.phone_number.empty()) { where_str<< L" AND Device_DevicePhoneNumber LIKE '%"<< _GUARD(search_condition.phone_number) <<"%' "<<endl; } if (!search_condition.description.empty()) { where_str<< L" AND Device_DeviceDescription LIKE '%"<< SQLParameter::escapeSQL(search_condition.description, true) <<"%' "<<endl; } if (!search_condition.user_name.empty()) { where_str<< L" AND Device_UserLDAPAccount LIKE N'%"<< SQLParameter::escapeSQL(search_condition.user_name, true) <<"%' "<<endl; } if (!search_condition.device_name_or_phone_number.empty()) { std::wstring local_device_name_or_phone_number = SQLParameter::escapeSQL(search_condition.device_name_or_phone_number, true); where_str<< L" AND (Device_DevicePhoneNumber LIKE '%"<< local_device_name_or_phone_number <<"%' "<<endl << L" OR Device_DeviceName LIKE N'%"<< local_device_name_or_phone_number <<"%') "<<endl; } …… return where_str; }

这样,就通过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 SearchNameOrPhone(generics.ListAPIView): serializer_class = PagedDeviceSerializer def get_queryset(self): page_index = self.request.query_params.get('page', None) page_size = self.request.query_params.get('page_size', None) name_or_phone = self.request.query_params.get('device_name_or_phone_number', None) search_result = Device.objects.filter(Q(Name=name_or_phone) | Q(PhoneNumber=name_or_phone)).order_by('Name') out_put_devices = [] for db_device in search_result: out_put_device = { 'description': db_device.Description, 'device_name': db_device.Name, 'email': User.objects.get(Id=db_device.UserId).Email, …… } out_put_devices.append(out_put_device) return out_put_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 resultdef 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社区发展也很蓬勃,对服务器初级开发人员来说非常友好。


    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/20542.html

    分享给朋友:

    “python 服务器” 的相关文章

    搬瓦工带防御:如何提升VPS安全性,抵御DDoS攻击

    搬瓦工VPS的基本介绍 搬瓦工(Bandwagon Host)作为一家知名的VPS提供商,以其稳定的网络连接和出色的性能赢得了众多用户的青睐。无论是个人网站搭建、企业应用部署,还是科学上网需求,搬瓦工VPS都能提供灵活且高效的解决方案。它的价格相对亲民,同时支持多种操作系统和自定义配置,满足了不同用...

    如何选择合适的Windows VPS服务: 实用指南与推荐

    在寻找合适的Windows VPS服务时,了解主要服务商的特点无疑是一个重要的步骤。市面上众多提供Windows VPS服务的商家中,vpsdime.com、raksmart.com、ion.krypt.asia以及bacloud.com等都是值得考虑的选择。这些服务商在多个地区运营,提供了不同版本...

    Windows SSH Client安装与配置指南

    在Windows 10版本1809及以后的版本中,微软引入了OpenSSH客户端,这让很多用户的远程管理变得更为便捷。作为一个IT爱好者,我发现这个特性非常有用,它让我能够轻松地通过SSH协议安全地连接和管理远程服务器。接下来,我将分享一些Windows SSH客户端的安装和配置过程,方便大家快速上...

    宝塔安装全攻略:轻松管理你的服务器与网站

    宝塔面板,凭借其简单易用的特性,已经成为很多用户搭建和管理网站的首选工具。作为一款开源的服务器管理软件,宝塔面板提供了丰富的功能和灵活的操作方式,让无论是新手还是经验丰富的用户都能轻松上手。我在使用宝塔面板的过程中,深刻体会到它带来的便利和高效。 功能与特点 宝塔面板最大的一大优势在于其直观的用户界...

    搬瓦工Plan v2:高性价比VPS套餐详解与用户指南

    搬瓦工Plan v2作为一个限量版VPS套餐,给很多用户带来了新的选择。与之前的The Plan套餐相比,Plan v2在配置与流量方面都实现了显著的提升。这款套餐不仅是一种实用的解决方案,也为不同需求的用户提供了灵活的选择。接下来,我将分享一些关于这个套餐的背景信息、主要升级点以及它适合哪些用户。...

    BBR加速开启:提升网络性能的最佳实践

    什么是BBR? 在网络领域,BBR(Bottleneck Bandwidth and Round-trip propagation time)是一个备受关注的TCP拥塞控制算法,由Google开发的这一技术,旨在提升网络连接的传输速率和稳定性。BBR独特之处在于,它通过实时监测数据包的传输时延与丢包...