SpringCloud 使用 Zuul 构建微服务网关

avatar 2019年08月08日18:22:40 0 1275 views
Zuul 是 Netflix 开源的微服务网关,它可以和 Eureka、Ribbon、Hystrix 等组件配合使用。Zuul 的核心就是一系列过滤器,这些过滤器可以完成以下功能:
  • 身份认证和安全
  • 审查和监控
  • 动态路由
  • 压力测试
  • 负载均衡
  • 等待
目前,Zuul 使用默认 HTTP 客户端是 Apache HTTP Client。 下面介绍编写一个 Zuul 微服务网关和基本的配置。  

准备

Eureka 注册中心,启动在 8761 端口 一个或多个服务,注册在 Eureka 中 比如我目前 Eureka 上已经注册了两个服务 分别是用户中心的服务提供者(8080端口)和服务消费者(8081端口) 通过地址栏访问 http://localhost:8080/user/1 和 http://localhost:8081/hello 均能访问到两个服务 其中后者访问效果如下,下面以这个为示例,介绍网关的使用   下面我们来介绍 zuul,通过网关来访问这两个服务(通常我只暴露服务消费者,即 web 那个服务,服务提供者我们通常不暴露出去)  

一、编写 Zuul 微服务网关

1.创建一个 SpringBoot 项目 注意:目前我使用都是最新版本, SpringBoot 版本是 2.1.7.RELEASE,SpringCloud 版本是 Greenwich.RELEASE pom.xml 添加依赖
  1. <!-- Eureka client  -->
  2. <dependency>
  3.     <groupId>org.springframework.cloud</groupId>
  4.     <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  5. </dependency>
  6. <!-- zuul -->
  7. <dependency>
  8.     <groupId>org.springframework.cloud</groupId>
  9.     <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
  10. </dependency>
  2.启动类添加 @EnableZuulProxy 注解
  1. @SpringBootApplication
  2. @EnableZuulProxy
  3. public class SensApiGatewayApplication {
  4.     public static void main(String[] args) {
  5.         SpringApplication.run(SensApiGatewayApplication.class, args);
  6.     }
  7. }
  3.添加配置 application.yml
  1. server:
  2.   port: 9000
  3. spring:
  4.   application:
  5.     name: sens-api-gateway
  6. eureka:
  7.   client:
  8.     serviceUrl:
  9.       defaultZone: http://localhost:8761/eureka/
启动在 9000 端口,注册服务到 Eureka   4.运行启动类,启动 Zuul 在 9000 端口 可以看到网关注册到了  Eureka 通过地址栏访问 http://localhost:9000/服务名/请求路径,就可以访问到其他服务的路径 如我的用户中心服务消费者服务名为 sens-user-web (上图左侧的 Application 名称是全部大写,实际我的服务名是全小写的) 即下面两个请求结果是一样的 http://localhost:9000/sens-user-web/hello http://localhost:8081/hello   可见,网关帮我们转发了这个请求 如果想访问另一个服务,一样的方法 http://localhost:9000/sens-user-core/user/1 也能访问到 http://localhost:8080/user/1   可见,目前我们几乎没有任何其他的配置,就能实现通过网关访问到所有的请求 但是这还不够,下面我们会介绍自定义路由和容错回退的一些配置  

二、自定义路由

目前通过网关访问其他服务的路径,示例如下 http://localhost:9000/sens-user-web/hello http://localhost:9000/sens-user-core/user/1   现在我有两个需求
  • 首先我觉得 sens-user-web 太长,我想改成 user
  • sens-user-core 这个服务我不想暴露,我只想暴露服务消费者。访问生产者的话让网关响应404比较好
解决办法很简单,只需要几行配置 在 application.yml 添加配置
  1. zuul:
  2.   # 前缀,所有服务里的路径前加 /api
  3.   prefix: /api
  4.   routes:
  5.     sens-user-web: /user/**
  6.   # 排除某些路由, 支持正则表达式
  7.   ignored-patterns:
  8.     - /**/abc/efg
  9.   # 排除服务
  10.   ignored-services: sens-user-core
上面几行配置,分别是设置统一的前缀,单条路由重写,排除自定义路由(排除后,访问404),排除服务(该服务所有请求404)   这样一来 配置前是 http://localhost:9000/sens-user-web/hello 配置后是 http://localhost:9000/api/user/hello 注意:因为我没有排除 sens-user-web,所有之前的 http://localhost:9000/sens-user-web/hello 也可以访问  

三、设置超时时间

