概述

这是关于 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

目录结构

  1. ├─ client
  2. ├─ http
  3. ├── mysql.php //测试 MySQL 连接
  4. ├── redis.php //测试 Redis 连接
  5. ├─ tcp
  6. ├── mysql.php //测试 MySQL 连接
  7. ├── redis.php //测试 Redis 连接
  8. ├─ websocket
  9. ├── index.html //实现 API 调用量展示
  10. ├─ controller
  11. ├─ Order.php //实现 MySQL CURD
  12. ├─ Product.php //实现 Redis 调用
  13. ├─ Statistic.php //模拟 API 调用数据
  14. ├─ server
  15. ├─ config
  16. ├── config.php //默认配置
  17. ├── mysql.php //MySQL 配置
  18. ├── redis.php //Redis 配置
  19. ├─ core
  20. ├── Common.php //公共方法
  21. ├── Core.php //核心文件
  22. ├── HandlerException.php //异常处理
  23. ├── callback //回调处理
  24. ├── OnRequest.php
  25. ├── OnReceive.php
  26. ├── OnTask.php
  27. ├── ...
  28. ├── mysql
  29. ├── MysqlDB.php
  30. ├── MysqlPool.php
  31. ├── redis
  32. ├── RedisDB.php
  33. ├── RedisPool.php
  34. ├─ log -- 需要 读/写 权限
  35. ├── ...
  36. ├─ index.php //入口文件

代码

server/core/redis/RedisPool.php

  1. <?php
  2. if (!defined('SERVER_PATH')) exit("No Access");
  3. class RedisPool
  4. {
  5. private static $instance;
  6. private $pool;
  7. private $config;
  8. public static function getInstance($config = null)
  9. {
  10. if (empty(self::$instance)) {
  11. if (empty($config)) {
  12. throw new RuntimeException("Redis config empty");
  13. }
  14. self::$instance = new static($config);
  15. }
  16. return self::$instance;
  17. }
  18. public function __construct($config)
  19. {
  20. if (empty($this->pool)) {
  21. $this->config = $config;
  22. $this->pool = new chan($config['master']['pool_size']);
  23. for ($i = 0; $i < $config['master']['pool_size']; $i++) {
  24. go(function() use ($config) {
  25. $redis = new RedisDB();
  26. $res = $redis->connect($config);
  27. if ($res === false) {
  28. throw new RuntimeException("Failed to connect redis server");
  29. } else {
  30. $this->pool->push($redis);
  31. }
  32. });
  33. }
  34. }
  35. }
  36. public function get()
  37. {
  38. if ($this->pool->length() > 0) {
  39. $redis = $this->pool->pop($this->config['master']['pool_get_timeout']);
  40. if (false === $redis) {
  41. throw new RuntimeException("Pop redis timeout");
  42. }
  43. defer(function () use ($redis) { //释放
  44. $this->pool->push($redis);
  45. });
  46. return $redis;
  47. } else {
  48. throw new RuntimeException("Pool length <= 0");
  49. }
  50. }
  51. }

