springcloud5-服务网关zuul及gateway

Posted teayear

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springcloud5-服务网关zuul及gateway相关的知识,希望对你有一定的参考价值。

1,课程回顾
2,本章重点

zuul(zuul1,zuul2)
gateway
3,具体内容
3.1 zuul
3.1.1 zuul简介
https://www.springcloud.cc/
https://github.com/Netflix/zuul/wiki
https://www.springcloud.cc/spring-cloud-greenwich.html#_router_and_filter_zuul
Zuul 是从设备和网站到 Netflix 流媒体应用程序后端的所有请求的前门。作为边缘服务应用程序,Zuul 旨在支持动态路由、监控、弹性和安全性。路由是微服务架构不可或缺的一部分。例如,/可能被映射到您的web应用程序,/api/users被映射到用户服务,/api/shop被映射到商店服务。 Zuul是Netflix的基于JVM的路由器和服务器端负载平衡器。
3.1.1 zuul 的主要作用(为什么使用)
Netflix API 流量的数量和多样性有时会导致生产问题在没有警告的情况下迅速出现。我们需要一个允许我们快速改变行为以应对这些情况的系统。
Zuul 使用一系列不同类型的过滤器,使我们能够快速灵活地将功能应用到我们的边缘服务。这些过滤器帮助我们执行以下功能:
身份验证和安全性 - 确定每个资源的身份验证要求并拒绝不满足要求的请求。
洞察力和监控 - 在边缘跟踪有意义的数据和统计数据,以便为我们提供准确的生产视图。
动态路由 - 根据需要将请求动态路由到不同的后端集群。
压力测试 - 逐渐增加集群的流量以衡量性能。
减载 - 为每种类型的请求分配容量并丢弃超出限制的请求。
静态响应处理 - 直接在边缘构建一些响应,而不是将它们转发到内部集群
3.1.2 实现过程(参考项目 zuul)
1)引入jar:

<!--zuul jar-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!--nacos客户端jar-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>         

2) 启动类:
@SpringBootApplication
@EnableZuulProxy //开启zuul的代理功能
3)配置application.yml

server:
  #端口号配置
  port: 14151
spring:
  cloud:
    nacos:
      discovery:
        #注册地址
        server-addr: localhost:8848
  application:
    #注册服务名称
    name: zuulService
zuul:
  host:
    #总并发连接配置  默认值为200
    max-total-connections: 300
    #每个路由并发连接配置  默认值为20
    max-per-route-connections: 30
    #连接超时时长  默认为2000毫秒
    connect-timeout-millis:  5000
  #路由配置
  routes:
    #自定义路由名称
    order:
      #服务的唯一识别  一定要和想访问的服务注册到注册中心的名称一致
      serviceId: orderService
      #请求该服务的路径配置  **不确定,可以根据地址栏请求,动态路由
      path: /os/**
    goods:
      #服务的唯一识别  一定要和想访问的服务注册到注册中心的名称一致
      serviceId: goodsService
      #请求该服务的路径配置  **不确定,可以根据地址栏请求,动态路由
      path: /gs/**
    sns:
      #服务的唯一识别  一定要和想访问的服务注册到注册中心的名称一致
      serviceId: snsService
      #请求该服务的路径配置  **不确定,可以根据地址栏请求,动态路由

4)路由测试:
http://localhost:14151/ss/comment/selectOne/1?a=1&b=22&c=333nnd33
http://localhost:14151/gs/product/selectAll?current=1&size=2
5)过滤器实现:
jar(增加):

<!--springboot web包  过滤器需要-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>      

filter方法详解:
/**

  • 过滤类型 定义当前过滤器在什么起用
  • @return
    */
@Override
public String filterType() 
    //pre  在执行业务之前执行
    //route  在执行业务方法时执行
    //error  在执行业务时出现异常执行
    //post  在正常执行业务后或者出现异常后 执行
    return "pre";

