Spring Cloud学习|第五篇:路由网关-Zuul

1.Zuul简介

zuul是从设备和网站到后端应用程序所有请求的前门,为内部服务提供可配置的对外URL到服务的映射关系,基于JVM的后端路由器。为所有下游服务的网关层,所有前台请求均先达到zuul服务,而后在由zuul路由至下游服务

2.Zuul工作原理

​ Zuul是通过Servlet实现的,Zuul通过自定义的ZuulServlet来对请求进行控制,Zuul核心是一系列过滤器,可以在Http请求的发起和响应返回期间执行一系列的过滤器,Zuul包括如下4种过滤器:

(1) PRE过滤器: 它是请求具体实现逻辑前过滤,这种类型的过滤器可以做安全验证,如身份验证,参数验证等

(2) ROUTING过滤器: 它用于将请求路由到具体的微服务实例。默认,它使用Http Client进行网络请求

(3) POST过滤器: 它是处理具体逻辑后,返回前端前过滤

(4) ERROR过滤器: 它是在其它过滤器发生错误时执行的

@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
   try {
       // 省略代码
       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();
   }
}

分析上述代码:

(1) 最先执行preRoute(),当出现异常时,执行error()和postRoute()

(2) 执行route(),当出现异常时,执行error()和postRoute()

(3)最后执行postRoute(),当出现异常时,执行error(),而不执行postRoute()

​ Zuul是通过RequestContext对象来共享数据的,每个请求都会创建一个RequestContext对象,每个过滤器包括如下几个特性:

(1) FilterType: 表示过滤器类型

(2) FilterOrder:执行顺序,值越小,越先执行

(3) shouldFilter: 表示是否继续执行run中逻辑,如果为false则表示不执行

(4) run: 执行具体的过滤细节

3.入门案例

服务名端口用途
eureka-ribbon-client8764ribbon客户端
eureka-client8762、8763服务提供者
eureka-feign-client8765feign客户端
eureka-zuul-client5000zuul客户端
consul8500注册中心
  • 创建eureka-zuul-client服务
  • 引入依赖
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
  • 书写启动类,添加注解@EnableZuulProxy
@EnableZuulProxy
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaZuulClientApplication {

   public static void main(String[] args) {
       SpringApplication.run(EurekaZuulClientApplication.class, args);
   }
}
  • 配置application.yml中路由规则
