扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
本篇内容介绍了“如何理解Spring Cloud网关服务zuul ”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
十余年的望江网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。成都营销网站建设的优势是能够根据用户设备显示端的尺寸不同,自动调整望江建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。创新互联从事“望江网站设计”,“望江网站推广”以来,每个客户项目都认真落实执行。
可以看到服务端口和注册中心都在配置文件中配置化
上一篇我们讲了如何搭建网关zuul 服务。实现了基本的转发功能。这篇文章我们要讲述zuul过滤器的使用。和三个参数的使用
ribbon-isolation-strategy
信号量模式 在该模式下,接收请求和执行下游依赖在同一个线程内完成,不存在线程上下文切换所带来的性能开销。默认信号量模式
线程池 在该模式下,用户请求会被提交到各自的线程池中执行,把执行每个下游服务的线程分离,从而达到资源隔离的作用。当线程池来不及处理并且请求队列塞满时,新进来的请求将快速失败,可以避免依赖问题扩散。
隔离机制 Spring Cloud 默认的隔离机制hystrix 去做限流、熔断、隔离。一个是基于信号量。一个是基于线程池去完成
ignored-services
忽略指定的服务
ignored—patterns
忽略指定的url 路径
ignored-headers
忽略指定的header 头部信息
sensitiveHeaders
字段比较敏感,不希望传递给下游微服务。 设置空没有要忽略的敏感字段。全部传给下游服务
在上面ignored-headers 和 sensitiveHeaders 职责是有重叠的部分。其实他们是有着关系存在的。sensitiveHeaders的内容会最终合并到ignored-headers内。
验证一下 sensitiveHeaders 字段设置全区忽略 X-ABC
消费者服务 sensitiveHeaders 指定忽略 X-ABC,Authorization
服务提供者转发不做任何调整 在服务提供者 和 服务消费者 服务上。都创建一个 UrlFilter 拦截器 先设置一下 zuul的yml文件
zuul: host: # 目标主机的最大连接数,默认值为200 max-total-connections: 500 # 每个主机的初始连接数,默认值为20 max-per-route-connections: 100 routes: discovery-server: path: /server/** serviceId: cloud-discovery-server client-common: path: /client/** serviceId: cloud-discovery-client sensitiveHeaders: X-ABC,X-Foo # 所有路由的默认Hystrix隔离模式(ExecutionIsolationStrategy)为SEMAPHORE。如果此隔离模式是首选,则zuul.ribbonIsolationStrategy可以更改为THREAD ribbon-isolation-strategy: thread # 这个属性意思,* 忽略未指定的服务列表 有具体服务名称 忽略指定的服务 ignored-services: "*" # 忽略指定的url 路径 ignored—patterns: # 忽略指定的header 头部信息 ignored-headers: Authorization # 字段比较敏感,不希望传递给下游微服务。 设置空没有要忽略的敏感字段。全部传给下游服务 sensitive-headers: X-ABC ribbon: eager-load: # 强制加载,不设置会进行懒加载。spring 第一次请求会非常慢 enabled: true
package com.xian.cloud.filter; import lombok.extern.slf4j.Slf4j; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Enumeration; /** ** * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/29 13:57 */ @WebFilter(filterName = "test",urlPatterns = "/*") @Slf4j public class UrlFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { log.warn("UrlFilter init......."); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { Enumeration attributeNames = servletRequest.getAttributeNames(); HttpServletRequest req = (HttpServletRequest) servletRequest; String requestURI = req.getRequestURI(); String header = req.getHeader("X-Foo"); String abc = req.getHeader("X-ABC"); String authorization = req.getHeader("Authorization"); String tom = req.getParameter("tom"); String mike = req.getParameter("mike"); log.warn("过滤器:请求地址"+requestURI); log.warn("uuid:{}",header); log.warn("abc uuid:{}",abc); log.warn("authorization :{}",authorization); log.warn("tom :{}",tom); log.warn("mike :{}",mike); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { log.warn(" 过滤器被销毁"); } }
启动类上添加 @ServletComponentScan 用于扫描 过滤器 @WebFilter注解
package com.xian.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @Author: xlr * @Date: Created in 2:44 PM 2019/10/27 */ @EnableDiscoveryClient @SpringBootApplication //新增注解 @ServletComponentScan public class DiscoveryServerApplication { public static void main(String[] args) { SpringApplication.run(DiscoveryServerApplication.class, args); } }
使用postman 请求服务 服务端打印日志
2019-10-30 11:10:35.822 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : 过滤器:请求地址/server/hello 2019-10-30 11:10:35.826 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : uuid:x-foo 2019-10-30 11:10:35.826 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : abc uuid:null 2019-10-30 11:10:35.827 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : authorization :null 2019-10-30 11:10:35.827 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : tom :null 2019-10-30 11:10:35.828 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : mike :null 2019-10-30 11:10:35.852 INFO 48882 --- [nio-9012-exec-8] c.x.cloud.controller.DiscoverCotroller : invoked name = xian age = 20
sensitive-headers、ignored-headers (abc、authorization都在zuul被过滤掉)全局设置生效。
我们再次请求client 服务接口
在看一下 客户端打印日志
2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : 过滤器:请求地址/client/test 2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : uuid:null 2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : abc uuid:null 2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : authorization :null 2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : tom :null 2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : mike :null
全局设置,和单个服务定制设置 全部生效。
spring cloud Zuul包含了对请求的路由和过滤2个功能。路由功能负责将请求转发到具体的微服务上,而过滤器负责对请求的处理过程进行干预,是实现权限校验、服务聚合等功能的基础。
在实际运行时,路由映射和请求转发是由几个不同的过滤器完成的。每一个进入zuul的http请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。
zuul1.0 对流量的路由分发和过滤俩个功能。路由让流量分发到内部的微服务集群上。过滤对请求进行修改或者做个性化处理。实现鉴权、安全、监控、日志等。
运行过程中,流量请求会经过一系列的过滤器最终转发到对应的服务上去。
zuul 路由器主要根绝FilterType类型分为四种类型。一个静态响应StaticResponseFilter
pre 在请求被路由之前调用。对应spring boot 工程Filer 中的 doFilter 方法。
route 用于路由到原点,使用http 协议调用。可以理解负责转发请求到微服务
error 错误处理,在处理请求发生错误时被调用
post 用于后路由过滤,在route和error过滤器之后被调用
FilterTypeEnum 枚举类
package com.xian.cloud.enums; import lombok.Getter; /** ** * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/29 13:01 */ @Getter public enum FilterTypeEnum { PRE("pre","前置过滤"), ROUTE("route","路由请求时被调用"), POST("post","后置过滤器"), ERROR("error","后置错误处理"); private String type; private String desc; FilterTypeEnum(String type,String desc){ this.type = type; this.desc = desc; } }
过滤器代码编写
package com.xian.cloud.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import com.xian.cloud.enums.FilterTypeEnum; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** ** * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/29 12:57 */ @Component @Slf4j public class BannedAccessFilter extends ZuulFilter { /** * 路由器的类型指定 * @return */ @Override public String filterType() { return FilterTypeEnum.PRE.getType(); } /** * 执行顺序 数值越小。执行顺序越靠前 * @return */ @Override public int filterOrder() { return 0; } /** * 返回一个boolean值来判断该过滤器是否要执行。我们可以通过此方法来指定过滤器的有效范围。 * @return */ @Override public boolean shouldFilter() { RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); String requestURI = request.getRequestURI(); if(StringUtils.isNotBlank(requestURI) && (requestURI.contains("client") || requestURI.contains("server"))){ return true; } return false; } /** * 核心业务处理 * @return * @throws ZuulException */ @Override public Object run() throws ZuulException { RequestContext context = RequestContext.getCurrentContext(); HttpServletResponse servletResponse = context.getResponse(); String uuid = UUID.randomUUID().toString(); //重写 X-Foo X-ABC值 context.addZuulRequestHeader("X-Foo", uuid); context.addZuulRequestHeader("X-ABC",uuid); log.info("X-Foo:{}",uuid); return null; } }
run方法是核心业务处理的方法 postman 请求 http://127.0.0.1:9083/server/server/hello?name=1111 携带之前设置的header参数
服务器打印日志
2019-10-30 14:44:44.841 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : 过滤器:请求地址/server/hello 2019-10-30 14:44:44.842 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : uuid:a65b2244-e9a5-456d-968f-eaaba45b6f49 2019-10-30 14:44:44.842 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : abc uuid:null 2019-10-30 14:44:44.842 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : authorization :null 2019-10-30 14:44:44.842 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : tom :null 2019-10-30 14:44:44.842 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : mike :null 2019-10-30 14:44:44.844 INFO 48882 --- [nio-9012-exec-9] c.x.cloud.controller.DiscoverCotroller : invoked name = 1111 age = 20
可以看到 postman设置的X-Foo 值被 BannedAccessFilter run方法业务覆盖掉了
拦截的请求对参数的修改操作都是在这里。这里单独介绍一下 RequestContext 这个类
官方文档描述
To pass information between filters, Zuul uses a RequestContext. Its data is held in a ThreadLocal specific to each request. Information about where to route requests, errors and the actual HttpServletRequest and HttpServletResponse are stored there. The RequestContext extends ConcurrentHashMap, so anything can be stored in the context. FilterConstants contains the keys that are used by the filters installed by Spring Cloud Netflix (more on these later).
请求要在过滤器之间传递信息,Zuul使用 RequestContext。其数据按照每个请求的ThreadLocal进行。关于路由请求,错误以及实际HttpServletRequest和HttpServletResponse的路由信息。RequestContext扩展ConcurrentHashMap,所以任何东西都可以存储在上下文中。
ThreadLocal保证了请求与请求都是独立的,相互不影响。扩展ConcurrentHashMap 保证内部过滤器并发操作的安全性。
需要注意点 都在代码注释上描述出来了。请根据自己的业务场景定制自己的过滤器。
package com.xian.cloud.filter; import com.alibaba.fastjson.JSONObject; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import com.netflix.zuul.http.HttpServletRequestWrapper; import com.netflix.zuul.http.ServletInputStreamWrapper; import com.xian.cloud.enums.FilterTypeEnum; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.springframework.util.StreamUtils; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** ** * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/29 16:07 */ @Component @Slf4j public class UpdateParamsFilter extends ZuulFilter { @Override public String filterType() { return FilterTypeEnum.PRE.getType(); } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { // 获取到request RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); // 获取请求参数name String name = ""; try { // 请求方法 String method = request.getMethod(); log.info(String.format("%s >>> %s", method, request.getRequestURL().toString())); // 获取请求的输入流 InputStream in = request.getInputStream(); String body = StreamUtils.copyToString(in, Charset.forName("UTF-8")); // 如果body为空初始化为空json if (StringUtils.isBlank(body)) { body = "{}"; } log.info("body" + body); // 转化成json JSONObject json = JSONObject.parseObject(body); // 关键步骤,一定要get一下,下面才能取到值requestQueryParams if ("GET".equals(method)) { request.getParameterMap(); Map > requestQueryParams = ctx.getRequestQueryParams(); if (requestQueryParams == null) { requestQueryParams = new HashMap<>(); } //TODO 写业务代码 List arrayList = new ArrayList<>(); arrayList.add("hello mike"); //requestQueryParams.put("mike",arrayList); ctx.setRequestQueryParams(requestQueryParams); } //post和put需重写HttpServletRequestWrapper if ("POST".equals(method) || "PUT".equals(method)) { //TODO 在这里修改或者添加内容 json.put("tom","hello tom"); String newBody = json.toString(); final byte[] reqBodyBytes = newBody.getBytes(); // 重写上下文的HttpServletRequestWrapper ctx.setRequest(new HttpServletRequestWrapper(request) { @Override public ServletInputStream getInputStream() throws IOException { return new ServletInputStreamWrapper(reqBodyBytes); } @Override public int getContentLength() { return reqBodyBytes.length; } @Override public long getContentLengthLong() { return reqBodyBytes.length; } }); } } catch (IOException e) { e.printStackTrace(); } return null; } }
“如何理解Spring Cloud网关服务zuul ”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注创新互联网站,小编将为大家输出更多高质量的实用文章!
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流