/**

  • 多个过滤器同时执行时,通过该方法确定先后顺序 返回值越小,执行优先级越高
  • @return
    /
    @Override
    public int filterOrder()
    return 3;

    /
    *
  • 是否让该过滤器生效 返回false不生效
  • @return
    /
    @Override
    public boolean shouldFilter()
    return true;

    /
    *
  • 开发过滤功能
  • @return
  • @throws ZuulException
    */
    @Override
    public Object run() throws ZuulException

    上下文对象:
com.netflix.zuul.context.RequestContext
编写IP过滤过滤器:          
package com.aaa.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @ fileName:IllegalIPFilter
 * @ description:
 * @ author:zhz
 * @ createTime:2022/2/21 20:37
 * @ version:1.0.0
 */
@Component
public class IllegalIPFilter extends ZuulFilter 
    @Value("$illegal_ip")
    private String illegalIp;
    @Override
    public String filterType() 
        return "pre";
    
    @Override
    public int filterOrder() 
        return 0;
    
    @Override
    public boolean shouldFilter() 
        return true;
    
    @Override
    public Object run() throws ZuulException 
        //具体过滤器业务代码
        System.out.println("进入了IP过滤器。。。。。");
        //获取zuul中提供的请求上下文对象
        RequestContext requestContext = RequestContext.getCurrentContext();
        //获取request
        HttpServletRequest request =requestContext.getRequest();
        //获取response对象
        HttpServletResponse response = requestContext.getResponse();
        //获取请求用户的IP地址
        String remoteAddr = request.getRemoteAddr();
        System.out.println("请求IP为:"+remoteAddr);
        System.out.println(illegalIp+"............");
        //判断IP地址是否合法   192.168.1.120
        //illegalIp=192.168.1.120,192.168.1.110,192.168.1.134,192.168.1.143
        if(illegalIp.contains(remoteAddr))
            //添加响应方法
               response.setCharacterEncoding("gbk");
            try 
                //阻止程序继续运行
                requestContext.setSendZuulResponse(false);
                //response.sendError(HttpStatus.SC_FORBIDDEN,"禁止访问!");
                response.getWriter().println("IP非法,禁止访问!!!");
             catch (IOException e) 
                e.printStackTrace();
            
        
        return null;
    


编写脏字过滤器:
package com.aaa.zs.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
/**
 * @ fileName:IllegalCharFilter
 * @ description:
 * @ author:zhz
 * @ createTime:2022/2/22 9:43
 * @ version:1.0.0
 */
@Component
public class IllegalCharFilter extends ZuulFilter 
    @Value("$illegal_char")
    private String illegalChar;
    @Override
    public String filterType() 
        return "pre";
    
    @Override
    public int filterOrder() 
        return 4;
    
    @Override
    public boolean shouldFilter() 
        return true;
    
    @Override
    public Object run() throws ZuulException 
        System.out.println("非法字符过滤器。。。。。。。。。。。。");
        //实例化zuul封装请求上下文对象
        RequestContext currentContext = RequestContext.getCurrentContext();
        //通过上下问对象获取request,response
        HttpServletRequest request = currentContext.getRequest();
        HttpServletResponse response = currentContext.getResponse();
        //获取请求的所有参数 //?a=1&b=2&c=3&aa=11   parameterNames [a,b,c,aa]
        Enumeration<String> parameterNames = request.getParameterNames();
        //根据逗号分割 非法字符串 [tmd,nnd,nm,yyds]
        String[] illegalCharAarray = illegalChar.split(",");
        //循环遍历所有参数名称
        while(parameterNames.hasMoreElements())
            //第1次获取a  第2次获取b  ...
            String parameterName = parameterNames.nextElement();
            //根据名称获取参数值 第1次获取1  第2次获取2  ...
            String parameterValue = request.getParameter(parameterName);
            for (String illegalChar : illegalCharAarray) 
                 if(parameterValue.contains(illegalChar))
                     //直接阻止程序运行
                     currentContext.setSendZuulResponse(false);
                     //回显错误
                     response.setCharacterEncoding("GBK");
                     try 
                         response.getWriter().println("请求中含有非法参数,禁止访问");
                      catch (IOException e) 
                         e.printStackTrace();
                     
                 
            
        
        return null;
    

编写单点登录过滤器(单点登录用到)
跨域过滤(参考3.4 方法2)