server/core/redis/RedisDB.php

  1. <?php
  2. if (!defined('SERVER_PATH')) exit("No Access");
  3. class RedisDB
  4. {
  5. private $master;
  6. private $slave;
  7. private $config;
  8. public function __call($name, $arguments)
  9. {
  10. // TODO 主库的操作
  11. $command_master = ['set', 'hset', 'sadd'];
  12. if (in_array($name, $command_master)) {
  13. $db = $this->_get_usable_db('slave');
  14. } else {
  15. $db = $this->_get_usable_db('master');
  16. }
  17. $result = call_user_func_array([$db, $name], $arguments);
  18. return $result;
  19. }
  20. public function connect($config)
  21. {
  22. //主库
  23. $master = new Swoole\Coroutine\Redis();
  24. $res = $master->connect($config['master']['host'], $config['master']['port']);
  25. if ($res === false) {
  26. throw new RuntimeException($master->errCode, $master->errMsg);
  27. } else {
  28. $this->master = $master;
  29. }
  30. //从库
  31. $slave = new Swoole\Coroutine\Redis();
  32. $res = $slave->connect($config['slave']['host'], $config['slave']['port']);
  33. if ($res === false) {
  34. throw new RuntimeException($slave->errCode, $slave->errMsg);
  35. } else {
  36. $this->slave = $slave;
  37. }
  38. $this->config = $config;
  39. return $res;
  40. }
  41. private function _get_usable_db($type)
  42. {
  43. if ($type == 'master') {
  44. if (!$this->master->connected) {
  45. $master = new Swoole\Coroutine\Redis();
  46. $res = $master->connect($this->config['master']['host'], $this->config['master']['port']);
  47. if ($res === false) {
  48. throw new RuntimeException($master->errCode, $master->errMsg);
  49. } else {
  50. $this->master = $master;
  51. }
  52. }
  53. return $this->master;
  54. } elseif ($type == 'slave') {
  55. if (!$this->slave->connected) {
  56. $slave = new Swoole\Coroutine\Redis();
  57. $res = $slave->connect($this->config['slave']['host'], $this->config['slave']['port']);
  58. if ($res === false) {
  59. throw new RuntimeException($slave->errCode, $slave->errMsg);
  60. } else {
  61. $this->slave = $slave;
  62. }
  63. }
  64. return $this->slave;
  65. }
  66. }
  67. }

client/http/redis.php

  1. <?php
  2. $demo = [
  3. 'type' => 'SW',
  4. 'token' => 'Bb1R3YLipbkTp5p0',
  5. 'param' => [
  6. 'class' => 'Product',
  7. 'method' => 'set',
  8. 'param' => [
  9. 'key' => 'C4649',
  10. 'value' => '订单-C4649'
  11. ],
  12. ],
  13. ];
  14. $ch = curl_init();
  15. $options = [
  16. CURLOPT_URL => 'http://10.211.55.4:9509/',
  17. CURLOPT_POST => 1,
  18. CURLOPT_POSTFIELDS => json_encode($demo),
  19. ];
  20. curl_setopt_array($ch, $options);
  21. curl_exec($ch);
  22. curl_close($ch);

client/tpc/redis.php

  1. <?php
  2. class Client
  3. {
  4. private $client;
  5. public function __construct() {
  6. $this->client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
  7. $this->client->on('Connect', [$this, 'onConnect']);
  8. $this->client->on('Receive', [$this, 'onReceive']);
  9. $this->client->on('Close', [$this, 'onClose']);
  10. $this->client->on('Error', [$this, 'onError']);
  11. }
  12. public function connect() {
  13. if(!$fp = $this->client->connect("0.0.0.0", 9510, 1)) {
  14. echo "Error: {$fp->errMsg}[{$fp->errCode}]".PHP_EOL;
  15. return;
  16. }
  17. }
  18. public function onConnect() {
  19. fwrite(STDOUT, "测试RPC (Y or N):");
  20. swoole_event_add(STDIN, function() {
  21. $msg = trim(fgets(STDIN));
  22. if ($msg == 'y') {
  23. $this->send();
  24. }
  25. fwrite(STDOUT, "测试RPC (Y or N):");
  26. });
  27. }
  28. public function onReceive($cli, $data) {
  29. echo '[Received]:'.$data;
  30. }
  31. public function send() {
  32. $demo = [
  33. 'type' => 'SW',
  34. 'token' => 'Bb1R3YLipbkTp5p0',
  35. 'param' => [
  36. 'class' => 'Product',
  37. 'method' => 'get',
  38. 'param' => [
  39. 'code' => 'C4649'
  40. ],
  41. ],
  42. ];
  43. $this->client->send(json_encode($demo));
  44. }
  45. public function onClose() {
  46. echo "Client close connection".PHP_EOL;
  47. }
  48. public function onError() {
  49. }
  50. }
  51. $client = new Client();
  52. $client->connect();

