如何借助Spring Cloud Gateway实现两个微服务的组合调用
问题定位
你碰到的调用失败情况,完全是Spring Cloud Gateway默认设计下的正常表现:
- 网关从定位上就是面向外部客户端的统一流量入口,默认的路由规则都是为外部请求设计的,本身没有适配后端服务内部回调网关的场景
- 设计逻辑上也不建议内部服务的调用链路绕经网关,否则会无端增加网络跳数,引入网络隔离、循环调用、性能损耗等不必要的故障点
调用失败的具体原因
你目前遇到的几类连通性问题,都对应常见的配置误区:
- 用Eureka服务名发请求失败:原生
RestTemplate不具备服务名解析、负载均衡的能力,直接把Eureka服务名当主机名写在请求地址里,会走常规DNS解析流程,肯定连不上 - 从服务A内部通过网关端口调服务B失败,通常逃不开三个原因:
- 路由匹配不上:如果网关给服务B配的路由规则是匹配
/serviceB/**这类带前缀的路径,你直接请求/masina/xxx的路径根本触发不了转发,直接404 - 网络不通:如果服务A跑在容器里,容器里的
localhost指的是容器自己,根本碰不到宿主机上的4000端口网关 - 被过滤器拦了:如果网关配了全局鉴权、IP白名单之类的逻辑,内部发的请求没带对应凭证,会被直接打回401/403
- 路由匹配不上:如果网关给服务B配的路由规则是匹配
可落地的组合调用实现方案
方案一(最优解):内部服务调用根本不用走网关
网关是给外部访问用的,服务之间本身就有注册中心的能力,直接点对点调用就好,没必要绕一圈走网关,既快又少出问题。
实现步骤:
- 给RestTemplate加负载均衡能力,加上
@LoadBalanced注解:
@Configuration public class RestTemplateConfig { @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
- 调服务B的时候直接用Eureka里的服务名,不用写IP端口,也不用经过网关:
// 直连正常,不需要指定端口,不需要走网关 Masina masina = rest.getForObject("http://SERVICE-B/masina/" + marca, Masina.class);
你之前试服务名调用失败,九成九是没加这个@LoadBalanced注解,RestTemplate根本不知道要把服务名解析成实际的实例地址。
方案二:业务要求必须走网关时的适配方法
如果确实需要复用网关的统一鉴权、限流逻辑在内部调用场景,调整这几个点就能通:
- 别用
localhost当网关地址:本地IDE跑的时候同机调试localhost是通的,容器部署的时候要把服务A和网关放到同一个网络里,用网关的容器名、内网IP当请求地址 - 路径要和网关路由规则对得上:如果网关规则是只有路径带
/serviceB前缀才转发给服务B,你调用的时候就得把前缀带上,写http://网关地址:4000/serviceB/masina/xxx,不然匹配不到路由 - 给内部调用的路径加白名单,不要让全局鉴权过滤器把请求拦了
方案三:直接在网关层实现组合调用,不用改后端代码
如果不想在微服务A的代码里写聚合逻辑,完全可以依托网关原生能力做无侵入聚合:
- 在网关层配自定义路由,当外部用户访问
/serviceASomeEndpoint/both/{id}时,网关并行发两个请求,分别拿微服务A、微服务B的接口数据 - 在网关层把两个返回的数据拼合好,直接返回给用户,后端微服务完全不用改业务代码
内容的提问来源于stack exchange,提问作者MyProblems
相关产品推荐
相关产品推荐

