SpringCloud -- Zuul

    技术2022-07-11  125

    Zuul

    简介搭建Zuul路由控制配置简单路由规则引入eureka路由配置优化默认路由规则路由前置配置 过滤器过滤器执行生命周期自定义过滤器使用场景 负载均衡,熔断保护Zuul的高可用

    简介

    在微服务中,为了保证不被恶意攻击,我们需要隐藏我们各个微服务的地址,统一提供一个在外的地址提供访问,这个时候我们就需要一个网关来承担这样的责任; zuul的介绍 在Netflix的这套组件中,zuul也是不错的一套网关

    搭建Zuul

    创建maven工程导入依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>demoCloud2020</artifactId> <groupId>com.ace</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>zuul-demo</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> </dependencies> </project> 创建启动器 @SpringBootApplication @EnableZuulProxy // 开启Zuul的网关功能 public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } 配置文件创建 server: port: 10010 #服务端口 spring: application: name: api-gateway #指定服务名

    路由控制

    网关要做到在所有微服务前首先被访问,一夫当关作用,那么首先他就要有路由转发的作用;

    配置简单路由规则

    springboot是在引入依赖的时候,很多的代码都已经完成所以无需我们操作什么,我们需要引用或者配置;

    zuul: routes: user-service: # 这里是路由id,随意写,尽量便于区分 path: /user-service/** # 这里是映射路径,对应配置的实际url url: http://127.0.0.1:8081 # 映射路径对应的实际url地址

    这样配置后,就会生效了 我们可以测试下 访问网关 可以访问获取实际数据(微服务还是使用之前的)

    引入eureka

    在上面的配置中,我们把url地址写死了,这样的弊端我们之前也提到了很不好,所以我们需要引入eureka来修改这个问题,从配置中心中获取地址信息;

    依赖导入 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> 启动类添加注解 @SpringBootApplication @EnableZuulProxy // 开启Zuul的网关功能 @EnableDiscoveryClient //注册中心注册 public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } 配置文件修改 port: 10010 #服务端口 spring: application: name: api-gateway #指定服务名 eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka zuul: routes: user-service: # 这里是路由id,随意写 path: /user-service/** # 这里是映射路径 serviceId: user-service # 指定服务名称 测试 测试结果还是一样可以生效

    路由配置优化

    在刚才的配置中,我们的规则是这样的:

    zuul.routes.<route>.path=/xxx/**: 来指定映射路径。<route>是自定义的路由名zuul.routes.<route>.serviceId=/user-service:来指定服务名。

    而大多数情况下,我们的<route>路由名称往往和 服务名会写成一样的。因此Zuul就提供了一种简化的配置语法:zuul.routes.<serviceId>=<path>

    比方说上面我们关于user-service的配置可以简化为一条:

    zuul: routes: user-service: /user-service/** # 这里是映射路径

    默认路由规则

    在使用Zuul的过程中,上面讲述的规则已经大大的简化了配置项。但是当服务较多时,配置也是比较繁琐的。因此Zuul就指定了默认的路由规则:

    默认情况下,一切服务的映射路径就是服务名本身。 例如服务名为:user-service,则默认的映射路径就是:/user-service/**

    也就是说,刚才的映射规则我们完全不配置也是OK的,不信就试试看。

    如果想要禁用某个路由规则,可以这样:

    zuul: ignored-services: - user-service - consumer

    路由前置配置

    zuul: prefix: /api # 添加路由前缀 routes: user-service: /user-service/** # 这里是映射路径

    我们通过zuul.prefix=/api来指定了路由的前缀,这样在发起请求时,路径就要以/api开头。

    路径/api/user-service/user/1将会被代理到/user-service/user/1

    忽略路由前缀:

    zuul: prefix: /api routes: user-service: path: /user/** serviceId: user-service strip-prefix: false # 是否在转发时,去除路由前缀,这里不去除,映射路径中的user就会继续转发

    此时,只需要访问:http://localhost:10010/api/user/1

    过滤器

    Zuul作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作我们往往是通过Zuul提供的过滤器来实现的。

    过滤器执行生命周期

    正常流程: 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。 异常流程: 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。

    自定义过滤器

    ZuulFilter ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法: public abstract ZuulFilter implements IZuulFilter{ abstract public String filterType(); abstract public int filterOrder(); boolean shouldFilter();// 来自IZuulFilter Object run() throws ZuulException;// IZuulFilter } shouldFilter:返回一个Boolean`值,判断该过滤器是否需要执行。返回true执行,返回false不执行。run:过滤器的具体业务逻辑。filterType:返回字符串,代表过滤器的类型。包含以下4种: pre:请求在被路由之前执行route:在路由请求时调用post:在routing和errror过滤器之后调用error:处理请求时发生错误调用 filterOrder:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。 实现自定义过滤器 在我们知道其顶级父类后就很简单了,我们之间采用继承的方法这样就可以实现自定义过滤器了 @Component public class LoginFilter extends ZuulFilter { @Override public String filterType() { return FilterConstants.PRE_TYPE; } @Override public int filterOrder() { return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { // 获取请求上下文 RequestContext ctx = RequestContext.getCurrentContext(); // 获取request对象 HttpServletRequest request = ctx.getRequest(); // 获取请求参数 String token = request.getParameter("access-token"); // 判断是否存在 if(StringUtils.isBlank(token)){ // 不存在,未登录,拦截 ctx.setSendZuulResponse(false); // 设置返回状态码 ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); } return null; } }

    使用场景

    场景非常多:

    请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了异常处理:一般会在error类型和post类型过滤器中结合来处理。服务调用时长统计:pre和post结合使用。

    负载均衡,熔断保护

    Zuul中默认就已经集成了Ribbon负载均衡和Hystix熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。因此建议我们手动进行配置:

    hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 6000 ribbon: ConnectTimeout: 1000 ReadTimeout: 2000 MaxAutoRetries: 0 MaxAutoRetriesNextServer: 1

    Zuul的高可用

    在微服务中很多的程序都会被搭建成集群的样子,因为这样可以防止其中一个微服务宕机后,业务流程还可以继续走下去,所以一般zuul也是要搭建成集群形式的,但是zuul搭建成集群后,这样在访问的时候访问地址就会比较多,为了统一管理,这个时候,我们一般可以采用nginx 挡在zuul前面,这样就可以访问zuul的集群;

    Processed: 0.013, SQL: 9