client/websocket/index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <meta name="description" content="">
  8. <meta name="keywords" content="">
  9. <title>Demo</title>
  10. <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
  11. <script src="http://echarts.baidu.com/gallery/vendors/echarts/echarts.min.js"></script>
  12. </head>
  13. <body>
  14. <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
  15. <div id="main" style="width: 900px;height:400px;"></div>
  16. <script type="text/javascript">
  17. if ("WebSocket" in window) {
  18. // 基于准备好的dom,初始化echarts实例
  19. var myChart = echarts.init(document.getElementById('main'));
  20. var wsServer = 'ws://10.211.55.4:9509';
  21. var ws = new WebSocket(wsServer);
  22. ws.onopen = function (evt) {
  23. if (ws.readyState == 1) {
  24. console.log('WebSocket 连接成功...');
  25. } else {
  26. console.log('WebSocket 连接失败...');
  27. }
  28. if (ws.readyState == 1) {
  29. ws.send('开始请求...');
  30. } else {
  31. alert('WebSocket 连接失败');
  32. }
  33. };
  34. ws.onmessage = function (evt) {
  35. console.log('Retrieved data from server: ' + evt.data);
  36. var evt_data = jQuery.parseJSON(evt.data);
  37. myChart.setOption({
  38. xAxis: {
  39. data : evt_data.time
  40. },
  41. series: [{
  42. data: evt_data.value
  43. }]
  44. });
  45. };
  46. ws.onerror = function (evt) {
  47. alert('WebSocket 发生错误');
  48. console.log(evt);
  49. };
  50. ws.onclose = function() {
  51. alert('WebSocket 连接关闭');
  52. console.log('WebSocket 连接关闭...');
  53. };
  54. // 指定图表的配置项和数据
  55. $.ajax({
  56. url : 'http://10.211.55.4:9509/', // 请求url
  57. type : "post", // 提交方式
  58. dataType : "json", // 数据类型
  59. data : {
  60. 'type' : 'SW',
  61. 'token' : 'Bb1R3YLipbkTp5p0',
  62. 'param' : {
  63. 'class' : 'Statistic',
  64. 'method' : 'init'
  65. }
  66. },
  67. beforeSend:function() {
  68. },
  69. success : function(rs) {
  70. if (rs.code != 1) {
  71. alert('获取数据失败');
  72. } else {
  73. var option = {
  74. title: {
  75. text: 'API 调用量',
  76. x:'center'
  77. },
  78. tooltip: {
  79. trigger: 'axis',
  80. axisPointer: {
  81. animation: false
  82. }
  83. },
  84. xAxis: {
  85. type : 'category',
  86. data : rs.data.time
  87. },
  88. yAxis: {
  89. type: 'value',
  90. boundaryGap: [0, '100%'],
  91. name: '使用量',
  92. splitLine: {
  93. show: false
  94. }
  95. },
  96. series: [{
  97. name: '使用量',
  98. type: 'line',
  99. showSymbol: false,
  100. hoverAnimation: false,
  101. data: rs.data.value
  102. }]
  103. };
  104. // 使用刚指定的配置项和数据显示图表。
  105. if (option && typeof option === "object") {
  106. myChart.setOption(option, true);
  107. }
  108. }
  109. },
  110. error : function(){
  111. alert('服务器请求异常');
  112. }
  113. });
  114. } else {
  115. alert("您的浏览器不支持 WebSocket!");
  116. }
  117. </script>
  118. </body>
  119. </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 需要源码的,加我微信。(菜单-> 加我微信-> 扫我)

本文欢迎转发,转发请注明作者和出处,谢谢!