zuul:
 routes:
   hiapi:
     path: /hiapi/**
     serviceId: eureka-client
   ribbonapi:
     path: /ribbonapi/**
     serviceId: eureka-ribbon-client
   feignapi:
     path: /feignapi/**
     serviceId: eureka-feign-client
  • 访问服务

    (1) 访问 http://localhost:8766/hiapi/hi?name=lisi结果如下

    hi lisi, i am from port:8762

    hi lisi, i am from port:8763

    (2) 访问http://localhost8766/ribbonapi/hi?name=lisi结果与上边相同

    (3) 访问http://localhost8766/feignapi/hi?name=lisi结果与上边相同

4.Zuul其它操作

  • 在Zuul上配置熔断器

    (1) 在Zuul中实现熔断功能需要实现 ZuulFallbackProvider接口

    (2) getRoute()方法:用于确定具体路由服务

    (3) fallbackResponse():返回信息处理

@Component
public class MyZuulFallbackProvider implements FallbackProvider {

    @Override
    public String getRoute() {
        return "eureka-client";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    private ClientHttpResponse response(final HttpStatus status) {

        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 0;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("sorroy,have error".getBytes());
            }

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

    }
}
  • 自定义ZuulFilter

    自定义ZuulFilter需要集成ZuulFilter,重写其filterTyepe()、filterOrder()、shouldFilter()、run()方法,以下自定义ZuulFilter用于判定是否携带token

    方法作用
    shouldFilter表示是否执行run()方法,false:否,true:是
    RequetContext每个请求均会维护一个RequestContext
    setSendZuulResponse表示是否执行下一个过滤器,false:否,true:是

    代码演示:

@Component
public class MyFilter extends ZuulFilter {

    private Logger logger = LoggerFactory.getLogger(MyFilter.class);

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String token = request.getParameter("token");
        if(Objects.isNull(token)){
            logger.warn("token不存在");
            currentContext.setSendZuulResponse(false);
            currentContext.setResponseStatusCode(401);
            try {
                currentContext.getResponse().getWriter().write("token is empty");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        logger.info("OK");
        return null;
    }
}

5.Zuul常见使用方式

  • 每个平台使用独立的Zuul网关层,从而使Zuul集群化,以下截图来自《深入理解Spring Cloud与微服务构建》

    [外链图片转存失败(img-B1d4KUag-1567216688888)(C:\Users\zycao\AppData\Roaming\Typora\typora-user-images\1567216636803.png)]

  • 通过Nginx根据负载均衡策略将请求路由至Zuul集群,以下截图来自《深入理解Spring Cloud与微服务构建》

[外链图片转存失败(img-3LR6vDCR-1567216688889)(C:\Users\zycao\AppData\Roaming\Typora\typora-user-images\1567216653706.png)]

6.Zuul常见配置

规则解释例子
/**匹配任意路径及字符/provider/a,/provider/bbb,/provider/aa/bb/cc
/*匹配任意数量字符,只有一层路径/provider/a,/provider/bbb,/provider/ccdc
/?匹配单个字符,一层路径/provider/a,/provider/c
  • 自定义指定服务访问路径

path:表示请求前缀路径

serviceId:表示服务名

zuul:
	routes:
		provider-service:
			path: /provider/**
			serviceId: provider-service
  • 简化写法

自动会将服务映射至provider-service

zuul:
	routes:
		provider-service: /provider/**
  • 另一种写法

path与serviceId均不需要,自动映射,此种情况下,则会自动生成path = /provider-service/**,serviceId = provdier-service

zuul:
	routes:
		provider-service:
  • 直接配置服务ip:port

url:服务ip:port

zuul:
	routes:
		provider-service:
			ptah: /provider/**
			url: http://localhost:8000
  • forward本地跳转

url: forward:/本地请求路径,当请求/provider路径时,会跳转至zuul服务本地的路径前缀为/provider的接口,如url:http://localhost:7000/provider/add?a=10&b=20,则会自动匹配zuul服务中的/provider/add服务

zuul:
  routes:
    provider-service:
      path: /provider/**
      url: forward:/provider
  • 相同路径,后者覆盖前者

如下配置,则会请求至provider-service-b服务

zuul:
	routes:
		provider-service-a:
			path: /provider/**
			serviceId: provider-service-a
        provider-service-b:
        	path: /provider/**
        	serviceId: provider-service-b
  • 使用指定前缀

prefix: /pre,表示请求前缀为pre,但实际请求路径为/provider/,可以通过stripPrefix=false关闭此功能,则启作用的路径为/pre/provider/,一般不配置功能

zuul:
	prefix: /pre
	routes:
		provider-a: /provider/**
  • 服务屏蔽及路径屏蔽

ignored-service:屏蔽服务,当请求该服务时,会报404不存在

ignored-patterns:屏蔽路径

zuul:
	ignored-service: provider-b
	ignored-patterns: /provider-b/**
	prefix: /pre
	routes:
		provider-a: /provider-a/**
  • 敏感头信息

在构建系统时,有许多请求头信息不需要传递至下层服务,则可在zuul层进行拦截,而不传递

zuul:
	routes:
		provider-service:
			path: /provider/**
			sensitiveHeaders: Cookie,Set-Cookie,Authorization
			serviceId: provider-service
  • 重试机制

在生产环境中,可能存在请求失败,需重试情况,为了用户无感知,可通过zuul设置重试,但要考虑服务幂等性问题

zuul:
	retryable: true # 开启重试
	
ribbon:
	MaxAutoRetries: 1 # 同一个服务重试的次数
	MaxAutoRetriesNextServer: 1 # 切换相同服务数量
  • 自定义请求路由

通过上述路由配置我们知道,当只服务服务,不配置路径及serviceId时,则会默认生成path:/provider-service/**,servcieId: provider-service,但有时候我们想自定义生成的路径,则可采用如下方式,定义返回的格式为 v e r s i o n / {version}/ version/{name}

@Bean
public PatternServiceRouteMapper serviceRouteMapper(){
    return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)", "${version}/${name}");
}

7.参考资料

  • 《重新定义Springcloud实战》
  • 《Springcloud微服务实战》
  • 《深入理解Spring Cloud与微服务构建》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值