搞定thrift双向消息
thrift作为脱胎于facebook的rpc框架,各方面都非常优秀。清晰的分层设计,多语言的支持,以及不输protocolbuffer的效率(compact下优于protocolbuffer),都让thrift拥有越来越多的使用者。
作为一个RPC框架,thrift支持的是open->client--rpc-->server->close的短连接模式。在实际应用中,却经常会有客户端建立连接后,等待服务端数据的长连接模式,也可以称为双向连接。通常的方案有三种,可参考http://dongxicheng.org/search-engine/thrift-bidirectional-async-rpc/,文中提到第三种方法会修改源码,而实际操作过程中发现这其实是作者小小的理解错误,实现thrift双向通信并没有这么复杂,经过一番实验,发现只需要如下理解和实现即可轻松实现一个thrift的双向连接。
- 双向连接的service必须为oneway,否则会因为recv函数抛出remote close异常
- 客户端重用建立client的protocol,开线程使用processor.Process(protocol,protocol)监听服务端callback的消息。
- 服务端使用ProcessorFactory,使用TConnectionInfo中的transport作为向客户端发送消息的client的transport
搞定以上三步,即可实现一个thrift双向连接,这里附上实验代码,客户端使用C#(sorry for my pool C#),服务端使用C++
thrift
service HandshakeService{
oneway void HandShake();
}
service CallbackService{
oneway void Push(: string msg);
}
client
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Thrift.Collections;
using Thrift.Protocol;
using Thrift.Server;
using Thrift.Transport;
using System.Threading;
using Thrift;
using System.IO; namespace ThriftBidirection
{
class Program
{
class CallbackServiceImply : CallbackService.Iface
{
int msgCount = ;
public void Push(string msg)
{
Console.WriteLine("receive msg {0}: {1}", msgCount++, msg);
}
}
//服务处理线程
static void ProcessThread(TProtocol protocol)
{
TProcessor processor = new CallbackService.Processor(new CallbackServiceImply());
while (true)
{
try
{
//////////////////////////////////////////////////////////////////////////
///模仿server行为,同时重用client端protocol
///相当于同时重用一个连接
while (processor.Process(protocol, protocol)) { };
///connection lost, return
return;
}
catch (IOException) //not fatal error, resume
{
continue;
}
catch (TException) //fatal error
{
return;
}
}
}
//服务器状态监听线程
static void MonitorThread(TTransport trans, Action<string> callback)
{
while (true)
{
try
{
if (!trans.Peek())
{
callback("连接中断");
}
Thread.Sleep();
}
catch (Thrift.TException ex)
{
callback(ex.Message);
return;
}
}
} static void Main(string[] args)
{
TTransport transport = new TBufferedTransport(new TSocket("localhost", ));
TProtocol protocol = new TBinaryProtocol(transport);
HandshakeService.Client client = new HandshakeService.Client(protocol);
Action<TProtocol> processAction = new Action<TProtocol>(ProcessThread);
Action<TTransport, Action<string>> monitorAction = new Action<TTransport, Action<string>>(MonitorThread); transport.Open();
processAction.BeginInvoke(protocol, (result) =>
{
processAction.EndInvoke(result);
}, null);
monitorAction.BeginInvoke(transport, (msg) =>
{
Console.WriteLine("连接中断: {0}", msg);
}, (result) =>
{ }, null); for (int i = ; i < ; ++i)
{
client.HandShake();
Thread.Sleep();
}
Console.Read();
transport.Close();
}
}
}
server
// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it. #include "HandshakeService.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <boost/make_shared.hpp>
#include <thrift/server/TThreadPoolServer.h>
#include <thrift/concurrency/PlatformThreadFactory.h>
#include "CallbackService.h" using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;
using namespace apache::thrift::concurrency; using boost::make_shared;
using boost::shared_ptr; class HandshakeServiceHandler : virtual public HandshakeServiceIf {
public:
HandshakeServiceHandler(const boost::shared_ptr<TTransport> &trans)
: m_client(make_shared<TBinaryProtocol>(trans))
{
boost::once_flag flag = BOOST_ONCE_INIT;
m_flag = flag;
} virtual ~HandshakeServiceHandler()
{
m_thread->interrupt();
m_thread->join();
} void CallbackThread()
{
while(true)
{
try
{
m_client.Push("server push msg");
}
catch (TException)
{
return;
}
boost::this_thread::sleep_for(boost::chrono::milliseconds());
}
} void HandShake() {
// Your implementation goes here
printf("HandShake\n");
boost::call_once(boost::bind(&HandshakeServiceHandler::_StartThread, this), m_flag);
} void _StartThread()
{
m_thread.reset(new boost::thread(boost::bind(&HandshakeServiceHandler::CallbackThread, this)));
} boost::shared_ptr<TTransport> m_trans;
CallbackServiceClient m_client;
shared_ptr<boost::thread> m_thread;
boost::once_flag m_flag;
}; class ProcessorFactoryImply : public TProcessorFactory
{
virtual boost::shared_ptr<TProcessor> getProcessor(
const TConnectionInfo& connInfo)
{
return make_shared<HandshakeServiceProcessor>(make_shared<HandshakeServiceHandler>(connInfo.transport));
}
}; int main(int argc, char **argv) {
int port = ;
shared_ptr<TProcessorFactory> processorFactory(new ProcessorFactoryImply());
shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
shared_ptr<ThreadManager> threadMgr = ThreadManager::newSimpleThreadManager();
boost::shared_ptr<PlatformThreadFactory> threadFactory =
boost::shared_ptr<PlatformThreadFactory>(new PlatformThreadFactory()); threadMgr->threadFactory(threadFactory);
threadMgr->start();
TThreadPoolServer server(processorFactory,serverTransport, transportFactory, protocolFactory, threadMgr);
server.serve();
return ;
}

