[关闭]
@JaySon 2016-08-28T09:16:41.000000Z 字数 2163 阅读 620

最近在画像系统中遇到的一些问题

腾讯


spp-微线程的使用

KM上的介绍: 微线程框架原理介绍

栈空间

栈空间只有128K(虽然有接口可以调大栈空间), 如果需要创建大的局部变量, 可以用new申请堆空间(注意释放内存),或者用std::string, std::vector等辅助(STL内部是使用new申请的堆空间,且已经做好了内存管理,不过可能存在一定的复制开销)

收发包

微线程中尽量少用static变量,除非是使用const static。
因为微线程切换上下文的时候,可能导致static中的变量不可信。

微线程本身是异步非阻塞的,尽量不要在其中自己使用tcp/udp收发包,阻塞的网络IO会导致spp并发量减少,不能最大程度发挥spp的性能。利用框架内的mt_tcpsendrcv/mt_udpsendrcv来进行网络IO阻塞的请求,框架会在阻塞的时候切换到另外的微线程(协程)处理,从而提高并发处理量。

spp-redis的使用

hiredis中的API内使用的是阻塞的模式。
但是hiredis中有单纯对包进行格式化的函数可以利用,KM上已经有人结合hiredis对包格式化的函数与spp微线程的异步IO请求,但只有GET/SET/Expire, HSET/HGET/HGETALL未实现,代码质量也挺好,可以在其上进行扩展使用。
spp微线程封装高性能redis-client API

l5使用

部分代码中存在使用l5获取IP:port之后马上调用ApiRouteResultUpdate,这是错误的用法。L5用法
利用l5做负载均衡,需要上报的是跟IP:port之间交互总耗时,l5用于评估server的负载,再做负载均衡。
获取了IP:port之后马上调用ApiRouteResultUpdate,会让l5一直认为通讯的server一直处于低负载。
另外l5连接时传入的超时时间是float型的秒,而不是毫秒

api设计存在的问题

高度耦合

有两个类之间的设计如下

  1. class A {
  2. void sendXX() {
  3. B b* = getBPtr();
  4. b->filterYY(this, ...); // 把this传入B, 导致两个类高度耦合,而且在编译时出现循环依赖
  5. }
  6. void sendYY() {...}
  7. };
  8. class B {
  9. int filterYY(A* pa, ...) {
  10. if (xx) {
  11. // ...
  12. } else {
  13. pa->sendYY(); // 类A类B高度耦合
  14. }
  15. }
  16. };

面向对象的一个设计思想就是一个类的一个函数只做好一件事,类B的filterXX函数本来只应判断是否需要过滤,通过返回值告知caller该数据是否需要过滤。
但是实现中把类A的sendYY的功能也放到filterXX中做了,结果需要A传入this指针,一方面导致了编译的循环依赖(虽然可以通过前置声明来解决),另外一方面导致代码结构混乱(本来收发包的函数应该归拢在类A甚至更上面一层,类B只是做数据解析的工作)。

代码接口反人类

  1. int isExist(...) {
  2. if (...) {
  3. return -1; // 不存在
  4. } else {
  5. return 0; // 存在
  6. }
  7. }

结果导致外面调用的时候出现

  1. if ( isExist(xx) == 0 && isExist(yy) == 0) {
  2. // do sth
  3. }

看到这样的代码第一反应是xx,yy两者都不存在的时候才doSth,但是实际上是完全相反的情况。

monitor/LOG的使用

定位问题时可以加 monitor/LOG 来对问题进行定位,但是定位完了之后,需要清理冗余的上报/LOG的清理。毕竟monitor/ERROR_LOG是用于监控是否异常的工具而不是debug的工具。
monitor/ERROR_LOG过多,会导致问题早就暴露了但被淹没在信息海中,不被发现。

protobuf

实测protobuf序列化之后的二进制数据中可能含有\x00的字符,如果想使用std::string存储protobuf字符串的话,构造函数时不能使用string(const char *)的构造方法,要使用string(const char* s, size_t n)的方法指定大小。
取出时使用str.data()结合str.size()使用。不要使用str.c_str()与strlen(cstr)

小结

虽然大数据处理的瓶颈不在于实时系统,但是一个代码结构不清晰的系统是难以维护&扩展的。早晚会造成疲于奔命处理bug的状态。
比较好的C++代码实践,可以参考《Effective C++》和Google的C++编码规范
《More Effective C++》比较老旧,其中涉及到的一些坑在后面的C++标准中被修正,其最佳实践也变了。虽然有"More",但个人感觉看《Effective C++》的第三版足矣。
《Modern Effective C++》主要介绍C++11对C++的一些填坑以及良好的代码实践,目前C++11难以推行,可以不读。

腾讯在基础组件、框架上有多年的积累,不要放弃这些积累从原点开始跑,使用之前阅读相关的文档,经验。
当然使用之前要review一下代码,看其是否适用、稳定。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注