一光年

[SpringCloud-极简示例] 7. 加入Gateway,让微服务有一个统一调用入口

2019.06.24

如果把所有的微服务都部署在服务器上,通过Eureka他们可以相安无事的顺利运行起来。

但对于外部调用者来讲,事情就有一些麻烦。他也许一开始还记得谁是Consumer谁是Provider,但当系统扩展到一定阶段,微服务个数也从数个扩展到了数十个数百个。除了服务提供者,谁还能理出个头绪来呢?

不仅如此,每个微服务都需要做一些基础的但是却重复的工作,例如鉴权。因为他们暴露给我调用者,每一个服务接口都要保证自己的调用安全。

再比如,一个复杂的流程需要调用ABCEDFG等任务,当D任务失败的情况下,前面任务需要回退处理。

domo7-framework

这显然不应该是调用者应该做的事情。系统应该有一个统一的调用入口,将所有的微服务隐藏在门后面。门后面的鉴权、转发、限流等统一处理,都与调用者无关。这就是SpringCloud中的Zuul或者Gateway组件。

Zuul或Gateway都是完成相同网关功能的组件,Zuul已经停止维护。

创建Gateway项目

在Spring Initializr上创建一个MS-Gateway项目,包含以下三个依赖:

<dependency>			 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

加入EurekaClient注解

在主启动程序中,加入以下注解。

package com.example.springcloud.gateway;
...

@SpringBootApplication
@EnableDiscoveryClient
@EnableEurekaClient
public class WsGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(WsGatewayApplication.class, args);
    }
}

配置Gateway

修改application.yml配置文件如下:


server:
  port: 9000
  
spring:
  application:
    name: ms-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false // 设为true,则所有service可以服务名直接访问
      routes:
        - id: provider
          uri: lb://MS-PROVIDER // 访问PROVIDER服务
          predicates:
            - Path=/provider/**
          filters:
            - StripPrefix= 1
        - id: consumer
          uri: lb://MS-CONSUMER // 访问CONSUMER服务
          predicates:
            - Path=/consumer/**

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8765/eureka/
  instance:
    prefer-ip-address: true

测试一下

将目前的服务按以下顺序启动:

  1. MS-Eureka
  2. MS-Gateway
  3. MS-Provider
  4. MS-Consumer

启动完成后

访问 http://localhost:9000/provider/1,应答内容为:

{"id":1,"name":"Name-1"}

访问 http://localhost:9000/consumer/product/2,应答内容为:

{"id":2,"name":"Name-2"}