通常我们通过网关访问一个路径,如 http://localhost:9000/sens-user-web/hello, 网关会自己转发到  sens-user-web 服务中的 /hello 路径, 因为 Zuul 内部使用了 Ribbon 做负载均衡,那么对于一些时间比较长的请求,可能会被作为超时处理。 默认时间是 1 秒   示例 访问 http://localhost:9000/sens-user-web/hello 我在 sens-user-web 服务 /hello 方法里写一个 sleep,睡眠5s,看会不会超时(也可以通过打断点暂停的方法测试)
  1. @GetMapping("/hello")
  2. public String index() throws InterruptedException {
  3.     System.out.println("进来了");
  4.     //主线程暂停5s
  5.     Thread.sleep(5000);
  6.     return "Hello, this is user web page.";
  7. }
效果如下 解决办法 在 pom.xml 中设置超时时间
  1. # 超时时间,ribbon和hystrix是同时生效的,哪个值小哪个生效
  2. hystrix:
  3.   command:
  4.     default:
  5.       execution:
  6.         isolation:
  7.           thread:
  8.             timeoutInMilliseconds: 60000
  9. ribbon:
  10.   ReadTimeout: 60000
  11.   ConnectTimeout: 3000
 

四、Zuul 的容错和回归

当服务不可用的时候,网关会响应 50X 之类的错 比如超时是这样的     这是系统默认的界面,很不友好 我们可以重写回归响应的方法,给用户一个比较好的提示   解决办法如下 在网关项目中新建一个类
  1. package com.liuyanzhao.sens.api.gateway.config;
  2. import com.netflix.hystrix.exception.HystrixTimeoutException;
  3. import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
  4. import org.springframework.http.HttpHeaders;
  5. import org.springframework.http.HttpStatus;
  6. import org.springframework.http.MediaType;
  7. import org.springframework.http.client.ClientHttpResponse;
  8. import org.springframework.stereotype.Component;
  9. import java.io.ByteArrayInputStream;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.nio.charset.Charset;
  13. /**
  14.  * @author 言曌
  15.  * @date 2019-08-08 10:34
  16.  */
  17. @Component
  18. public class MyFallbackProvider implements FallbackProvider {
  19.     @Override
  20.     public String getRoute() {
  21.         //表明是为哪个微服务提供回退,*表示所有微服务
  22.         return "*";
  23.     }
  24.     @Override
  25.     public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
  26.         if(cause instanceof HystrixTimeoutException) {
  27.             return response(HttpStatus.GATEWAY_TIMEOUT);
  28.         }
  29.         return this.fallbackResponse();
  30.     }
  31.     public ClientHttpResponse fallbackResponse() {
  32.         return this.response(HttpStatus.INTERNAL_SERVER_ERROR);
  33.     }
  34.     private ClientHttpResponse response(final HttpStatus status) {
  35.         return new ClientHttpResponse() {
  36.             @Override
  37.             public HttpStatus getStatusCode() throws IOException {
  38.                 return status;
  39.             }
  40.             @Override
  41.             public int getRawStatusCode() throws IOException {
  42.                 return status.value();
  43.             }
  44.             @Override
  45.             public String getStatusText() throws IOException {
  46.                 return status.getReasonPhrase();
  47.             }
  48.             @Override
  49.             public void close() {
  50.             }
  51.             @Override
  52.             public InputStream getBody() throws IOException {
  53.                 return new ByteArrayInputStream("服务不可用,请稍后再试。Service is not available, please try again later.".getBytes());
  54.             }
  55.             @Override
  56.             public HttpHeaders getHeaders() {
  57.                 //headers设定
  58.                 HttpHeaders headers = new HttpHeaders();
  59.                 MediaType mediaType = new MediaType("application""json", Charset.forName("UTF-8"));
  60.                 headers.setContentType(mediaType);
  61.                 return headers;
  62.             }
  63.         };
  64.     }
  65. }
重启网关服务  

五、管理端点

当 @EnableZuulProxy 和 Spring Boot Actuator配合使用时,Zuul 会暴露两个端点:/actuator/routes, /actuator/filters 。借助这两个端点,我们可以方便地、直观地查看级管理 Zuul。 通常,我们直接访问是会报 404,如下配置可以解决 在 application.yml 添加
  1. # 开启权限访问端点,如 /actuator/routes, /actuator/filters
  2. management:
  3.   endpoints:
  4.     web:
  5.       exposure:
  6.         include: '*'
这个配置在 springboot 1.x 版本是 management.security.enabled=false,端点路径是 /routes和 /filters  
  • 路由端点
该端点主要是查看路由列表 http://localhost:9000/actuator/routes   GET 是获取路由列表,POST请求是立即刷新路由列表  
  • 过滤器路由路径
该端点可以查看所有的过滤器,了解 error、post、pre、route 四种过滤器有哪些,是否启动,执行顺序(order) 是多少。 http://localhost:9000/actuator/filters     源码地址:https://github.com/saysky/sens-web/tree/master/sens-api-gateway
  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
avatar
已通过评论:0   待审核评论数:0