swoolefy是基于swoole实现的轻量级高性能框架,框架支持http,websocket,udp服务器,以及基于tcp实现可扩展的rpc服务,同时支持composer包方式安装部署项目。基于实用,swoolefy抽象Event事件处理类,实现与底层的回调的解耦,支持同步|异步调用,内置view、log、session、mysql、redis、memcached、mongodb等常用组件等。

swoolefy经过多个版本的迭代和实践,以及优化,性能稳定,目前已经至1.0.6版本。

1.0.6版本主要的更新:

 github:https://github.com/bingcool/swoolefy

文档:https://www.kancloud.cn/bingcoolhuang/php-swoole-swoolefy/587501

下面是一个rpc的demo:

RPC的服务端:

1、设置协议层配置文件protocol/rpc/config.php,添加packet设置:

'packet'=>[
		// 服务端使用长度检查packet时,设置包头结构体,如果使用eof时,不需要设置,底层会读取package_eof
		'server'=>[
			'pack_header_struct' => ['length'=>'N', 'version'=>'a10', 'name'=>'a30', 'request_id'=>'a12'],
			'pack_length_key' => 'length',
			'serialize_type' => 'json'
		],
		//客户端解包设置
		'client' => [
				'pack_check_type' => 'length',
				'pack_header_struct' => ['length'=>'N', 'version'=>'a10', 'name'=>'a30', 'request_id'=>'a12'],
				'pack_length_key' => 'length',
				'serialize_type' => 'json'
		]

	],

2、服务端的控制器中

<?php
namespace Service\Coms\Book;

use Swoolefy\Core\Swfy;
use Swoolefy\Core\SController;
use Swoolefy\Core\Application;
use Swoolefy\Core\Task\TaskManager;

class BookmanageService extends SController {

