八、Zuul构建微服务网关

1、为什么要使用微服务网关

    1.1、没有网关的话,存在的问题

  •         客户端要多次请求不同微服务,增加客户端复杂性
  •         存在跨域请求问题
  •         认证复杂,每个项目都要单独一套认证
  •         难以重构,如果微服务做修改,所有调用的客户端都要改一遍
  •         某些微服务使用防火墙/不友好协议,直接访问比较困难

    使用微服务网关就可以解决以上问题

    1.2、使用微服务网关的有点

  •         易于监控
  •         易于认证,不需要每个微服务都进行认证
  •         减少客户端与微服务端交互次数

2、Zuul简介

    Zuul是微服务网关插件,本身已经集成了Ribbon和Hystrix,并且可以和Eureka配合使用。Zuul的核心是一系列过滤器,这些过滤器可以完成以下功能:

  •         身份认证与安全
  •         审查和监控
  •         负载分配和动态路由:动态分配到不同的后端集群
  •         静态响应处理

3、编写Zuul微服务网关

    3.1、添加依赖

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

    3.2、启动类添加注解@EnableZuulProxy

        声明Zuul代理,已经整合Ribbon定位微服务,整合Hystrix实现容错

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
  public static void main(String[] args) {
    SpringApplication.run(ZuulApplication.class, args);
  }
}

    3.3、编写配置文件application.yml

server:
  port: 8040
spring:
  application:
    name: microservice-gateway-zuul
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

4、Zuul的路由端点

    @EnableZuulProxy与SpringBootActuator配合使用时,Zuul会暴露一个路由管理端点/routes。直观地查看及管理Zuul的路由。(zuul里面已经整合了actuator,所以不需要再增加依赖)

    访问http://localhost:8040/routes,即可获得如下结果

{
    "/microservice-provider-user/**" : "microservice-provider-user",
    "/microservice-consumer-movie/**": "microservice-consumer-movie"
}

5、路径配置详解

    5.1、自定义指定微服务的访问路径

