第 85 篇文章
这是关于 Swoole 入门学习的第九篇文章:Swoole Redis 连接池的实现。
概述
收到读者反馈,“亮哥,文章能多点图片吗?就是将运行结果以图片的形式展示...”
我个人觉得这是比较懒、动手能力差的表现,恩... 要勤快些。
但谁让文章是写给你们看的那,我以后尽量文章写的图文并茂一点。
上篇文章 分享了 MySQL 连接池,这篇文章 咱们来分享下 Redis 连接池。
在上篇文章的基础上进行简单调整即可,将实例化 MySQL 的地方,修改成实例化 Redis 即可,还要注意一些方法的调整。
这篇文章仅仅只实现一个 Redis 连接池,篇幅就太少了,顺便将前几篇整合一下。
Demo 中大概包含这些点:
实现 MySQL 连接池
实现 MySQL CURD 方法的定义
实现 Redis 连接池
实现 Redis 方法的定义
满足 HTTP、TCP、WebSocket 调用
提供 Demo 供测试
调整 目录结构
HTTP 调用:
实现 读取 MySQL 中数据的 Demo
实现 读取 Redis 中数据的 Demo

TCP 调用:
实现 读取 MySQL 中数据的 Demo
实现 读取 Redis 中数据的 Demo

WebSocket 调用:
- 实现 每秒展示 API 调用量 Demo

