微服务网关zuul
zuul
解决问题:
不同的微服务一般会有不同的网络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以维护。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-neRUtMiV-1613658706664)(…/image/image-20200813101956296.png)]
如果让客户端直接与各个微服务通讯,可能会有很多问题:
客户端会请求多个不同的服务,需要维护不同的请求地址,增加开发难度
在某些场景下存在跨域请求的问题
加大身份认证的难度,每个微服务需要独立认证
因此,我们需要一个微服务网关,介于客户端与服务器之间的中间层,所有的外部请求都会先经过微服
务网关。客户端只需要与网关交互,只知道一个网关地址即可,这样简化了开发还有以下优点:
1、易于监控
2、易于认证
3、减少了客户端与各个微服务之间的交互次数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ugx80Jwe-1613658706667)(…/image/image-20200813102356471.png)]
一、微服务网关概述
1、 什么是微服务网关
API网关是一个服务器,是系统对外的唯一入口。API网关封装了系统内部架构,为每个客户端提供
一个定制的API。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在
网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和
管理服务。
2、 作用和应用场景
网关具有的职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。当然,最主
要的职责还是与“外界联系”。
3、常见的API网关实现方式
3.1 Kong
基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。
问题:只支持Http协议;二次开发,*扩展困难;提供管理API,缺乏更易用的管控、配置方
式。
3.2 Zuul
Netflix开源,功能丰富,使用JAVA开发,易于二次开发;需要运行在web容器中,如Tomcat。
问题:缺乏管控,无法动态配置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如
Nginx;
3.3 Traefik
Go语言开发;轻量易用;提供大多数的功能:服务路由,负载均衡等等;提供WebUI
问题:二进制文件部署,二次开发难度大;UI更多的是监控,缺乏配置、管理能力;
Spring Cloud Gateway
SpringCloud提供的网关服务
3.4 Nginx+lua实现
使用Nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用
问题:自注册的问题和网关本身的扩展性
二、zuul网关
1、zuul概述
ZUUL是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用,Zuul组件的
核心是一系列的过滤器,这些过滤器可以完成以下功能:
动态路由:动态将请求路由到不同后端集群
压力测试:逐渐增加指向集群的流量,以了解性能
负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
静态响应处理:边缘位置进行响应,避免转发到内部集群
身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。Spring Cloud对Zuul进行
了整合和增强。
Spring Cloud对Zuul进行了整合和增强
2、快速入门
2.1 搭建zuul网关服务
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
开启网关代理
@EnableZuulProxy
@SpringBootApplication
@EnableZuulProxy
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class, args);
}
}
application.yml配置转发规则
server:
port: 7000 #服务端口
spring:
application:
name: zuul-server #指定服务名
zuul:
routes:
product-service: # 这里是路由id,随意写
path: /product-service/** # 这里是映射路径
url: http://127.0.0.1:9444 # 映射路径对应的实际url地址
sensitiveHeaders: #默认zuul会屏蔽cookie,cookie不会传到下游服务,这里设置为空则取
#消默认的黑名单,如果设置了具体的头信息则不会传到下游服务
只需要在application.yml文件中配置路由规则即可:
product-service:配置路由id,可以随意取名
url:映射路径对应的实际url地址
path:配置映射路径,这里将所有请求前缀为/product-service/的请求,转发到http://127.0.0.1:
9444处理
配置好Zuul路由之后启动服务
2.2 搭建客户端服务
引用eureka-client-demo1服务
2.3 测试
访问:client
localhost:9444/service/demo1
{
"code": "ok",
"port": 9444,
"url": "/service/demo1"
}
通过网关访问:localhost:7000/product-service/service/demo1
{
"code": "ok",
"port": 9444,
"url": "/service/demo1"
}
3、面向服务的路由
微服务一般是由几十、上百个服务组成,对于一个URL请求,最终会确认一个服务实例进行处理。如果
对每个服务实例手动指定一个唯一访问地址,然后根据URL去手动实现请求匹配,这样做显然就不合
理。
Zuul支持与Eureka整合开发,根据ServiceID自动的从注册中心中获取服务地址并转发请求,这样做的
好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例的时候不用修改Zuul的
路由配置。
修改网关服务加入注册中心
3.1 添加eureka依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.2 添加注解 @EnableEurekaClient
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class, args);
}
}
3.3 添加配置
因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而
是通过服务名称来访问,而且Zuul已经集成了Ribbon的负载均衡功能。
zuul:
routes:
product-service: # 这里是路由id,随意写
path: /product-service/** # 这里是映射路径
serviceId: eureka-client1
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9001/eureka
##是否需要将自己注册注册中心
register-with-eureka: true
##是否需要检索服务信息
fetch-registry: true
serviceId: 指定需要转发的微服务实例名称
3.4 测试
访问:client
localhost:9444/service/demo1
{
"code": "ok",
"port": 9444,
"url": "/service/demo1"
}
通过网关访问:localhost:7000/product-service/service/demo1
{
"code": "ok",
"port": 9444,
"url": "/service/demo1"
}
3.5 简化路由配置
在刚才的配置中,我们的规则是这样的:
zuul.routes..path=/xxx/** : 来指定映射路径。 是自定义的路由名
zuul.routes..serviceId=/product-service :来指定服务名。
而大多数情况下,我们的 路由名称往往和服务名会写成一样的。因此Zuul就提供了一种简化的
配置语法: zuul.routes.=
上面的配置可以简化为一条:
zuul:
routes:
eureka-client1: /product-service/**
测试访问:localhost:7000/product-service/service/demo1
成功
3.6 默认的路由规则
在使用Zuul的过程中,上面讲述的规则已经大大的简化了配置项。但是当服务较多时,配置也是比较繁
琐的。因此Zuul就指定了默认的路由规则:
默认情况下,一切服务的映射路径就是服务名本身。
例如服务名为: eureka-client1 ,则默认的映射路径就是: /eureka-client1/**
路由规则不进行配置
访问:localhost:7000/eureka-client1/service/demo1
成功
4、Zuul中的过滤器
路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础;而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。
过滤器可以说是Zuul
实现API
网关功能最为核心的部件,每一个进入Zuul
的HTTP请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。
4.1 ZuulFilter简介
Zuul 中的过滤器跟我们之前使用的javax.servlet.Filter
不一样,javax.servlet.Filter
只有一种类型,可
以通过配置 urlPatterns
来拦截对应的请求。而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对
应的使用场景。
- PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请
求的微服务、记录调试信息等。 - ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用
Apache HttpClient
或Netfilx Ribbon
请求微服务。 - POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP
Header、收集统计信息和指标、将响应从微服务发送给客户端等。 - ERROR:在其他阶段发生错误时执行该过滤器。
Zuul提供了自定义过滤器的功能实现起来也十分简单,只需要编写一个类去实现zuul提供的接口
public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
public abstract String filterType();
public abstract int filterOrder();
}
public interface IZuulFilter {
/**
* a "true" return from this method means that the run() method should be invoked
*
* @return true if the run() method should be invoked. false will not invoke the run() method
*/
boolean shouldFilter();
/**
* if shouldFilter() is true, this method will be invoked. this method is the core method of a ZuulFilter
*
* @return Some arbitrary artifact may be returned. Current implementation ignores it.
* @throws ZuulException if an error occurs during execution.
*/
Object run() throws ZuulException;
}
ZuulFilter是过滤器的*父类。在这里我们看一下其中定义的4个最重要的方法
shouldFilter :返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
run :过滤器的具体业务逻辑。
filterType :返回字符串,代表过滤器的类型。包含以下4种:
pre :请求在被路由之前执行
routing :在路由请求时调用
post :在routing和errror过滤器之后调用
error :处理请求时发生错误调用
filterOrder :通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。
4.2 生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CDeLaRRM-1613658706670)(…/image/image-20200813162348200.png)]
正常流程:
请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的
服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。
异常流程:
整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕
后,会将请求交给POST过滤器,最后返回给用户。
如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求
不会再到达POST过滤器了。
不同过滤器的场景:
请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了
异常处理:一般会在error类型和post类型过滤器中结合来处理。
服务调用时长统计:pre和post结合使用。
内置过滤器列表:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SHHUxgQc-1613658706673)(…/image/image-20200813162440447.png)]
4.3 自定义过滤器
@Component
public class LoginFilter extends ZuulFilter {
/**
* 过滤器类型:
*
* @return
*/
@Override
public String filterType() {
//登录校验 ,前置拦截
return "pre";
}
/**
* 过滤器顺序:多个过滤器的情况下,数字越小越优先执行
*
* @return
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 是否向下执行 true执行,false不执行
*
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 业务逻辑执行
*
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
//1.获取zuul提供的上下文对象
RequestContext ctx = RequestContext.getCurrentContext();
//2.从上下文中获取request对象
HttpServletRequest request = ctx.getRequest();
//3.从请求中获取token
String token = request.getParameter("access_token");
if (StringUtils.isBlank(token)) {
//没有token,登录校验失败
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
}
//返回null表示校验通过,可以把用户信息放入上下文,继续向下执行
return null;
}
}
RequestContext:用于在过滤器之间传递消息。它的数据保存在每个请求的ThreadLocal中。它
用于存储请求路由到哪里、错误、HttpServletRequest、HttpServletResponse都存储在
RequestContext中。RequestContext扩展了ConcurrentHashMap,所以,任何数据都可以存储
在上下文中
测试
http://localhost:7000/eureka-client1/service/demo1
访问失败
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WRUBSD7P-1613658706677)(…/image/image-20200813163741492.png)]
http://localhost:7000/eureka-client1/service/demo1?access_token=1
访问通过
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7i4fkRny-1613658706678)(…/image/image-20200813163726508.png)]
5、zuul网关存在的问题
5.1 Zuul网关存在的问题
在实际使用中我们会发现直接使用Zuul会存在诸多问题,包括:
性能问题
Zuul1x版本本质上就是一个同步Servlet,采用多线程阻塞模型进行请求转发。简单讲,每来
一个请求,Servlet容器要为该请求分配一个线程专门负责处理这个请求,直到响应返回客户
端这个线程才会被释放返回容器线程池。如果后台服务调用比较耗时,那么这个线程就会被
阻塞,阻塞期间线程资源被占用,不能干其它事情。我们知道Servlet容器线程池的大小是有
限制的,当前端请求量大,而后台慢服务比较多时,很容易耗尽容器线程池内的线程,造成
容器无法接受新的请求。
不支持任何长连接,如websocket
5.2 zuul网关替代方案
Zuul2.x版本
SpringCloud Gateway
上一篇: 神奇的python(六)之python的串口操作(pyserial)
下一篇: poc
推荐阅读
-
玩转SpringCloud(F版本) 四.路由网关(zuul)
-
Spring Cloud系列-Zuul网关集成JWT身份验证
-
微服务架构下使用Spring Cloud Zuul作为网关将多个微服务整合到一个Swagger服务上
-
跟我学SpringCloud | 第十篇:服务网关Zuul高级篇
-
(springCloud-8 Zuul网关路由的基本配置feign实现接口调用)
-
(四)surging 微服务框架使用系列之网关
-
玩转SpringCloud(F版本) 四.路由网关(zuul)
-
微服务-网关服务
-
Spring Cloud(十):服务网关 Zuul(路由)【Finchley 版】
-
你连微服务的网关都说不清楚,还天天鼓捣着要把项目拆分微服务?