Dec 20th, 2010
by sammychen.
上篇日志提到Thrift ThreadPoolServer有时候会出现较多的close wait状态,有朋友问我这是不是thrift的bug?写过Server比较多的同志们应该能意识到这个问题的原因,不值得说,可是我今天实在是太郁闷无聊了,我就写写我的想法吧。
我觉得这当然不能算是Thrift的Bug,如果出现了这样的问题,其实是因为错误的选择了Server的类型,错误的实现了Client,过于保守的Server Max Connection配置等等原因。
对于ThreadPoolServer而言,每一个客户端连接,Server端都需要提供一个固定的线程来维护,在空闲时,线程堵塞在read()操作,等待客户端数据的到来。Thrift ThreadPoolServer中使用的默认线程池是定长线程池,意味着Server端能提供的线程池数是有限的。当线程用完时,新的连接将不能得到Server殷勤的服务,它不会在乎你的生死,你必须等待。
举个例子:
我有一个用Thrift ThreadPoolServer(使用SimpleThreadPool)实现的Server,最大支持100个连接,现在有100个客户端连接到我的Server。实现客户端的程序员都很在乎TCP连接建立的开销,因此,他们都维护了一个长连接。这个时候,如果有第101个客户端连接到Server了,会发生什么情况呢?
Server会接受这个连接,连接成功建立;
Server没有合适的线程来处理这个连接,于是将这个连接放到暂存列表;
如果这个时候有线程空闲了,则一切顺利,这个线程将接管这个连接;
但遗憾的是,我们没有空闲线程,所以这个连接一直处于空闲状态,直到客户端程序timeout(如果设置了timeout的话);
连接timeout,意味着暂存列表里的连接已经失效了,此时对应的socket处于CLOSE_WAIT中(出现了本文开头的情况),遗憾的是,我们依然没有空闲的线程来处理这个连接,所以它一直处于CLOSE_WAIT中。
终于,某一个时刻,有一个客户端关闭了连接,我们有了空闲线程,它去查看暂存列表。发现有一个socket fd,尝试去接管它,对这个fd执行read(),然后得到一个Connection Reset error,终于,我们可以优雅的关闭它了(CLOSE_WAIT结束)。
以上就是全部的故事。
那么,要怎么办?
如果连接数太多,为什么不用NonBlockingServer呢?Thrift有基于libevent的实现,虽然它的ThreadPool限制了NonblockingServer的性能,但是,你可以方便的实现一个存/取线程更高效的ThreadPool.
你可以实现一个TimeoutCachedThreadPool来替代SimpleThreadPool.
提高Max Connection的值.
Aug 21st, 2010
by sammychen.
Thrift是一个非常棒的工具,是Facebook的开源项目,目前的开发非常的活跃,由Apache管理,所以用的是Apache Software License,这非常重要,因为可以放心的对其修改并用到自己的项目中。
谈到修改Thrift,这非常重要。因为我觉得如果要严肃的使用Thrift,不可避免的要深入了解它,并几乎都要修改Thrift的代码。一个通信框架,它不可能帮你做到所有的事情,也不可能在不了解的情况下就贸然的使用。
1
Thrift 的Java 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的话,可以看看这个东西。
May 7th, 2010
by sammychen.
准备工作:
了解thrift; http://incubator.apache.org/thrift/
了解protocol buffers;
八卦一则:据说Google早有protocol buffers, 有Goolge员工跳到Facebook,然后Facebook就有了thrift;
下载thrift
下载boost
安装:
./bootstrap.sh
./configure –with-boost=$BOOST_ROOT 相信很多人的机器上并没有Boost这个东西,那么您需要首先安装一下Boost.
您可能还会碰到
syntax error near unexpected token `MONO,’
可能缺少pkg-config,需要下载,编译,安装。否则请把pkg.m4拷贝到thrift下的alocal目录。
make
这个时候您很有可能还碰到 编译器抱怨libtools版本不够高,下载个最新版的libtools,编译安装吧。然后重来一遍。
sudo make install,这下应该好了。
Apr 21st, 2010
by sammychen.
组里开发一个文件系统的新项目,用C/C++编写,好久没有接触C/C++了,我已经变成了一个Java Coder了,又折腾了不少时间的Object C,看来要转变下大脑了。
RPC框架最后决定使用Thrift,Facebook提供的工具,看起来非常棒。
首页上的Wiki和Tutorial还都很挫,看起来使用Thrift的同学们对这个要求不高啊。