微服务深入浅出(7)-- 网关路由Zuul

Zuul用于构建边界服务,致力于动态路由,过滤,监控,弹性伸缩和安全等方向。

1、Zuul+Ribbon+Eureka结合,可以实现智能路由和负载均衡

2、网关将所有服务的API接口统一聚合统一暴露

3、网关统一爆率接口后,可以做身份和权限认证

4、实现监控功能,实时日志输出

5、流量监控,实现降级和限流

6、方便测试

1、网关存在的必要性

不同的微服务有不同的请求地址,如果一个客户端需要访问多个接口才能完成一个业务需求的话,可能存在以下问题:

# 客户端会多次请求不同微服务,增加客户端的复杂性

# 存在跨域请求,在一定场景下处理相对复杂

# 认证复杂,每一个服务都需要独立认证

# 难以重构,随着项目的迭代,可能需要重新划分微服务,如果客户端直接和微服务通信,那么重构会难以实施

# 某些微服务可能使用了其他协议,直接访问有一定困难

 

而微服务网关可以很好的解决这个问题:

这样客户端只需要和网关交互,而无需直接调用特定微服务的接口,而且方便监控,易于认证,减少客户端和各个微服务之间的交互次数。

2、主流解决方案

# Spring Cloud Gateway

# Zuul

Zuul基于 servlet 2.5(使用3.x),使用阻塞API。 它不支持任何 长连接 ,如 web sockets。而Gateway建立在Spring Framework 5,Project 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 的好处是规则可以配置的更加灵活。例如一个常见的规则。

  1. 对于任意的 AJAX 请求,返回 Access-Control-Allow-Origin 为 *,且 Access-Control-Allow-Credentials 为 true,这是一个常用的允许任意源跨域的配置,但是不允许请求携带任何 Cookie

  2. 如果一个被信任的请求者需要携带 Cookie,那么将它的 Origin 增加到白名单中。对于白名单中的请求,返回 Access-Control-Allow-Origin 为该域名,且 Access-Control-Allow-Credentials 为 true,这样请求者可以正常的请求接口,同时可以在请求接口时携带 Cookie

  3. 对于 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 次
