十二、Spring cloud服务网关(Zuul)

一、核心概念

1、网关
  网关是程序或者系统之间的连接节点,扮演者程序或系统之间的门户,允许他们之间通过通讯协议交换信息,他们可能是同构和异构的系统。

  例如:

  • REST API 网关
  • WebServices 网关

2、我对网关的理解
  因为微服务架构会将一个大系统分成一个个独立的子系统,每个子系统都会有独立的 域名 或者 IP,若子系统非常多的话,就会导致使用者需要记住很多 URL,这样显然不是用户友好的。

  而在引入了 Zuul(网关)之后,我们只要记住网关的 IP,然后将各个子系统映射的路径加上即可。比如:

zuul.routes.user-service-provider=/user-service/**

就是将 user-service-provider 子系统映射为 /user-service/** 路径。原来我们访问 user-service-provider 的某个接口,比如:http;//localhost:9090/user/find/all,现在通过网关,我们只需要通过:http;//localhost:5050/user-service/user/find/all (5050是网关的端口),就可达到同样的效果。网关的作用就是在访问 http;//localhost:5050/user-service/user/find/all 时,将请求转换成 http;//localhost:9090/user/find/all

二、整合 Zuul

结构图

  创建 Zuul 代理应用:zuul-proxy

(一)添加 Zuul 依赖

		<!-- 依赖 Spring Cloud Netflix Zuul -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

(二)激活 Zuul

/**
 * 注解 @EnableZuulProxy:激活 Zuul
 * 注解 @SpringCloudApplication:激活 @SpringBootApplication、@EnableDiscoveryClient和EnableCircuitBreaker
 */
@EnableZuulProxy
@SpringCloudApplication
public class ZuulProxyApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulProxyApplication.class, args);
    }
}

(三)配置 Zuul

# Zuul 代理应用
spring.application.name=zuul-proxy

#服务端口
server.port=5050

management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

1、配置 Zuul 路由规则

