Thrift多线程误用惨案

转自:http://blog.sina.com.cn/s/blog_98cf2a6f0101a1ob.html

Thrift个人觉得还是挺不错的一个rpc工具,相对而言比较容易上手。



其实断断续续接触thrift也有一年半了,没想到这次还是掉坑里了。由于之前大多是调用方情形,接触多线程下的thrift不多,也就是这不熟悉,导致了我一天的功夫全砸在了一个很低级的错误上面。


惨案背景:小文件存储系统基本功能开发完毕。控制器、系统对外接口、存储节点三大模块通过Thrift来进行跨机器远程通信。出于进行压力和并发测试的目的,写了个测试程序,多线程多线程同时进行文件存储。运行多次出现以下几点状况:
1、程序运行几十秒之后卡死
2、莫名滴偶尔有类似connection reset peer错误报出
3、程序崩溃


由于在存储节点端,对文件以及一些公共数据区域进行了加锁,总共四把,虽然用的都是局部锁,但毕竟不常用,心里对这个拿不准,加上程序运行卡死的现象,所以上来就往死锁的方面考虑。各种打印线程id和变量、跟踪程序等等。可是加锁前后区域输出均正常。无奈只好从最初起点传输端开始,逐步跟进。几经折腾,最后发现test_save函数中client->save(rsp, req);前后均正常,但到在server端偶尔会出现接受数据为空的现象,并且出现为空之后整个系统也就进入卡死状态了。很诡异啊,就这一句rpc调用,client端调用前后正常,到server端就出现数据不一致现象。我性了你的邪,搞毛东东啊。还好接触对她thrift不算陌生,也就没有往怀疑其工具可用性的方向走,而是继续老实巴交滴review自己的代码。最后发现还是自己too young too simple了。


原因:thrift client是非线程安全,多线程下使用可能导致server和客户端程序崩溃。client的每次远程方法调用其实是多次调用socket写操作,非原子操作。多线程交错使用,使从代码上看来是一次性完整的东西会出现错乱。所以必须从socket层次一开始就保证其独立性。这就是根本原因了。


出错具体代码如下:


boost::shared_ptr socket(new TSocket("192.168.3.223", 9090));
boost::shared_ptr transport(new TBufferedTransport(socket));
boost::shared_ptr protocol(new TBinaryProtocol(transport));
transport->open();


for (int i=0; i<20; i++) //开二十个线程
{
ManageFileClient* temp_client = new ManageFileClient(protocol);
char temp[100] = "\0"; 
sprintf(temp, "./get_%d.txt", i); 
string path = temp;
u_int32_t start_id = 100000 * i;
args[i].client = temp_client;
args[i].path = path;
args[i].start_id = start_id; 
pthread_create(&pt_id[i], NULL, test_save, &args[i]);
}


for (int i=0; i<20; i++)
{
pthread_join(pt_id[i], NULL);
printf("thread %d over\n", i);
}


error code分析:从表面来看,我在循环中new 出client对象,似乎是意识到了要保证其独立性的问题。可惜终究是个半吊子,因为上面已经说明,最根本在于socket对象的共享,问题是出在socket写错乱这里。错误代码中把socket声明放在循环外,这就注定悲剧了。


AC代码如下:
pthread_t pt_id[20];
save_args args[20];
for (int i=1; i<20; i++)
{
boost::shared_ptr socket(new TSocket("192.168.3.223", 9090));
boost::shared_ptr transport(new TBufferedTransport(socket));
boost::shared_ptr protocol(new TBinaryProtocol(transport));
transport->open();
ManageFileClient* temp_client = new ManageFileClient(protocol);


char temp[100] = "\0";
sprintf(temp, "./get_%d.txt", i);
string path = temp;
u_int32_t start_id = 100000 * i;
args[i].client = temp_client;
args[i].path = path;
args[i].start_id = start_id;
pthread_create(&pt_id[i], NULL, test_save, &args[i]);
}
for (int i=0; i<20; i++)
{
pthread_join(pt_id[i], NULL);
printf("thread %d over\n", i);
}


吃一堑长一智,想了想,其实这个坑不止存在于thrift,在涉及多线程socket读写的情形下,都是需要注意的问题。恩,记下来备忘。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值