服务端研发应具备的技能(3)

2,数据传输

我这里指的数据传输意思是当我们写好处理程序时往往由于需要多机来同时处理以达到处理性能要求(单机情况下不能达到处理性能要求)

而这时需要各处理机程序可以无序化的对等条件下处理数据,这样可以方便新机器上程序部署扩充

这里我常用的就是两种方式1,gearman方式; 2,RPC方式

用起来很方便也很灵活



2.1 gearman队列

如下为图示简单描述:

我这里举个例子:有人(许多人)A会写信,然后投到附近的邮筒中,然后邮局B统一处理将信件投到对应地点, 最后有人C会取信件

在这个过程中A就相当于client,  B相当于server,  C相当于worker,(你A只管投,C只管取,当然没有数据话C也会一直查看)

也就是client往队列中投递数据,worker负责取走队列中的数据然后处理,而这中间的队列就是gearman





这里client常用的内个函数为

gearman_client_do_background
gearman_client_create
gearman_client_free
gearman_client_add_server
gearman_client_set_timeout

其中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常用的函数

gearman_worker_create
gearman_worker_add_server
gearman_worker_set_timeout
gearman_worker_add_function
gearman_worker_work
gearman_job_workload
gearman_job_workload_size
其实过程如果client完成的话,情况都差不多,只不过多了几个函数

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个等待处理

另外

status
五列数据,分别为 name, 队列中job数量, job 正处理的数量, worker正处理数量, queue长度


待续




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值