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读写的情形下,都是需要注意的问题。恩,记下来备忘。

thrift客户端调用不支持多线程,非线程安全相关推荐

  1. 【java的socket编程】结合多线程Thread实现通信(使用线程池和非线程池对比)、java开发UDP/IP网络程序

    结合多线程实现socket 使用非线程池(拓展Thread) 使用线程池(Executor pool) 使用DatagramPacket DatagramSocket开发UDP/IP程序 使用UDP获 ...

  2. Java多线程:线程安全和非线程安全的集合对象

    转载自  Java多线程:线程安全和非线程安全的集合对象 一.概念: 线程安全:就是当多线程访问时,采用了加锁的机制:即当一个线程访问该类的某个数据时,会对这个数据进行保护,其他线程不能对其访问,直到 ...

  3. 关于MFC下多线程,在线程中创建非模态对话框以及消息传递

    2019独角兽企业重金招聘Python工程师标准>>> 关于MFC下多线程,在线程中创建非模态对话框以及消息传递 问题: 1.在子对话框中它的子对话框需要实现脱离父对话框的束缚.(因 ...

  4. VC++中多线程学习(MFC多线程)一(线程的创建、线程函数如何调用类成员呢?如何调用主对话框的成员?、MFC中的工作线程和界面线程的区别)

    这里废话不多讲了,因为项目原因,需要开启线程进行处理,在不了解线程的情况下,直接百度一下,然后就使用了,结果可想而知,出现了异常,所以花了一天时间系统学习一下多线程,这里主要是针对win32编程方面的 ...

  5. 企业微信H5_网页jssdk调用 判断当前客户端版本是否支持指定JS接口

    接上一篇:企业微信H5_网页jssdk调用,ticket签名config及示例https://gblfy.blog.csdn.net/article/details/123170569 文章目录 一. ...

  6. java多线程安全解决方案_《Java多线程编程核心技术(第2版)》 —1.2.8 实例变量共享造成的非线程安全问题与解决方案...

    1.2.8 实例变量共享造成的非线程安全问题与解决方案 自定义线程类中的实例变量针对其他线程可以有共享与不共享之分,这在多个线程之间交互时是很重要的技术点. 1.不共享数据的情况 不共享数据的情况如图 ...

  7. python程序多线程_Python-多线程编程

    threading模块 threading 模块的对象 对 象描 述 Thread 表示一个执行线程的对象 Lock 锁原语对象(和 thread 模块中的锁一样) RLock 可重入锁对象,使单一线 ...

  8. 初学Java多线程:线程简介

     Java多线程初学者指南系列教程http://developer.51cto.com/art/200911/162925.htm 初学Java多线程:线程简介 2009-06-29 17:49 ...

  9. java servlet 多线程_Servlet的多线程和线程安全

    线程安全 首先说明一下对线程安全的讨论,哪种情况我们可以称作线程安全? 网上对线程安全有很多描述,我比较喜欢<Java并发编程实战>给出的定义,"当多个线程访问某个类时,不管运行 ...

最新文章

  1. Go 语言 XML处理
  2. Codeforces Beta Round #9 (Div. 2 Only) C. Hexadecimal's Numbers dfs
  3. 技术分享:NodeJS中的Events(事件触发器)讲解
  4. (器) 构建自由通行的IOS开发者地图
  5. fabricjs上手指南
  6. SqlServer事务回滚失败
  7. 2013年思杰合作伙伴移动性解决方案巡展
  8. 使用HttpClient下载网络图片
  9. 字幕 Subtitle
  10. 建站系列:有云服务器和域名怎么建设网站?
  11. 【手把手教你Ubuntu】Ubuntu 13.04 Linux 3D桌面完全教程,显卡驱动安装方法
  12. java拦截通过url访问页面,必须通过登录页面访问目标页面
  13. Python代码画圣诞树--turtle绘图
  14. 大数据发展必备三个条件
  15. webpack学习之 style-loader / css-loader
  16. uni-app如何使用HBuildX云打包发布app
  17. TTCALL 网络电话
  18. Flutter高仿微信-第22篇-支付-二维码收款(二维码)
  19. Sers微服务2.1.1
  20. EasyExcel 复杂数据导出

热门文章

  1. 重磅:微信小程序开放公测了!
  2. ucinet使用手册_ucinet使用手册
  3. linux编译寻找包含的头文件,Linux编写内核模块编译时找不到头文件
  4. 修复MacBook Touch Bar(触控栏)无法正常工作的方法
  5. Blizzard Transitions for Mac - 动态风雪过渡效果FCPX转场
  6. 第十二届 2021年1月 蓝桥杯青少年组省赛C++组 第1题--第3题(python3实现)
  7. PHPMailer目录和命名空间没有错,但是始终class not found,的原因和解决方法
  8. cookie session token区别_cookie、session与token的真正区别
  9. Java笔记-CXF增加拦截器与自定义拦截器
  10. Java工作笔记-使用CXF接入及创建WebService