zuul: 
    routes: 
        microservice-provider-user: /user/**

    只要是/user/**的请求都会自动在microservice-provider-user对应的服务中请求

    5.2、忽略指定微服务

zuul: 
    ignored-services: microservice-provider-user,microservice-consumer-movie


#zuul: 
#    ignored-services: '*'        #忽略所有,只路由指定微服务
#    routes: 
#        microservice-provider-user: /user/**

5.3、同时指定微服务的serviceId和对应路径

zuul: 
    routes: 
        user-route:             #任意起的名字,作为该配置的标识
            service-id: provider-microservice-user
            path: /user/**

    本例配置效果同5.1

5.4、同时指定path和URL

zuul: 
    routes: 
        user-route:    #任意起的名字,作为该配置的标识
            url: http://localhost:8000/
            path: /user/**

6、Zuul的安全与Header

7、Zuul上传文件

8、Zuul的过滤器

    过滤器是Zuul的核心组件

    8.1、过滤器的类型与请求生命周期

  •     pre:这种过滤器在请求路由之前被调用,可做身份验证
  •     routing:将请求路由到微服务,用于构建发送给微服务的请求,使用HttpClient/Ribbon
  •     post:在路由到微服务以后执行,可为响应添加指定内容
  •     error:在其他阶段发生错误时执行该过滤器

过滤器类型

    8.2、编写Zuul过滤器

        8.2.1、过滤器类代码

public class PreRequestLogFilter extends ZuulFilter {
  private static final Logger LOGGER = LoggerFactory.getLogger(PreRequestLogFilter.class);
  private static int n = 1;

  @Override
  public String filterType() {
    return FilterConstants.PRE_TYPE;  //过滤器类型
  }

  @Override
  public int filterOrder() {
    return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;//过滤器执行顺序
  }

  @Override
  public boolean shouldFilter() {
    return true;                    //过滤器是否执行
  }

  @Override
  public Object run() {        //过滤器业务逻辑,该例打印了请求的HTTP方法及请求地址
    RequestContext ctx = RequestContext.getCurrentContext();
    HttpServletRequest request = ctx.getRequest();
    PreRequestLogFilter.LOGGER.info(String.format("send %s request to %s", request.getMethod(), request.getRequestURL().toString()));
    PreRequestLogFilter.LOGGER.info("——————————————————————拦截到第"+(n++)+"次请求!");
    return null;
  }
}
  •         filterType:过滤器类型——pre、route、post、error
  •         filterOrder:返回int值来指定过滤器的执行顺序,不同过滤器可相同数字
  •         shouldFilter:返回boolean确定该过滤器是否执行
  •         run:过滤器具体的业务逻辑

        8.2.2、启动类添加

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
  public static void main(String[] args) {
    SpringApplication.run(ZuulApplication.class, args);
  }

  @Bean
  public PreRequestLogFilter preRequestLogFilter() {
    return new PreRequestLogFilter();
  }
}

    8.3、禁用过滤器

        zuul.<SimpleClassName>.<filterType>.disable=true

        例如:zuul.PreRequestLogFilter.pre.disable=true

9、Zuul的容错与回退

    Zuul中已经默认整合了Hystrix

    9.1、编写Zuul的回退类

@Component
public class UserFallbackProvider implements ZuulFallbackProvider {
  @Override
  public String getRoute() {
    // 表明是为哪个微服务提供回退
    return "microservice-provider-user";
  }

  @Override
  public ClientHttpResponse fallbackResponse() {
    return new ClientHttpResponse() {
      @Override
      public HttpStatus getStatusCode() throws IOException {
        // fallback时的状态码
        return HttpStatus.OK;
      }

      @Override
      public int getRawStatusCode() throws IOException {
        // 数字类型的状态码,本例返回的其实就是200,详见HttpStatus
        return this.getStatusCode().value();
      }

      @Override
      public String getStatusText() throws IOException {
        // 状态文本,本例返回的其实就是OK,详见HttpStatus
        return this.getStatusCode().getReasonPhrase();
      }

      @Override
      public void close() {
      }

      @Override
      public InputStream getBody() throws IOException {
        // 响应体
        return new ByteArrayInputStream("用户微服务不可用,请稍后再试。".getBytes());
      }

      @Override
      public HttpHeaders getHeaders() {
        // headers设定
        HttpHeaders headers = new HttpHeaders();
        MediaType mt = new MediaType("application", "json", Charset.forName("UTF-8"));
        headers.setContentType(mt);

        return headers;
      }
    };
  }
}

之后,当microservice-provider-user微服务无法正常响应时,将返回——“用户微服务不可用,请稍后再试”

10、Zuul的高可用

    启动多个Zuul的网关服务,均注册到EurekaServer中即可。然后前置nginx将请求分发到各个网关即可!

11、使用Sidecar整合非JVM微服务

12、使用zuul聚合微服务

    如果客户端需要访问多个微服务,如果通过网关分别调用、分别返回的话,网络开销、耗时、流量消耗都会很不好。这时我们可以在Zuul整合客户端的请求,客户端只给Zuul网关发送一个请求,网关中分别请求各个微服务,然后给客户端返回一个最终结果!

    12.1、启动类代码

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
  public static void main(String[] args) {
    SpringApplication.run(ZuulApplication.class, args);
  }

  @Bean
  @LoadBalanced
  public RestTemplate restTemplate() {
    return new RestTemplate();
  }
}

    12.2、Service层代码

@Service
public class AggregationService {
  @Autowired
  private RestTemplate restTemplate;

  @HystrixCommand(fallbackMethod = "fallback")
  public Observable<User> getUserById(Long id) {
    // 创建一个被观察者
    return Observable.create(observer -> {
      // 请求用户微服务的/{id}端点
      User user = restTemplate.getForObject("http://microservice-provider-user/{id}", User.class, id);
      observer.onNext(user);
      observer.onCompleted();
    });
  }

  @HystrixCommand(fallbackMethod = "fallback")
  public Observable<User> getMovieUserByUserId(Long id) {
    return Observable.create(observer -> {
      // 请求电影微服务的/user/{id}端点
      User movieUser = restTemplate.getForObject("http://microservice-consumer-movie/user/{id}", User.class, id);
      observer.onNext(movieUser);
      observer.onCompleted();
    });
  }

  public User fallback(Long id) {
    User user = new User();
    user.setId(-1L);
    return user;
  }
}

    12.3、Controller层代码

@RestController
public class AggregationController {
  public static final Logger LOGGER = LoggerFactory.getLogger(ZuulApplication.class);

  @Autowired
  private AggregationService aggregationService;

  @GetMapping("/aggregate/{id}")
  public DeferredResult<HashMap<String, User>> aggregate(@PathVariable Long id) {
    Observable<HashMap<String, User>> result = this.aggregateObservable(id);
    return this.toDeferredResult(result);
  }

  public Observable<HashMap<String, User>> aggregateObservable(Long id) {
    // 合并两个或者多个Observables发射出的数据项,根据指定的函数变换它们
    return Observable.zip(
            this.aggregationService.getUserById(id),
            this.aggregationService.getMovieUserByUserId(id),
            (user, movieUser) -> {
              HashMap<String, User> map = Maps.newHashMap();
              map.put("user", user);
              map.put("movieUser", movieUser);
              return map;
            }
    );
  }

  public DeferredResult<HashMap<String, User>> toDeferredResult(Observable<HashMap<String, User>> details) {
    DeferredResult<HashMap<String, User>> result = new DeferredResult<>();
    // 订阅
    details.subscribe(new Observer<HashMap<String, User>>() {
      @Override
      public void onCompleted() {
        LOGGER.info("完成...");
      }

      @Override
      public void onError(Throwable throwable) {
        LOGGER.error("发生错误...", throwable);
      }

      @Override
      public void onNext(HashMap<String, User> movieDetails) {
        result.setResult(movieDetails);
      }
    });
    return result;
  }
}

猜你喜欢

转载自blog.csdn.net/sky_helloword/article/details/86021047