08-微服务网关【API网关】之Zuul

Zuul 网关

介绍

Zuul是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用,Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

  • 动态路由:动态将请求路由到不同后端集群
  • 压力测试:逐渐增加指向集群的流量,以了解性能
  • 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
  • 静态响应处理:边缘位置进行响应,避免转发到内部集群
  • 身份认证和安全:识别每一个资源的验证要求,并拒绝那些不符的请求。Spring Cloud对Zuul进行了整合和增强

搭建Zuul

引入坐标

<!--zuul网关-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

配置启动类,开启网关服务器功能

@SpringBootApplication
@EnableZuulProxy	//开启Zuul的网关功能
public class ZuulServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ZuulServerApplication.class, args);
	}

}

yml配置

server:
  port: 8080 #端口
spring:
  application:
    name: api-gateway-server #服务名称

路由yml配置

基础路由yml配置

zuul:
  routes:
    product-service: # 这里是路由id,随意写
      path: /product-service/** # 这里是映射路径
      url: http://127.0.0.1:9001 # 映射路径对应的实际url地址

参数解释:

  • product-service:配置路由id,可以随意取名
  • url:映射路径对应的实际url地址
  • path:配置映射路径,这里将所有请求前缀为/product-service/的请求,转发到http://127.0.0.1:9001处理

配置好Zuul路由之后启动服务,在浏览器中输入 http://localhost:8080/product-service/product/1 ,即可访问到订单微服务。

面向服务的路由配置

简述

微服务一般是由几十、上百个服务组成,对于一个URL请求,最终会确认一个服务实例进行处理。如果对每个服务实例手动指定一个唯一访问地址,然后根据URL去手动实现请求匹配,这样做显然就不合理。

Zuul支持与Eureka整合开发,根据ServiceID自动的从注册中心中获取服务地址并转发请求,这样做的好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例的时候不用修改Zuul的路由配置。

(1)引入Eureka坐标

<!--引入EurekaClient-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

(2)启动类开启Zuul

@SpringBootApplication
@EnableZuulProxy	//开启Zuul的网关功能
@EnableDiscoveryClient    //Eureka的服务发现,可省略
public class ZuulServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ZuulServerApplication.class, args);
	}

}

(3)在Zuul网关服务中配置Eureka的注册中心相关信息

#配置Eureka
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    prefer-ip-address: true #使用ip地址注册

(4)修改路由中的映射配置

#路由配置
zuul:
  routes:
    product-service: # 这里是路由id,随意写
      path: /product-service/** # 这里是映射路径
#      url: http://127.0.0.1:9001 # 映射路径对应的实际url地址,下面的是根据Eureka获取所有的链接
      serviceId: service-product #配置转发的微服务名称

因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而是通过服务名称来访问,并且Zuul已经集成了Ribbon的负载均衡功能。

  • serviceId: 指定需要转发的微服务实例名称

依次启动Eureka,商品微服务,API网关服务,在浏览器上通过访问 http://localhost:8080/product-service/product/1 查看最终效果。

简化路由的配置

  • zuul.routes.route.path=/xxx/**: 来指定映射路径。route是自定义的路由名

  • zuul.routes.route.serviceId=service-provider:来指定服务名。

而大多数情况下,我们的route路由名称往往和服务名会写成一样的。

因此Zuul就提供了一种简化的配置语法:zuul.routes.serviceId=path

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

zuul:
  routes:
    service-product: # 这里是路由id,随意写
#      path: /product-service/** # 这里是映射路径
#      url: http://127.0.0.1:9001 # 映射路径对应的实际url地址,下面的是根据Eureka获取所有的链接
      serviceId: service-product #配置转发的微服务名称

#    ================ 改为以下方式,达到简化目的 ===================
zuul: 
  routes: 
  	#如果路由id和对应的微服务的serviceId一致的话
    service-product: /product-service/**

一致体现在,若不写id的话,默认使用的是微服务名称作为路径,因此 默认的id等于serviceId的值

因为路由id和对应的微服务的serviceId一致,则省略id,直接配置 【服务名称:映射路径】

默认的路由规则

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

  • 默认情况下,一切服务的映射路径就是服务名本身
    • 例如服务名为: service-product ,则默认的映射路径就是: /service-product/**
#zuu1中的默认路由配置
#如果当前的微服务名称 service-product、则默认的请求映射路径/service-product/**

如下:

Zuul加入后的系统架构

8-1

全部的yml配置总结

server:
  port: 8080 #端口
spring:
  application:
    name: api-zuul-server #服务名称

#配置Eureka
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    prefer-ip-address: true #使用ip地址注册


#路由配置方式1(基础)
zuul:
  routes:
    product-service: # 这里是路由id,随意写
      path: /product-service/** # 这里是映射路径
      url: http://127.0.0.1:9001 # 映射路径对应的实际url地址
      #这里将所有请求前缀为/product-service/的请求,转发到http://127.0.0.1:9001处理
      #在浏览器中输入 http://localhost:8080/product-service/product/1


#路由配置方式2(面向服务,需要搭配Eureka)
#zuul:
#  routes:
#    product-service: # 这里是路由id,随意写
#      path: /product-service/** # 这里是映射路径
#      serviceId: service-product #配置转发的微服务名称,浏览器访问:http://localhost:8080/product-service/product/1


#路由配置方式3(简化路由)
#zuul:
#  routes:
#    #以商品微服务为例
#    #service-product: #路由id,随便写
#    #path: /product-service/** #映射路径
#    #serviceId: service-product #配置转发的微服务的服务名称
#    service-product: /product-service/**  #如果路由id和对应的微服务的serviceId的值一致的话,可简写配置。浏览器访问:http://localhost:8080/product-service/product/1


#路由配置方式4(默认路由)
## 默认情况下,一切服务的映射路径就是服务名本身,
## 如 service-product ,则默认的请求映射路径 /service-product/** ,浏览器访问:http://localhost:8080/service-product/product/1
## 如 service-order ,则默认的请求映射路径 /service-order/** ,浏览器访问:http://localhost:8080/service-order/order/buy/1

过滤器

Zuul 过滤器简介

Zuul 中的过滤器跟我们之前使用的 javax.servlet.Filter 不一样,javax.servlet.Filter 只有一种类型,可以通过配置 urlPatterns 来拦截对应的请求。而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对应的使用场景。

  1. PRE:在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  2. ROUTING:将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
  3. POST:在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTPHeader、收集统计信息和指标、将响应从微服务发送给客户端等。
  4. ERROR:在其他阶段发生错误时执行该过滤器。Zuul提供了自定义过滤器的功能实现起来也十分简单,只需要编写一个类去实现zuul提供的接口
/**
* 编写一个类去实现zuul提供的 ZuulFilter 接口
*/
@Component
public class LoginFilter extends ZuulFilter {
	@Override
	public String filterType() {
		return null;
	}

