唔,我也不知道我接下来要讲的东西是不是配得上这个题目,总之就是分享一下我在搞 Blessing Skin Server 的缓存与插件机制时的一些经验(大佬们就请忽略吧)
既然要实现松散耦合的缓存机制,那就是要做到有没有缓存都没事。有缓存的话就走缓存,然后那边的模块内部实现一个包括过期时间呀啥啥的缓存机制,没有收到缓存模块的响应的时候就继续走原来的应用逻辑,一样可以正常响应。
因为我们是要实现应用逻辑与缓存逻辑的解耦,所以正常应用逻辑内是不能有对那些缓存的依赖的。那么我们要通过什么来和缓存模块通信呢?巧的是,Laravel 正好提供了基于 Event 和 Listener 的观察者模式,我们就可以用这种设计模式来解耦缓存模块。
首先,我们在即将获取一个可能需要缓存的数据之前,触发一个 GetDataEvent
(举个栗子),接下来判断这个 Event 是否返回了响应,如有则使用响应的内容,没有的话就继续正常的应用逻辑来获取数据。
譬如说,我们需要把皮肤的预览图给缓存下来,而不是每次都去生成:
<?php
namespace App\Events;
use App\Events\Event;
use App\Models\Texture;
use Illuminate\Queue\SerializesModels;
class GetSkinPreview extends Event
{
use SerializesModels;
public $texture;
public $size;
/**
* 这里我们并没有做什么,就是把实例化时传进来的参数保存到对象里去
*
* @return void
*/
public function __construct(Texture $texture, $size)
{
$this->texture = $texture;
$this->size = $size;
}
}
// 控制器中的方法
public function preview($tid, $size = 250)
{
// 触发事件
$responses = Event::fire(new GetSkinPreview($t, $size));
// 当然,如果你有多个 Listener 的话,在这里你可能需要遍历 Event 所返回的响应
if (isset($responses[0]) && $responses[0] instanceof \Symfony\Component\HttpFoundation\Response) {
// 这个返回的响应类型是看你自己对 Event 的需求的
// 比如这里我们需要 Listener 返回一个 Http 响应流
return $responses[0];
} else {
/* 原来业务逻辑中的生成预览 */
return Response::png();
}
}
而这个 GetDataEvent
,我们是可以注册 Listener 上去的,具体如何添加 Event 和 Listener 请参考 Laravel 文档。假设我们在这里给这个事件注册了一个 CacheDataListener
,那么在 GetDataEvent 这个事件在应用逻辑中被触发的时候,Laravel 的 Event Dispatcher 就会把事件分发到我们刚刚注册的监听器里,我们就可以在监听器的 handle
方法中处理缓存逻辑并返回缓存后的数据了。
<?php
namespace App\Listeners;
use Storage;
use App\Events\GetSkinPreview;
class CacheSkinPreview
{
/**
* 处理缓存逻辑并返回一个 Http 响应流
*
* @param GetSkinPreview $event
* @return void
*/
public function handle(GetSkinPreview $event)
{
$tid = $event->texture->tid;
if (!Storage::disk('cache')->has("preview/$tid")) {
/* 这里生成预览并保存到缓存文件 */
}
return \Response::png(Storage::disk('cache')->get("preview/$tid"));
}
}
这样下来,我们就可以让应用逻辑和缓存逻辑(差不多)完全分离开来,想要使用其他的缓存驱动,例如 Redis 的话,只要新建一个 Listener 并监听 GetDataEvent
就可以了。你甚至可以把缓存机制放到插件里去,而这也就是我本来的目的(笑)
以上只是我的个人见解,如果有什么不对的地方,还请大佬们多指教啊 (つд⊂)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。