thrift客户端调用不支持多线程,非线程安全
生活随笔
收集整理的這篇文章主要介紹了
thrift客户端调用不支持多线程,非线程安全
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
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對象,似乎是意識到了要保證其獨立性的問題??上ЫK究是個半吊子,因為上面已經說明,最根本在于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也有一年半了,沒想到這次還是掉坑里了。由于之前大多是調用方情形,接觸多線程下的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對象,似乎是意識到了要保證其獨立性的問題??上ЫK究是個半吊子,因為上面已經說明,最根本在于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客户端调用不支持多线程,非线程安全的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 获取当前时间月加1 ,年加1
- 下一篇: 3-5:HTTP协议之Cookie和Se