适用场景
考虑如下场景:
- 存在
服务A
和服务B
服务A
和服务B
都有各自的REST API- 用户只能访问
服务A
, 如果要访问服务B
,则需通过服务A
来访问
针对这个场景,可能大家都会想到应该用Nginx
来做, 简单快捷。事实也确实如此, 如果仅仅是做请求转发,完全可以通过Nignx
。
继续加条件:
- 调用
服务B
的API 需要签名校验; 服务A
和服务B
的 REST API 路径没有规律;
这时候如果继续用Nginx
, 那么会出现如下问题:
-
Nginx无法调用
服务B
时候,无法生成并携带签名(当然其他方式比如通过Openresty
+Lua
可以做到, 但相对要复杂,不在本文讨论范围之内) -
服务A
和服务B
的路径没有规律,意味着我们判断将请求转发给服务A
还是服务B
, 只能将服务A
的所有接口都罗列在nignx配置文件上,匹配则转发到服务A
, 否则转发到服务B
。 配置文件类似:location /data/query1/list { proxy_pass http://服务A; } location /data/query1/list2 { proxy_pass http://服务A; } location /data/query/list2 { proxy_pass http://服务A; } .....
比较臃肿繁琐,并且后期服务A
加了接口之后,也需要修改Nginx
配置文件, 容易遗漏产生问题。
这种情况下,我们用代码来转发请求就比较合适。
Java实现
需求
根据上面的场景,服务A
需要具备如下功能:
- 如果用户请求的是
服务A
本身的接口, 那么执行服务A
的接口逻辑; - 如果用户请求的不是
服务A
的接口,那么服务A
就需要调用服务B
的接口,然后将服务B
的响应流写入到服务A
对用户的响应流中。
实现 - 需求1
利用SpringBoot(或Spring MVC), 我们可以很轻松的实现第一点功能。
我们在服务A
中添加如下代码:
@RequestMapping({"/**"})
public void proxy(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
proxyServlet.service(request, response);
}
我们仅需 @RequestMapping({"/**"})
, 就可以做到,如果请求是服务A
本身的具有的接口, 那么就不会走我们的proxy
方法; 否则就会走我们的proxy
方法, 去请求服务B
。
实现 - 需求2
上面代码中的 proxyServlet
, 封装了请求服务B
的代码。 也就是承担着第二点需求的功能。这个 ProxyServlet
我们直接采用 开源项目 HTTP-Proxy-Servlet 来实现。
具体的用法,参考 HTTP-Proxy-Servlet 的 README
文档,很清楚。 这里就不在赘述。基本思路是在ProxyController
(就是包含上面proxy
方法的controller
) 初始化以后, 手动构造一个ProxyServlet
作为全局变量即可。如果需要特殊的签名,也可以通过更改ProxyServlet
的源码来实现,非常简单。
至此,上述问题完美解决。
Q.E.D.