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 {
@Override
public String getRoute() {
return "hi-service"; // 应用名称或者serviceId,或者是正则表达式,如*
} @Override
public 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() {
@Override
public HttpStatus getStatusCode() throws IOException {
return status;
} @Override
public int getRawStatusCode() throws IOException {
return status.value();
} @Override
public String getStatusText() throws IOException {
return status.getReasonPhrase();
} @Override
public void close() {
} @Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("fallback".getBytes());
} @Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}

在Zuul中使用过滤器:

@Component
public class MyFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE; // 前置过滤器
    }

    @Override
public int filterOrder() {
return 0; // 优先级为0,数字越大,优先级越低
} @Override
public boolean shouldFilter() {
return true; // 是否执行该过滤器,此处为true,说明需要过滤
} @Override
public 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;
}
}

微服务深入浅出(7)-- 网关路由Zuul的更多相关文章

  1. .net core 微服务之Api网关(Api Gateway)

    原文:.net core 微服务之Api网关(Api Gateway) 微服务网关目录 1. 微服务引子 2.使用Nginx作为api网关 3.自创api网关(重复轮子) 3.1.构建初始化 3.2. ...

  2. SOA与ESB,微服务与API网关

    SOA与ESB,微服务与API网关 SOA: ESB: 微服务: API网关: 参考资料: 1.漫画微服务,http://www.sohu.com/a/221400925_100039689 2.SO ...

  3. 微服务&#183;API网关

    阅文时长 | 3.52分钟 字数统计 | 1232字符 主要内容 | 1.什么是API网关 2.微服务中的API网关 3.几种部署策略 『微服务·API网关』 编写人 | SCscHero 编写时间 ...

  4. AspNetCore微服务下的网关-Kong(一)

    Kong是Mashape开源的高性能高可用API网关和API服务管理层.它基于OpenResty,进行API管理,并提供了插件实现API的AOP.Kong在Mashape 管理了超过15,000 个A ...

  5. 一站式入口服务|爱奇艺微服务平台 API 网关实战 原创 弹性计算团队 爱奇艺技术产品团队

    一站式入口服务|爱奇艺微服务平台 API 网关实战 原创 弹性计算团队 爱奇艺技术产品团队

  6. .NET Core微服务之基于Steeltoe集成Zuul实现统一API网关

    Tip: 此篇已加入.NET Core微服务基础系列文章索引,本篇接上一篇<基于Steeltoe使用Eureka实现服务注册与发现>,所演示的示例也是基于上一篇的基础上而扩展的. => ...

  7. 基于spring-cloud的微服务(4)API网关zuul

    API网关是微服务架构中的很重要的一个部分,内部有多个不同的服务提供给外部来使用,API网关可以对外做统一的入口,也可以在网关上做协议转换,权限控制和请求统计和限流等其他的工作 spring-clou ...

  8. 小D课堂 - 新版本微服务springcloud+Docker教程_6-06 zuul微服务网关集群搭建

    笔记 6.Zuul微服务网关集群搭建     简介:微服务网关Zull集群搭建 1.nginx+lvs+keepalive      https://www.cnblogs.com/liuyisai/ ...

  9. Net分布式系统之六:微服务之API网关

    本人建立了个人技术.工作经验的分享微信号,计划后续公众号同步更新分享,比在此更多具体.欢迎有兴趣的同学一起加入相互学习.基于上篇微服务架构分享,今天分享其中一个重要的基础组件“API网关”. 一.引言 ...

随机推荐

  1. 对一致性Hash算法及java实现(转)

    一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法.一致性 ...

  2. apache +PHP多版本 切换的问题

    在开发中切换php版本的时候出错 经过2小时的日子排查终于找到是因为切换版本后加载的php7ts.dll模块还是原来版本的,因此保pid file 错误 解决方法 PHPIniDir "H: ...

  3. DataTable学习笔记 - 01

    DataTable 是 jQuery 的一个插件. 代码上来吧, <!DOCTYPE html> <html> <head> <meta charset=&q ...

  4. 谁能告诉delphi7 的updatebatch使用属性说明?

    谁能告诉delphi7 的updatebatch使用属性说明? ADODataSet1.UpdateBatch(arAll); 就是提交你的数据集到数据库 arCurrentOnly the upda ...

  5. php三种方法从控制结构或脚本中跳出

    PHP中,如果希望停止一段代码,根据需要达到的效果不同,可以有三种方法实现: 1. break: 如果在循环中使用了break语句,脚本就会从循环体后面的第一条语句开始执行: 2. continue: ...

  6. C++解析命令行参数(仿C语言args)

    说好不造轮子的,就管不住这手 #include <cstdio> #include <string> #include <vector> bool ParseCha ...

  7. 【开发工具IDE】eclipse的SVN提交忽略target等多余文件

    这个build失败的解决方案就是不要把你项目的 target目录放在src repository 里面,还有 .project 和 .classpath最好也别放到src repository 里. ...

  8. ansible操作(一)

    ansible晋级操作之ad-hoc命令 所谓的ad-hoc命令! 如果我们敲入一些命令去比较快的完成一些事情,而不需要将这些执行的命令特别保存下来, 这样的命令就叫做 ad-hoc 命令.Ansib ...

  9. 【刷题】洛谷 P3455 [POI2007]ZAP-Queries

    题目描述 Byteasar the Cryptographer works on breaking the code of BSA (Byteotian Security Agency). He ha ...

  10. Android ListView 几个重要属性

    Android ListView 几个重要属性http://blog.csdn.net/avenleft/article/details/7334060 android:transcriptMode= ...