package com.aaa.zs.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @fileName:CrossDomainFilter
 * @description:后台跨域过滤器
 * @author:zz
 * @createTime:2020/11/6 16:20
 * @version:1.0.0
 */
@Component
public class CrossDomainFilter extends ZuulFilter 
    @Override
    public String filterType() 
        return "pre";
    
    @Override
    public int filterOrder() 
        return 0;
    
    @Override
    public boolean shouldFilter() 
        return true;
    
    @Override
    public Object run() throws ZuulException 
        // 获取request对象
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        HttpServletResponse response = ctx.getResponse();
        // 这些是对请求头的匹配,网上有很多解释
        response.setHeader("Access-Control-Allow-Origin",request.getHeader("origin"));
        response.setHeader("Access-Control-Allow-Credentials","true");
        response.setHeader("Access-Control-Allow-Methods","GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH");
        response.setHeader("Access-Control-Allow-Headers","authorization, content-type");
        response.setHeader("Access-Control-Expose-Headers","X-forwared-port, X-forwarded-host");
        response.setHeader("Vary","Origin,Access-Control-Request-Method,Access-Control-Request-Headers");
        // 跨域请求一共会进行两次请求 先发送options 是否可以请求
        // 调试可发现一共拦截两次 第一次请求为options,第二次为正常的请求 eg:get请求
        //  equalsIgnoreCase 忽略大小写
        if ("OPTIONS".equalsIgnoreCase(request.getMethod()))
            ctx.setSendZuulResponse(false); //验证请求不进行路由
            ctx.setResponseStatusCode(HttpStatus.OK.value());//返回验证成功的状态码
            ctx.set("isSuccess", true);
            return null;
        
        // 第二次请求(非验证,eg:get请求不会进到上面的if) 会走到这里往下进行
        // 不需要token认证
        ctx.setSendZuulResponse(true); //对请求进行路由
        ctx.setResponseStatusCode(HttpStatus.OK.value());
        ctx.set("isSuccess", true);
        return null;
    
    public static void main(String[] args) 
        System.out.println("a".equals("A"));
        System.out.println("a".equalsIgnoreCase("A"));
    


测试过滤器的优先级,测试过滤器的启用和禁用等功能

3.2 gateway
https://spring.io/projects/spring-cloud-gateway#overview
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
3.3.1 概念
该项目提供了一个用于在Spring WebFlux之上构建API网关的库。Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到API,并为它们提供跨领域的关注点,例如:安全性,监视/指标和弹性。
主要功能: 路由功能route 断言功能predicate 过滤功能filter

webFlux官网文档:
https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html
3.3.2 特点
建立在Spring Framework 5,Project Reactor和Spring Boot 2.0之上
能够匹配任何请求属性上的路由。
谓词和过滤器特定于路由。
断路器集成。
Spring Cloud DiscoveryClient集成
易于编写的谓词和过滤器
请求速率限制(https://www.cnblogs.com/forezp/p/10140316.html)
路径改写
3.3.3 zuul和gateway对比(面试题)
相同点:
1、底层都是servlet
2、两者均是web网关,处理的是http请求
不同点:
1,getway的性能会远高于zuul1
getway和zuul 在zuul没有进行参数调优的时候,getway的性能会远高于zuul。在空负载的时候,SpringCloud Gateway比zuul 1 性能高50%左右,在模拟处理50ms业务后,,SpringCloud Gateway比zuul 1 性能高9倍左右。

zuul优化参数参考如下:

在实际生产使用中,zuul 1虽然使用的是同步io,但是可以通过参数优化提高性能理论上可以达到极限性能,而springcloud gateway使用的是异步io,不需优化既可以达到接近极限的性能。
Zuul 1.x,是一个基于阻塞io的API Gateway。Zuul已经发布了Zuul 2.x,基于Netty,也是非阻塞的,支持长连接,但Spring Cloud暂时还没有整合计划。
SpringCloud GetWay 是基于webFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty(异步非阻塞)。
2、是否支持异步
zuul1 仅支持同步
gateway支持异步。理论上gateway则更适合于提高系统吞吐量(但不一定能有更好的性能),最终性能还需要通过严密的压测来决定

3.3.4 工作原理

  客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。该处理程序通过特定于请求的过滤器链运行请求。筛选器由虚线分隔的原因是,筛选器可以在发送代理请求之前和之后运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请求。发出代理请求后,将运行“后”过滤器逻辑。              

3.3.5 具体使用
1)创建项目,引入jar包

