博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【spring cloud】spring cloud zuul 路由网关
阅读量:5102 次
发布时间:2019-06-13

本文共 13170 字,大约阅读时间需要 43 分钟。

GitHub源码地址:

版本介绍:

UTF-8
UTF-8
1.8
2.0.4.RELEASE
Finchley.SR1
4.2.1

 

====================================================

参考地址:

====================================================

一.简单介绍

 Zuul作为微服务系统的网关组件,用于构建边界服务,致力于动态路由、过滤、监控、弹性伸缩和安全。

为什么需要Zuul

Zuul、Ribbon以及Eureka结合可以实现智能路由和负载均衡的功能;

网关将所有服务的API接口统一聚合,统一对外暴露。

外界调用API接口时,不需要知道微服务系统中各服务相互调用的复杂性,保护了内部微服务单元的API接口;

网关可以做用户身份认证和权限认证,防止非法请求操作API接口;

网关可以实现监控功能,实时日志输出,对请求进行记录;

网关可以实现流量监控,在高流量的情况下,对服务降级;

API接口从内部服务分离出来,方便做测试。

 

 

Zuul通过Servlet来实现,通过自定义的ZuulServlet来对请求进行控制。核心是一系列过滤器,可以在Http请求的发起和响应返回期间执行一系列过滤器。Zuul采取了动态读取、编译和运行这些过滤器。过滤器之间不能直接通信,而是通过RequestContext对象来共享数据,每个请求都会创建一个RequestContext对象。

Zuul生命周期如下图。 当一个客户端Request请求进入Zuul网关服务时,网关先进入”pre filter“,进行一系列的验证、操作或者判断。然后交给”routing filter“进行路由转发,转发到具体的服务实例进行逻辑处理、返回数据。当具体的服务处理完成后,最后由”post filter“进行处理,该类型的处理器处理完成之后,将Request信息返回客户端。 

 

 

二.配置过程

1.pom依赖

    
org.springframework.boot
spring-boot-starter-actuator
    
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
    
org.springframework.cloud
spring-cloud-starter-netflix-zuul

 

2.配置application.properties

spring.application.name=springcloud-ms-gatewayserver.port=8001eureka.client.service-url.defaultZone=http://127.0.0.1:8000/eureka/#因为parent的pom.xml中  添加了连接数据库的依赖,所以 需要配置数据库连接相关配置spring.datasource.continue-on-error=false spring.datasource.url=jdbc:mysql://localhost:3306/springcloudtest?useSSL=false&useUnicode=true&characterEncoding=UTF-8spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.username=rootspring.datasource.password=rootzuul.host.socket-timeout-millis=10000#网关最大超时时间10szuul.host.connect-timeout-millis=10000#网关最大连接数 默认200zuul.host.max-total-connections=200#给要路由的API请求加前缀 可加可不加zuul.prefix=/v1#需要忽略的服务 ,号分隔  配置后将不会被路由zuul.ignored-services=spring-cloud-ms-eureka#配置了zuul之后,那么整个分布式系统,对外则只暴露http://zuul-IP:zuul-port/v1/服务自定义地址/具体API请求地址#这一组配置后,访问http://localhost:8001/v1/ms-member/member/showMember 即相当于直接访问会员服务http://localhost:9000/member/showMember#zuul.routes.xx  xx随便写,zuul中唯一即可#zuul.routes.xx.serviceId=eureka中注册的服务名 即各个服务配置文件中的spring.application.namezuul.routes.member.serviceId=springcloud-ms-member#zuul.routes.xx.path=/自定义的地址    /**表示下级也可以访问zuul.routes.member.path=/ms-member/**#这一组配置后,所有访问积分服务的请求 即可直接访问网关地址http://localhost:8001/v1/ms-integral/具体接口地址   ,由zuul进行路由转发和负载均衡zuul.routes.integral.serviceId=springcloud-ms-integralzuul.routes.integral.path=/ms-integral/**#这一组配置后,所有访问商品服务的请求 即可直接访问网关地址http://localhost:8001/v1/ms-goods/具体接口地址  ,由zuul进行路由转发和负载均衡zuul.routes.goods.serviceId=springcloud-ms-goodszuul.routes.goods.path=/ms-goods/**#txmanager地址 必填tm.manager.url=http://127.0.0.1:7000/tx/manager/

 

 

 

3.启动类注解

核心注解@EnableZuulProxy

同时需要添加注解,服务发现,注册eureka

