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的双向连接。

  1. 双向连接的service必须为oneway,否则会因为recv函数抛出remote close异常
  2. 客户端重用建立client的protocol,开线程使用processor.Process(protocol,protocol)监听服务端callback的消息。
  3. 服务端使用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双向消息的更多相关文章

  1. 分分钟搞定IOS远程消息推送

    一.引言 IOS中消息的推送有两种方式,分别是本地推送和远程推送,本地推送在http://my.oschina.net/u/2340880/blog/405491这篇博客中有详细的介绍,这里主要讨论远 ...

  2. JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查)

    前言:关于Vue框架,好几个月之前就听说过,了解一项新技术之后,总是处于观望状态,一直在犹豫要不要系统学习下.正好最近有点空,就去官网了解了下,看上去还不错的一个组件,就抽空研究了下.最近园子里vue ...

  3. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查

    前言:之前博主分享过knockoutJS和BootstrapTable的一些基础用法,都是写基础应用,根本谈不上封装,仅仅是避免了html控件的取值和赋值,远远没有将MVVM的精妙展现出来.最近项目打 ...

  4. iOS基于MBProgressHUD的二次封装,一行搞定,使用超简单

    MBProgressHUD的使用,临时总结了几款最常用的使用场景: 1.提示消息 用法: [YJProgressHUD showMessage:@"显示文字,1s隐藏" inVie ...

  5. iOS开发三步搞定百度推送

    iOS开发三步搞定百度推送   百度推送很简单,准备工作:在百度云推送平台注册应用,上传证书. 步骤一: 百度云推送平台 http://push.baidu.com/sdk/push_client_s ...

  6. Webcast / 技术小视频制作方法——自己动手录制video轻松搞定

    Webcast / 技术小视频制作方法——自己动手录制video轻松搞定 http://blog.sina.com.cn/s/blog_67d387490100wdnh.html 最近申请加入MSP的 ...

  7. 【微服务】之三:从零开始,轻松搞定SpringCloud微服务-配置中心

    在整个微服务体系中,除了注册中心具有非常重要的意义之外,还有一个注册中心.注册中心作为管理在整个项目群的配置文件及动态参数的重要载体服务.Spring Cloud体系的子项目中,Spring Clou ...

  8. 多key业务,数据库水平切分架构一次搞定

    数据库水平切分是一个很有意思的话题,不同业务类型,数据库水平切分的方法不同. 本篇将以"订单中心"为例,介绍"多key"类业务,随着数据量的逐步增大,数据库性能 ...

  9. [转] Java程序员学C#基本语法两个小时搞定(对比学习)

    Java程序员学C#基本语法两个小时搞定(对比学习)   对于学习一门新的语言,关键是学习新语言和以前掌握的语言的区别,但是也不要让以前语言的东西,固定了自己的思维模式,多看一下新的语言的编程思想. ...

随机推荐

  1. vue+django前后端分析解决csrf token问题

    vue-resource post数据 参考:https://www.cnblogs.com/linxizhifeng/p/8995077.html 阅读django CsrfViewMiddlewa ...

  2. Debussy的安装与使用

    1.概述 Debussy是NOVAS Software, Inc ( 思源科技 )发展的HDL Debug & Analysis tool,这套软体主要不是用来跑模拟或看波形,它最强大的功能是 ...

  3. 【Maven】应用Maven生成jar,包含关联库

    1. java project直接export到处jar包就可以,但在导出的过程中需要指定main class入口. 2. spring boot的项目,应用maven管理库,希望打成jar包,部署到 ...

  4. 剑指offer 面试67题

    面试67题: 题目: 链接:https://www.nowcoder.com/questionTerminal/1277c681251b4372bdef344468e4f26e?commentTags ...

  5. Java底层代码实现多文件读取和写入

    需求: "E:/data/"目录下有四个文件夹,如下: 每个文件夹下有几个.csv文件,如下: 将每个文件夹下的.csv文件合并成一个以该文件夹命名的.csv文件. 做法: 找到& ...

  6. yii常用操作函数

    <?php defined('YII_DEBUG') or define('YII_DEBUG', true); //当在调试模式下,应用会保留更多日志信息,如果抛出异常,会显示详细的错误调用堆 ...

  7. 定制AIX操作系统的shell环境

    操作系统与外部最主要的接口就叫做shell.shell是操作系统最外面的一层.shell管理你与操作系统之间的交互:等待你输入,向操作系统解释你的输入,并且处理各种各样的操作系统的输出结果. shel ...

  8. post请求和get请求content_type的种类

    get请求的headers中没有content-type这个字段,post 的 content-type 有两种 : application/x-www-form-urlencoded 这种就是一般的 ...

  9. 四、golang内置函数、递归、闭包、数组切片和map

    一.总体内容 1.内置函数.递归函数.闭包 2.数组和切片 3.map数据结构 4.package介绍 一.内置函数 注意:值类型用new来分配内存,引用类型用make来分配内存 1.close:主要 ...

  10. 20165101刘天野 2017-2018-2 《Java程序设计》第7周学习总结

    #20165101刘天野 2017-2018-2 <Java程序设计>第7周学习总结 教材学习内容总结 第十一章JDBC与MySQL数据库 JDBC简介 JDBC(Java Databas ...