概述

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

<?php

if (!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

<?php

if (!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

<?php

class 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/', // 请求url
type : "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 需要源码的,加我微信。(菜单-> 加我微信-> 扫我)

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

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. TimePicker - NG-ZORRO设置

    前言 依照官方例子,要给nz-time-picker组件设置某些时间范围不能选择. 正文 根据列表里设置的开始时间与结束时间,去限制弹框的时间组件选择范围.这里用到了组件的nzDisabledHour ...

  2. java基础集合简介Map(三)下

    --Map接口简介 今天来看一看map集合,map映射接口,用于存放键值对,<key,value>,通过key来查找value,顾名思义key不能为空,唯一且不重复,不然底层怎么查呢! 可 ...

  3. 这几种JavaScript语法不要轻易使用,容易出事

    文章目录 12种不宜使用的JavaScript语法 1. == 2. with 3. eval 4. continue 5. switch 贯穿 6. 单行的块结构 7. ++和-- 8. 位运算符 ...

  4. Linux命令-grep,sed,awk

    grep (global search regular expression[RE] and print out the line) 正则表达式全局搜索并将行打印出来 在文件中查找包含字符串" ...

  5. 【并发编程】Java并发编程传送门

    本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. [并发编程系列博客传送门](https://www.cnblogs.com/54 ...

  6. 一个小工具帮你搞定实时监控Nginx服务器

    Linux运维工程师的首要职责就是保证业务7 x 24小时稳定的运行,监控Web服务器对于查看网站上发生的情况至关重要.关注最多的便是日志变动,查看实时日志文件变动大家第一反应应该是'tail -f ...

  7. SpringCloud(三):服务消费以及负载均衡(RestTemplate+Ribbon)

    一.什么是Ribbon: Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法. 将Netflix的中间层服务连接在一起.Ribbon客户端组件提供一系列完善的配置项如连 ...

  8. scrapy框架(三)

    scrapy框架(三) CrawlSpider类 创建CrawlSpider  # 创建项目后 $ scrapy genspider -t crawl spider_name website_doma ...

  9. BOM的补充

    1.首先我们要知道BOM是干什么的? BOM和DOM.ES是JavaScript的重要三个组成部分: Bom的核心操作是window:简单来说就是用来操作浏览器的,他是js访问浏览器的接口,它里面封装 ...

  10. 深入了解angularjs中的$digest与$apply方法,从区别聊到使用优化

     壹 ❀ 引 如果有人问,在angularjs中修改模型数据为何视图会同步更新呢,我想大多数人一定会回答脏检查(Dirty Checking)相关概念.没错,在angularjs中作用域(scope) ...