【基础】利用thrift实现一个非阻塞带有回调机制的客户端
假设读者对thrift有一定了解。
客户端有时需要非阻塞的去发送请求,给定服务端一个请求,要求其返回一个计算结果。但是客户端不想等待服务端处理完,而是想发送完这个指令后自己去做其他事情,当结果返回时自动的去处理。
比如举个形象点的例子:饭店的Boss让小弟A把本周店里的欠条收集起来放到自己桌子上,然后又告诉自己的小秘书坐在自己办公室等着小弟A把欠条拿过来,然后统计一下一共有多少,然后Boss自己出去半点事儿。
Boss相当于client,小弟A相当于server,而小秘书相当于client端的回调函数(callback)。怎么讲呢?Boss不想等待小弟处理完,因为他老人家公务繁忙,还要去干别的呢。于是他把接下来处理欠条的任务托管给了小秘书,于是自己一个人出去了。
OK,那么我们基本了解了整个工作流程,来看看实现的方法。thrift去实现client异步+回调的方法关键点在于:thrift生成的client中有个send_XXX()和recv_XXX()方法。send_XXX()相当于告知server去处理东西,可以立即返回;而调用recv_XXX就是个阻塞的方法了,直到server返回结果。所以,我们可以在主线程调用完send_XXX()之后,然后另开一个线程去调用send_XXX(),该线程在等到server回复后自动调用callback方法,对结果进行一些处理(当然callback在修改client状态时需要进行同步操作)。这样的模式下,我们可以做很多事情,比如分布式环境下的观察者模式。当然了需要注意的一点就是,各个线程接受到结果的顺序跟请求顺序不一定一样,因为server处理不通请求时间不通或者网络环境的影响都可能导致这种情形。所以如果你对接受这些结果时不是幂等操作时需要注意一下。
thrift脚本:
//只有一个方法,client发送一个消息,server换回一个消息
service TestServ{
string ping(1: string message),
}
server端采用TNBlockingServer实现
#include "TestServ.h" #include <iostream> #include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TNonblockingServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/concurrency/PosixThreadFactory.h> using namespace std; 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::shared_ptr; class TestServHandler : virtual public TestServIf {
public:
TestServHandler() {
// Your initialization goes here
} void ping(std::string& _return, const std::string& message) {
_return = "hello, i am server! ";
sleep();// do something time-consuming/ 这里我们在server端加一些耗时的操作
cout<<"Request from client: "<<message<<endl;
} }; int main(int argc, char **argv) {
int port = ; shared_ptr<TestServHandler> handler(new TestServHandler());
shared_ptr<TProcessor> processor(new TestServProcessor(handler));
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager();
shared_ptr<PosixThreadFactory> threadFactory = shared_ptr<PosixThreadFactory > (new PosixThreadFactory());
threadManager->threadFactory(threadFactory);
threadManager->start();
TNonblockingServer server(processor, protocolFactory, port, threadManager);
server.serve();
return ;
}
client端实现:
#include "TestServ.h" #include <iostream>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TBufferTransports.h> #include "test_constants.h" using namespace std;
using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using boost::shared_ptr; class AsynTestClient;
void * wait_recv(void * parg );
struct PARG {
AsynTestClient * pthis;
string message;
}; class AsynTestClient {
private:
unsigned int d_cnt_recv;//< 客户端接受到server响应次数的计数器. pthread_rwlock_t m_cnt_recv;//< 计数器的读写锁.
vector<pthread_t> m_ids; public:
TestServClient * d_client;
void call_back(string & _return){
//输出服务器返回信息并把返回计数加1
cout<<"server msg: "<<_return<<endl;
pthread_rwlock_wrlock( &m_cnt_recv );
d_cnt_recv ++;
pthread_rwlock_unlock( &m_cnt_recv );
}
explicit AsynTestClient(boost::shared_ptr<TProtocol> & protocol){
pthread_rwlock_init( &m_cnt_recv, NULL );
d_cnt_recv = ;
d_client = new TestServClient( protocol );
} ~AsynTestClient(){
delete d_client;
pthread_rwlock_destroy( &m_cnt_recv );
} void asyn_ping( const string & message) {
//发送请求
d_client->send_ping(message);
//初始化每个等待回调线程的参数
PARG * parg = new PARG;
parg->pthis = this;
parg->message = message;
//把新生成的线程id放入全局数组维护
pthread_t m_id;
m_ids.push_back(m_id);
//启动线程,从此只要接受到服务器的返回结果就调用回调函数。
if( != pthread_create( &m_id, NULL, wait_recv, reinterpret_cast< void * > (parg) ) ) {
return;
}
}
};
int main(int argc, char **argv) { boost::shared_ptr<TSocket> socket(new TSocket("localhost", ));
boost::shared_ptr<TTransport> transport(new TFramedTransport(socket));
boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport)); //TestServClient client(protocol); transport->open();
AsynTestClient client(protocol);
string message = "hello, i am client! ";
client.asyn_ping(message); while(true){
sleep();//这里相当于client去做别的事情了
} transport->close();
return ;
}
void * wait_recv(void * parg ) {
PARG * t_parg = reinterpret_cast< PARG * >(parg);//强制转化线程参数
string _return;
t_parg->pthis->d_client->recv_ping(_return);
t_parg->pthis->call_back(_return);
}
其实大家可以注意到,我并没有使用asyn_ping(const string & message, void(*)call_back(void));这种方式去定义它,这是因为asyn_ping本身可以获取callback函数的指针。回调的本质是任务的托管、时间的复用,也就是说等待结果返回后自动去调用一段代码而已,所以本质上上面就是回调机制。如果你想使用传函数指针的方式,也可以实现出来。
注意:编译时需要-L$(LIB_DIR) -lthrift -lthriftnb -levent。
【基础】利用thrift实现一个非阻塞带有回调机制的客户端的更多相关文章
- java并发编程(8)原子变量和非阻塞的同步机制
原子变量和非阻塞的同步机制 一.锁的劣势 1.在多线程下:锁的挂起和恢复等过程存在着很大的开销(及时现代的jvm会判断何时使用挂起,何时自旋等待) 2.volatile:轻量级别的同步机制,但是不能用 ...
- js多物体多方向缓动动画加带有回调机制
一.获取一组div元素 var boxs = document.getElementsByTagName('div'); 二.封装获取属性值的函数 function getStyle(dom, att ...
- Java基础——NIO(二)非阻塞式网络通信与NIO2新增类库
一.NIO非阻塞式网络通信 1.阻塞与非阻塞的概念 传统的 IO 流都是阻塞式的.也就是说,当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读取或写入,该线程在 ...
- Linux非阻塞IO(五)使用poll实现非阻塞的回射服务器客户端
前面几节我们讨论了非阻塞IO的基本概念.Buffer的设计以及非阻塞connect的实现,现在我们使用它们来完成客户端的编写. 我们在http://www.cnblogs.com/inevermore ...
- 爬虫基础--IO多路复用单线程异步非阻塞
最近一直的学习爬虫 ,进行基础的学习 性能相关 参考 https://www.cnblogs.com/wupeiqi/p/6229292.html # 目标:单线程实现并发HTTP请求 # # so ...
- 使用OTP原则构建一个非阻塞的TCP服务器
http://erlangcentral.org/wiki/index.php/Building_a_Non-blocking_TCP_server_using_OTP_principles CONT ...
- 基于委托的C#异步编程的一个小例子 带有回调函数的例子
我创建的是一个winform测试项目:界面如下: 设置: 下面是代码: using System; using System.Collections.Generic; using System.Com ...
- blocking(非阻塞)回调函数
回调函数不会造成阻塞 function loop() { setTimeout(loop, 0) } loop 死循环 while(true)
- nginx学习(二)——基础概念之异步非阻塞
上面讲了很多关于nginx的进程模型,接下来,我们来看看nginx是如何处理事件的. 有人可能要问了,nginx采用多worker的方式来处理请求,每个worker里面只有一个主线程,那能够处理的并发 ...
随机推荐
- [Linux 维护]收集centos系统性能指标
#!/bin/bash # awk 'END{print}' get the last row iplist=$(cat ~/fanr/shell/Weekly/ip.list) for _IP in ...
- 自动kill慢查询
在生产环境中,DB服务器经常会被并发的慢查询压挂,因此事前进行sql审核避免烂SQL很重要.万一不小心慢sql还是跑到线上,并且并发还不小,这是dba肯定会收到告警.dba上线处理第一时间是定位并ki ...
- 2、HDFS和Yarn的基础学习笔记
日志 --排错 .log:通过log4j记录的,记录大部分应用程序的日志信息 .out:记录标准输出和标准错误日志,少量记录 hdfs 常用shell -ls -put < ...
- Linux服务器文件删除空间未释放的问题
一.问题起源 在Linux系统中,通过rm删除文件将会从文件系统的目录结构上解除链接(unlink),如果文件是被打开的(有一个进程正在使用),那么进程将仍然可以读取该文件磁盘空间也一直被占用 这样就 ...
- SQL Server中的RAND函数的介绍和区间随机数值函数的实现
工作中会遇到SQL Server模拟数据生成以及数值列值(如整型.日期和时间数据类型)随机填充等等任务,这些任务中都要使用到随机数.鉴于此,本文将对SQL Server中随机数的使用简单做个总 ...
- windows下 MySQL手动安装与卸载
下载文件以后进行解压 ,指定文件的具体位置 1.安装 选择路径下的mysqld --intall 指定服务名称 --设置配置文件 例子: C:\Users\Administrator\Desktop ...
- label的for属性
一.使用介绍 <label>专为input元素服务,为其定义标记. for属性规定label与哪个表单元素绑定 label和表单控件绑定方式又两种: 1.将表单控件作为label的内容,这 ...
- Android View和ViewGroup
View和ViewGroup Android的UI界面都是由View和ViewGroup及其派生类组合而成的. 其中,View是所有UI组件的基类,而 ViewGroup是容纳这些组件的容器,其本身也 ...
- AC日记——单词翻转 1.7 27
27:单词翻转 总时间限制: 1000ms 内存限制: 65536kB 描述 输入一个句子(一行),将句子中的每一个单词翻转后输出. 输入 只有一行,为一个字符串,不超过500个字符.单词之间以空 ...
- Apache http Server 2.4 安装与配置
前言 Apache官网从2.2之后,不再提供windows的msi或exe安装版本,现在Apache http Server有两个分支2.2及2.4 注意事项 如果之前有安装2.2的版本,请先卸载 A ...