制作公司网站价格,网站建设网站形象,西安企业管理咨询有限公司,百度搜索量最大的关键词背景
【MQ】一套为海量消息和高并发热点消息#xff0c;提供高可用精准延时服务的解决方案
我现在有一个需求#xff0c;就是监听 RabbitMQ 一个延时交换机的消息数#xff0c;而 RabbitTemplate 是不存在对应的方法来获取的。 而我们在 RabbitMQ 的控制台却可以发现延时交…背景
【MQ】一套为海量消息和高并发热点消息提供高可用精准延时服务的解决方案
我现在有一个需求就是监听 RabbitMQ 一个延时交换机的消息数而 RabbitTemplate 是不存在对应的方法来获取的。 而我们在 RabbitMQ 的控制台却可以发现延时交换机的消息数所以其开放的 http-api 里存在我们需要的数据通过抓包可得 而我们查看这个包构造请求抓包分析的技巧这里不做介绍 当然你完全可以去看 RabbitMQ 的 http-api 开放文档但是我觉得有点多还不如直接抓包 URL
http://rabbithost:15672/api/exchanges/{virtualHost}/{exchange}?msg_rates_age60msg_rates_incr5
Method
GET
Header
Authorization: Basic EncryptUtil.encodeBase64(String.format(%s:%s, rabbitMQConfig.getUsername(), rabbitMQConfig.getPassword()));
很快我们就能写一个 OpenFeign 客户端 FeignClient(name rabbitmq-service, url ${okr.mq.http-api})
public interface RabbitMQHttpFeignClient {GetMapping(/exchanges/{virtualHost}/{exchange}?msg_rates_age60msg_rates_incr5)DelayExchangeVO getMessagesDelayed(RequestHeader(HttpHeaders.AUTHORIZATION) String authorization,PathVariable(virtualHost) String virtualHost,PathVariable(exchange) String exchange);}
但是你会发现virtualHost 是带 / 的但是最终的 url 并没有转义导致路由出错报了 404
400 是参数未通过验证、401 未通过身份认证、403 无权限
先说结论
配置一个 Contract 协议约定并设置 decodeSlash 为 false
Component
public class OpenFeignConfig {Beanpublic Contract notdecodeSlashContract(){// 无自定义处理器、默认的 ConversionService、取消 %2F - / 的解码return new SpringMvcContract(Collections.emptyList(), new DefaultConversionService(), Boolean.FALSE);}}decodeSlash直译就是“斜杠解码”
encode: / → %2F decode: %2F → /
而我们就是阻止 %2F → / 那我们为什么要阻止呢
问题分析
首先我们可能会想它是如何转义的是传入的时候转义还是最终一起转义
如果是最终一起转义那 / 必然不能被转义否则那些路由都会失效所以如果是最终转义无法满足我们的需求
这里写了个简单的方法方便理解
public static P String buildUrl(String baseUrl, MapString, ListString queryParams, MapString, P pathParams) {queryParams Optional.ofNullable(queryParams).orElseGet(Map::of);pathParams Optional.ofNullable(pathParams).orElseGet(Map::of);return UriComponentsBuilder.fromHttpUrl(baseUrl).queryParams(new LinkedMultiValueMap(queryParams)).buildAndExpand(pathParams).encode() // 开启译码模式.toUriString();
}如果在传入的时候转义才能实现我们的效果
public static P String buildUrl(String baseUrl, MapString, ListString queryParams, MapString, P pathParams) {queryParams Optional.ofNullable(queryParams).orElseGet(Map::of);pathParams Optional.ofNullable(pathParams).orElseGet(Map::of);return UriComponentsBuilder.fromHttpUrl(baseUrl).encode() // 开启译码模式这里之后路径参数/ 也会被转义为 %2F.queryParams(new LinkedMultiValueMap(queryParams)).buildAndExpand(pathParams).toUriString();
}那 OpenFeign 是哪种呢如果我们没看源码我们可能没法判断但我们可以知道OpenFeign 在解析路径参数的时候用的是 PathVariableParameterProcessor
参考文章文章
通过自定义注解 自定义处理器的方式处理请求我们通过
data.indexToExpander().put(context.getParameterIndex(), o - URLEncoder.encode(String.valueOf(o), Charset.defaultCharset());
我们给 {name} 对应的 index 提供了一个解析器但是貌似没啥用如果进行双重编码导致 % 也也被转义了但如果只是一重编码最终 / 还是以 / 的形式出现
这一度让我觉得是玄学
但我对比了 PathVariableParameterProcessor 类的实现发现其并没有专门对字符串进行编码所以我猜测底层是定然编码了的所以我进行了调试一步步找到了关键代码 你会发现如果传入 / 会被转义成 %2F 也就是说传入时确实已经编码了你甚至可以实现传入 %2F 但并设置其已编码所以不会再次编码等等无论如何各种方式让字符串为 %2F
但是这里有一个属性 encodeSlash如果为 false则将最终结果的 %2F 给重新解码成 /
说实话我完全不知道为啥要这样太放剑了如果是路径参数也是个 uri也有这样的编程方式但是我觉得很不规范
这也是我不熟悉 SpringMvcContract 导致的啦不知道还有这么一个参数 decodeSlash
new SpringMvcContract(Collections.emptyList(), new DefaultConversionService(), false)
decodeSlash 设置为 false 后encodeSlash 就为 true%2F 就不会重新解码成 / 了最终也就能达到我们的预期的效果了