Swoole Redis 连接池的实现的更多相关文章

  1. Swoole 中使用 PDO 连接池、Redis 连接池、Mysqli 连接池

    连接池使用说明 所有连接池的实现均基于 ConnectionPool 原始连接池: 连接池的底层原理是基于 Channel 的自动调度: 开发者需要自己保证归还的连接是可重用的: 若连接不可重用,需要 ...

  2. Redis 连接池的问题

      目录 Redis 连接池的问题    1 1.    前言    1 2.解决方法    1     前言 问题描述:Redis跑了一段时间之后,出现了以下异常. Redis Timeout ex ...

  3. 红眼技术博客 » redis连接池红眼技术博客 » redis连接池

    红眼技术博客 » redis连接池 redis连接池

  4. redis连接池操作

    /** * @类描述 redis 工具 * @功能名 POJO * @author zxf * @date 2014年11月25日 */public final class RedisUtil { p ...

  5. java操作redis redis连接池

    redis作为缓存型数据库,越来越受到大家的欢迎,这里简单介绍一下java如何操作redis. 1.java连接redis java通过需要jedis的jar包获取Jedis连接. jedis-2.8 ...

  6. 三:Redis连接池、JedisPool详解、Redisi分布式

    单机模式: package com.ljq.utils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; ...

  7. 压测过程中,获取不到redis连接池,发现redis连接数高

    说明:图片截得比较大,浏览器放大倍数看即可(涉及到隐私,打了码,请见谅,如果有疑问,欢迎骚扰). 最近在压测过程中,出现获取不到redis连接池的问题 xshell连接redis服务器,查看连接数,发 ...

  8. Redis连接池

    package com.lee.utils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; impor ...

  9. Redis】Java中使用Jedis操作Redis(Maven导入包)、创建Redis连接池

    如果我们使用Java操作Redis, 需要确保已经安装了 redis 服务及 Java redis 驱动. Maven项目可以直接在pom.xml中加入jedis包驱动: <!-- https: ...

随机推荐

  1. webpack 环境搭建

    Webpack环境搭建 一.安装node 1.node官网下载node并安装----node里面内置了npm所以用在安装npm了 2.命令行输入node -v查看node是否安装成功 二.全局安装we ...

  2. python学习-class封装

    # 封装 类=属性+行为 抽像 -class StudentV2: # 类属性 所有的实例可以共享 .不属于任何实例的特性. is_people = True # 类方法 1.装饰器.2.参数是cls ...

  3. sql语句对int类型进行模糊查询

    重点:select * from course where cast(courseId as char) like '%118%'; 首先可以将int类型转换为string类型的值再进行模糊查询,用方 ...

  4. 《Java知识应用》Java压缩文件和解压缩

    今天通过Java实现一下:文件的压缩和解压缩. 1. 压缩 准备文件: 准备代码:(压缩) import java.io.BufferedInputStream; import java.io.Buf ...

  5. js对象属性的查询(点运算符和方括号运算符的区别)

    js中可以通过点(.)和方括号([ ])运算符来获取属性的值.运算符的左侧应该是一个表达式,它返回一个对象.对于点(.)来说,右侧必须是一个以属性名称命名的简单标识符.对于方括号 ([ ])来说方括号 ...

  6. Winform中使用Timer实现滚动字幕效果(附代码下载)

    场景 效果 注: 博客主页: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 新建一个Fo ...

  7. Scrcpy用电脑控制Android手机(支持Windows/macOS/Linux)

    一.scrcpy简介 注意:拼写是scrcpy,非Python爬虫框架Scrapy. scrcpy可以通过adb调试的方式来将手机屏幕投到电脑上,并可以通过电脑控制您的Android设备.它可以通过U ...

  8. kotlin之变量的可空与非空

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/218 kotlin之变量的可空与非空 上面一篇文章,介绍了 ...

  9. Gitlab + Jenkins 的 CI 实践

    0x00 事件 为了开发人员更高效的更新应用而采取的 CI 方式实践. 0x01 过程记录 1.Jenkins 设置 安装插件 Gitlab Hook Plugin Build Authorizati ...

  10. leetcode菜鸡斗智斗勇系列(2)--- 把一个ipv4地址转换成一串数字

    1.原题: https://leetcode.com/problems/defanging-an-ip-address/ 这道题本身很简单, Given a valid (IPv4) IP addre ...