Redis 实战

Redis的使用场景测试用例

准备

  • RedisController.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\Api\Controller;

use App\Api\Controller as BaseController;
use App\Support\RedisTool;
use Collective\Annotations\Routing\Annotations\Annotations\{
Controller, Post
};

/**
* Class RedisController
* @package App\Api\Controller
* @Controller(prefix="/api/redis")
*/
class RedisController extends BaseController
{
private $redis;

public function __construct()
{
$this->redis = RedisTool::getInstance();
}
}
  • RedisTool.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

namespace App\Support;

use App\Concerns\Singleton;
use Illuminate\Support\Facades\Log;

class RedisTool extends \Redis
{
use Singleton;

public function __construct()
{
parent::__construct();

$host = config('database.redis.default.host');
$port = config('database.redis.default.port');

if (! $this->connect($host, $port)) {
Log::error("redis fail to connect to $host:$port");
}
}
}
  • Singleton.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

namespace App\Concerns;

trait Singleton
{
public static function getInstance()
{
static $instance = null;

if (is_null($instance)) {
$instance = new static();
}

return $instance;
}
}

基于 redis 字符串 string 类型的简单缓存

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @Post("/test_string")
*/
public function test_string()
{
$this->redis->set('cache_key', json_encode([
'data-list' => '这是个缓存数据'
]), JSON_UNESCAPED_UNICODE);

$res = $this->redis->get('cache_key');
var_dump($res);
}

基于 redis 列表 list 类型的简单队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 实现队列
* @Post("/test_queue")
*/
public function test_queue()
{
// 进队列
$user_id = mt_rand(100000, 999999);
$this->redis->rPush('queue_name', json_encode(['user_id' => $user_id]));
$user_id = mt_rand(100000, 999999);
$this->redis->rPush('queue_name', json_encode(['user_id' => $user_id]));
$user_id = mt_rand(100000, 999999);
$this->redis->rPush('queue_name', json_encode(['user_id' => $user_id]));

echo "数据进队列成功 \n";

// 查看队列
$res = $this->redis->lRange('queue_name', 0, 100);
echo "当前队列数据为: \n";
print_r($res);

// 出队列
$this->redis->lPop('queue_name');
echo "数据出队列成功 \n";

// 查看队列
$res = $this->redis->lRange('queue_name', 0, 100);
echo "当前队列数据为: \n";
print_r($res);
}

基于 redis 字符串 setnx 的悲观锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* 实现悲观锁机制
* @Post("/test_lock_pessimism")
*/
public function test_lock_pessimism()
{
$timeout = 5000;
do {
$microtime = microtime(true) * 1000;
$microtimeout = $microtime + $timeout + 1;

// 上锁
$isLock = $this->redis->setnx('lock.count', $microtimeout);
if (!$isLock) {
$getTime = $this->redis->get('lock.count');
if ($getTime > $microtime) {
// 睡眠, 降低抢锁频率 缓解redis压力
usleep(5000);
// 未超时继续等待
continue;
}
// 超时, 抢锁, 可能有几毫秒级时间差可忽略
$previousTime = $this->redis->getSet('lock.count', $microtimeout);
if ((int)$previousTime < $microtime) {
break;
}
}

} while (!$isLock);

$count = $this->redis->get('count') ?: 0;

// 业务逻辑
echo "执行count加1操作~~\n\n";

$this->redis->set('count', $count + 1);

// 删除锁
$this->redis->del('lock.count');
// 打印count值
$count = $this->redis->get('count');

echo "count值为: $count\n";
}

基于 redis 事务的乐观锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 实现悲观锁机制
* @Post("/test_lock_optimistic")
*/
public function test_lock_optimistic()
{
// 监视count值
$this->redis->watch('count');

// 开启事务(Redis的事务和MySQL事务概念不一样)
$this->redis->multi();

// 操作count
$time = time();
$this->redis->set('count', $time);

// 模拟并发下其他进程进行set count操作 请执行下面操作
// $this->redis->set('count', 'is simulate');
sleep(10);

// 提交事务
$res = $this->redis->exec();

if ($res) {
// 成功
echo 'Success: ' . $time . "\n";
return;
}

// 失败
echo 'Fail: ' . $time . "\n";
}

基于 redis 的发布订阅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 发布
* @Post("/test_publish")
*/
public function test_publish()
{
$this->redis->publish('msg', '来自MSG频道的推送');
echo "MSG频道消息推送成功 \n";
$this->redis->close();
}

/**
* 订阅
* @Post("/test_subscribe")
*/
public function test_subscribe()
{
// ini_set('default_socket_timeout', -1);

echo "订阅MSG频道, 等待消息推送...\n";

$callback = function ($msg) {
echo $msg;
};

$this->redis->subscribe(['msg'], $callback);
}

Powered by Hexo and Hexo-theme-hiker

Copyright © 2017 - 2023 Keep It Simple And Stupid All Rights Reserved.

访客数 : | 访问量 :