SpringCloud系列八:Zuul网关服务

一. 为什么需要网关呢?

第一,我们知道我们要进入一个服务本身,很明显我们没有特别好的办法,直接输入IP地址+端口号,我们知道这样的做法很糟糕的,这样的做法大有问题,首先暴露了我们实体机器的IP地址,别人一看你的IP地址就知道服务部署在哪里,让别人很方便的进行攻击操作。

 

第二,我们这么多服务,我们是不是要挨个调用它呀,我们这里假设做了个权限认证,我们每一个客户访问的都是跑在不同机器上的不同的JVM上的服务程序,我们每一个服务都需要一个服务认证,这样做烦不烦呀,明显是很烦的。

 

那么我们这时候面临着这两个极其重要的问题,这时我们就需要一个办法解决它们。首先,我们看IP地址的暴露和IP地址写死后带来的单点问题,我是不是对这么服务本身我也要动态的维护它服务的列表呀,我需要调用这服务本身,是不是也要一个负载均衡一样的玩意,

 

还有关于IP地址暴露的玩意,我是不是需要做一个代理呀,像Nginx的反向代理一样的东西,还有这玩意上部署公共的模块,比如所有入口的权限校验的东西。因此我们现在需要Zuul API网关。它就解决了上面的问题,你想调用某个服务,它会给你映射,把你服务的IP地址映射成

 

某个路径,你输入该路径,它匹配到了,它就去替你访问这个服务,它会有个请求转发的过程,像Nginx一样,服务机器的具体实例,它不会直接去访问IP,它会去Eureka注册中心拿到服务的实例ID,即服务的名字。我再次使用客户端的负载均衡ribbon访问其中服务实例中的一台。

 

API网关主要为了服务本身对外的调用该怎么调用来解决的,还有解决权限校验的问题,你可以在这里整合调用一系列过滤器的,例如整合shiro,springsecurity之类的东西。

 

Zuul的原理图如下:

二  应用场景

说了这么多,我们来总结一下网关的应用场景:

(1)黑白名单:实现通过IP地址控制禁止访问网关功能,此功能是应用层面控制实现,再往前也可以通过网络传输方面进行控制访问。

(2)日志:实现访问日志的记录,可用于分析访问、处理性能指标,同时将分析结果支持其他模块功能应用。

(3)协议适配:实现通信协议校验、适配转换的功能。

(4)身份认证:负责网关访问身份认证验证,此模块与“访问认证中心”通信,实际认证业务逻辑交移“访问认证中心”处理。

(5)计流限流:实现微服务访问流量计算,基于流量计算分析进行限流,可以定义多种限流规则。

(6)路由:路由是API网关很核心的模块功能,此模块实现根据请求,锁定目标微服务并将请求进行转发。此模块需要与“服务发布管理中心”通信。“服务发布管理中心”实现微服务发布注册管理功能,与其通信获得目标微服务信息。

 

三 zuul和feign的应用场景和区别

(1)、zuul作为整个应用的流量入口,接收所有的请求,如app、网页等,并且将不同的请求转发至不同的处理微服务模块,其作用可视为nginx。

(2)、feign则是将当前微服务的部分服务接口暴露出来,并且主要用于各个微服务之间的服务调用。

(3)、两者的应用层次以及原理均不相同。

(4)、zuul也含有hystrix和ribbon,基于http通讯的,可以直接代理服务就行。在它和服务间增加feign只会增加通讯消耗,没有特别的意义。feign在服务互相调用的时候用就行了,可以仿rpc通讯。

(5)、Feign主要作客户端流控,Feign的负载均衡是基于Eureka实现的,Zuul主要作服务端流控,并且Zuul的负载均衡结合Eureka实现易用性较好,并且Zuul我一般用在对第三方提供访问接口。

 

四 具体案例

在以前案例的基础上,我们保持注册中心和服务提供者不变,新建zuul maven工程,如下图:

Pom文件代码如下,重点是引入了spring-cloud-starter-zuul依赖:

<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">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.wx</groupId>
  <artifactId>cloud_zuul</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.9.RELEASE</version>
	</parent>

	<!-- 使用dependencyManagement进行版本管理 -->
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Camden.SR6</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>

	</dependencyManagement>

	<dependencies>
		<!-- 引入eureka依赖 -->
		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
		<!-- 引入zuul依赖,它本身又依赖了spring-boot-starter-actuator/spring-boot-starter-hystrix/spring-boot-starter-ribbon -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
        
	</dependencies>

	<build>
		<plugins>
			<!-- 设置通过jdk1.8编译项目 -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<encoding>utf-8</encoding>
				</configuration>
			</plugin>
			<!-- 拷贝resouce资源到output directory -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-resources-plugin</artifactId>
				<configuration>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
			<!-- 可通过maven命令运行项目和打包运行 -->
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<executable>true</executable>
					<includeSystemScope>true</includeSystemScope>
				</configuration>
			</plugin>
			
		</plugins>
	</build>
	
