以zookeeper服务为例
绝大部分的服务都会提供基于TCP的长连接接口,供其他程序连接并提供服务,zookeeper也不例外,我们就可以使用这些接口
来通过php或者c封装成扩展。
一般不会使用http/https这种无状态的连接,因为我们连接上服务后需要做很多次请求的,需要保持着连接,所以要用长连接
的形式,然后最终使用完后要在析构函数主动释放连接,php回收机制会在销毁这个对象的同时断开连接。
基于网络socket来实现tcp连接。
socket_read() 本身会阻塞,等待接收信息,但是当收到指定字节的信息(可能没接收完全,服务端还没发送完)就不再阻塞了
,所以需要重复多次来读取信息直到完全接收。
如果服务端一直未发送消息,socket_read 将会一直阻塞着。
如果服务端断开连接,客户端 socket_read 也不会阻塞了。
zookeeper服务端使用 socketServer 服务来代替。
代码仓库:https://gitee.com/phprao/socket.git
<?php
class ZookeeperTest
{
protected $errorCode;
protected $errorMsg;
protected $len = 8129;// 每次读取数据的字节数
protected $client = null;
protected $rcvtimeo = 3;
protected $sndtimeo = 3;
/**
* @return mixed
*/
public function getErrorCode()
{
return $this->errorCode;
}
/**
* @return mixed
*/
public function getErrorMsg()
{
return $this->errorMsg;
}
public function __construct($host, $port, $rcvtimeo = null, $sndtimeo = null)
{
$this->connectTo($host, $port);
$this->setTimeout($rcvtimeo, $sndtimeo);
}
protected function setTimeout($rcvtimeo, $sndtimeo){
if(!is_null($rcvtimeo) && is_int($rcvtimeo)){
$this->rcvtimeo = $rcvtimeo;
}
if(!is_null($sndtimeo) && is_int($sndtimeo)){
$this->sndtimeo = $sndtimeo;
}
socket_set_option($this->client, SOL_SOCKET, SO_RCVTIMEO, ['sec'=>$this->rcvtimeo, 'usec'=>0]); // 发送超时
socket_set_option($this->client, SOL_SOCKET, SO_SNDTIMEO, ['sec'=>$this->sndtimeo, 'usec'=>0]); // 接收超时
}
protected function connectTo($host, $port)
{
// 1. 创建
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket == FALSE) {
$this->setError(1000, socket_strerror(socket_last_error()));
return false;
}
// 2. 链接
$result = socket_connect($socket, $host, $port);
if ($result == FALSE) {
$this->setError(10001, 'connect failed');
return false;
} else {
$this->client = $socket;
return true;
}
}
protected function setError($code, $msg)
{
$this->errorCode = $code;
$this->errorMsg = $msg;
}
protected function sendCmdAndReturn($cmd)
{
// 发送指令
if (!socket_write($this->client, $cmd, strlen($cmd))) {
$this->setError(10002, socket_strerror(socket_last_error()));
return false;
}
// 接收结果
$getMsg = '';
// 3. 从服务端读取全部的数据
do {
$out = @socket_read($this->client, $this->len);
$getMsg .= $out;
if (strlen($out) < $this->len) {
break;
}
} while (true);
return $this->initResult($getMsg);
}
protected function initResult($msg)
{
return $msg;
}
public function set($key, $value)
{
$cmd = "set {$key} {$value}";
return $this->sendCmdAndReturn($cmd);
}
public function get($key, $value)
{
$cmd = "get {$key}";
return $this->sendCmdAndReturn($cmd);
}
public function __destruct()
{
socket_close($this->client);
}
}
$zk = new ZookeeperTest('127.0.0.1', 8888);
var_dump($zk);
$re = $zk->set('name', 'rao');
var_dump($re);
var_dump($zk->getErrorMsg());