package com.swapping.springcloud.ms.gateway;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;import org.springframework.cloud.netflix.zuul.EnableZuulProxy;import org.springframework.cloud.openfeign.EnableFeignClients;@EnableCircuitBreaker    //Hystrix Dashboard必须加@EnableHystrixDashboard//展示熔断器仪表盘@EnableZuulProxy    //网关映射 注解@EnableFeignClients    //feign调用注解@EnableDiscoveryClient@SpringBootApplication(scanBasePackages = "com.swapping")public class SpringcloudMsGatewayApplication {    public static void main(String[] args) {        SpringApplication.run(SpringcloudMsGatewayApplication.class, args);    }}

 

 >>>>>>>>>>>>>路由转发

4.启动网关服务 

springcloud-ms-gateway  端口8001

启动ms-member服务,端口9000

那么现在访问

http://localhost:9000/member/showMember即直接访问ms-member服务,的/member/showMember这个API

现在可以访问网关,通过zuul进行动态路由

http://localhost:8001/v1/ms-member/member/showMember统一访问网关地址,由网关进行统一路由转发

至此,zuul实现路由转发! 

  >>>>>>>>>>>>>路由拦截

 5.使用zuul进行路由拦截

现在既然所有的访问接口都是由zuul网关暴露出去的,那所有的请求都来找网关,这样的话在网关的过滤器中就可以做很多的事情。

Filter是Zuul的核心,用来实现对外服务的控制。Filter的生命周期有4个,分别是“PRE”、“ROUTING”、“POST”、“ERROR”,整个生命周期可以用下图来表示

 

 

5.1 Zuul大部分功能都是通过过滤器来实现的,这些过滤器类型对应于请求的典型生命周期。

  • PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  • ERROR:在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

 5.2 在zuul中默认已经实现的filter有:

类型 顺序 过滤器 功能
pre -3 ServletDetectionFilter 标记处理Servlet的类型
pre -2 Servlet30WrapperFilter 包装HttpServletRequest请求
pre -1 FormBodyWrapperFilter 包装请求体
route 1 DebugFilter 标记调试标志
route 5 PreDecorationFilter 处理请求上下文供后续使用
route 10 RibbonRoutingFilter serviceId请求转发
route 100 SimpleHostRoutingFilter url请求转发
route 500 SendForwardFilter forward请求转发
post 0 SendErrorFilter 处理有错误的请求响应
post 1000 SendResponseFilter 处理正常的请求响应

5.3 zuul还提供了一类特殊的过滤器,分别为:StaticResponseFilter和SurgicalDebugFilter

StaticResponseFilter:StaticResponseFilter允许从Zuul本身生成响应,而不是将请求转发到源。

SurgicalDebugFilter:SurgicalDebugFilter允许将特定请求路由到分隔的调试集群或主机

 

5.4 在zuul中定义自定义的过滤器

  例如:自定义一个请求被路由之前的过滤器,用于验证请求中是否携带auth,如果携带安全验证,则成功路由;否则统一终止路由,返回响应

自定义filter需要继承ZuulFilter【注意,自定义Filter需要注入spring 使用注解@Component】