	public function test($params) {
		// 客户端发送过来的数据     
		var_dump($params);
		$data = $params;
        //客户端的头部与服务端头部相同,当然可以根据返回数据不同,返回包头可以不同。
		$header = ['length'=>'', 'version'=>'1.0.1', 'name'=>'bingcool', 'request_id'=>$this->getRpcPackHeader()['request_id']];
		//   返回数据给客户端     
		$this->send($this->fd, $data, $header);
	}

至此,很很简单的服务端就ok了,其实就是设置一下packet就可以了。

RPC的客户端:

client端是基于swoole_client实现的,所以环境中必须安装swoole和swoole_serialize扩展。这个客户端其实是可以用在Apache|php-fpm的环境中,并且支持并行调用。

1、在/protocol/rpc/RpcServer.php的onWorkerStart中注册client的访问的服务,或者在start_init配置项中的类的onWorkerStart函数中注册服务,因为该类必须继承于Swoolefy\Core\StartInit的。

/**
	 * onWorkerStart
	 * @param    $server
	 * @return   
	 */
	public function onWorkerStart($server, $worker_id) {
		if(Swfy::isWorkerProcess()) {
			$productServiceConfig = [
				'servers' => '127.0.0.1:9504',
				'timeout' => 0.5,
				'noblock' => 0
			];

			$orderServiceConfig = array(
				'servers' => '127.0.0.1:9506',
				'timeout' => 0.5,
				'noblock' => 0
			);
			//客户端pack长度检查设置
			$setting = array(
				'open_length_check'     => 1,
			    'package_length_type'   => 'N',
			    'package_length_offset' => 0,       //第N个字节是包长度的值
			    'package_body_offset'   => 56,       //第几个字节开始计算长度
			    'package_max_length'    => 2000000,  //协议最大长度
			);
			// 包头结构体设置,默认客户端与服务端包头结构体相同
			$header_struct = array(
				'length'=>'N', 
				'version'=>'a10', 
				'name'=>'a30', 
				'request_id'=>'a12'
			);
			// 注册product服务
			RpcClientManager::getInstance(true)->registerService('productService', productServiceConfig, $setting, $header_struct,[
				// swoole_client 长连接             	
				'swoole_keep' => false
			]);
			// 注册order服务
			RpcClientManager::getInstance(true)->registerService('orderService', $orderServiceConfig, $setting, $header_struct,[
            // swoole_client 长连接
				'swoole_keep' => true
			]);
		}
	}

2、在controller或者model中可以直接调用向服务端发送数据
a、阻塞调用,请求/应答模式

public function test() {
		$callable = 'Service\Coms\Book\BookmanageService::test';
		$params = ['content'=>'我是rpc的客户端,我向你发送消息'];
		$header = ['length'=>'', 'version'=>'1.0.0', 'name'=>'bingcool'];
        //调用发送数据
		$client1 = RpcClientManager::getInstance()->getServices('productService')->buildHeaderRequestId($header)->waitCall($callable, $params);
        //阻塞等待数据返回
		$res1 = $client1->waitRecv();
		var_dump($res1);
}

上面将返回一个数据,数组里有两个元素,arr[0]是包头,arr[1]是包体数据,例如:

array(2) {
  [0] => array(4) {
    ["length"] => int(83)
    ["version"] => string(10) "1.0.0"
    ["name"] => string(30) "bingcool"
    ["request_id"] => string(12) "d189dfd5b997"
  }
  [1] => array(4) {
    ["content"] => array(1) {
      ["content"] => string(16) "我是rpc的服务端,我已收到你发送的消息"
  }
}

b、并行调用,然后在时间内等待接收所有数据,再进行数据聚合

// productService 调用
$callable = 'Service\Coms\Book\BookmanageService::test';
$params = ['content'=>'hhhhhhhhhhhhhhhh'];
$header = ['length'=>'', 'version'=>'1.0.1', 'name'=>'bingcool'];
$client2 = RpcClientManager::getInstance()->getServices('productService')->buildHeaderRequestId($header)->waitCall($callable, $params);

// orderService 调用
$callable = 'Service\Coms\Book\BookmanageService::test';
$params = ['content'=>'hhhhhhhhhhhhhhhh'];
$header = ['length'=>'', 'version'=>'1.0.1', 'name'=>'bingcool'];
$client3 = RpcClientManager::getInstance()->getServices('orderService')->buildHeaderRequestId($header)->waitCall($callable, $params);

// productService 调用
$callable = 'Service\Coms\Book\BookmanageService::test';
$params = ['content'=>'hhhhhhhhhhhhhhhh'];
$header = ['length'=>'', 'version'=>'1.0.1', 'name'=>'bingcool'];
$client4 = RpcClientManager::getInstance()->getServices('productService')->buildHeaderRequestId($header)->waitCall($callable, $params);

// productService 调用
$callable = 'Service\Coms\Book\BookmanageService::test';
$params = ['content'=>'hhhhhhhhhhhhhhhh'];
$header = ['length'=>'', 'version'=>'1.0.1', 'name'=>'bingcool'];
$client5 = RpcClientManager::getInstance()->getServices('productService')->buildHeaderRequestId($header)->waitCall($callable, $params);
// 并行等待数据返回,这里返回的是所有调用的数据,例如上面调用4次,那么将返回给数组$res中4个整包元素
$res = RpcClientManager::getInstance()->multiRecv();
var_dump($res);
~~~
那么如何获取其中某一个的客户端的数据呢,很简单
~~~
// 获取整包数据,包括包头和包体
$pack_data = $client2->getResponsePackData();
// 分离包头包体
list($pack_header, $pack_body) = $pack_data;

// 直接获取包头
$pack_header = $client2->getResponsePackHeader();
或者
$pack_header = $pack_data[0];

// 直接获取包头
$pack_body = $client2->getResponsePackBody();
或者
$pack_body =  $pack_data[1];

这是一个简单的说明,具体可以参考文档。



新闻来源:基于 Swoole 的轻量级高性能框架 swoolefy 1.0.6 发布