准备模型类和数据表
开始之前,我们先创建文章表、模型类和控制器:
在生成的文章表 posts
迁移类中,编写表结构如下:
<?php
use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;
class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->integer('views')->unsigned()->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('posts');
}
}
新增了文章标题、内容和浏览数字段。
在 .env
中配置数据库连接信息:
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=redis_demo
DB_USERNAME=root
DB_PASSWORD=root
创建 redis_demo
数据库,运行 php artisan migrate
在这个数据库中创建 posts
数据表。
热门浏览文章排行榜功能实现
维护基于文章浏览数的有序集合
在 PostController
中,定义一个文章浏览方法 show
:
use AppModelsPost;
use IlluminateSupportFacadesRedis;
public function show(Post $post)
{
$post->increment('views');
if ($post->save()) {
// 将当前文章浏览数 1,存储到对应 Sorted Set 的 score 字段
Redis::zincrby('popular_posts', 1, $post->id);
}
return 'Show Post #' . $post->id;
}
我们使用 popular_posts
作为热门浏览文章排行榜有序集合的键名,当更新文章模型浏览数字段成功后,调用 Redis
门面的 zincrby
方法,通过 ZINCRBY
指令将对应文章浏览数(score)做 1 操作,有序集合内的文章成员(member)通过文章 ID 进行标识。
这样一来,随着文章的增多,用户浏览量的增长,Redis 底层会维护一个基于文章浏览数进行排序的有序集合,要实现热门浏览文章排行榜,只需要逆序从这个集合获取指定数量的成员即可获取对应的文章 ID 集合。
读取有序集合元素生成排行榜
接下来,我们就来实现这个排行榜。我们限定排行榜的大小是 10,即只显示浏览量最多的前十篇文章,这可以通过 ZREVRANGE
指令实现,对应到 Laravel 代码,我们需要在 PostController
中新增一个 popular
方法如下:
// 获取热门文章排行榜
public function popular()
{
// 获取浏览器最多的前十篇文章
$postIds = Redis::zrevrange('popular_posts', 0, 9);
if ($postIds) {
$idsStr = implode(',', $postIds);
// 查询结果排序必须和传入时的 ID 排序一致
$posts = Post::whereIn('id', $postIds)
->select(['id', 'title', 'views'])
->orderByRaw('field(`id`, ' . $idsStr . ')')
->get();
} else {
$posts = null;
}
dd($posts->toArray());
}
非常简单,通过 Redis
门面调用 zrevrange
方法来执行 ZREVRANGE
指令,并传入有序集合的键名、元素区间,由于集合中存储的元素是文章 ID,所以对于返回的结果,还需要再次到数据库中去查询完整的文章记录,此外,我们还要按照传入的 ID 顺序对返回结果进行排序,否则数据库查询返回的结果顺序又变成基于 ID 值大小的排序了。
这样一来,就可以获取到排行榜中的文章数据了。
我们在 routes/web.php
为上述控制器方法注册路由:
Route::get('/posts/popular', [PostController::class, 'popular']);
Route::get('/posts/{post}', [PostController::class, 'show']);
测试热门浏览文章排行榜
最后,我们来测试上述排行榜代码是否可以正常工作。
基本思路是编写一个文章模型工厂生成测试文章,然后随机浏览文章构建基于 Redis 的排行榜有序集合,最后访问排行榜数据。
先创建文章模型工厂:
代码语言:javascript复制php artisan make:factory PostFactory
编写对应的模型工厂类代码如下:
代码语言:javascript复制<?php
namespace DatabaseFactories;
use AppModelsPost;
use IlluminateDatabaseEloquentFactoriesFactory;
class PostFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Post::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'title' => trim($this->faker->sentence, '.'),
'content' => $this->faker->paragraphs(3, true),
];
}
}
然后我们创建一个 Artisan 命令类用于对文章进行模拟访问:
代码语言:javascript复制 php artisan make:command MockViewPosts
编写 MockViewPosts
实现代码如下:
<?php
namespace AppConsoleCommands;
use AppModelsPost;
use IlluminateConsoleCommand;
use IlluminateSupportFacadesHttp;
use IlluminateSupportFacadesRedis;
class MockViewPosts extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mock:view-posts';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Mock View Posts';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
// 1、先清空 posts 表
Post::truncate();
// 2、删除对应的 Redis 键
Redis::del('popular_posts');
// 3、生成 100 篇测试文章
Post::factory()->count(100)->create();
// 4、模拟对所有文章进行 10000 次随机访问
for ($i = 0; $i < 10000; $i ) {
$postId = mt_rand(1, 100);
$response = Http::get('http://redis-demo.test/posts/' . $postId);
$this->info($response->body());
}
}
}
这里我们使用了 Laravel 自带的 HTTP 客户端发起对 /posts/{post}
路由的模拟访问,所以需要先安装 Guzzle 这个 HTTP 扩展包才可以正常访问测试路由:
composer require guzzlehttp/guzzle
运行 php artisan mock:view-posts
,在浏览器中访问 http://redis-demo.test/posts/popular
,就可以看到可以返回热门文章排行榜数据了:
本系列教程首发在学院君网站(xueyuanjun.com),你可以点击页面左下角阅读原文链接查看最新更新的教程。