package com.swapping.springcloud.ms.gateway.filter;import com.alibaba.fastjson.JSON;import com.netflix.zuul.ZuulFilter;import com.netflix.zuul.context.RequestContext;import com.netflix.zuul.exception.ZuulException;import com.swapping.springcloud.ms.core.response.UniVerResponse;import org.apache.commons.lang3.StringUtils;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;/** * 路由拦截 * * * >>>>>zuul的filter过滤器的生命周期有一下四个: * *  PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。 *  ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。 *  POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。 *  ERROR:在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。 * * *  Zuul中默认实现了很多Filter,也可以自己自定义过滤器 * *  下面是自己自定义过滤器 *  实际使用中我们可以结合shiro、oauth2.0等技术去做鉴权、验证 * */@Componentpublic class AuthFilter extends ZuulFilter{    @Override    public String filterType() {        return "pre";//可以在请求被路由之前调用    }    @Override    public int filterOrder() {        return 0;//filter执行顺序,通过数字指定 ,优先级为0,数字越大,优先级越低    }    @Override    public boolean shouldFilter() {        return true;// 是否执行该过滤器,此处为true,说明需要过滤    }    /**     *  filter需要执行的具体操作     *     * 例如:本filter实际执行的逻辑 是验证所有的访问请求中,是否包含安全信息auth     * @return     * @throws ZuulException     */    @Override    public Object run() throws ZuulException {        RequestContext ctx = RequestContext.getCurrentContext();        HttpServletRequest request = ctx.getRequest();        String auth = request.getParameter("auth");        //TODO 此处可以做日志记录        System.out.println("zuul拦截--请求前验证---auth:"+auth);        //成功的情况        if (StringUtils.isNotBlank(auth)){            ctx.setSendZuulResponse(true); //对请求进行路由            ctx.setResponseStatusCode(200);            ctx.set("isSuccess", true);        }else {            //失败的情况            UniVerResponse res = new UniVerResponse();            res.beFalse3("zuul拦截--请求前验证---没有auth登录验证",UniVerResponse.ERROR_BUSINESS);            ctx.setSendZuulResponse(false); //不对请求进行路由            ctx.setResponseStatusCode(res.getCode());//设置返回状态码            ctx.setResponseBody(JSON.toJSONString(res));//设置返回响应体            ctx.set("isSuccess", false);            ctx.getResponse().setContentType("application/json;charset=UTF-8");//设置返回响应体格式,可能会乱码        }        return null;    }}

 

5.5 测试自定义拦截

启动网关服务 ms-gateway 端口:8001

启动服务注册中心eureka

启动要被路由的服务ms-member 端口:9000

@RestController@RequestMapping("/member")public class MemberController {    @Autowired    MemberService memberService;    @RequestMapping(value = "/showMember")    public String showMember(){        return "会员服务正常,会员服务是服务消费者,也就是服务调用者,会调用商品服务进行商品购买,减少库存,增加销量;\n\r同时调用积分服务,增加积分";    }

 

访问地址:携带auth验证

http://localhost:8001/v1/ms-member/member/showMember?auth=111

 

访问地址:不携带auth

 

 

5.6 如果你想禁用 zuul中的filter,可以这么做:

如果你想禁用一个,只需设置zuul.<SimpleClassName>.<filterType> .disable = true。按照惯例,过滤器后面的包是Zuul过滤器类型。例如,要禁用org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter 需设置 zuul.SendResponseFilter.post.disable = true

 

 

 >>>>>>>>>>>>>路由熔断

 6.使用zuul进行路由熔断

 类似与服务之间的feign调用熔断器设定,网关路由各个服务的请求也可以做路由熔断,在不能成功路由到具体服务上的请求时,进行降级处理,定制返回内容【也就是设置统一响应体】

目前路由熔断仅能 支持服务级别的熔断,不支持具体到某个URL进行熔断

 自定义熔断类需要实现FallbackProvider接口【注意,自定义熔断类需要注入spring 使用注解@Component】

package com.swapping.springcloud.ms.gateway.fallback;import com.alibaba.fastjson.JSON;import com.swapping.springcloud.ms.core.response.UniVerResponse;import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.client.ClientHttpResponse;import org.springframework.stereotype.Component;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;/** * 路由熔断 * * 类似与服务之间的feign调用熔断器设定,网关路由各个服务的请求也可以做路由熔断, * 在不能成功路由到具体服务上的请求时,进行降级处理,定制返回内容【也就是设置统一响应体】 * * 目前路由熔断仅能 支持服务级别的熔断,不支持具体到某个URL进行熔断 * */@Componentpublic class UniVerFallback implements FallbackProvider {    /**     * 指定 可以熔断拦截 哪些服务     * @return     */    @Override    public String getRoute() {//        return "springcloud-ms-member";//指定 可熔断某个服务 服务名是配置文件中配置的各服务的serviceId        return "*"; //指定    可熔断所有服务    }    /**     * 指定  熔断后返回的定制化内容     * @param route     * @param cause     * @return     */    @Override    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {        return new ClientHttpResponse() {            @Override            public HttpStatus getStatusCode() throws IOException {                return HttpStatus.OK;            }            @Override            public int getRawStatusCode() throws IOException {                return 200;            }            @Override            public String getStatusText() throws IOException {                return "OK";            }            @Override            public void close() {            }            /**             * 设置响应体             * @return             * @throws IOException             */            @Override            public InputStream getBody() throws IOException {                //TODO 此处可以做日志记录                UniVerResponse uniVerResponse = new UniVerResponse();                uniVerResponse.beFalse3(route+"服务凉凉了",UniVerResponse.ERROR_BUSINESS);                return new ByteArrayInputStream(JSON.toJSONString(uniVerResponse).getBytes());            }            /**             * 设置响应头信息             * @return             */            @Override            public HttpHeaders getHeaders() {                HttpHeaders headers = new HttpHeaders();                headers.setContentType(MediaType.APPLICATION_JSON_UTF8);//指定响应体 格式+编码方式                return headers;            }        };    }}

 

保证eureka启动,

然后重启网关服务ms-gateway,端口:8001

关闭ms-member服务,端口:9000

 然后访问地址:来访问ms-member的接口

http://localhost:8001/v1/ms-member/member/showMember?auth=111

 

就可以对指定的服务进行熔断!!!

 

 >>>>>>>>>>>>>路由重试

 7.使用zuul进行路由重试【慎用】

在服务暂时不可用的情况下,可能是服务重启正被注册中心扫描或者其他原因,我们想要对发起的请求进行重试,zuul结合Spring Retry实现路由重试

  7.1 网关服务添加依赖

org.springframework.retry
spring-retry

  7.2 application.properties添加配置

#路由重试#是否开启路由重试,针对于 查询接口可以使用,但是对于非幂等的新增或更新接口,使用路由重试会出现很大的问题,应该注意#是否开启重试功能zuul.retryable=true#对当前服务的重试次数[不包含首次访问],也就是说实际访问接口的次数是3+1次ribbon.MaxAutoRetries=3#切换相同Server的次数ribbon.MaxAutoRetriesNextServer=0

  7.3 重启网关服务,更改一下ms-member的测试接口

@RequestMapping(value = "/showMember")    public String showMember(){        System.out.println("请求到达!!!!");        try {            Thread.sleep(10000);        } catch (InterruptedException e) {            e.printStackTrace();        }        return "会员服务正常,会员服务是服务消费者,也就是服务调用者,会调用商品服务进行商品购买,减少库存,增加销量;\n\r同时调用积分服务,增加积分";    }

可以看到,要访问的接口 休眠10s,而网关配置的超时时间设置为2s

结合上面的重试次数的配置,请求到达的次数应该是3+1次。

 

启动ms-member服务,访问地址:

http://localhost:8001/v1/ms-member/member/showMember?auth=111

 

可以看到,请求总共到达了4次,首次+重试3次

请求结果可以看到,最终服务被熔断。

  

   7.4 路由重试注意

  

是否开启路由重试,针对于 查询接口可以使用,但是对于非幂等的新增或更新接口,使用路由重试会出现很大的问题,应该注意
开启重试在某些情况下是有问题的,比如当压力过大,一个实例停止响应时,路由将流量转到另一个实例,很有可能导致最终所有的实例全被压垮。说到底,断路器的其中一个作用就是防止故障或者压力扩散。用了retry,断路器就只有在该服务的所有实例都无法运作的情况下才能起作用。这种时候,断路器的形式更像是提供一种友好的错误信息,或者假装服务正常运行的假象给使用者。不用retry,仅使用负载均衡和熔断,就必须考虑到是否能够接受单个服务实例关闭和eureka刷新服务列表之间带来的短时间的熔断。如果可以接受,就无需使用retry。

 

 

=============结束================

参考:http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html

参考:https://www.cnblogs.com/cralor/p/9234697.html

转载于:https://www.cnblogs.com/handsome1013/p/11276162.html

你可能感兴趣的文章
Python入门-函数
查看>>
[HDU5727]Necklace(二分图最大匹配,枚举)
查看>>
距离公式汇总以及Python实现
查看>>
设计模式之装饰者模式
查看>>
一道不知道哪里来的容斥题
查看>>
Blender Python UV 学习
查看>>
window添加右键菜单
查看>>
入手腾龙SP AF90mm MACRO
查看>>
python学习4 常用内置模块
查看>>
Window7上搭建symfony开发环境(PEAR)
查看>>
ResolveUrl的用法
查看>>
Linux内核态、用户态简介与IntelCPU特权级别--Ring0-3
查看>>
第23月第24天 git命令 .git-credentials git rm --cached git stash clear
查看>>
java SE :标准输入/输出
查看>>
一些方便系统诊断的bash函数
查看>>
【转载】基于vw等viewport视区相对单位的响应式排版和布局
查看>>
<转>关于MFC的多线程类 CSemaphore,CMutex,CCriticalSection,CEvent
查看>>
jquery中ajax返回值无法传递到上层函数
查看>>
css3之transform-origin
查看>>
[转]JavaScript快速检测浏览器对CSS3特性的支持
查看>>