广州网站建设网页制作开发,江西邢台网络科技,免费客源软件,中国企业500强排名名单Zuul用于构建边界服务#xff0c;致力于动态路由#xff0c;过滤#xff0c;监控#xff0c;弹性伸缩和安全等方向。 1、ZuulRibbonEureka结合#xff0c;可以实现智能路由和负载均衡 2、网关将所有服务的API接口统一聚合统一暴露 3、网关统一爆率接口后#xff0c;可以做…Zuul用于构建边界服务致力于动态路由过滤监控弹性伸缩和安全等方向。 1、ZuulRibbonEureka结合可以实现智能路由和负载均衡 2、网关将所有服务的API接口统一聚合统一暴露 3、网关统一爆率接口后可以做身份和权限认证 4、实现监控功能实时日志输出 5、流量监控实现降级和限流 6、方便测试 1、网关存在的必要性 不同的微服务有不同的请求地址如果一个客户端需要访问多个接口才能完成一个业务需求的话可能存在以下问题 # 客户端会多次请求不同微服务增加客户端的复杂性 # 存在跨域请求在一定场景下处理相对复杂 # 认证复杂每一个服务都需要独立认证 # 难以重构随着项目的迭代可能需要重新划分微服务如果客户端直接和微服务通信那么重构会难以实施 # 某些微服务可能使用了其他协议直接访问有一定困难 而微服务网关可以很好的解决这个问题 这样客户端只需要和网关交互而无需直接调用特定微服务的接口而且方便监控易于认证减少客户端和各个微服务之间的交互次数。 2、主流解决方案 # Spring Cloud Gateway # Zuul Zuul基于 servlet 2.5使用3.x使用阻塞API。 它不支持任何 长连接 如 web sockets。而Gateway建立在Spring Framework 5Project Reactor和Spring Boot 2之上使用非阻塞API。 Websockets得到支持并且由于它与Spring紧密集成所以将会是一个更好的开发体验。 参考https://juejin.im/post/5aa4eacbf265da237a4ca36f 3、模拟场景 客户端请求后端服务网关提供后端服务的统一入口。后端的服务都注册到Zookeeper、Consul或者Eureka 服务发现、配置管理中心服务。网关通过负载均衡。转发到具体的后端服务。 4、Zuul Zuul 提供了四种过滤器的 API动态读取、编译和运行这些过滤器。过滤器之间不能相互通讯只能通过RequestContext对象共享数据。 # 前置Pre鉴权、请求转发、增加请求参数等行为 一般来说整个服务的鉴权逻辑可以很复杂。 客户端App、Web、Backend权限组用户、后台人员、其他开发者实现OAuth、JWT使用方式Token、Cookie、SSO而对于后端应用来说它们其实只需要知道请求属于谁而不需要知道为什么所以 Gateway 可以友善的帮助后端应用完成鉴权这个行为并将用户的唯一标示透传到后端而不需要、甚至不应该将身份信息也传递给后端防止某些应用利用这些敏感信息做错误的事情。Zuul 默认情况下在处理后会删除请求的 Authorization 头和 Set-Cookie 头也算是贯彻了这个原则。 # 后置Post统计返回值和调用时间、记录日志、增加跨域头等行为 使用 Gateway 做跨域相比应用本身或是 Nginx 的好处是规则可以配置的更加灵活。例如一个常见的规则。 对于任意的 AJAX 请求返回 Access-Control-Allow-Origin 为 *且 Access-Control-Allow-Credentials 为 true这是一个常用的允许任意源跨域的配置但是不允许请求携带任何 Cookie 如果一个被信任的请求者需要携带 Cookie那么将它的 Origin 增加到白名单中。对于白名单中的请求返回 Access-Control-Allow-Origin 为该域名且 Access-Control-Allow-Credentials 为 true这样请求者可以正常的请求接口同时可以在请求接口时携带 Cookie 对于 302 的请求即使在白名单内也必须要设置 Access-Control-Allow-Origin 为 *否则重定向后的请求携带的 Origin 会为 null有可能会导致 iOS 低版本的某些兼容问题 Gateway 可以统一收集所有应用请求的记录并写入日志文件或是发到监控系统相比 Nginx 的 access log好处主要也是二次开发比较方便比如可以关注一些业务相关的 HTTP 头或是将请求参数和返回值都保存为日志打入消息队列中便于线上故障调试。也可以收集一些性能指标发送到类似 Statsd 这样的监控平台。 # 路由Route一般只需要选择 Zuul 中内置的即可 #错误Error一般只需要一个这样可以在 Gateway 遇到错误逻辑时直接抛出异常中断流程并直接统一处理返回结果 错误过滤器的主要用法就像是 Jersey 中的 ExceptionMapper 或是 Spring MVC 中的 ExceptionHandler 一样在处理流程中认为有问题时直接抛出统一的异常错误过滤器捕获到这个异常后就可以统一的进行返回值的封装并直接结束该请求。 总结关键特性 1、Type规定类型 2、Execution Order规定执行顺序Order值越小越优先 3、Criteria规定执行所需要的条件 4、Action如果符合条件则执行Action 一个请求会先按顺序通过所有的前置过滤器之后在路由过滤器中转发给后端应用得到响应后又会通过所有的后置过滤器最后响应给客户端。在整个流程中如果发生了异常则会跳转到错误过滤器中。 5、注解配置 /** * 这个接口需要鉴权鉴权方式是 OAuth */Authorization(OAuth)RequestMapping(value /users/{id}, method RequestMethod.DELETE)public void del(PathVariable int id) { //... }/** * 这个接口可以缓存并且每个 IP/User 每秒最多请求 10 次 */CacheableRateLimiting(limit 10/1s, scope {IP, USER})RequestMapping(value /users/{id}, method RequestMethod.GET)public void info(PathVariable int id) { //... } 6、稳定性 # 隔离机制 在 Zuul 中每一个后端应用都称为一个 Route为了避免一个 Route 抢占了太多资源影响到其他 Route 的情况出现Zuul 使用 Hystrix 对每一个 Route 都做了隔离和限流。 Hystrix 的隔离策略有两种基于线程或是基于信号量。Zuul 默认的是基于线程的隔离机制这意味着每一个 Route 的请求都会在一个固定大小且独立的线程池中执行这样即使其中一个 Route 出现了问题也只会是某一个线程池发生了阻塞其他 Route 不会受到影响。一般使用 Hystrix 时只有调用量巨大会受到线程开销影响时才会使用信号量进行隔离策略对于 Zuul 这种网络请求的用途使用线程隔离更加稳妥。 # 重试机制 Zuul 的路由主要有 Eureka 和 Ribbon 两种方式简单介绍下 Ribbon 支持哪些容错配置。 重试的场景分为三种 okToRetryOnConnectErrors只重试网络错误okToRetryOnAllErrors重试所有错误OkToRetryOnAllOperations重试所有操作这里不太理解猜测是 GET/POST 等请求都会重试重试的次数有两种 MaxAutoRetries每个节点的最大重试次数MaxAutoRetriesNextServer更换节点重试的最大次数一般来说我们希望只在网络连接失败时进行重试、或是对 5XX 的 GET 请求进行重试不推荐对 POST 请求进行重试无法保证幂等性会造成数据不一致。单台的重试次数可以尽量小一些重试的节点数尽量多一些整体效果会更好。 如果有更加复杂的重试场景例如需要对特定的某些 API、特定的返回值进行重试那么也可以通过实现 RequestSpecificRetryHandler 定制逻辑不建议直接使用 RetryHandler因为这个子类可以使用很多已有的功能。 7、Tomcat Tomcat的最大并发数是可以配置的实际运用中最大并发数与硬件性能和CPU数量都有很大关系的。更好的硬件更多的处理器都会使Tomcat支持更多的并发。 Tomcat 默认的HTTP实现是采用阻塞式的Socket通信每个请求都需要创建一个线程处理当一个进程有500个线程在跑的话那性能已经是很低很低了。Tomcat默认配置的最大请求数是150也就是说同时支持150个并发。具体能承载多少并发需要看硬件的配置CPU越多性能越高分配给JVM的内存越多性能也就越高但也会加重GC的负担。当某个应用拥有 250个以上并发的时候应考虑应用服务器的集群。操作系统对于进程中的线程数有一定的限制 Windows 每个进程中的线程数不允许超过 2000 Linux 每个进程中的线程数不允许超过 1000 在Java中每开启一个线程需要耗用1MB的JVM内存空间用于作为线程栈之用此处也应考虑。 8、实际应用 引入依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-server/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-zuul/artifactId/dependency 启动类开启zuul代理 SpringBootApplication
EnableEurekaClient
EnableZuulProxy
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
} 配置文件配置路由信息 server:port: 9009
spring:application:name: zuul-client
eureka:client:service-url:defaultZone: http://localhost:9001/eureka/
zuul:routes:hiapi:path: /hiapi/**serviceId: hi-service 访问http://localhost:9009/hiapi/hi如果hi-service部署了多个实例那么zuul在路由转发就做了负载均衡。 当然也可以使用url属性代替serviceId属性通过指定ipport的方式的url地址来直接访问当然这种情况很少出现 如果想自己维护负载均衡的服务列表可以使用如下方式 zuul:routes:hiapi:path: /hiapi/**serviceId: hiapi-v1
ribbon:eureka:enabled: false
hiapi-v1:ribbon:listOfServers: http://localhost:9007,http://localhost:9008,http://localhost:9009/hiapi/hi 配置API接口的版本号 zuul:routes:hiapi:path: /hiapi/**serviceId: hi-service prefix: v1 那么访问路径将变为http://localhost:9009/v1/hiapi/hi 集成Hystrix实现熔断器 Component
public class MyFallbackProvider implements FallbackProvider {Overridepublic String getRoute() {return hi-service; // 应用名称或者serviceId或者是正则表达式如*}Overridepublic ClientHttpResponse fallbackResponse(String route, final 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() {Overridepublic HttpStatus getStatusCode() throws IOException {return status;}Overridepublic int getRawStatusCode() throws IOException {return status.value();}Overridepublic String getStatusText() throws IOException {return status.getReasonPhrase();}Overridepublic void close() {}Overridepublic InputStream getBody() throws IOException {return new ByteArrayInputStream(fallback.getBytes());}Overridepublic HttpHeaders getHeaders() {HttpHeaders headers new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);return headers;}};}
} 在Zuul中使用过滤器 Component
public class MyFilter extends ZuulFilter {Overridepublic String filterType() {return FilterConstants.PRE_TYPE; // 前置过滤器 }Overridepublic int filterOrder() {return 0; // 优先级为0数字越大优先级越低}Overridepublic boolean shouldFilter() {return true; // 是否执行该过滤器此处为true说明需要过滤}Overridepublic Object run() throws ZuulException {RequestContext ctx RequestContext.getCurrentContext();HttpServletRequest request ctx.getRequest();String token request.getParameter(token);if (StringUtils.isBlank(token)) {ctx.setSendZuulResponse(false);ctx.setResponseStatusCode(401);try {ctx.getResponse().getWriter().write(token is empty);} catch (IOException e) { }}return null;}
} 转载于:https://www.cnblogs.com/ijavanese/p/9198203.html