目录结构
├─ client│ ├─ http│ ├── mysql.php //测试 MySQL 连接│ ├── redis.php //测试 Redis 连接│ ├─ tcp│ ├── mysql.php //测试 MySQL 连接│ ├── redis.php //测试 Redis 连接│ ├─ websocket│ ├── index.html //实现 API 调用量展示├─ controller│ ├─ Order.php //实现 MySQL CURD│ ├─ Product.php //实现 Redis 调用│ ├─ Statistic.php //模拟 API 调用数据├─ server│ ├─ config│ ├── config.php //默认配置│ ├── mysql.php //MySQL 配置│ ├── redis.php //Redis 配置│ ├─ core│ ├── Common.php //公共方法│ ├── Core.php //核心文件│ ├── HandlerException.php //异常处理│ ├── callback //回调处理│ ├── OnRequest.php│ ├── OnReceive.php│ ├── OnTask.php│ ├── ...│ ├── mysql│ ├── MysqlDB.php│ ├── MysqlPool.php│ ├── redis│ ├── RedisDB.php│ ├── RedisPool.php│ ├─ log -- 需要 读/写 权限│ ├── ...├─ index.php //入口文件
代码
server/core/redis/RedisPool.php
<?phpif (!defined('SERVER_PATH')) exit("No Access");class RedisPool{private static $instance;private $pool;private $config;public static function getInstance($config = null){if (empty(self::$instance)) {if (empty($config)) {throw new RuntimeException("Redis config empty");}self::$instance = new static($config);}return self::$instance;}public function __construct($config){if (empty($this->pool)) {$this->config = $config;$this->pool = new chan($config['master']['pool_size']);for ($i = 0; $i < $config['master']['pool_size']; $i++) {go(function() use ($config) {$redis = new RedisDB();$res = $redis->connect($config);if ($res === false) {throw new RuntimeException("Failed to connect redis server");} else {$this->pool->push($redis);}});}}}public function get(){if ($this->pool->length() > 0) {$redis = $this->pool->pop($this->config['master']['pool_get_timeout']);if (false === $redis) {throw new RuntimeException("Pop redis timeout");}defer(function () use ($redis) { //释放$this->pool->push($redis);});return $redis;} else {throw new RuntimeException("Pool length <= 0");}}}
server/core/redis/RedisDB.php
<?phpif (!defined('SERVER_PATH')) exit("No Access");class RedisDB{private $master;private $slave;private $config;public function __call($name, $arguments){// TODO 主库的操作$command_master = ['set', 'hset', 'sadd'];if (!in_array($name, $command_master)) {$db = $this->_get_usable_db('slave');} else {$db = $this->_get_usable_db('master');}$result = call_user_func_array([$db, $name], $arguments);return $result;}public function connect($config){//主库$master = new Swoole\Coroutine\Redis();$res = $master->connect($config['master']['host'], $config['master']['port']);if ($res === false) {throw new RuntimeException($master->errCode, $master->errMsg);} else {$this->master = $master;}//从库$slave = new Swoole\Coroutine\Redis();$res = $slave->connect($config['slave']['host'], $config['slave']['port']);if ($res === false) {throw new RuntimeException($slave->errCode, $slave->errMsg);} else {$this->slave = $slave;}$this->config = $config;return $res;}private function _get_usable_db($type){if ($type == 'master') {if (!$this->master->connected) {$master = new Swoole\Coroutine\Redis();$res = $master->connect($this->config['master']['host'], $this->config['master']['port']);if ($res === false) {throw new RuntimeException($master->errCode, $master->errMsg);} else {$this->master = $master;}}return $this->master;} elseif ($type == 'slave') {if (!$this->slave->connected) {$slave = new Swoole\Coroutine\Redis();$res = $slave->connect($this->config['slave']['host'], $this->config['slave']['port']);if ($res === false) {throw new RuntimeException($slave->errCode, $slave->errMsg);} else {$this->slave = $slave;}}return $this->slave;}}}
client/http/redis.php
<?php$demo = ['type' => 'SW','token' => 'Bb1R3YLipbkTp5p0','param' => ['class' => 'Product','method' => 'set','param' => ['key' => 'C4649','value' => '订单-C4649'],],];$ch = curl_init();$options = [CURLOPT_URL => 'http://10.211.55.4:9509/',CURLOPT_POST => 1,CURLOPT_POSTFIELDS => json_encode($demo),];curl_setopt_array($ch, $options);curl_exec($ch);curl_close($ch);
client/tpc/redis.php
<?phpclass Client{private $client;public function __construct() {$this->client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);$this->client->on('Connect', [$this, 'onConnect']);$this->client->on('Receive', [$this, 'onReceive']);$this->client->on('Close', [$this, 'onClose']);$this->client->on('Error', [$this, 'onError']);}public function connect() {if(!$fp = $this->client->connect("0.0.0.0", 9510, 1)) {echo "Error: {$fp->errMsg}[{$fp->errCode}]".PHP_EOL;return;}}public function onConnect() {fwrite(STDOUT, "测试RPC (Y or N):");swoole_event_add(STDIN, function() {$msg = trim(fgets(STDIN));if ($msg == 'y') {$this->send();}fwrite(STDOUT, "测试RPC (Y or N):");});}public function onReceive($cli, $data) {echo '[Received]:'.$data;}public function send() {$demo = ['type' => 'SW','token' => 'Bb1R3YLipbkTp5p0','param' => ['class' => 'Product','method' => 'get','param' => ['code' => 'C4649'],],];$this->client->send(json_encode($demo));}public function onClose() {echo "Client close connection".PHP_EOL;}public function onError() {}}$client = new Client();$client->connect();
client/websocket/index.html
<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content=""><meta name="keywords" content=""><title>Demo</title><script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script><script src="http://echarts.baidu.com/gallery/vendors/echarts/echarts.min.js"></script></head><body><!-- 为ECharts准备一个具备大小(宽高)的Dom --><div id="main" style="width: 900px;height:400px;"></div><script type="text/javascript">if ("WebSocket" in window) {// 基于准备好的dom,初始化echarts实例var myChart = echarts.init(document.getElementById('main'));var wsServer = 'ws://10.211.55.4:9509';var ws = new WebSocket(wsServer);ws.onopen = function (evt) {if (ws.readyState == 1) {console.log('WebSocket 连接成功...');} else {console.log('WebSocket 连接失败...');}if (ws.readyState == 1) {ws.send('开始请求...');} else {alert('WebSocket 连接失败');}};ws.onmessage = function (evt) {console.log('Retrieved data from server: ' + evt.data);var evt_data = jQuery.parseJSON(evt.data);myChart.setOption({xAxis: {data : evt_data.time},series: [{data: evt_data.value}]});};ws.onerror = function (evt) {alert('WebSocket 发生错误');console.log(evt);};ws.onclose = function() {alert('WebSocket 连接关闭');console.log('WebSocket 连接关闭...');};// 指定图表的配置项和数据$.ajax({url : 'http://10.211.55.4:9509/', // 请求urltype : "post", // 提交方式dataType : "json", // 数据类型data : {'type' : 'SW','token' : 'Bb1R3YLipbkTp5p0','param' : {'class' : 'Statistic','method' : 'init'}},beforeSend:function() {},success : function(rs) {if (rs.code != 1) {alert('获取数据失败');} else {var option = {title: {text: 'API 调用量',x:'center'},tooltip: {trigger: 'axis',axisPointer: {animation: false}},xAxis: {type : 'category',data : rs.data.time},yAxis: {type: 'value',boundaryGap: [0, '100%'],name: '使用量',splitLine: {show: false}},series: [{name: '使用量',type: 'line',showSymbol: false,hoverAnimation: false,data: rs.data.value}]};// 使用刚指定的配置项和数据显示图表。if (option && typeof option === "object") {myChart.setOption(option, true);}}},error : function(){alert('服务器请求异常');}});} else {alert("您的浏览器不支持 WebSocket!");}</script></body></html>
还涉及到,OnMessage.php、OnTask.php 、OnWorkerStart.php 等,就不贴代码了。
运行
小框架的启动/关闭/热加载,看看这篇文章: 第六篇:Swoole 整合成一个小框架
里面 Demo 在 client 文件夹下。
http 目录下的文件,放到自己虚拟目录下,用浏览器访问。
tcp 目录下的文件,在 CLI 下运行。
websocket 目录下的文件,直接点击在浏览器访问。
扩展
官方协程 Redis 客户端手册:
https://wiki.swoole.com/wiki/page/589.html
大家可以尝试使用官方提供的其他方法。
小结
Demo 代码仅供参考,里面有很多不严谨的地方,根据自己的需要进行修改 ...
上面的 Demo 需要源码的,加我微信。(菜单-> 加我微信-> 扫我)
推荐阅读
本文欢迎转发,转发请注明作者和出处,谢谢!
本文分享自微信公众号 - 新亮笔记(XinLiangTalk)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。


