init
This commit is contained in:
+30
@@ -0,0 +1,30 @@
|
||||
# GlobalData
|
||||
进程间数据共享组件,用于分布式数据共享。服务端基于[Workerman](https://github.com/walkor/Workerman)。客户端可用于任何PHP项目。
|
||||
|
||||
# 服务端
|
||||
```php
|
||||
use Workerman\Worker;
|
||||
require_once __DIR__ . '/../../Workerman/Autoloader.php';
|
||||
require_once __DIR__ . '/../src/Server.php';
|
||||
|
||||
$worker = new GlobalData\Server('127.0.0.1', 2207);
|
||||
|
||||
Worker::runAll();
|
||||
```
|
||||
|
||||
# 客户端
|
||||
```php
|
||||
require_once __DIR__ . '/../src/Client.php';
|
||||
|
||||
$global = new GlobalData\Client('127.0.0.1:2207');
|
||||
|
||||
var_export(isset($global->abc));
|
||||
$global->abc = array(1,2,3);
|
||||
var_export($global->abc);
|
||||
unset($global->abc);
|
||||
var_export($global->abc);
|
||||
$global->abc = array(1,2,3);
|
||||
var_export($global->abc);
|
||||
var_export($global->cas('abc', array(1,2,3), array(5,6,7)));
|
||||
var_export($global->abc);
|
||||
```
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name" : "workerman/globaldata",
|
||||
"type" : "library",
|
||||
"homepage": "http://www.workerman.net",
|
||||
"license" : "MIT",
|
||||
"require": {
|
||||
"workerman/workerman" : ">=3.3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {"GlobalData\\": "./src"}
|
||||
}
|
||||
}
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
namespace GlobalData;
|
||||
/**
|
||||
* Global data client.
|
||||
* @version 1.0.3
|
||||
*/
|
||||
class Client
|
||||
{
|
||||
/**
|
||||
* Timeout.
|
||||
* @var int
|
||||
*/
|
||||
public $timeout = 5;
|
||||
|
||||
/**
|
||||
* Heartbeat interval.
|
||||
* @var int
|
||||
*/
|
||||
public $pingInterval = 25;
|
||||
|
||||
/**
|
||||
* Global data server address.
|
||||
* @var array
|
||||
*/
|
||||
protected $_globalServers = array();
|
||||
|
||||
/**
|
||||
* Connection to global server.
|
||||
* @var resource
|
||||
*/
|
||||
protected $_globalConnections = null;
|
||||
|
||||
/**
|
||||
* Cache.
|
||||
* @var array
|
||||
*/
|
||||
protected $_cache = array();
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
* @param array | string $servers
|
||||
*/
|
||||
public function __construct($servers)
|
||||
{
|
||||
if(empty($servers))
|
||||
{
|
||||
throw new \Exception('servers empty');
|
||||
}
|
||||
$this->_globalServers = array_values((array)$servers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to global server.
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getConnection($key)
|
||||
{
|
||||
$offset = crc32($key)%count($this->_globalServers);
|
||||
if($offset < 0)
|
||||
{
|
||||
$offset = -$offset;
|
||||
}
|
||||
|
||||
if(!isset($this->_globalConnections[$offset]) || !is_resource($this->_globalConnections[$offset]) || feof($this->_globalConnections[$offset]))
|
||||
{
|
||||
$address = $this->_globalServers[$offset];
|
||||
if(strpos($address,'unix://')!==0){
|
||||
$address = "tcp://{$address}";
|
||||
}
|
||||
|
||||
$connection = stream_socket_client($address, $code, $msg, $this->timeout);
|
||||
if(!$connection)
|
||||
{
|
||||
throw new \Exception($msg);
|
||||
}
|
||||
stream_set_timeout($connection, $this->timeout);
|
||||
if(class_exists('\Workerman\Timer') && php_sapi_name() === 'cli')
|
||||
{
|
||||
$timer_id = \Workerman\Timer::add($this->pingInterval, function($connection)use(&$timer_id)
|
||||
{
|
||||
$buffer = pack('N', 8)."ping";
|
||||
if(strlen($buffer) !== @fwrite($connection, $buffer))
|
||||
{
|
||||
@fclose($connection);
|
||||
\Workerman\Timer::del($timer_id);
|
||||
}
|
||||
}, array($connection));
|
||||
}
|
||||
$this->_globalConnections[$offset] = $connection;
|
||||
}
|
||||
return $this->_globalConnections[$offset];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Magic methods __set.
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __set($key, $value)
|
||||
{
|
||||
$connection = $this->getConnection($key);
|
||||
$this->writeToRemote(array(
|
||||
'cmd' => 'set',
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
), $connection);
|
||||
$this->readFromRemote($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic methods __isset.
|
||||
* @param string $key
|
||||
*/
|
||||
public function __isset($key)
|
||||
{
|
||||
return null !== $this->__get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic methods __unset.
|
||||
* @param string $key
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __unset($key)
|
||||
{
|
||||
$connection = $this->getConnection($key);
|
||||
$this->writeToRemote(array(
|
||||
'cmd' => 'delete',
|
||||
'key' => $key
|
||||
), $connection);
|
||||
$this->readFromRemote($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic methods __get.
|
||||
* @param string $key
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
$connection = $this->getConnection($key);
|
||||
$this->writeToRemote(array(
|
||||
'cmd' => 'get',
|
||||
'key' => $key,
|
||||
), $connection);
|
||||
return $this->readFromRemote($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cas.
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function cas($key, $old_value, $new_value)
|
||||
{
|
||||
$connection = $this->getConnection($key);
|
||||
$this->writeToRemote(array(
|
||||
'cmd' => 'cas',
|
||||
'md5' => md5(serialize($old_value)),
|
||||
'key' => $key,
|
||||
'value' => $new_value,
|
||||
),$connection);
|
||||
return $this->readFromRemote($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add.
|
||||
* @param string $key
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function add($key, $value)
|
||||
{
|
||||
$connection = $this->getConnection($key);
|
||||
$this->writeToRemote(array(
|
||||
'cmd' => 'add',
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
), $connection);
|
||||
return $this->readFromRemote($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment.
|
||||
* @param string $key
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function increment($key, $step = 1)
|
||||
{
|
||||
$connection = $this->getConnection($key);
|
||||
$this->writeToRemote(array(
|
||||
'cmd' => 'increment',
|
||||
'key' => $key,
|
||||
'step' => $step,
|
||||
), $connection);
|
||||
return $this->readFromRemote($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to global server.
|
||||
* @param string $buffer
|
||||
*/
|
||||
protected function writeToRemote($data, $connection)
|
||||
{
|
||||
$buffer = serialize($data);
|
||||
$buffer = pack('N',4 + strlen($buffer)) . $buffer;
|
||||
$len = fwrite($connection, $buffer);
|
||||
if($len !== strlen($buffer))
|
||||
{
|
||||
throw new \Exception('writeToRemote fail');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from global server.
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function readFromRemote($connection)
|
||||
{
|
||||
$all_buffer = '';
|
||||
$total_len = 4;
|
||||
$head_read = false;
|
||||
while(1)
|
||||
{
|
||||
$buffer = fread($connection, 8192);
|
||||
if($buffer === '' || $buffer === false)
|
||||
{
|
||||
throw new \Exception('readFromRemote fail');
|
||||
}
|
||||
$all_buffer .= $buffer;
|
||||
$recv_len = strlen($all_buffer);
|
||||
if($recv_len >= $total_len)
|
||||
{
|
||||
if($head_read)
|
||||
{
|
||||
break;
|
||||
}
|
||||
$unpack_data = unpack('Ntotal_length', $all_buffer);
|
||||
$total_len = $unpack_data['total_length'];
|
||||
if($recv_len >= $total_len)
|
||||
{
|
||||
break;
|
||||
}
|
||||
$head_read = true;
|
||||
}
|
||||
}
|
||||
return unserialize(substr($all_buffer, 4));
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
namespace GlobalData;
|
||||
use Workerman\Protocols\Frame;
|
||||
use Workerman\Worker;
|
||||
|
||||
/**
|
||||
* Global data server.
|
||||
*/
|
||||
class Server
|
||||
{
|
||||
/**
|
||||
* Worker instance.
|
||||
* @var worker
|
||||
*/
|
||||
protected $_worker = null;
|
||||
|
||||
/**
|
||||
* All data.
|
||||
* @var array
|
||||
*/
|
||||
protected $_dataArray = array();
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
* @param string $ip
|
||||
* @param int $port
|
||||
*/
|
||||
public function __construct($ip = '0.0.0.0', $port = 2207)
|
||||
{
|
||||
if (strpos($ip, 'unix://') === 0) {
|
||||
$worker = new Worker($ip);
|
||||
$worker->protocol = Frame::class;
|
||||
} else {
|
||||
$worker = new Worker("frame://$ip:$port");
|
||||
}
|
||||
|
||||
$worker->count = 1;
|
||||
$worker->name = 'globalDataServer';
|
||||
$worker->onMessage = array($this, 'onMessage');
|
||||
$worker->reloadable = false;
|
||||
$this->_worker = $worker;
|
||||
}
|
||||
|
||||
/**
|
||||
* onMessage.
|
||||
* @param TcpConnection $connection
|
||||
* @param string $buffer
|
||||
*/
|
||||
public function onMessage($connection, $buffer)
|
||||
{
|
||||
if($buffer === 'ping')
|
||||
{
|
||||
return;
|
||||
}
|
||||
$data = unserialize($buffer);
|
||||
if(!$buffer || !isset($data['cmd']) || !isset($data['key']))
|
||||
{
|
||||
return $connection->close(serialize('bad request'));
|
||||
}
|
||||
$cmd = $data['cmd'];
|
||||
$key = $data['key'];
|
||||
switch($cmd)
|
||||
{
|
||||
case 'get':
|
||||
if(!isset($this->_dataArray[$key]))
|
||||
{
|
||||
return $connection->send('N;');
|
||||
}
|
||||
return $connection->send(serialize($this->_dataArray[$key]));
|
||||
break;
|
||||
case 'set':
|
||||
$this->_dataArray[$key] = $data['value'];
|
||||
$connection->send('b:1;');
|
||||
break;
|
||||
case 'add':
|
||||
if(isset($this->_dataArray[$key]))
|
||||
{
|
||||
return $connection->send('b:0;');
|
||||
}
|
||||
$this->_dataArray[$key] = $data['value'];
|
||||
return $connection->send('b:1;');
|
||||
break;
|
||||
case 'increment':
|
||||
if(!isset($this->_dataArray[$key]))
|
||||
{
|
||||
return $connection->send('b:0;');
|
||||
}
|
||||
if(!is_numeric($this->_dataArray[$key]))
|
||||
{
|
||||
$this->_dataArray[$key] = 0;
|
||||
}
|
||||
$this->_dataArray[$key] = $this->_dataArray[$key]+$data['step'];
|
||||
return $connection->send(serialize($this->_dataArray[$key]));
|
||||
break;
|
||||
case 'cas':
|
||||
$old_value = !isset($this->_dataArray[$key]) ? null : $this->_dataArray[$key];
|
||||
if(md5(serialize($old_value)) === $data['md5'])
|
||||
{
|
||||
$this->_dataArray[$key] = $data['value'];
|
||||
return $connection->send('b:1;');
|
||||
}
|
||||
$connection->send('b:0;');
|
||||
break;
|
||||
case 'delete':
|
||||
unset($this->_dataArray[$key]);
|
||||
$connection->send('b:1;');
|
||||
break;
|
||||
default:
|
||||
$connection->close(serialize('bad cmd '. $cmd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
use GlobalData\Client;
|
||||
require_once __DIR__ . '/../src/Client.php';
|
||||
|
||||
$global = new Client('127.0.0.1:2207');
|
||||
|
||||
$i = $j = 10000;
|
||||
$t = microtime(true);
|
||||
while($i--)
|
||||
{
|
||||
$global->a = array(1,3);
|
||||
}
|
||||
|
||||
echo ceil($j/(microtime(true)-$t))."qps\n";
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
use Workerman\Worker;
|
||||
use GlobalData\Server;
|
||||
require_once __DIR__ . '/../../Workerman/Autoloader.php';
|
||||
require_once __DIR__ . '/../src/Server.php';
|
||||
$worker = new Server();
|
||||
Worker::runAll();
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
use GlobalData\Client;
|
||||
require_once __DIR__ . '/../src/Client.php';
|
||||
|
||||
$global = new Client('127.0.0.1:2207');
|
||||
|
||||
echo "\n\nisset(\$global->abc)=";
|
||||
var_export(isset($global->abc));
|
||||
|
||||
echo "\n\n\$global->add('abc', 10)=";
|
||||
var_export($global->add('abc', 10));
|
||||
echo "\nnow \$global->abc=";
|
||||
var_export($global->abc);
|
||||
|
||||
echo "\n\n\$global->increment('abc', 2)=";
|
||||
var_export($global->increment('abc', 2));
|
||||
echo "\nnow \$global->abc=";
|
||||
var_export($global->abc);
|
||||
|
||||
echo ";\n\nset \$global->abc=";
|
||||
$global->abc = array(1,2,3);
|
||||
var_export($global->abc);
|
||||
|
||||
echo ";\n\nunset(\$global->abc)\n";
|
||||
unset($global->abc);
|
||||
echo "\nnow \$global->abc=";
|
||||
var_export($global->abc);
|
||||
|
||||
echo "\n\nisset(\$global->abc)=";
|
||||
var_export(isset($global->abc));
|
||||
|
||||
echo ";\n\nset \$global->abc=";
|
||||
$global->abc = array(1,2,3);
|
||||
var_export($global->abc);
|
||||
|
||||
echo ";\n\n\$global->cas('abc', array(1,2,3), array(5,6,7))=";
|
||||
var_export($global->cas('abc', array(1,2,3), array(5,6,7)));
|
||||
|
||||
echo ";\n\nnow \$global->abc=";
|
||||
var_export($global->abc);
|
||||
Reference in New Issue
Block a user