• 路由网关(Zuul)


    上一篇已经讲了微服务组件中的 分布式配置中心,本章讲述 由JAVA编写的服务路由网关Zuul

    - Zuul

    路由是微服务体系结构的一个组成部分。例如 / 可以映射到您的Web应用程序,/api/users映射到用户服务,/api/shop映射到商店服务。Zuul是Netflix的基于JVM的开发的路由和服务器端负载均衡器。

    为什么需要服务网关?

    如果没有服务网关,多个服务提供给前端调用地址管理错综复杂,增加了客户端的复杂性,认证也相对麻烦,每个服务都需要编写相同的认证….

    Zuul架构图Zuul架构图

    画图工具:https://www.processon.com/

    Zuul 可以做什么?

    • 身份认证
    • 审查与监控
    • 压力测试
    • 金丝雀测试
    • 动态路由
    • 服务迁移
    • 负载分配
    • 安全
    • 静态响应处理
    • 主动/主动流量管理

    Zuul的规则引擎允许基本上写任何JVM语言的规则和过滤器,内置Java和Groovy的支持。

    注意:配置属性 zuul.max.host.connections 已被取代的两个新的属性,zuul.host.maxTotalConnections并且zuul.host.maxPerRouteConnections 它的缺省值分别200和20。

    注意:所有路线的默认Hystrix隔离模式(ExecutionIsolationStrategy)为SEMAPHOREzuul.ribbonIsolationStrategy 如果此隔离模式是首选,可以更改为THREAD

    官方文档:http://cloud.spring.io/spring-cloud-static/Dalston.SR2/#_router_and_filter_zuul

    - 准备工作

    1.启动Consul
    2.创建 battcn-gateway-service 和 battcn-hello-service

    - battcn-gateway-service

    1.在项目 pom.xml 中添加 ZUUL 依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    2.App.java中开启 zuul 代理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @EnableZuulProxy
    @EnableDiscoveryClient
    @SpringBootApplication
    public class ZuulApplication {

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

    3.zuul.routes.routesName.ZuulProperties.ZuulRoute 配置的方式,详细可以参考源码org.springframework.cloud.netflix.zuul.filters.ZuulProperties

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    server:
    port: 9000

    zuul:
    routes:
    hello-service: #自定义的 路由名称
    path: /api-hello/** #路由路径
    serviceId: battcn-hello-service #VIP 模式中的 serviceId

    spring:
    application:
    name: battcn-gateway-service
    cloud:
    consul:
    host: localhost
    port: 8500
    enabled: true
    discovery:
    enabled: true
    prefer-ip-address: true

    - battcn-hello-service

    hello-service 只需要以下依赖,能注册到consul中就行(单纯的一个服务)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>
    1
    2
    3
    4
    5
    6
    7
    8
    @EnableDiscoveryClient
    @SpringBootApplication
    public class HelloApplication {

    public static void main(String[] args) {
    SpringApplication.run(HelloApplication.class, args);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    server:
    port: 9001

    spring:
    application:
    name: battcn-hello-service
    cloud:
    consul:
    host: localhost
    port: 8500
    enabled: true
    discovery:
    enabled: true
    prefer-ip-address: true

    - 测试一把

    访问:http://localhost:9000/api-hello/hello 从地址中可以看出我们访问的是 battcn-gateway-service,且满足 zuul.routes.routesName.path 规则,因此实际请求地址是http://localhost:9001/hello

    1
    hello:battcn-hello-service		#表示请求成功

    - 自定义过滤器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import org.springframework.stereotype.Component;

    import javax.servlet.http.HttpServletRequest;

    /**
    * 网关认证过滤器(Demo演示,实际根据自身业务考虑实现)
    * @author Levin
    * @date 2017-08-14.
    */
    @Component
    public class GatewayZuulFilter extends ZuulFilter {

    /**
    * per:路由之前,如实现认证、记录调试信息等
    * routing:路由时
    * post:路由后,比如添加HTTP Header
    * error:发生错误时调用
    */
    @Override
    public String filterType() {
    return "pre";
    }

    /**
    * 过滤器顺序,类似@Filter中的order
    */
    @Override
    public int filterOrder() {
    return 0;
    }

    /**
    * 这里可以写逻辑判断,是否要过滤,本文true,永远过滤。
    */
    @Override
    public boolean shouldFilter() {
    return true;
    }

    /**
    * 过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。
    */
    @Override
    public Object run() {
    RequestContext ctx = RequestContext.getCurrentContext();
    HttpServletRequest request = ctx.getRequest();
    String token = request.getParameter("token");
    if(token == null) {
    ctx.setSendZuulResponse(false);
    ctx.setResponseStatusCode(404);
    ctx.setResponseBody("token cannot be empty");
    }
    return null;
    }
    }

    实际开发中token应该从请求头中获取,该处只是为了方便演示效果

    访问:http://localhost:9000/api-hello/hello?token=1

    1
    hello:battcn-hello-service

    访问:http://localhost:9000/api-hello/hello

    1
    token cannot be empty

    - 路由映射流程

    图不是很严谨,凑合着看看吧…

    HanderMappingHanderMapping

    初次启动项目我们可以看到

    1
    2
    3
    4
    2017-08-14 17:19:06.090  INFO 11544 --- [nio-9000-exec-1] o.s.c.n.zuul.web.ZuulHandlerMapping      : Mapped URL path [/api-hello/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
    2017-08-14 17:19:06.090 INFO 11544 --- [nio-9000-exec-1] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/battcn-gateway-service/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
    2017-08-14 17:19:06.090 INFO 11544 --- [nio-9000-exec-1] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/battcn-hello-service/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
    2017-08-14 17:19:06.090 INFO 11544 --- [nio-9000-exec-1] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/consul/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]

    有兴趣的还可以阅读下 com.netflix.loadbalancer.LoadBalancerContext,通过它可以发现每次请求的IP和端口,需要在application.yml 中配置

    1
    2
    3
    logging:
    level:
    com.netflix: DEBUG
    1
    2
    3
    4
    5
    6
    2017-08-14 17:32:16.793 DEBUG 14316 --- [nio-9000-exec-7] c.n.zuul.http.HttpServletRequestWrapper  : Path = null
    2017-08-14 17:32:16.793 DEBUG 14316 --- [nio-9000-exec-7] c.n.zuul.http.HttpServletRequestWrapper : Transfer-Encoding = null
    2017-08-14 17:32:16.793 DEBUG 14316 --- [nio-9000-exec-7] c.n.zuul.http.HttpServletRequestWrapper : Content-Encoding = null
    2017-08-14 17:32:16.793 DEBUG 14316 --- [nio-9000-exec-7] c.n.zuul.http.HttpServletRequestWrapper : Content-Length header = -1
    2017-08-14 17:32:16.793 DEBUG 14316 --- [nio-9000-exec-7] c.n.loadbalancer.ZoneAwareLoadBalancer : Zone aware logic disabled or there is only one zone
    2017-08-14 17:32:16.793 DEBUG 14316 --- [nio-9000-exec-7] c.n.loadbalancer.LoadBalancerContext : battcn-hello-service using LB returned Server: 192.168.31.221:9001 for request /hello

    - Zuul Fallback

    定义GatewayZuulFallback 实现 ZuulFallbackProvider 接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    /**
    * @author Levin
    * @date 2017-08-14.
    */
    @Component
    public class GatewayZuulFallback implements ZuulFallbackProvider {
    @Override
    public String getRoute() {
    return "battcn-hello-service";//指定回退的服务名
    }

    @Override
    public ClientHttpResponse fallbackResponse() {
    return new ClientHttpResponse() {
    @Override
    public HttpStatus getStatusCode() throws IOException {
    return HttpStatus.INTERNAL_SERVER_ERROR; //INTERNAL_SERVER_ERROR(500, "Internal Server Error")
    }

    @Override
    public int getRawStatusCode() throws IOException {
    return this.getStatusCode().value();// 500
    }

    @Override
    public String getStatusText() throws IOException {
    return this.getStatusCode().getReasonPhrase();// Internal Server Error
    }

    @Override
    public void close() {}

    @Override
    public InputStream getBody() throws IOException {
    return new ByteArrayInputStream(getStatusText().getBytes());//也可以随意写自己想返回的内容
    }

    @Override
    public HttpHeaders getHeaders() {
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
    return httpHeaders;
    }
    };
    }
    }

    步骤:停掉 battcn-hello-service 启动 battcn-gateway-service

    访问:http://localhost:9000/api-hello/hello?token=1

    显示:

    1
    Internal Server Error  # 代表Fallback成功

    - 路由配置详解

    zuul 相关的官方文档还是比较其全了,本文也是参考官方然后简单讲述应用场景,具体开发请结合自身业务扩展….

    1.自定义指定微服务路径,以下两种配置方式结果相同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    zuul:
    routes:
    battcn-hello-service: /api-hello/**
    #两种配置方式结果相同
    zuul:
    routes:
    hello-service:
    path: /api-hello/**
    serviceId: battcn-hello-service

    2.忽略指定服务,多个服务逗号分隔,Set<String> ignoredServices 这样一来zuul就会忽略service1和service2,只会代理其它的

    1
    2
    zuul:
    ignored-services: battcn-hello-service1,battcn-hello-service2

    3.指定Path和Url

    1
    2
    3
    4
    5
    zuul:
    routes:
    hello-service:
    path: /api-hello/** #路由路径
    url: http://localhost:9001/ #指定URL地址

    4.使用Zuul但不指定Url(该方式在Eureka有效,Consul还未找到解决方法…)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    zuul:
    routes:
    battcn-hello-service: /api-hello/**
    ribbon:
    eureka:
    enabled: false #为Ribbon禁用 eureka,不会破坏Zuul的Hystrix和Ribbon特性
    battcn-hello-service:
    ribbon:
    listOfServers: localhost:9001,localhost:9002
  • 相关阅读:
    servlet 项目 ,,启动没问题,,但是,一请求也面就报错误。。。。求解决。。。。。。。。。。。。。各种百度,都没解决了啊。。。。。急急急急急急急急急急急急急急急急急急
    js 表单不为空,数字长度验证
    cordova 安卓项目打包 release安装包
    Vue-cli构建项目, 组件中js代码引入图片路径问题
    Vue-cli 构建项目 的`.vue`组件中, scss中添加背景图路径问题
    解决 Cordova命令突然无法使用问题.
    VS Code 自用插件备份
    Cordova环境搭建
    前端渲染与后端渲染方式大体对比
    JavaScript-获取当前元素的相关元素或节点--方法总结
  • 原文地址:https://www.cnblogs.com/lywJ/p/10715593.html
Copyright © 2020-2023  润新知