php搭建简单rpc(解决mongodb连接数的问题)
2017-11-20 16:00
274 查看
rpc解释
RPC 的全称是 Remote Procedure Call 是一种进程间通信方式。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的,本质上编写的调用代码基本相同。某网上解释,具体基础解释可以自行百度,其实我通俗的解释好了就是远程调用方法
现在有两台服务器A和B,这时候两台服务器分别运行不同的php服务。这时候两个服务想要互相调用彼此的代码最原始的方式就是彼此开放api接口进行http协议交互。
然后我们可以使用rpc进行代理直接本地服务器代理运行返回结果,rpc使用的C(client)/S(server)结构
目前支持rpc的框架有很多 swoole 和 workman 不一定需要自己手动构建rpc
流程
server端tcp服务器(steam_socket_server)
client连接tcp服务器(steam_socket_clinet)
server端解析接受的字节
本地运行结果
server端将运行结果传输回client端
rpc可以解决mongodb无限连接池的问题,mongodb的连接数跟服务器php-fpm挂载数相关系 理论值为mongodb的连接数 = 服务器php-fpm总数 - 2 如果负载的服务器多了 连接数很容易撑不下去。因为我们采用的是api服务器直接去连接mongodb没有架设一层代理层,这就是为什么我要去构建rpc层的主要原因
server层代码
<?php $errno = 0; $errstr = ''; //先握手 判断 端口是否能正常注册 // creating the socket... $socket = stream_socket_server('tcp://127.0.0.1:8850', $errno, $errstr); if (!$socket) { exit("[create socket error]:{$errno}-{$errstr}"); } fclose($socket); //receive message while (true) { // disconnected every 1 seconds... $this->receiveMessage('127.0.0.1','8850',1); } function receiveMessage($ipServer,$portNumber,$nbSecondsIdle) { $errno = 0; $errstr = ''; // creating the socket... $socket = stream_socket_server('tcp://'.$ipServer.':'.$portNumber, $errno, $errstr); //设置堵塞 类似 redis的队列堵塞 直到拿到数据为止 stream_set_blocking($socket,1); if (!$socket) { $logData = [ 'errno' => $errno, 'errstr' => $errstr, 'msg' => 'create socket error' ]; CoobarLog::error($logData,'rpcerror'); echo "$errstr ($errno)" . PHP_EOL; } else { // while there is connection, i'll receive it... if I didn't receive a message within $nbSecondsIdle seconds, the following function will stop. while ($conn = @stream_socket_accept($socket,$nbSecondsIdle)) { $message = fread($conn, 2048); //1.解析message 进行安全验证 //2.根据message的参数进行本地代理运行 参考我目前的协议 module class function parmas 通过module+class+function 定位需要代理的方法然后call_user_func_array去调用 //3.返回本地运行结果 $result = ********** fwrite($conn,json_encode($result)); fclose($conn); } fclose($socket); } }
推荐rpc的启动方式使用nohup 后台注册线程方式
nohup php rpcserver.php &
client代码
<?php /** * Created by PhpStorm. * User: hls * Date: 2017/11/16 * Time: 下午3:03 */ namespace phprpc; class RpcClient { private static $config = []; private $requestBody = []; private static $init = true; const LENGTH_MAX = 4028; /** * 实例化 * @param $config */ public static function instance($config) { if (!isset($config['host'])) 4000 throw new \Exception('no rpc host',100); if (!isset($config['port'])) throw new \Exception('no rpc port',101); self::$config = $config; self::$init = false; return new self(); } /** * @param $module 模块名 * @param $class 类名 * @param array $params 参数 数组 * @return array|mixed|\stdClass * @throws RpcClientException */ public function sendMessage($module,$class,$function,array $params) { if (self::$init) throw new \Exception('no instance rpc',102); $connection = $this->connect(); $this->setRequestBody($module,$class,$function,$params); return $this->send($connection,json_encode(self::getRequestBody())); } /** * 发送消息 * @param $connection * @param $string * @return array|mixed|\stdClass * @throws RpcClientException */ private function send($connection,$string) { fwrite($connection,$string); $returnData = fgets($connection,self::LENGTH_MAX); fclose($connection); if (empty(json_decode($returnData,true))) throw new \Exception('rpc server return data not illegal'); return json_decode($returnData,true); } /** * 连接rpc服务器 * @param array $config * @return bool|null|resource * @throws RpcClientException */ private function connect($config = []) { $errno = ''; $errstr = ''; if (empty($config)) { $config = self::$config; } $connection = stream_socket_client("tcp://{$config['host']}:{$config['port']}",$errno,$errstr); //重连两次 for ($i = 0;$i < 2;$i++) { if (!$connection) { $connection = stream_socket_client("tcp://{$config['host']}:{$config['port']}",$errno,$errstr); } else { break; } } if (!$connection) throw new \Exception($errstr,$errno); return $connection; } /** * 获取请求包体 * @return array */ private function getRequestBody() { return [ 'username' => $this->requestBody['username'], 'password' => $this->requestBody['password'], 'module' => $this->requestBody['module'], 'class' => $this->requestBody['class'], 'function' => $this->requestBody['function'], 'params' => $this->requestBody['params'], ]; } /** * 设置请求包体 * @param $module * @param $class * @param $params * @return bool */ public function setRequestBody($module,$class,$function,$params) { $this->requestBody['username'] = isset(self::$config['username']) ? self::$config['username'] : ''; $this->requestBody['password'] = isset(self::$config['password']) ? self::$config['password'] : ''; $this->requestBody['module'] = $module; $this->requestBody['class'] = $class; $this->requestBody['function'] = $function; $this->requestBody['params'] = $params; return true; } }
客户端调用
$config = ['host' => '10.46.231.6','port' => 5200,'username' => 'coobar','password' => '52pAVVvxBAIGDPPj']; $result = RpcClient::instance($config)->sendMessage('mongodb','Handle','getConfig',[TurntableConfig::getConfigName(),'172']);
就是比较简单的rpc的原型
还需要在这上面增加优化的地方
rpc-client端需要支持多个rpc-server的轮询支持,防止单台rpc-server服务器压力过大或者服务器崩溃 导致rpc服务器失败
对rpc-server端进行权限认证
对rpc-client 进行重连机制,我目前代码使用的默认重连2次
rpc-server端的日志体系,需要记录每次执行的上行参数和下行返回结果,方便排查问题
rpc-server端进行异常监听,这个很重要,很多东西都是通过实践进行优化
最后开心的是
使用rpc调用mongodb后,连接数不涨啦不涨啦!!!但是现在还是需要进行各种的压测和特殊情况实践,如果直接用于生产环境估计够呛的!相关文章推荐
- CentOS 6搭建LAMP和phpRedisAdmin遇到的问题及解决方法分享
- PHP环境搭建以及解决wampapache服务启动不了的问题
- mongo 固定集合,大文件存储,简单优化 + 三招解决MongoDB的磁盘IO问题
- 编写简单的连接MongoDB数据库C++程序 && 解决编译C++程序时链接MongoDB动态库失败的问题
- 解决PHP使用普通账号连接mongodb报错问题
- 搭建windows+Nginx+PHP配置指南 以及 之后的问题解决
- Win7下PHP+Apache+MySql平台的搭建 以及遇到问题解决
- php + apache 环境搭建过程中出现的问题及解决方法
- 64位win7手动搭建php开发环境Apache+MySQL+php及相关配置,问题解决方法
- 解决WAMP搭建PHP环境后后局域网其他机器无法访问的问题
- Mongodb 副本集搭建问题总结及解决办法
- PHP和JAVA的XML-RPC中文问题解决办法
- php使用ftp远程上传文件类(解决主从文件同步问题的简单方法)
- php无法连接mongodb 3.0问题解决
- php环境搭建问题(简单记录二------php5.2不加载mysql扩展)
- 最简单的lamp环境搭建及问题解决
- RPC利器——PHPRPC(解决PHP调用python问题)
- 一种解决hadoop搭建出现的各种问题的简单粗暴的办法
- [绝对经典]Linux+Mysql+Apache+Php开发环境搭建全过程及问题的解决办法
- php 解决 引用 路径 php 中引用 ext 解决路径问题 简单实用