*/
@Cacheable
@RateLimiting(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、实际应用

引入依赖

     <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-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属性,通过指定ip+port的方式的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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247471.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# webkit内核 网页填表

比如我要操作的是下面的input 用到的方法是 调用如下&#xff1a; webkit.StringByEvaluatingJavaScriptFromString("document.getElementsByClassName(login_i_con_li_ipt name)[0].valueThis is a Demo."); 类似这种div在webkit中好像是无法通过常规方法模拟的 这时…

p字间距 html段落内文字设置字间距间隔

只对段落p内文字设置字间距&#xff0c;段落<p>是html段落标签&#xff0c;以<p>开始&#xff0c;以</p>结束&#xff0c;通常文章分段使用p标签&#xff0c;而有时小局部布局也可以使用p来布局。通过css设置其样式实现排版目的。 这里针对p设置字间距&…

基本数据类型

上节回顾 1.循环打印数列1&#xff0c;3&#xff0c;5&#xff0c;.........&#xff0c;99 for i in range(100):if i%21:print (i) 2.turtle 库 # penup 抬笔 # pendown 落笔 # pensize 画笔大小 # pencolor 画笔颜色## 画笔运动函数 # fd 前进 # bk 后退 # goto 到达指定的坐…

修改系统默认 alert 弹框样式

修改默认 alert 弹框&#xff0c;思路很简单&#xff0c;定义一个 alert(e) 函数&#xff0c;加载最开头即可。 css部分&#xff1a; <style>#msg{width:266px;position: fixed;z-index:999;top: 49%;margin-top:-80px;left:50%;margin-left:-133px;background:#fff;bo…

:nth-child(n)与:nth-of-type(n)为啥显示不对呢

首先是二者的区别 :nth-child(n) 是选择父元素的第n个子元素。 :nth-of-type(n) 是选择父元素的第n个同类型的子元素 举个例子&#xff1a; <div class"read"><h1>title</h1><p>paragraph1</p><p>paragraph2</p> <!…

css3 box-shadow阴影(内外阴影与发光)讲解

基础说明&#xff1a; 外阴影&#xff1a;box-shadow: X轴 Y轴 Rpx color; 属性说明&#xff08;顺序依次对应&#xff09;&#xff1a; 阴影的X轴(可以使用负值) 阴影的Y轴(可以使用负值) 阴影模糊值&#xff08;大小&#xff09; 阴影的颜色 内阴影&#xff1a;b…

远程链接错误:这可能是由于credssp加密oracle修正

此错误解决办法 1.WinR 输入regedit打开注册表 找到对应的以下目录HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System 此时如果System下没有CredSSP时创建CredSSP没有Parameters时,创建Parameters 创建方法:右建>>新建>>项 2.在Para…

SpringBoot入门最详细教程

https://www.jianshu.com/p/af3d5800f763 网上有很多springboot的入门教程&#xff0c;自己也因为项目要使用springboot&#xff0c;所以利用业余时间自学了下springboot和springcloud&#xff0c;使用下来发现springboot还是挺简单的&#xff0c;体现了极简的编程风格&#xf…

通过Vue CLI3 快速创建Vue项目并部署到tomcat

1、前提 首先你要安装好nodejs和yarn,直接在官网下载安装包&#xff0c;一键安装即可&#xff0c;不需要什么环境配置&#xff0c;我安装的是最新版本&#xff08;node-v10.13.0、yarn-1.12.3&#xff09; 2、安装 同时写Vue CLI 3和Vue CLI 2 的原因是官方默认的是3&#x…

简述区块链(1)- 也许只有这一篇

一、唠叨两句 最近一直在考虑一个事情&#xff0c;就是怎么给不太了解技术的人讲清楚区块链。我先试着写下来&#xff0c;然后在逐步打磨吧&#xff0c;目标就是让哪些说看区块链看的云里雾里的同学能对区块链有一些认知。 二、定义 简单的给区块链下个定义&#xff1a;基于加密…

Vue CLI 3.0脚手架如何在本地配置mock数据json

前后端分离的开发模式已经是目前前端的主流模式&#xff0c;至于为什么会前后端分离的开发我们就不做过多的阐述&#xff0c;既然是前后端分离的模式开发肯定是离不开前端的数据模拟阶段。 我们在开发的过程中&#xff0c;由于后台接口的没有完成或者没有稳定之前我们都是采用…

python 通过下载包setup.py安装模块

下载安装包&#xff0c;并解压到相应的位置 1、打开cmd 2、到达安装目录 3、python setup.py build 4、python setup.py install 转载于:https://www.cnblogs.com/liuchunxiao83/p/11207340.html

Anaconda3自带jupyter

1、cmd命令行中输入 JupyterNotebook 2、系统自动调起下面页面&#xff08;注册端口冲突是打不开的&#xff09; 转载于:https://www.cnblogs.com/liuchunxiao83/p/11207385.html

理解Shadow DOM

1. 什么是Shadow DOM? Shadow DOM 如果按照英文翻译的话可以理解为 影子DOM, 何为影子DOM呢&#xff1f;可以理解为一般情况下使用肉眼看不到的DOM结构&#xff0c;那如果一般情况下看不到的话&#xff0c;那也就是说我们无法直接控制操纵的DOM结构。 Shadow DOM 它是HTML的一…

046 实例11-自动轨迹绘制

目录 一、"自动轨迹绘制"问题分析1.1 问题分析1.2 自动轨迹绘制二、"自动轨迹绘制"实例讲解2.1 自动轨迹绘制2.2 数据接口定义2.3 数据文件三、"自动轨迹绘制"举一反三3.1 理解方法思维3.2 应用问题的扩展一、"自动轨迹绘制"问题分析 …

python 操作 elasticsearch-7.0.2 遇到的问题

错误一&#xff1a;TypeError: search() got an unexpected keyword argument doc_type&#xff0c;得到不预期外的参数 解决方法&#xff1a;elasticsearch7里不用文档类型&#xff0c;所以去掉 doc_typecredit_data 错误二&#xff1a;RequestError(400, illegal_argument_ex…

Java生鲜电商平台-订单中心服务架构与异常订单逻辑

Java生鲜电商平台-订单中心服务架构与异常订单逻辑 订单架构实战中阐述了订单系统的重要性&#xff0c;并从订单系统的信息架构和流程上对订单系统有了总体认知&#xff0c;同时还穿插着一些常见的订单业务规则和逻辑。上文写到订单的拆单部分时搁置了&#xff0c;现在接上文继…

Vuex的全面用法总结

1. vuex简介 vuex是专门用来管理vue.js应用程序中状态的一个插件。他的作用是将应用中的所有状态都放在一起&#xff0c;集中式来管理。需要声明的是&#xff0c;这里所说的状态指的是vue组件中data里面的属性。了解vue的同学应该是明白data是怎么回事的吧&#xff0c;如果不懂…

vue中通过第三方代理解决跨域问题

最近在学node&#xff0c;遇到了跨域的问题&#xff0c;来记录下方法 首页服务端的框架是通过express-generator 搭建起来的 npm install -g express-generator 具体接下来的细节不多说&#xff0c;今天主要说跨域 的问题 左侧为服务端项目结构&#xff0c;www为可执行文件&am…

Java生鲜电商平台-生鲜供应链(采购管理)

Java生鲜电商平台-生鲜供应链(采购管理) 在生鲜供应链系统中采购中心这一模块&#xff0c;它是电商公司管理采购的模块&#xff0c;包含供应商管理&#xff0c;采购订单管理&#xff0c;采购商品管理&#xff0c;在该模块中采购订单是采购中心的核心模块。在其他的比如供应商的…