欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Cloud核心组件之 Zuul

程序员文章站 2022-06-13 21:27:04
...

为什么需要使用微服务网关呢?

客户端直接和微服务通信会存在问题(跨域访问的问题):
1、多次请求不同微服务,增加了客户端复杂性。
2、存在跨域请求,在一定场景下处理相对复杂。
3、认证复杂,每一个服务都需要独立认证。
4、难以重构,项目迭代,重新划分微服务。重构难以实施。
5、某些微服务使用其他协议,直接访问有一定困难。

跨域请求是两次请求,第一次是预请求,特点是不携带任何数据,请求消息头和请求体都不带数据,只有请求行知道要请求谁。预请求的请求方式是OPTIONS。用于跨域访问。具体的可以搜索一下Resultful架构7个HTTP方法。

Zuul的基础含义

Zuul,也就是微服务API网关。核心是一系列的过滤器。这个组件是负责网络路由的,存在于前端和后端之间,所有的前端请求访问都需要经过它来调度和过滤。有一个网关之后,可以做统一的降级、限流、认证授权、安全、负载分配、多区域弹性。

前端类似于android、ios、pc前端、微信小程序、H5等,不用去关心后端有多少个服务,知道有一个网关,所有请求都往网关走,网关会根据请求中的一些特征,将请求转发给后端的各个服务。

网关过滤器:

使用网关依然可以实现微服务间的调用。但是会出现消息头丢失。提示无权访问,说明消息头没有携带过去,访问用户问题,最后执行查询的是用户微服务的事,用户微服务中有拦截器,拦截器执行的时候会存东西到请求域中,按照规则写也是正确的token,存到请求域中,在查询的时候应该是可以查询成功的,但是却报了无权访问。也就是上一步没有取出权限,再往前一步说明没存进去,也就是拦截器没存进去,但是没用网关之前可以使用,现在用了网关那就是网关的问题。
涉及到访问规则:
访问的时候前台怎么怎么调用用户微服务的controller,请求先到网关,根据路由规则到controller,最后controller执行,报无权访问。怎么根据配置调用UserController中的方法呢?方法调用可能有些不合适,准确的说是请求。这就是两次请求,前台先请求网关,网关根据路由规则请求User微服务,最后由user微服务将结果返回给网关,网关响应给页面。
也就是用户携带的消息头到了网关那,第二次请求是网关发起,但是没发送给user微服务。
具体实现需要写一个过滤器网关类。

过滤器执行时机

当一个HTTP请求到达API网关之后,可以在访问目标微服务这一过程中进行一些自定义操作。
(1)pre阶段
访问目标微服务之前执行,请求会被pre类型的过滤器进行处理,做一些前置通知。这类过滤器包括请求的校验等。
(2)routing阶段
路由目标微服务时执行,路由请求转发时将外部请求转发到具体服务实例上去的过程。做后置通知,当服务实例将请求结果都返回之后,此阶段完成。
(3)error阶段
产生异常之后执行。它最后也是流向post阶段,因为它需要通过post过滤器将最终结果返回给请求客户端
(4)post阶段
目标微服务执行完成之后执行,在此阶段中,我们可以对处理结果进行一些加工或转换等,进行最终通知。

动态路由
如果想实现动态刷新路由规则的功能,只需将API网关服务的配置文件通过Config连接的Git仓库存储和管理即可。

实战篇:

1、导入依赖:

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
    </dependencies>

2、创建管理微服务并在配置文件中配置路由规则:

server:
  port: 8011
spring:
  application:
    name: cnooc-manager #微服务管理员网关
eureka:
  client:
    serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址
      defaultZone: http://127.0.0.1:6868/eureka/
  instance:
    prefer-ip-address: true
zuul:
  routes:
    cnooc-user: # 用户微服务
      path: /user/**  #配置请求URL的请求规则
      serviceId: cnooc-user #指定Eureka注册中心中的服务id

其中cnooc-user自定义,path是路由访问规则自定义。serviceId是cnooc-user微服务在Eureka注册中心中的服务id。
达到什么目的呢?
Spring Cloud核心组件之 Zuul
对,就是这样把访问的入口统一成一个,前台的访问请求都先到网关这,网关端口定义成8011,配置文件配置请求URL反问规则,这样就可以在前台访问进来时根据用户请求动态查询配置文件中的配置的访问规则。
http://localhost:8011/user/user/findAll
两个user是正确的,第一个是网关配置的path,第二个是user服务中RequestMapping中配置的访问路径。

3、新建网关启动类,并添加注解@EnableZuulProxy 开启微服务网关的支持

@SpringBootApplication
@EnableEurekaClient 
@EnableZuulProxy //开启微服务网关的支持
public class ManagerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ManagerApplication.class,args);
    }
}

4、出现的问题,网关发现请求给微服务消息头丢失。

**
 * 过滤器网关
 */
@Component // 使之进入容器
public class WebZuulFilter extends ZuulFilter{
    /**
     * 用于指定过滤器的执行时机:
     *     pre   访问目标微服务之前执行     前置通知
     *     route 路由目标微服务时执行       后置通知
     *     error 产生异常之后执行           异常通知
     *     post  目标微服务执行完成之后执行  最终通知
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 用于指定过滤器执行的优先级(当有多个过滤器时,确定谁先执行)
     * 返回值越小,执行时间越早。
     * 取值正整数和零
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 用于指定过滤器的核心方法是否执行
     * 当返回true时,表示执行
     * 返回false时,不执行
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤器的核心方法,要处理的事情都写在此处
     * @return 放行返回null,其他情况都是不放行(但是我们可以通过设置一些方法的参数来实现return null时仍然不放行)
     * @throws ZuulException
     *
     * 此处我们的核心业务是:
     *      取出当前请求的消息头Authorization,
     *      在把它添加到请求目标微服务的请求消息头中
     */
    @Override
    public Object run() throws ZuulException {
        //1.获取请求上下文对象
        RequestContext context = RequestContext.getCurrentContext();
        //2.取出当前请求对象
        HttpServletRequest request = context.getRequest();
        //3.取出消息头
        String header = request.getHeader("Authorization");
        //4.把权限信息加入到请求目标微服务的请求消息头中
        context.addZuulRequestHeader("Authorization",header);
        return null;//放行之后就到目标微服务
    }
}