后端电商前端【免费下载链接】CRMEBCRMEB开源商城免费开源多语言商城系统Tp6框架商城系统可商用包含小程序商城、H5商城、公众号商城、PC商城、App支持分销、拼团、砍价、秒杀、优惠券、积分、会员等级、小程序直播、页面DIY前后端分离方便二开更有详细使用文档、接口文档、数据字典、二开文档/视频教程欢迎大家提出宝贵意见和建议项目地址https://gitcode.com/gh_mirrors/cr/CRMEB点击查看免费下载本篇技术指南围绕 CRMEB 仓库中打包的 cache-plugin.rst 文档展开系统讲解 Guzzle 3 的Guzzle\Plugin\Cache\CachePlugin从插件装配、缓存键生成、Vary请求区分到cache.override_ttl、cache.key_filter、cache.revalidate等全部参数再深入到条件 GET 再验证revalidation与 Doctrine/Zend 缓存适配器的底层实现。读完本文你将能够在任何基于 Guzzle 3 的 PHP 项目中独立配置一个符合 RFC 2616 语义的 HTTP 响应缓存层并能基于源码定位和定制缓存行为。说明本文讨论的CachePlugin属于Guzzle 3vendor/guzzle/guzzle这一代实现。CRMEB 主项目在 crmeb/composer.json 中声明的 HTTP 客户端为guzzlehttp/guzzle: 6.5.5而 Guzzle 3 代码与文档仍作为依赖保留在crmeb/vendor/guzzle/guzzle目录中其中的缓存插件文档与实现是一份完整的、可独立运行的参考如Guzzle\Http\EntityBody仍在 QrcodeServices.php 与 StoreOrderServices.php 中被引用。文中所指路径均相对当前仓库根目录。一、CachePlugin 是什么一个私有透明代理缓存Guzzle\Plugin\Cache\CachePlugin是 Guzzle 3 提供的 HTTP 缓存插件。它实现了一个private transparent proxy cache私有透明代理缓存对应用而言它是透明的——代码不用关心缓存细节只负责发请求、收响应但底层它会把可缓存的 HTTP 响应保存下来在后续请求中直接复用从而减少网络往返、降低源站压力。它的缓存判定逻辑严格遵循RFC 2616HTTP/1.1 缓存语义完全使用 HTTP 头部来控制是否可以缓存、缓存多长时间由响应的Cache-Control、Expires等头部决定支持Vary头实现同一 URL 下的多版本缓存条目支持基于ETag与Last-Modified的再验证revalidation用条件 GET 与源站确认内容是否变化。在 CachePlugin.php 的类注释中作者还明确指出该插件同时实现了RFC 5861 的stale-if-errorCache-Control 扩展当请求遇到错误如500 Internal Server Error、DNS 失败等 cURL 异常时允许返回已经过期的缓存响应兜底。二、快速开始把缓存插件挂到 Client 上原文档给出的最小可运行示例本节予以完整保留并补充注释use Guzzle\Http\Client; use Doctrine\Common\Cache\FilesystemCache; use Guzzle\Cache\DoctrineCacheAdapter; use Guzzle\Plugin\Cache\CachePlugin; use Guzzle\Plugin\Cache\DefaultCacheStorage; $client new Client(http://www.test.com/); $cachePlugin new CachePlugin(array( storage new DefaultCacheStorage( new DoctrineCacheAdapter( new FilesystemCache(/path/to/cache/files) ) ) )); // 将缓存插件作为事件订阅者挂到客户端 $client-addSubscriber($cachePlugin); // 第一次请求未命中缓存直接访问源站并写入缓存 $client-get(http://www.wikipedia.org/)-send(); // 第二次请求缓存已存在向源站发起条件请求做再验证 // 若源站返回 304则直接从缓存中返回合并后的响应 $client-get(http://www.wikipedia.org/)-send();这段代码揭示了三个核心步骤选择后端存储FilesystemCache文件系统缓存→DoctrineCacheAdapter适配器→DefaultCacheStorageGuzzle 的缓存存储实现。构造插件通过storage选项把存储对象注入CachePlugin。订阅事件addSubscriber($cachePlugin)使插件开始监听请求生命周期事件。值得注意的是CachePlugin构造函数的灵活性它既接受完整配置数组也接受单独的适配器或存储对象。从 CachePlugin.php 可以看到构造器会按以下优先级兜底传入CacheAdapterInterface→ 自动包装为DefaultCacheStorage传入CacheStorageInterface→ 直接使用传入其他已知缓存对象 → 通过CacheAdapterFactory::fromCache()自动适配什么都不传且环境中存在Doctrine\Common\Cache\ArrayCache→ 使用内存数组缓存进程内有效适合测试作为默认存储若两者都没有则抛出InvalidArgumentException。三、请求拦截与缓存查找流程原文档描述了插件的工作流程拦截 GET 和 HEAD 请求 → 基于请求方法与 URL 生成哈希键 → 检查缓存 → 校验缓存规则与新鲜度 → 命中则直接返回。这一流程在源码中以四个事件订阅点实现见 CachePlugin.php订阅事件回调方法职责request.before_sendonRequestBeforeSend发送前查缓存命中且满足请求约束则直接注入缓存响应跳过网络请求request.sentonRequestSent响应到达后若可缓存则写入存储并给响应附加缓存状态头request.erroronRequestError请求出错如收到 5xx时按stale-if-error规则尝试返回过期缓存request.exceptiononRequestExceptioncURL 层异常如连接失败时同样尝试用过期缓存兜底3.1 缓存键的生成缓存键由DefaultCacheStorage::getCacheKey()计算见 DefaultCacheStorage.phpreturn $this-keyPrefix . md5($request-getMethod() . . $url);即keyPrefixmd5(HTTP方法 URL)。这意味着 GET 与 HEAD 各自独立缓存条目同一 URL 下的多个响应如不同Accept头对应的变体则由Vary机制在同一个键下区分详见下文。3.2 命中判定与缓存状态头在onRequestBeforeSend中CachePlugin.php插件会给请求附加Via头标识请求经过了GuzzleCache若命中缓存则基于响应Date/Last-Modified计算并设置Age头调用canResponseSatisfyRequest()校验新鲜度与再验证需求满足条件后设置cache.hit参数并直接把缓存响应设为请求的响应。在addResponseHeaders()CachePlugin.php中插件会向响应追加一组标准的缓存调试头便于观测缓存行为X-Cache-Lookup: HIT/MISS from GuzzleCache——本次请求是否在缓存中查到了响应X-Cache: HIT from GuzzleCache/MISS from GuzzleCache——是否直接命中出错兜底时为HIT_ERROR from GuzzleCacheWarning: 110 ... Response is stale——响应已过期但仍被使用时按 RFC 2616 给出警告111 ... Revalidation failed表示再验证失败后使用了缓存。这些头在 CachePluginTest.php 的testInjectsSatisfiableResponses测试中均有断言验证。3.3 新鲜度校验的判定矩阵canResponseSatisfyRequest()CachePlugin.php按 RFC 2616 语义依次判断请求携带Cache-Control: max-ageN时响应年龄大于 N 则判定不可满足响应已过期isFresh() false时检查请求的max-stale指令——若允许过期且未超出容忍范围仍可命中否则若响应的Cache-Control: max-ageN已过期判定不可满足最后交由再验证策略决定是否需要条件请求。对应测试数据见 CachePluginTest.php 的satisfyProvider例如max-age20的请求面对年龄 100 的响应判为不可满足max-stale15的请求面对过期 10 秒的响应则判为可满足。这一矩阵直接决定缓存命中率与一致性之间的平衡。四、Vary为同一 URL 保存多个缓存变体原文档指出缓存键只由请求方法与 URL 决定因此多个响应可以映射到同一个缓存键。为了区分同一 URL 下不同请求头产生的不同内容必须使用VaryHTTP 头。在DefaultCacheStorage中每个缓存条目都会记录响应携带的Vary值并在存取时用requestsMatch()DefaultCacheStorage.php逐一比对请求头与缓存条目中记录的Vary头字段只要任一指定头部不一致就会选择/创建不同的缓存条目。因此响应声明Vary: Accept时Accept: application/json与Accept: application/xml的请求会得到各自的缓存副本不声明Vary时相同 URL 的请求共享同一个缓存条目。DefaultCacheStorageTest.php 的testCachesMultipleRequestsWithVary验证了同一缓存键下可以并存两条不同Accept的条目且它们的内容、Content-Type与响应体键bodyDigest各不相同。五、缓存选项详解从请求级到客户端级原文档给出了一批通过请求params对象控制的运行时选项以下逐一结合源码展开。5.1 覆盖缓存 TTLcache.override_ttl默认情况下TTL 由响应头决定但当响应未携带任何新鲜度策略时DefaultCacheStorage构造器会使用默认 3600 秒见 DefaultCacheStorage.php。通过请求参数可以强制覆盖 TTL// 若该响应可缓存则缓存 100 秒 $request-getParams()-set(cache.override_ttl, 100);其底层读取逻辑位于DefaultCacheStorage::cache()DefaultCacheStorage.php优先级为cache.override_ttl 响应Cache-Control: max-age 默认 TTL3600 秒若响应还带stale-if-error指令TTL 还会按指令值追加stale-if-error为布尔真时 TTL 翻倍为数值时加上该秒数。5.2 自定义缓存决策can_cache当对接的服务既不返回缓存头、又返回常规情况下不应缓存的响应时可以在构造插件时传入自定义的can_cache对象其类型为Guzzle\Plugin\Cache\CanCacheInterface。最便捷的方式是使用Guzzle\Plugin\Cache\CallbackCanCacheStrategy直接以闭包/回调决定某个请求或响应是否可缓存CallbackCanCacheStrategy.php$plugin new CachePlugin(array( can_cache function ($request) { // 例如仅缓存带 token 的登录态请求 return $request-hasHeader(X-Api-Key); } ));从构造逻辑看CachePlugin.php若can_cache是 callable会自动包装为CallbackCanCacheStrategy否则按CanCacheStrategyInterface对象使用。默认策略DefaultCanCacheStrategyDefaultCanCacheStrategy.php只允许缓存GET 与 HEAD请求其他方法一概不缓存请求带Cache-Control: no-store时不缓存响应必须成功2xx且canCache()即具有可缓存性未声明no-store、no-cache等时才写入缓存。5.3 再验证行为控制cache.revalidate通过cache.revalidate参数可以改变请求的再验证行为设置为never绝不发送再验证请求总是从源站获取最新响应设置为skip不进行再验证始终直接使用缓存中的响应。这两个语义在源码中对应两个内置策略类——SkipRevalidation.phprevalidate()恒返回true即总是认为缓存有效与 DenyRevalidation.phprevalidate()恒返回false即总是认为缓存无效。原文档中的never与skip正是这两种策略在参数层的映射。5.4 缓存键过滤cache.key_filter当请求 URL 携带的查询字符串参数每次都不相同例如时间戳、随机数会导致每个请求 URL 唯一而永远无法命中缓存。此时可用cache.key_filter在生成缓存键前剔除指定查询参数// 生成缓存键时忽略 a 与 q 两个查询参数 $request-getParams()-set(cache.key_filter, a, q);格式为逗号分隔的查询参数名列表。源码实现位于DefaultCacheStorage::getCacheKey()DefaultCacheStorage.php先explode(,, $filter)逐个trim后从 URL 查询串中移除再计算 MD5 缓存键。DefaultCacheStorageTest.php 的testCanFilterCacheKeys展示了典型场景请求带?authfoo时本无法命中设置cache.key_filter auth后即可命中。5.5 其他构造器选项与 Client 级全局配置原文档指出CachePlugin构造器还支持自定义再验证实现、自定义缓存键生成器、自定义缓存决策策略与自定义缓存存储对象等扩展点。综合构造器注释CachePlugin.php完整的选项表如下选项类型默认值说明storageCacheStorageInterface内存ArrayCache包装的DefaultCacheStorage实际存储响应的对象revalidationRevalidationInterfaceDefaultRevalidation再验证策略can_cachecallable 或CanCacheInterfaceDefaultCanCacheStrategy是否允许缓存请求/响应的决策auto_purgeboolfalse发送 PUT/POST/DELETE/PATCH 等非幂等请求时是否自动 PURGE 对应 URL 的缓存此外可以通过 Client 的配置选项为该客户端创建的所有请求统一设置缓存参数原文档示例完整保留$client new Guzzle\Http\Client(http://www.test.com, array( request.params array( cache.override_ttl 3600, params.cache.revalidate never ) )); echo $client-get(/)-getParams()-get(cache.override_ttl); // 3600 echo $client-get(/)-getParams()-get(cache.revalidate); // never这样新创建的请求会自动继承这些params实现全局缓存策略。六、缓存再验证Revalidation机制剖析这是原文档的另一核心主题。当缓存插件判定一个 GET 请求需要再验证时会向源站发送条件 GETconditional GET源站返回304 Not Modified时返回由缓存响应与新响应合并后的头加上缓存实体内容组成的响应。DefaultRevalidation.php 完整实现了这套流程1. 判定是否需要再验证shouldRevalidate第 49-68 行。以下任一条件成立则触发再验证请求带Pragma: no-cache请求或响应带Cache-Control: no-cache/must-revalidate响应没有 Cache-Control 头但带有ETag此时按强验证器强制再验证。2. 构造条件请求createRevalidationRequest第 95-119 行克隆原请求移除Pragma与Cache-Control避免再次触发缓存逻辑缓存响应有Last-Modified则设置If-Modified-Since头缓存响应有ETag则设置If-None-Match头从事件分发器中移除CachePlugin监听器防止再验证请求触发无限递归的再验证。3. 处理源站响应返回200直接以新响应作为请求响应若新响应可缓存则更新缓存handle200Response第 129-137 行——对应原文档服务器不支持验证直接采用新响应返回304校验ETag一致后用新响应的Date、Expires、Cache-Control、ETag、Last-Modified五个头替换缓存中的旧头再写回缓存并返回缓存实体handle304Response第 148-173 行——对应原文档合并头 缓存实体返回404资源已不存在删除缓存条目并抛出异常handleBadResponse第 77-85 行。DefaultRevalidationTest.php 的cacheRevalidationDataProvider给出了四组端到端用例Pragma: no-cache强制再验证且 304 通过、must-revalidate, no-cache时 200 覆盖缓存、再验证遇到 500 时回退、以及 304 但ETag不一致时判定失败。testRevalidatesResponsesAgainstOriginServer还验证了三次请求只产生 3 次真实网络请求其中后两次均由 304 兜住直接证明了缓存的节省效果。七、缓存适配器Guzzle 不重复造轮子原文档强调Guzzle 在缓存与日志方面复用成熟生态通过适配器接入既有的缓存方案。缓存插件需要一个缓存适配器cache adapter来存储响应Guzzle 3 原生支持Doctrine 2.0与Zend Framework两套缓存后端。7.1 Doctrine 缓存适配器use Doctrine\Common\Cache\ArrayCache; use Guzzle\Cache\DoctrineCacheAdapter; use Guzzle\Plugin\Cache\CachePlugin; $backend new ArrayCache(); $adapter new DoctrineCacheAdapter($backend); $cache new CachePlugin($adapter);这里直接把适配器作为CachePlugin的唯一构造参数插件会自动包装成DefaultCacheStorage。DoctrineCacheAdapter.php 只是把 Guzzle 的fetch/save/delete/contains接口转发给 Doctrine 的Cache接口。Doctrine\Common\Cache\ArrayCache适合内存缓存快速开始中的FilesystemCache则提供跨请求的文件级持久化。Doctrine 缓存家族RedisCache、MemcachedCache、ApcuCache等均可通过该适配器接入。7.2 Zend Framework 缓存适配器use Guzzle\Cache\ZendCacheAdapter; use Zend\Cache\Backend\TestBackend; $backend new TestBackend(); $adapter new ZendCacheAdapter($backend); $cache new CachePlugin($adapter);对应 Zend Framework 的缓存后端测试中常用TestBackend生产可换用File、Memcached等后端。7.3 自动适配入口CacheAdapterFactory如果不想手动包一层CacheAdapterFactory::fromCache()CacheAdapterFactory.php会根据传入对象自动选择已是CacheAdapterInterface→ 直接返回是 Doctrine 的Cache→ 包装为DoctrineCacheAdapter是 Zend 的StorageInterface→ 包装为Zf2CacheAdapter其他类型 → 抛出InvalidArgumentException。因此new CachePlugin($someDoctrineCache)这类简化写法在底层都能正确展开。八、实战建议与注意事项结合原文档与源码给出几条可直接落地的实践结论明确适用边界该插件设计为私有透明代理缓存会缓存授权与私有数据见 CachePlugin.php 注释因此对用户私有接口务必通过can_cache决策或Cache-Control: no-store关闭缓存避免敏感数据泄漏。善用缓存调试头上线后通过X-Cache/X-Cache-Lookup/Warning头可以快速判断 HIT/MISS/过期兜底三种状态配合Age头观察缓存寿命。过滤搅局查询参数凡 URL 带有时间戳、随机数、登录态 token 等唯一化参数都应配置cache.key_filter否则缓存形同虚设。理解再验证的价值304 再验证是缓存一致性关键——它既避免重复传输完整实体又保证内容更新后能及时拿到新数据ETag变化或must-revalidate都会强制再验证。失效管理auto_purge开启后非幂等请求PUT/POST/DELETE/PATCH会自动清理对应 URL 的缓存条目也可手动调用$cachePlugin-purge($url)DefaultCacheStorage::purge()会同时清除 GET/HEAD/POST/PUT/DELETE 五种方法的缓存条目DefaultCacheStorage.php。运行环境前提本插件代码基于 PHP 5.3 与 Guzzle 3见 composer.json适配器依赖doctrine/cache、zendframework/zend-cache等扩展包当前仓库仅保留了 Guzzle 3 的代码与文档实际项目若要使用应确认依赖已被正确引入。九、总结Guzzle\Plugin\Cache\CachePlugin为 Guzzle 3 提供了完整的 HTTP 缓存能力事件驱动的请求拦截、基于方法与 URL 的哈希缓存键、Vary多变体存储、按 RFC 2616 语义的新鲜度判定、基于ETag/Last-Modified的条件请求再验证以及通过适配器对接 Doctrine/Zend 等成熟缓存后端。本文所有运行细节均可在 src/Guzzle/Plugin/Cache 源码与 tests/Guzzle/Tests/Plugin/Cache 测试中找到对应的实现证据与端到端用例是深入理解乃至二次定制该缓存插件的可靠起点。赞分享后端电商前端【免费下载链接】CRMEBCRMEB开源商城免费开源多语言商城系统Tp6框架商城系统可商用包含小程序商城、H5商城、公众号商城、PC商城、App支持分销、拼团、砍价、秒杀、优惠券、积分、会员等级、小程序直播、页面DIY前后端分离方便二开更有详细使用文档、接口文档、数据字典、二开文档/视频教程欢迎大家提出宝贵意见和建议项目地址https://gitcode.com/gh_mirrors/cr/CRMEB点击查看免费下载相关推荐FRI-STARKs原理深入理解可扩展透明论证的核心机制FRI STARKs原理深入理解可扩展透明论证的核心机制 FRI STARKs是现代零知识证明技术中的重要突破代表了可扩展透明知识论证的最前沿。作为STAR文档教程PyRDP透明代理技术详解与实战配置指南PyRDP透明代理技术详解与实战配置指南 透明代理基础概念 透明代理Transparent Proxy是一种网络中间人技术它能够在客户端和服务端无感知的情网络安全渗透测试探索IReader开源跨平台小说阅读器的创新体验探索IReader开源跨平台小说阅读器的创新体验 IReader是一款专为小说爱好者设计的开源阅读器支持Android和桌面平台提供无广告、隐私保护的阅读移动开发桌面应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?