接上篇文章:https://blog.csdn.net/qq_33333654/article/details/103269348
参考文献:http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html
demo代码地址:https://download.csdn.net/download/qq_33333654/12014918
Zuul的核心
Filter是Zuul的核心,用来实现对外服务的控制。Filter的生命周期有4个,分别是“PRE”、“ROUTING”、“POST”、“ERROR”,整个生命周期可以用下图来表示。
Zuul大部分功能都是通过过滤器来实现的,这些过滤器类型对应于请求的典型生命周期。
- PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
- ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
- POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
- ERROR:在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。
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 | 处理正常的请求响应 |
禁用指定的Filter
可以在application.yml中配置需要禁用的filter,格式:
zuul:
FormBodyWrapperFilter:
pre:
disable: true
自定义Filter
在zuul项目中创建MyFilter继承ZuulFilter
package com.example.gatewayservicezuulsimple.zuulFilter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; /** * @ProjectName: gateway-service-zuul-simple * @Package: com.example.gatewayservicezuulsimple.zuulFilter * @ClassName: MyFilter * @Author: MC * @Description: ${description} * @Date: 2019/12/4 0004 15:17 * @Version: 1.0 */ public class MyFilter extends ZuulFilter { private final Logger logger = LoggerFactory.getLogger(MyFilter.class); @Override public String filterType() { return "pre"; //定义filter的类型,有pre、route、post、error四种 } @Override public int filterOrder() { return 0; //定义filter的顺序,数字越小表示顺序越高,越先执行 } @Override public boolean shouldFilter() { return true; //表示是否需要执行该filter,true表示执行,false表示不执行 } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); logger.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString()); String token = request.getParameter("token");// 获取请求的参数 if (StringUtils.isNotBlank(token)) { ctx.setSendZuulResponse(true); //对请求进行路由 ctx.setResponseStatusCode(200); ctx.set("isSuccess", true); return null; } else { ctx.setSendZuulResponse(false); //不对其进行路由 ctx.setResponseStatusCode(400); ctx.setResponseBody("token is empty"); ctx.set("isSuccess", false); return null; } } }
zuul项目yml文件调整:
zuul: routes: api-a: path: /client/** serviceId: spring-cloud-config-client
zuul项目启动类添加:
@SpringBootApplication @EnableZuulProxy public class GatewayServiceZuulSimpleApplication { public static void main(String[] args) { SpringApplication.run(GatewayServiceZuulSimpleApplication.class, args); } @Bean public MyFilter myFilter(){ return new MyFilter(); } }
启动:注册中心、rabbitmq、server项目、client项目、zuul项目
浏览器访问:http://localhost:8888/client/hello
返回的是:
token is empty
再次请求:http://localhost:8888/client/hello?token=xxx
返回的是:
hello_i_im_mysql_update1
路由熔断
zuul项目创建myFallback类:
package com.example.gatewayservicezuulsimple.fallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider; 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; /** * @ProjectName: spring-cloud-config-server * @Package: fallback * @ClassName: myFallback * @Author: MC * @Description: ${description} * @Date: 2019/12/4 0004 15:38 * @Version: 1.0 */ @Component public class myFallback implements ZuulFallbackProvider { private final Logger logger = LoggerFactory.getLogger(myFallback.class); //指定要处理的 service。 @Override public String getRoute() { return "spring-cloud-config-client"; } @Override public ClientHttpResponse fallbackResponse() { 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() { } @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream("The service is unavailable.".getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }; } }
关闭zuul项目、server项目、client项目,打开client32项目(另外一个clent项目,内容与client项目一样)。
依次启动server项目、client项目、client32项目、zuul项目。
浏览器访问:http://localhost:8888/client/hello?token=xxx
获取信息同上,一切正常。
然后手动关闭client32项目,浏览器再多次请求http://localhost:8888/client/hello?token=xxx
发现输出
The service is unavailable.
路由重试
关闭zuul项目、server项目、client项目、client32项目
分别在client项目与client32项目中创建:
@RequestMapping("/testZuul") public String testZuul() { return "is 9031 client"; }
@RequestMapping("/testZuul") public String testZuul() { System.out.println("请求了32的client。。。。。"); try{ Thread.sleep(1000000); }catch ( Exception e){ System.out.println(e); } return "is 9032 client"; }
在zuul项目的pom中添加核心依赖:
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
zuul项目yml文件调整:
server: port: 8888 eureka: client: service-url: defaultZone: http://peer1:8000/eureka/,http://peer2:8001/eureka/,http://peer3:8002/eureka/ spring: application: name: gateway-service-zuul # cloud: # loadbalancer: # retry: # enabled: true # 开启重试 zuul: routes: api-a: path: /client/** serviceId: spring-cloud-config-client retryable: true # 开启重试 ribbon: # connectTimeout: 2000 # 请求连接的超时时间 # readTimeout: 5000 # 请求处理的超时时间 maxAutoRetries: 2 # 对当前实例的重试次数 maxAutoRetriesNextServer: 0 # 切换实例的重试次数 # okToRetryOnAllOperations: false # 对所有操作请求都进行重试 # hello: # path: /hello/** # url: http://localhost:9031/ # baidu: # path: /it/** # url: https://blog.csdn.net/qq_33333654/category_9436879.html
依次启动server项目、client项目、client32项目、zuul项目。
浏览器访问:
http://localhost:8888/client/testZuul?token=xxx
放回的是
hello_i_im_mysql_update1
手动关闭client项目,再次访问http://localhost:8888/client/testZuul?token=xxx
浏览器返回:
The service is unavailable.
查看client32项目输出:请求了32的client。。。。。
Zuul高可用
我们实际使用Zuul的方式如上图,不同的客户端使用不同的负载将请求分发到后端的Zuul,Zuul在通过Eureka调用后端服务,最后对外输出。因此为了保证Zuul的高可用性,前端可以同时启动多个Zuul实例进行负载,在Zuul的前端使用Nginx或者F5进行负载转发以达到高可用性。