	@Override
	public int filterOrder() {
		return 0;
	}

	@Override
	public boolean shouldFilter() {
		return false;
	}

	@Override
	public Object run() throws ZuulException {
		return null;
	}
}
  • ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法
  • shouldFilter :返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
  • run:过滤器的具体业务逻辑。
  • filterType :返回字符串,代表过滤器的类型。包含以下4种:
    • pre :请求在被路由之前执行
    • routing :在路由请求时调用
    • post :在routing和errror过滤器之后调用
    • error :处理请求时发生错误调用
  • filterOrder :通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

生命周期

8-2

  • 正常流程:
    • 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。
  • 异常流程:
    • 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
    • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
    • 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的是,请求不会再到达POST过滤器了。
  • 不同过滤器的场景:
    • 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了
    • 异常处理:一般会在error类型和post类型过滤器中结合来处理。
    • 服务调用时长统计:pre和post结合使用。

自定义一个过滤器

/**
 * 自定义的zuul过滤器
 *  继承抽象父类
 */
@Component
public class LoginFilter extends ZuulFilter {
	/**
	 * 定义过滤器类型
	 *  pre
	 *  routing
	 *  post
	 *  error
	 */
	public String filterType() {
		return "pre";
	}


	/**
	 *  指定过滤器的执行顺序
	 *      返回值越小,执行顺序越高
	 */
	public int filterOrder() {
		return 1;
	}


	/**
	 * 当前过滤器是否生效
	 *  true : 使用此过滤器
	 *  flase : 不使用此过滤器
	 */
	public boolean shouldFilter() {
		return true;
	}


	/**
	 * 指定过滤器中的业务逻辑
	 *  身份认证:
	 *      1.所有的请求需要携带一个参数 : access-token
	 *      2.获取request请求
	 *      3.通过request获取参数access-token
	 *      4.判断token是否为空
	 *      4.1 token==null : 身份验证失败
	 *      4.2 token!=null : 执行后续操作
	 *  在zuul网关中,通过RequestContext的上下问对象,可以获取对象request对象
	 */
	public Object run() throws ZuulException {
		//System.out.println("执行了过滤器");
		//1.获取zuul提供的上下文对象RequestContext
		RequestContext ctx = RequestContext.getCurrentContext();
		//2.从RequestContext中获取request
		HttpServletRequest request = ctx.getRequest();
		//3.获取请求参数access-token
		String token = request.getParameter("access-token");
		//4.判断
		if (token == null) {
			//4.1 如果token==null ,拦截请求,返回认证失败
			ctx.setSendZuulResponse(false); // 拦截请求
			ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
		}
		//4.2 如果token!=null ,继续后续操作
		return null;
	}
}

访问:http://localhost:8080/product-service/product/1 失败

访问:http://localhost:8080/product-service/product/1?access-token=1234564 成功

内部源码

8-3

posted @ 2021-07-19 15:47  爱码士很优秀  阅读(186)  评论(0编辑  收藏  举报