2,数据传输
我这里指的数据传输意思是当我们写好处理程序时往往由于需要多机来同时处理以达到处理性能要求(单机情况下不能达到处理性能要求)
而这时需要各处理机程序可以无序化的对等条件下处理数据,这样可以方便新机器上程序部署扩充
这里我常用的就是两种方式1,gearman方式; 2,RPC方式
用起来很方便也很灵活
2.1 gearman队列
如下为图示简单描述:
我这里举个例子:有人(许多人)A会写信,然后投到附近的邮筒中,然后邮局B统一处理将信件投到对应地点, 最后有人C会取信件
在这个过程中A就相当于client, B相当于server, C相当于worker,(你A只管投,C只管取,当然没有数据话C也会一直查看)
也就是client往队列中投递数据,worker负责取走队列中的数据然后处理,而这中间的队列就是gearman
这里client常用的内个函数为
其中create为创建 ,add_server为注册投递队列服务机器,do_background即为将数据发送到队列中
例如 php程序中编写client端程序构造函数中可以注册队队相关信息,
//gmServer="your hostname or ip"; gmPort="your port default 4730", gmName="queue name"
function __construct($gmServer, $gmPort, $gmName)
{
parent::__construct();
$this->m_gmclient = new GearmanClient();
$this->m_gmclient->setTimeout(3000);
$this->m_gmServerEnv['gmServer'] = $gmServer;
$this->m_gmServerEnv['gmPort'] = $gmPort;
$this->m_gmServerEnv['gmName'] = $gmName;
if(!is_array($this->m_gmServerEnv['gmServer'])) {
$this->m_gmclient->addServer($this->m_gmServerEnv['gmServer'],$this->m_gmServerEnv['gmPort']);
} else {
foreach ($this->m_gmServerEnv['gmServer'] as $serverItem) {
$this->m_gmclient->addServer($serverItem, $this->m_gmServerEnv['gmPort']); }
}
}
}
这样完成注册后可以登录到注册机器上看下4370端口
注册完成后需要处理数据然后把数据发到注册的这个队列中,可以示例如下示:
这里注意看,是不是我们调用了do_backguound了(一般都会试几次,因为可以由于网络原因出现失败,毕竟这个是基于网络传输的)
function send($data) {
$try_cnt = 3;
while ( $try_cnt > 0 ) {
$this->m_gmclient->doBackground($this->m_gmServerEnv['gmName'], $data);
if ($this->m_gmclient->returnCode() != GEARMAN_SUCCESS) {
unset($this->m_gmclient);
$this->m_gmclient = new GearmanClient();
$this->m_gmclient->setTimeout(3000);
$this->m_gmclient->addOptions(GEARMAN_CLIENT_RETRY_ON_FULL);
if(!is_array($this->m_gmServerEnv['gmServer'])) {
$this->m_gmclient->addServer($this->m_gmServerEnv['gmServer'],$this->m_gmServerEnv['gmPort']);
} else {
foreach ($this->m_gmServerEnv['gmServer'] as $serverItem) {
$this->m_gmclient->addServer($serverItem, $this->m_gmServerEnv['gmPort']);
}
}
} else {
return 0;
}
$try_cnt-- ;
}
return -1;
}
好了,以上这两块完成后,说明client端可以把数据发到指定队列中了
那么worker端如何接收呢?
worker常用的函数
job_workload: 这个相当于从队列中取到数据了
add_function: 这个相当于一个回调函数,意思是由这个函数来处理从队列中接收到的数据
例如下面这个函数gearman_worker_work(&worker)即是去队列中取数,取的数由gearman_worker_add_function(&worker, gmname.c_str(), 0, handler_work, NULL);函数中handler_worker这个函数来处理
static void receiveGearman(gearman_worker_st& worker, size_t& timeout, std::string& gmname)
{
gearman_worker_set_timeout(&worker, timeout);
gearman_return_t ret;
ret= gearman_worker_add_function(&worker, gmname.c_str(), 0, handler_work, NULL);
if (ret != GEARMAN_SUCCESS) {
printf("%s", gearman_worker_error(&worker));
exit(1);
}
while(!b_exit) {
ret= gearman_worker_work(&worker);
if (ret == GEARMAN_TIMEOUT) {
printf("%s", "Worker timeout."); \
} else if (ret != GEARMAN_SUCCESS) {
printf("%s,%d", "Worker ret != GEARMAN_SUCCESS", ret);
}
usleep(1);
}
gearman_worker_free(&worker);
exit(1);
}
回调handler_worker例如:
其中char* pworkload即为得到的队列中的数据,你可以处理这个数据了,我这里把接收到的数据分割后使用for_each函数来调用proc_func分别处理分割后的数据
static void *handler_work(gearman_job_st *job, void *cb_arg, size_t *result_size, gearman_return_t *ret_ptr) {
const char *workload = NULL;
size_t workload_size = 0;
workload= (char*)gearman_job_workload(job);
workload_size= gearman_job_workload_size(job);
std::string str_workload(workload, workload_size);
char* pworkload = &str_workload[0];
std::vector<std::string> svec;
if ("" != str_workload ) {
boost::split( svec, pworkload, boost::is_any_of( "\n" ), boost::token_compress_on );
for_each(svec.begin(), svec.end(), proc_func);
} else {
;
}
svec.clear();
*result_size= 0;
*ret_ptr= GEARMAN_SUCCESS;
return NULL;
}
好了,到这里client和worker都可以使用了,可以多个机器上部署处理速度还是可以的,毕竟可以分布式来处理,
当这个服务部署完成后怎么用命令来查看队列是否正常,队列中是否有数据正在处理呢
可以使用如下命令查看:
例如我在本机上看4730端口队列是否正常,
$ (echo status; sleep 0.1) | nc 127.0.0.1 4730
analysis 0 0 1
do_task 72 1 1
.
这里我有两个队列名字analysis和do_task
可以看到do_task中正有72个等待处理
另外
待续