# /* 当前层级匹配  /** 递归层级匹配
url.pattern=/user-service/**

#指定 user-service-provider(服务映射:zuul.routes.${serviceId}=${url.pattern})
zuul.routes.user-service-provider=${url.pattern}

#配置 Ribbon(这样是白名单方式,一旦引入 Eureka,这项配置需要删除)
user-service-provider.ribbon.listOfServers=http://localhost:9090/

  在如此配置之后,访问 http://localhost:5050/user-service/user/find/all ,会被 Zuul 转发至 http://localhost:9090/user/find/all

2、配置 HTTP 客户端

注意:实际配置 Ribbon 底层 HTTP 调用客户端,并非 Zuul 独享此功能。

(1)默认客户端:HttpClient
装配类:HttpClientRibbonConfiguration

(2)可配置客户端:OkHttpClient
装配类:OkHttpRibbonConfiguration
激活配置:ribbon.okhttp.enabled=true

(三)Zuul 端点
实现:RoutesEndpoint
路径:/actuator/routes(即可发现所有的网关中的映射)
过滤器:

三、Spring Cloud 再整合

(一)服务依赖关系

  • eureka-server
    • user-service-provider
    • config-server
      • user-service-client
        • zuul-proxy

(二)将 zuul-proxy 配置为 config-client

在 config-server 中新建 configs/zuul-config.properties

#zuul proxy 配置内容

# /* 当前层级匹配  /** 递归层级匹配
url.pattern.provider=/user-service/**
url.pattern.client=/user-client/**

#指定 user-service-provider(服务映射:zuul.routes.${serviceId}=${url.pattern})
zuul.routes.user-service-provider=${url.pattern.provider}

#指定 user-service-client
zuul.routes.user-service-client=${url.pattern.client}

(三)zuul-proxy 整合作为 配置客户端 和 服务发现客户端

1、增加依赖

		<!-- 依赖 Config Client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <!-- 增加 Eureka Client 依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

2、配置 config-client 和 eureka-client

(1)bootstrap.properties(新建)

# Zuul 代理应用
spring.application.name=zuul-proxy

#配置客户端应用关联的应用(这里用配置文件 zuul-config.properties 文件名)
spring.cloud.config.name=zuul-config

#关联 profile
spring.cloud.config.profile=default

#关联 label
spring.cloud.config.label=master

#激活 config server 服务发现
spring.cloud.config.discovery.enabled=true

#config server 应用服务器的名称
spring.cloud.config.discovery.service-id=config-server

#eureka 客户端注册到 eureka 服务器
eureka.client.service-url.defaultZone=http://localhost:7070/eureka

(2)application.properties(改造)

#服务端口
server.port=5050

#配置 Ribbon(这样是白名单方式,一旦引入 Eureka,这项配置需要删除)
#user-service-provider.ribbon.listOfServers=http://localhost:9090/

management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

3、激活服务发现
  因为我们在 ZuulProxyApplication 引导类上加了 @SpringCloudApplication 注解,该注解继承了 @EnableDiscoveryClient,所以无需在单独加注解激活。

三、过滤器 ZuulFilter

  ZuulFilter 调用链:

  • ZuulServlet / ZuulServletFilter
    • ZuulRunner#preRoute()
    • ZuulRunner#route()
    • ZuulRunner#postRoute()
      • FilterProcessor#preRoute()
      • FilterProcessor#route()
      • FilterProcessor#postRoute()
        • FilterProcessor#runFilters()
          • ZuulFilter#runFilter()
            • ZuulFilter#run()

   ZuulServlet 关键代码:

	@Override
    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
        try {
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

            // Marks this request as having passed through the "Zuul engine", as opposed to servlets
            // explicitly bound in web.xml, for which requests will not have the same data attached
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();

            try {
                preRoute();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                route();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                postRoute();
            } catch (ZuulException e) {
                error(e);
                return;
            }

        } catch (Throwable e) {
            error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }

四、Zuul 自动装配

  ZuulServletFilter 使用范围更大,可以拦截所有 Servlet ,包括 ZuulServlet
  ZuulServlet 会有URL匹配模式,url-pattern。

  Zuul 两种激活模式:

1、@EnableZuulProxy

  导入 ZuulProxyMarkerConfiguration,随后生成一个 ZuulProxyMarkerConfiguration.Marker() Bean,这个 Bean 作为 ZuulProxyAutoConfiguration 装配的前置条件。

注意:ZuulProxyAutoConfiguration 扩展了 ZuulServerAutoConfiguration,所以 ZuulControllerZuulServlet 会被自动装配。

  ZuulControllerDispatcherServlet 来控制,它的映射地址是:/*DispatcherServlet 中注册了一个 ZuulHandlerMapping ,它控制映射到 ZuulController,具体实现可以参考 ZuulServerAutoConfiguration 中的实现:

@Bean
	public ZuulController zuulController() {
		return new ZuulController();
	}

	@Bean
	public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
		ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
		mapping.setErrorController(this.errorController);
		return mapping;
	}

  通过源码分析,ZuulController 将请求委派给 ZuulServlet ,所以所有的 ZuulFilter 实例都会被执行。

因此,访问 http://localhost:5050/user-service-client/user/find/all ,实际上是将请求递交给 DispatcherServlet 发送请求"/user-service-client/user/find/all"

  调用链:

  • DispatcherServlet
    • ZuulHandlerMapping
      • ZuulController
        • ZuulServlet
          • RibbonRoutingFilter

2、@EnableZuulServer

  导入 ZuulServerMarkerConfiguration ,随后生成一个 ZuulServerMarkerConfiguration.Marker() Bean,主要用作装配 ZuulServerAutoConfiguration

  ZuulProxyAutoConfiguration 与 父类 ZuulServerAutoConfiguration 区别:ZuulProxyAutoConfiguration 提供了 RibbonRoutingFilter

  调用链:

  • DispatcherServlet
    • ZuulHandlerMapping
      • ZuulController
        • ZuulServlet
          • ZuulFilter

猜你喜欢

转载自blog.csdn.net/panchang199266/article/details/84309556