一个简单的thrift双向通信就实现了。
搞定thrift双向消息的更多相关文章
- 分分钟搞定IOS远程消息推送
一.引言 IOS中消息的推送有两种方式,分别是本地推送和远程推送,本地推送在http://my.oschina.net/u/2340880/blog/405491这篇博客中有详细的介绍,这里主要讨论远 ...
- JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查)
前言:关于Vue框架,好几个月之前就听说过,了解一项新技术之后,总是处于观望状态,一直在犹豫要不要系统学习下.正好最近有点空,就去官网了解了下,看上去还不错的一个组件,就抽空研究了下.最近园子里vue ...
- JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查
前言:之前博主分享过knockoutJS和BootstrapTable的一些基础用法,都是写基础应用,根本谈不上封装,仅仅是避免了html控件的取值和赋值,远远没有将MVVM的精妙展现出来.最近项目打 ...
- iOS基于MBProgressHUD的二次封装,一行搞定,使用超简单
MBProgressHUD的使用,临时总结了几款最常用的使用场景: 1.提示消息 用法: [YJProgressHUD showMessage:@"显示文字,1s隐藏" inVie ...
- iOS开发三步搞定百度推送
iOS开发三步搞定百度推送 百度推送很简单,准备工作:在百度云推送平台注册应用,上传证书. 步骤一: 百度云推送平台 http://push.baidu.com/sdk/push_client_s ...
- Webcast / 技术小视频制作方法——自己动手录制video轻松搞定
Webcast / 技术小视频制作方法——自己动手录制video轻松搞定 http://blog.sina.com.cn/s/blog_67d387490100wdnh.html 最近申请加入MSP的 ...
- 【微服务】之三:从零开始,轻松搞定SpringCloud微服务-配置中心
在整个微服务体系中,除了注册中心具有非常重要的意义之外,还有一个注册中心.注册中心作为管理在整个项目群的配置文件及动态参数的重要载体服务.Spring Cloud体系的子项目中,Spring Clou ...
- 多key业务,数据库水平切分架构一次搞定
数据库水平切分是一个很有意思的话题,不同业务类型,数据库水平切分的方法不同. 本篇将以"订单中心"为例,介绍"多key"类业务,随着数据量的逐步增大,数据库性能 ...
- [转] Java程序员学C#基本语法两个小时搞定(对比学习)
Java程序员学C#基本语法两个小时搞定(对比学习) 对于学习一门新的语言,关键是学习新语言和以前掌握的语言的区别,但是也不要让以前语言的东西,固定了自己的思维模式,多看一下新的语言的编程思想. ...
随机推荐
- 2015.7.14(大盘结束红色,中色连坐4T)
中色今天的盘面相当有意思,现场直播庄家和散户斗法我估计中色要拉涨停了,不过你别跟,现在很危险了——就算是涨停,明天一个低开就把你给绕进去了 1.今天开市9:42发现中色的地位买入点良机16.13,此时 ...
- Django:popup弹出框简单应用实例
效果:在p1.html页面点击,弹出p2的弹出框,填写数据,在 popup_response页面处理数据 1.url设置 urlpatterns = patterns( url(r'^p1.html' ...
- AJAX实现弹窗显示详情,全选和批量删除
以Nation表为例,将Nation表显示在页面上,每一行数据前面加上复选框,后面加上查看详情,点击以弹窗形式显示每一行的数据,并且在表格最后一行加上全选复选框,点击选中全部数据,后面跟一个批量删除按 ...
- SQL Server 简单事务学习
select * from bank ) ) --打开一个事务 begin tran --定义临时变量 ; set @num = @num + @@error; --上一条语句出错次数 set @nu ...
- 查看linux系统版本信息(Oracle Linux、Centos Linux、Redhat Linux、Debian、Ubuntu)
一.查看Linux系统版本的命令(3种方法) 1.cat /etc/issue,此命令也适用于所有的Linux发行版. [root@S-CentOS home]# cat /etc/issue Cen ...
- 每日练习level-7
1.有序列表.无序列表.自定义列表如何使用?写个简单的例子.三者在语义上有什么区别?使用场景是什么? 能否嵌套? 有序列表是一列使用数字进行标记的项目,它使用<li>包含于<ol&g ...
- 前端之CSS样式
一.CSS 1.什么是CSS 层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的 ...
- 三、golang时间、流程控、函数
一.本篇内容 1.string和strconv使用 2.go中的时间和日期类型 3.流程控制 4.函数讲解 二.string和strconv使用 1. string.HasPrefix(s trin ...
- Deep Learning概述
1.深度学习发展简史 2.三步实现深度学习 2.1Neural Network 神经网络由模仿脑部神经系统发展而来,一个节点称为一个“Neuron”,包括连接在节点上面的weights和biases. ...
- 通过公钥解密密文思路(256bits RSA)
256bit RSA公钥安全系数极低,只需要几分钟即可破解密文,本文综合其他文章记录了一次解密256bits RSA加密的密文的过程,仅作为备忘. 1.分解公钥,分解出n与e: 1.1使用openss ...