Thrift采坑记录——Client多线程

一、Thrift 采坑

  1. Thrift 的Server/Client有个较为严重的bug(https://issues.apache.org/jira/browse/THRIFT-601 ),随机向thrift sever的监听端口发些数据,可能会导致Server OutOfMemory,细细看看代码,这个bug有点土。

  2. Thrift Client线程不安全,多线程下使用可能导致Server和客户端程序崩溃。Client的每次调用远程方法其实是有多次Socket写操作,因此每个线程中使用的Client要保证独立,如果多个线程混用同一个Client(其实是用同一个Socket),可能会导致传输的字节顺序混乱,使得Server OutOfMemory(参考1)

  3. Thrift定义数据结构时,尽量避免用map, 或者set。在cpp下, map被对应为std::map(rb tree)和std::set,thrift生成的类不会重载”<”,因此需要手动修改生成类,否则link没法通过。较为麻烦。

  4. 如果Client端基于效率考虑,要缓存Socket,需要重新实现其TTransport类,以支持 Socket缓存池。当然,这个实现其实跟thrift没多大关系,算是2次开发。但一般都要这么做的吧?

  5. 如果Client基于效率考虑,缓存了Socket,那么thrift Server端的模式选择就较为重要了。如果使用同步的TThreadPoolServer,那么无可避免的,客户端缓存1个Socket,Server端就会有一个线程一直处于Server状态,等待peek这个Socket上的数据。这个线程就不能用于其它请求了。所以,及时清理Client端的Socket及控制Socket池的大小是非常必要的。

  6. 听同事说CPP Thrift Server的Epoll NonBlocking模式有效率问题。其实,并发要求不高的Server用LT模式的EPoll其实很方便的,当然,这个要自己给Thrfit Server做patch了,不过也不麻烦。开发起来也是很方便的。我想给我们的Server加个EPOLLONESHOT的同步EPoll实现。

  7. CPP下的 TThreadPoolServer和TThreadServer由一个有趣的问题,如果有客户端维护长连接,那么对这个Server实例做析构的时候会堵塞(前面说过了,在peek中…)。

  8. 用valgrind看,thrift cpp似乎有一些内存问题。没细看。

  9. 无论是Java,还是CPP,Server端都无法通过合法的方式获取Client的ip, port。可以通过编写ThriftServerEventHandler可以处理这件事情。如果想要获取Client ip, port的话,可以看看这个东西。

二、关于socket和多线程

https://www.zhihu.com/question/56899596
上面的链接讨论了 多线程下socket的设计方案

结合项目的使用,由于服务器采取的是单线程同步模式,因此必然客户端所有的socket都是基于tcp( TSocket
)
而对于 TCP,通常多线程读写同一个 socket 是错误的设计,因为多线程读写同一个socket极容易造成socket数据混乱,就算进行加锁读写,但是发生 short write,你是不是要一直等到整条消息发送完才解锁(无论阻塞IO还是非阻塞IO),如果这样,你的临界区长度由对方什么时候接收数据来决定,一个慢的 peer 就把你的程序搞死了。

所以现在将Thrift的client改成多socket模式,即每一个线程都new一个client(即一个socket),这样就不会出现并发问题了。

发布了69 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/JustKian/article/details/103824442