</project>

 

新建ZuulApp启动类,加上@EnableZuulProxy注解,用来启动zuul服务网关,如图所示:

配置文件如图所示:

这里通过zuul.routes.<route>.path和zuul.routes.<route>.url进行配置,可以配置出各种地址访问规则,跟nginx类似,这里就不详述了,一般的项目也没必要有那么复杂的限制。感兴趣可以自行百度。我们一般使用面向服务的路由,我们可以让路由的path不是映射具体的url,而是具体的某个服务,而服务的url则交给Eureka服务发现机制自动维护,这类路由就是面向服务的路由,如上图所示就是配置的面向服务的路由。

依次启动服务:

(1)启动注册中心,端口号911

 (2)启动服务提供者1,端口号2221

 (3)启动服务提供者2,端口号2222

(4)启动zuul服务,端口号1111

然后随便调用服务提供者中的接口[http://localhost:1111/cartServ/flow/cart/details.do?id=2020211479],运行结果如下:

刷新后:

实际上,zuul帮我们默认做了如下配置:

zuul.routes.<服务名>.path=/<服务名>/**

zuul.routes.<服务名>.serviceId=<服务名>

所有我们上面的配置可以简化成:

接下来我们访问如下地址【http://localhost:1111/cart-service/flow/cart/details.do?id=2020211479】得到的结果跟上面一样。

 

五、请求过滤

为了在API网关中实现对客户端请求的校验,我们可以通过过滤器来实现对请求的拦截和过滤,实现方法比较简单,只需要继承ZuulFilter抽象类并实现其四个方法就行了。

 

修改cloud_zuul:

1.新增过滤器类

代码:

package com.wx.filters;

import javax.servlet.http.HttpServletRequest;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

/**
  * 继承ZuulFilter,并且实现其4个接口,用来进行请求过滤
  * @author wangxiang
  * @date 2020年9月18日
  */
public class AccessFilter extends ZuulFilter{
    /* 
     * shouldFilter 判断该过滤器是否需要被执行
     * 这里直接返回true,表示该过滤器对所有请求都会生效。
     * 实际运用中我们可以利用该函数指定过滤器的有效范围
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /*
     * 过滤器的具体逻辑
     * 这里我们通过ctx.setSendZuulResponse(false)让zuul过来请求,不对其进行路由
     * 然后通过ctx.setResponseStatusCode(401)设置了返回的错误码
     */
    @Override
    public Object run() {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        Object accessToken = request.getParameter("token");
        System.out.println("当前请求===>"+request.getRequestURL().toString());
        if(accessToken == null) {
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(401);
            context.setResponseBody("unAuthrized");
        }
        return null;
    }

    /* filterType 返回过滤器类型
     * 他决定了过滤器在请求的哪个生命周期中执行。这里定义为pre,代表会在请求被路由前执行。
     * pre:请求执行之前filter 
     * route: 处理请求,进行路由 
     * post: 请求处理完成后执行的filter 
     * error:出现错误时执行的filter
     */
    @Override
    public String filterType() {
        return "pre";
    }
    
    
    /* 
     * filterOrder 返回过滤器的执行顺序
     * 当请求在一个阶段有多个过滤器是,需要根据该方法的返回值来依次执行
     */
    @Override
    public int filterOrder() {
        return 0;
    }
}


2. 在启动类上把过滤器交给spring管理

 

3. 运行,输入地址:http://localhost:1111/cart-service/flow/cart/details.do?id=2020211479,结果如图:

输入地址:http://localhost:1111/cart-service/flow/cart/details.do?id=2020211479&token=aa,结果正常

4. Zuul中默认实现的Filter

类型

顺序

过滤器

功能

pre

-3

ServletDetectionFilter

标记处理Servlet的类型

pre

-2

Servlet30WrapperFilter

包装HttpServletRequest请求

pre

-1

FormBodyWrapperFilter

包装请求体

route

1

DebugFilter

标记调试标志

route

5

PreDecorationFilter

处理请求上下文供后续使用

route

10

RibbonRoutingFilter

serviceId请求转发

route

100

SimpleHostRoutingFilter

url请求转发

route

500

SendForwardFilter

forward请求转发

post

0

SendErrorFilter

处理有错误的请求响应

post

1000

SendResponseFilter

处理正常的请求响应

 

 

猜你喜欢

转载自blog.csdn.net/wx5040257/article/details/108700274