<?phpnamespace DoraRPC;/*** Class Client* https://github.com/xcl3721/Dora-RPC* by 蓝天 http://weibo.com/thinkpc*/class Client{//client obj poolprivate static $client = array();//for the async task listprivate static $asynclist = array();//for the async task resultprivate static $asynresult = array();//client config arrayprivate $serverConfig = array();//when connect fail will block error configprivate $serverConfigBlock = array();//this request guidprivate $guid;//1 random from specify group,2 specify by ip port//1 随机从指定group名称内选择客户端,2 指定ip进行连接private $connectMode = 0;//是否使用上一次已连接connectclient//用于减少单机长链接数//private $connectReuse = true;//current connect ip portprivate $connectIp = "";private $connectPort = 0;//config group nameprivate $connectGroup = "";//current using client obj key on static client arrayprivate $currentClientKey = "";public function __construct($serverConfig){if (count($serverConfig) == 0) {echo "cant found config on the Dora RPC..";throw new \Exception("please set the config param on init Dora RPC", -1);}$this->serverConfig = $serverConfig;}//$param = array("type"=>1,"group"=>"group1");//$param = array("type"=>2,"ip"=>"127.0.0.1","port"=>9567);/*** 更换连接模式,用于指定ip请求和普通请求切换* @param $param* @throws \Exception unknow mode parameter*/public function changeMode($param){switch ($param["type"]) {case 1:if ($param["group"] == "") {throw new \Exception("change mode parameter is wrong", -1);}$this->connectMode = 1;$this->connectGroup = $param["group"];$this->connectIp = "";$this->connectPort = "";$this->currentClientKey = "";break;case 2:if ($param["ip"] == "" || $param["port"] == "") {throw new \Exception("change mode parameter is wrong", -1);}$this->connectMode = 2;$this->connectGroup = "default";$this->connectIp = $param["ip"];$this->connectPort = $param["port"];$this->currentClientKey = "";break;default:throw new \Exception("change mode parameter is wrong", -1);break;}}/*** 返回当前连接模式及相关信息* @return array* @throws \Exception unknow mode*/public function getConnectMode(){switch ($this->connectMode) {case 1:return array("type" => 1, "group" => $this->connectGroup, "ip" => $this->connectIp, "port" => $this->connectPort);break;case 2:return array("type" => 2, "group" => "", "ip" => $this->connectIp, "port" => $this->connectPort);break;default:throw new \Exception("current connect mode is unknow", -1);break;}}//random get config keyprivate function getConfigObjKey(){if (!isset($this->serverConfig[$this->connectGroup])) {throw new \Exception("there is no one server can connect", 100010);}// if there is no config can use clean up the block listif (isset($this->serverConfigBlock[$this->connectGroup]) &&count($this->serverConfig[$this->connectGroup]) <= count($this->serverConfigBlock[$this->connectGroup])) {//clean up the block list$this->serverConfigBlock[$this->connectGroup] = array();}//if not specified the ip and port random get onedo {//get one config by random$key = array_rand($this->serverConfig[$this->connectGroup]);//if not on the block list.if (!isset($this->serverConfigBlock[$this->connectGroup][$key])) {return $key;}} while (count($this->serverConfig[$this->connectGroup]) > count($this->serverConfigBlock));throw new \Exception("there is no one server can connect", 100010);}//get current clientprivate function getClientObj(){//config obj key$key = "";//if not spc will randomswitch ($this->connectMode) {case 1:$key = $this->getConfigObjKey();$clientKey = $this->serverConfig[$this->connectGroup][$key]["ip"] . "_" . $this->serverConfig[$this->connectGroup][$key]["port"];//set the current client key$this->currentClientKey = $clientKey;$connectHost = $this->serverConfig[$this->connectGroup][$key]["ip"];$connectPort = $this->serverConfig[$this->connectGroup][$key]["port"];break;case 2://using spec$clientKey = trim($this->connectIp) . "_" . trim($this->connectPort);//set the current client key$this->currentClientKey = $clientKey;$connectHost = $this->connectIp;$connectPort = $this->connectPort;break;default:throw new \Exception("current connect mode is unknow", -1);break;}if (!isset(self::$client[$clientKey])) {$client = new \swoole_client(SWOOLE_SOCK_TCP | SWOOLE_KEEP);$client->set(array('open_length_check' => 1,'package_length_type' => 'N','package_length_offset' => 0,'package_body_offset' => 4,'package_max_length' => 1024 * 1024 * 2,'open_tcp_nodelay' => 1,'socket_buffer_size' => 1024 * 1024 * 4,));if (!$client->connect($connectHost, $connectPort, DoraConst::SW_RECIVE_TIMEOUT)) {//connect fail$errorCode = $client->errCode;if ($errorCode == 0) {$msg = "connect fail.check host dns.";$errorCode = -1;} else {$msg = \socket_strerror($errorCode);}if ($key !== "") {//put the fail connect config to block list$this->serverConfigBlock[$this->connectGroup][$key] = 1;}throw new \Exception($msg . " " . $clientKey, $errorCode);}self::$client[$clientKey] = $client;}//successreturn self::$client[$clientKey];}/*** 获取应用服务器信息 get the backend service stat* @param string $ip* @param string $port* @return array*/public function getStat($ip = "", $port = ""){$beformode = $this->getConnectMode();if ($ip != "" && $port != "") {$mode = array("type" => 2, "ip" => $ip, "port" => $port);$this->changeMode($mode);}$this->guid = $this->generateGuid();$packet = array('api' => array("cmd" => array('name' => "getStat",'param' => array(),),),'type' => DoraConst::SW_CONTROL_CMD,'guid' => $this->guid,);$sendData = Packet::packEncode($packet);$result = $this->doRequest($sendData, DoraConst::SW_MODE_WAITRESULT_SINGLE);if ($this->guid != $result["guid"]) {return Packet::packFormat($this->guid, "guid wront please retry..", 100100, $result["data"]);}//revert befor connect modeif ($ip != "" && $port != "") {//revert befor mode$this->changeMode($beformode);}return $result["data"];}/*** reload 指定服务器的代码* @param string $ip* @param string $port* @return array*/public function reloadServerTask($ip = "", $port = ""){$beformode = $this->getConnectMode();if ($ip != "" && $port != "") {$mode = array("type" => 2, "ip" => $ip, "port" => $port);$this->changeMode($mode);}$this->guid = $this->generateGuid();$packet = array('api' => array("cmd" => array('name' => "reloadTask",'param' => array(),),),'type' => DoraConst::SW_CONTROL_CMD,'guid' => $this->guid,);$sendData = Packet::packEncode($packet);$result = $this->doRequest($sendData, DoraConst::SW_MODE_WAITRESULT_SINGLE);if ($this->guid != $result["guid"]) {return Packet::packFormat($this->guid, "guid wront please retry..", 100100, $result["data"]);}//revert befor connect modeif ($ip != "" && $port != "") {//revert befor mode$this->changeMode($beformode);}return $result["data"];}/** mode 参数更改说明,以前版本只是sync参数不是mode* sync :* true 代表是否阻塞等待结果,* false 下发任务成功后就返回不等待结果,用于对接口返回没有影响的操作提速* 改版后----* mode :* 0 代表阻塞等待任务执行完毕拿到结果 ;* 1 代表下发任务成功后就返回不等待结果 ;* 2 代表下发任务成功后直接返回guid 然后稍晚通过调用阻塞接收函数拿到所有结果*//*** 单api请求* @param string $name api地址* @param array $param 参数* @param int $mode* @param int $retry 通讯错误时重试次数* @param string $ip 要连得ip地址,如果不指定从现有配置随机个* @param string $port 要连得port地址,如果不指定从现有配置找一个* @return mixed 返回单个请求结果* @throws \Exception unknow mode type*/public function singleAPI($name, $param, $mode = DoraConst::SW_MODE_WAITRESULT, $retry = 0, $ip = "", $port = ""){//get guid$this->guid = $this->generateGuid();$packet = array('api' => array("one" => array('name' => $name,'param' => $param,)),'guid' => $this->guid,);switch ($mode) {case DoraConst::SW_MODE_WAITRESULT:$packet["type"] = DoraConst::SW_MODE_WAITRESULT_SINGLE;break;case DoraConst::SW_MODE_NORESULT:$packet["type"] = DoraConst::SW_MODE_NORESULT_SINGLE;break;case DoraConst::SW_MODE_ASYNCRESULT:$packet["type"] = DoraConst::SW_MODE_ASYNCRESULT_SINGLE;break;default:throw new \Exception("unknow mode have been set", 100099);break;}$sendData = Packet::packEncode($packet);$result = $this->doRequest($sendData, $packet["type"]);//retry when the send failwhile ((!isset($result["code"]) || $result["code"] != 0) && $retry > 0) {$result = $this->doRequest($sendData, $packet["type"]);$retry--;}if ($this->guid != $result["guid"]) {return Packet::packFormat($this->guid, "guid wront please retry..", 100100, $result["data"]);}return $result;}/*** 并发请求api,使用方法如* $params = array(* "api_1117"=>array("name"=>"apiname1","param"=>array("id"=>1117)),* "api_2"=>array("name"=>"apiname2","param"=>array("id"=>2)),* )* @param array $params 提交参数 请指定key好方便区分对应结果,注意考虑到硬件资源有限并发请求不要超过50个* @param int $mode* @param int $retry 通讯错误时重试次数* @param string $ip 要连得ip地址,如果不指定从现有配置随机个* @param string $port 要连得port地址,如果不指定从现有配置找一个* @return mixed 返回指定key结果* @throws \Exception unknow mode type*/public function multiAPI($params, $mode = DoraConst::SW_MODE_WAITRESULT, $retry = 0, $ip = "", $port = ""){//get guid$this->guid = $this->generateGuid();$packet = array('api' => $params,'guid' => $this->guid,);switch ($mode) {case DoraConst::SW_MODE_WAITRESULT:$packet["type"] = DoraConst::SW_MODE_WAITRESULT_MULTI;break;case DoraConst::SW_MODE_NORESULT:$packet["type"] = DoraConst::SW_MODE_NORESULT_MULTI;break;case DoraConst::SW_MODE_ASYNCRESULT:$packet["type"] = DoraConst::SW_MODE_ASYNCRESULT_MULTI;break;default:throw new \Exception("unknow mode have been set", 100099);break;}$sendData = Packet::packEncode($packet);$result = $this->doRequest($sendData, $packet["type"]);//retry when the send failwhile ((!isset($result["code"]) || $result["code"] != 0) && $retry > 0) {$result = $this->doRequest($sendData, $packet["type"]);$retry--;}if ($this->guid != $result["guid"]) {return Packet::packFormat($this->guid, "guid wront please retry..", 100100, $result["data"]);}return $result;}private function doRequest($sendData, $type){//get client objtry {$client = $this->getClientObj();} catch (\Exception $e) {$data = Packet::packFormat($this->guid, $e->getMessage(), $e->getCode());return $data;}$ret = $client->send($sendData);//ok failif (!$ret) {$errorcode = $client->errCode;//destroy error client obj to make reconncetself::$client[$this->currentClientKey]->close(true);unset(self::$client[$this->currentClientKey]);// mark the current connection cannot be used, try another channel$this->serverConfigBlock[$this->connectGroup][$this->currentClientKey] = 1;if ($errorcode == 0) {$msg = "connect fail.check host dns.";$errorcode = -1;$packet = Packet::packFormat($this->guid, $msg, $errorcode);} else {$msg = \socket_strerror($errorcode);$packet = Packet::packFormat($this->guid, $msg, $errorcode);}return $packet;}//if the type is async result will record the guid and client handleif ($type == DoraConst::SW_MODE_ASYNCRESULT_MULTI || $type == DoraConst::SW_MODE_ASYNCRESULT_SINGLE) {self::$asynclist[$this->guid] = $client;}//recive the response$data = $this->waitResult($client);$data["guid"] = $this->guid;return $data;}//for the loop find the right result//save the async result to the asyncresult static var//return the right guid requestprivate function waitResult($client){while (1) {$result = $client->recv();if ($result !== false && $result != "") {$data = Packet::packDecode($result);//if the async result first deploy success willif ($data["data"]["guid"] != $this->guid) {// this data was not we want//it's may the async result//when the guid on the asynclist and have isresult =1 on data is async result//when the guid on the asynclist not have isresult field ond data is first success deploy msgif (isset(self::$asynclist[$data["data"]["guid"]]) && isset($data["data"]["isresult"]) && $data["data"]["isresult"] == 1) {//ok recive an async result//remove the guid on the asynclistunset(self::$asynclist[$data["data"]["guid"]]);//add result to async resultself::$asynresult[$data["data"]["guid"]] = $data["data"];self::$asynresult[$data["data"]["guid"]]["fromwait"] = 1;} else {//not in the asynclist drop this packetcontinue;}} else {//founded right datareturn $data;}} else {//time out$packet = Packet::packFormat($this->guid, "the recive wrong or timeout", 100009);return $packet;}}}public function getAsyncData(){//wait all the async result//when timeout all the error will return//这里有个坑,我不知道具体哪个client需要recivewhile (1) {if (count(self::$asynclist) > 0) {foreach (self::$asynclist as $k => $client) {if ($client->isConnected()) {$data = $client->recv();if ($data !== false && $data != "") {$data = Packet::packDecode($data);if (isset(self::$asynclist[$data["data"]["guid"]]) && isset($data["data"]["isresult"]) && $data["data"]["isresult"] == 1) {//ok recive an async result//remove the guid on the asynclistunset(self::$asynclist[$data["data"]["guid"]]);//add result to async resultself::$asynresult[$data["data"]["guid"]] = $data["data"];self::$asynresult[$data["data"]["guid"]]["fromwait"] = 0;continue;} else {//not in the asynclist drop this packetcontinue;}} else {//remove the resultunset(self::$asynclist[$k]);self::$asynresult[$k] = Packet::packFormat($this->guid, "the recive wrong or timeout", 100009);continue;}} else {//remove the resultunset(self::$asynclist[$k]);self::$asynresult[$k] = Packet::packFormat($this->guid, "Get Async Result Fail: Client Closed.", 100012);continue;}} // foreach the list} else {break;}}//while$result = self::$asynresult;self::$asynresult = array();return Packet::packFormat($this->guid, "OK", 0, $result);}//clean up the async list and resultpublic function clearAsyncData(){self::$asynresult = array();self::$asynclist = array();}private function generateGuid(){//to make sure the guid is unique for the async resultwhile (1) {$guid = md5(microtime(true) . mt_rand(1, 1000000) . mt_rand(1, 1000000));//prevent the guid on the async listif (!isset(self::$asynclist[$guid])) {return $guid;}}}public function __destruct(){}}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
1. 开源生态
2. 协作、人、软件
3. 评估模型