<!--gateway jar包-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos 服务发现 客户端包-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--fastjson 包 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
</dependency>       

2) 创建配置
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-host-route-predicate-factory

server:
  port: 14152  #当前服务端口号
spring:
  application:
    name: gatewayService  #当前服务名称
  cloud:
    gateway:
      routes: #表示多个路由
        - id: order_route  # 每个网关的唯一标识,不可以重复
          name: order-service #名称,可以省略配置
          #uri: http://localhost:14121 #匹配后提供服务的路由地址
          uri: lb://orderService  #匹配后提供服务的路由地址,必须和注册中心注册名称一致 lb 支持负载均衡  注册服务是多个
          predicates: # 断言,满足条件,就执行
            - Path=/order/**
        - id: goods_route  # 每个网关的唯一标识,不可以重复
          uri: lb://goodsService  #配置被访问服务的名称,必须和注册中心注册名称一致
          predicates: # 断言,满足条件,就执行
            - Path=/product/**,/productType/**  #多个路径可以用,隔开
            - Method=POST,GET,PUT,DELETE  #请求方式是 POST,GET,PUT或者DELETE
            - Query=id,\\d+  #要有参数id并且要求id为整数
            - Before=2023-02-22T11:25:40.666+08:00[Asia/Shanghai] #匹配这个时间之前的请求  UTC时间,也就是国际统一时间  +8:00 东八区
            - After=2021-02-21T12:15:30.666+08:00[Asia/Shanghai]  #匹配这个时间之后的请求
            - Between=2021-02-21T12:15:30.666+08:00[Asia/Shanghai],2023-02-21T12:15:30.666+08:00[Asia/Shanghai]
        - id: sns_route  # 每个网关的唯一标识,不可以重复
          name: sns-service #名称,可以省略配置
          #uri: http://localhost:14121 #匹配后提供服务的路由地址
          uri: lb://snsService  #匹配后提供服务的路由地址,必须和注册中心注册名称一致
          predicates: # 断言,满足条件,就执行
            - Path=/comment/**
    nacos:
      discovery:
        server-addr: localhost:8848/  #注册中心地址  gateway必须注册,才能发现其他服务
#自定义非法IP
illegal_ip: 192.168.41.174,192.168.1.110,192.168.1.134,192.168.1.143

3) 创建启动类

package com.aaa.gws;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
 * @ fileName:GatewayApp
 * @ description:
 * @ author:zhz
 * @ createTime:2022/2/22 11:14
 * @ version:1.0.0
 */
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApp 
    public static void main(String[] args) 
        SpringApplication.run(GatewayApp.class,args);
    

4) 启动项目,路由测试
先启动order服务,再启动gateway
http://localhost:14152/productType/selectOne/1?id=1
http://localhost:18888/order/selectOne?id=1
5)过滤器配置:
添加配置(application.yml):
#配置非法IP
illegal_ip: 192.168.1.120,192.168.1.110,192.168.1.134,192.168.1.143

#配置非法字符 BAT=baidu alibaba tencent
illegal_char: sb,nnd,tmd
#白名单配置
white_list: /order/selectOne,userLogin,/order/selectAll,memberLogin

非法字符滤器:
使用gateway过滤功能,过滤所有请求,获取所有请求中所有参数,如果请求参数中含有系统配置非法字符,直接不让提示错误。

package com.aaa.gs.filter;
import com.aaa.common.util.ResultStatus;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse以上是关于springcloud5-服务网关zuul及gateway的主要内容,如果未能解决你的问题,请参考以下文章

zuul网关

zuul网关

zuul网关

springcloud06(zuul网关)

Zuul与Gateway都有哪些区别

SpringCloud---API网关服务---Spring Cloud Zuul