Spring Cloud Zuul路由网关(学习总结)

一、简介

Spring Cloud Zuul主要的功能是:路由跳转以及路由过滤,本文主要讲解了这两方面的内容。在实际项目中,zuul中可能也会有相对复杂的逻辑,通常在zuul前面还有一个nginx反向代理,前端直接访问nginx,让nginx给我们代理到网关服务,网关服务再路由到具体的服务提供者上。zuul默认集成了ribbon实现了负载均衡功能。

二、准备工程

a. zuul-eureka-server,端口1111

b.zuul-service:服务提供者,端口2222

c.zuul-api-gateway:网关服务,端口3333

本文不对eureka服务注册中心的搭建做过多讲解,主要讲解api-gateway网关服务的搭建过程。

三、新建zuul-service服务提供者工程

zuuk-service也只是一个比较简答的服务提供者,主要是要暴露一个接口给外部进行访问。主要代码如下:

pom.xml:

<?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">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.springcloud.wsh</groupId>
	<artifactId>springcloud_zuul_service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>springcloud_zuul_service</name>
	<description>服务提供者</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Camden.SR6</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

启动类:

@SpringBootApplication
@EnableDiscoveryClient
public class SpringcloudZuulServiceApplication {

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

ZuulServiceController:

/**
 * @Title: ZuulServiceController
 * @ProjectName springcloud_zuul_api_gateway
 * @Description: 对外提供接口服务
 * @Author WeiShiHuai
 * @Date 2018/9/14 14:16
 */
@RestController
public class ZuulServiceController {

    private static Logger logger = LoggerFactory.getLogger(ZuulServiceController.class);

    @Value("${server.port}")
    private String port;

    @RequestMapping("/getInfo/{name}")
    public String getInfo(@PathVariable("name") String name) {
        String info = "hello " + name + " ,i am from zuul service,port = " + port;
        logger.info(info);
        return info;
    }

}

四、新建zuul-api-gateway工程

首先引入zuul的依赖,具体pom.xml如下:

<?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">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.springcloud.wsh</groupId>
	<artifactId>springcloud_api_gateway</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>springcloud_api_gateway</name>
	<description>Spring Cloud Zuul服务网关</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Camden.SR6</spring-cloud.version>
	</properties>

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

五、启动类加上@EnableZuulProxy注解

@EnableZuulProxy注解主要是用于开启zuul路由转发以及路由过滤功能。

扫描二维码关注公众号,回复: 3301472 查看本文章

/**
 * @Description: api网关启动类
 * @Author: WeiShiHuai
 * @Date: 2018/9/14 14:22
 * Zuul的主要功能是路由转发和过滤器
 * 通常情况下,可以在网关服务进行权限控制等,将具体业务逻辑与权限控制分开,有利于解耦; Zuul也可以实现对服务的负载均衡
 * Zuul主要作用是请求转发,和过滤,请求转发是做了负载均衡。Zuul也需要做一次集群,因为 Zuul是网关,可能需要做很复杂的逻辑,比如查数据库,还有静态资源,在最外一层需要再一个zuul或者nginx去路由。
 * 通常情况下由nginx做第一层比较好,第二层的一些例如权限等校验交由zuul处理
 * 路由配置方式: a、path-url方式  b、path-serviceId方式,推荐使用path-serviceId方式,方便后期网关服务维护
 */
@SpringBootApplication
@EnableDiscoveryClient
//@EnableZuulProxy注解用于开启Zuul路由功能(反向代理)
@EnableZuulProxy
public class SpringcloudApiGatewayApplication {

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

六、application.yml配置路由匹配规则

zuul的路由规则主要有两种配置方式,path-url以及path-serviceId两种方式,但是path-url不利于维护,要具体制定某个服务的url,其实在微服务中,每个服务都注册到eureka服务注册中心上,这个时候其实可以通过serviceId直接找到具体指向的服务。推荐使用path-serviceId方式配置。

server:
  port: 3333
spring:
  application:
    name: api-gateway
eureka:
  client:
    service-url:
      defaultZone: http://localhost:1111/eureka/
#服务路由:推荐使用serviceId方式配置路由,方便后期维护
zuul:
  routes:
    zuul-service-url:
    #通过path/url方式配置路由: 以/zuul-service-url开头的请求都转发到zuul-service
      path: /zuul-service-url/**
      url: http://localhost:2222/
    #通过path/serviceId方式配置路由: 以/zuul-service-serviceId开头的请求都转发到zuul-service
    zuul-service-serviceId:
      path: /zuul-service-serviceId/**
      #对应注册到Eureka中的serviceId
      serviceId:  zuul-service
#通过配置以上路由,前台我们访问地址:
# http://localhost:3333/zuul-service-url/getInfo/weixiaohuai
# >>>>>路由到(实际请求的地址)>>>>>
# http://localhost:2222/getInfo/weixiaohuai

# http://localhost:3333/zuul-service-serviceId/getInfo/weixiaohuai
# >>>>>路由到(实际请求的地址)>>>>>
# http://localhost:2222/getInfo/weixiaohuai

#通过配置以上路由,前台我们访问地址:

a. path-url方式:

# http://localhost:3333/zuul-service-url/getInfo/weixiaohuai

# >>>>>路由到(实际请求的地址)>>>>>

# http://localhost:2222/getInfo/weixiaohuai

b. path-serviceId方式:

# http://localhost:3333/zuul-service-serviceId/getInfo/weixiaohuai

# >>>>>路由到(实际请求的地址)>>>>>

# http://localhost:2222/getInfo/weixiaohuai

七、启动项目

分别启动eureka-servier、zuul-service、zuul-api-gateway

我们通过浏览器访问

http://localhost:3333/zuul-service-url/getInfo/weixiaohuai

可以看到,网关已经帮我们路由到zuul-service服务,成功返回数据,这是path-url方式。

接着我们访问 

http://localhost:3333/zuul-service-serviceId/getInfo/weixiaohuai

可以看到,网关同样帮我们路由到了zuul-service服务,成功返回数据,这是path-serviceId方式。

以上就完成了zuul路由跳转的功能,接下来我们谈谈zuul路由过滤的功能,在实际项目中,通常需要进行权限校验的功能,这里模拟验证url中是否传username以及password参数并且校验是否正确。zuul路由过滤是通过过滤器实现的,通常继承ZuulFilter类,并且实现其中的方法即可。

ZuulFilter中方法的介绍:

a. filterType:指定过滤器的类型,可选择的值有:

pre:在路由之前执行

routing:在路由的时候执行  

post:在路由完成之后执行  

error:路由发生错误时执行

b. filterOrder:指定过滤器的执行顺序,数字越大,优先级越低

c. shouldFilter:是否需要过滤,return true表示需要执行该过滤器

d. run:过滤器的具体实现逻辑

下面我们创建一个校验用户名的AccessUsernameZuulFilter:

/**
 * @Title: AccessUsernameZuulFilter
 * @ProjectName springcloud_zuul_api_gateway
 * @Description: 校验用户名的Zuul过滤器
 * @Author WeiShiHuai
 * @Date 2018/9/14 15:02
 */

@Component
public class AccessUsernameZuulFilter extends ZuulFilter {

    private static Logger logger = LoggerFactory.getLogger(AccessUsernameZuulFilter.class);
    private static final String USER_NAME = "weixiaohuai";

    /**
     * 过滤器类型
     * pre:在路由之前执行  routing:在路由的时候执行  post:在路由完成之后执行  error:路由发生错误时执行
     *
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 过滤器的执行顺序
     * 数字越大,优先级越低
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 是否需要过滤,true表示过滤,false表示不过滤
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤器具体校验逻辑,如权限校验等等
     * 实际项目中可以进行数据库查询权限数据库等等
     *
     * @return
     */
    @Override
    public Object run() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        //获取请求url中的username
        String username = request.getParameter("username");

        //说明用户名不为空且正确
        if (null != username && USER_NAME.equals(username)) {
            //表示让Zuul进行路由跳转
            requestContext.setSendZuulResponse(true);
            //设置响应码
            requestContext.setResponseStatusCode(200);
            logger.info("welcome,the username is valid!!");
            //记录这个过滤器的状态
            requestContext.set("isSuccess", true);
            return null;
        } else { //用户名不正确
            //表示让Zuul过滤这个请求,不进行路由跳转
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(401);
            //记录这个过滤器的状态
            requestContext.set("isSuccess", false);
            logger.info("sorry,the username is not valid, please try again!!");
            requestContext.setResponseBody("sorry,the username is not valid, please try again!!");
            return null;
        }
    }
}

注意:

//表示让Zuul进行路由跳转
 requestContext.setSendZuulResponse(true);

//表示让Zuul过滤这个请求,不进行路由跳转
requestContext.setSendZuulResponse(false);
            
//设置响应码
requestContext.setResponseStatusCode(200);

八、创建校验密码的过滤器AccessPasswordZuulFilter 

跟校验用户名的类似,注意在shouldFilter()方法中拿到全局保存的isSuccess变量,由于标识该过滤器是否需要被执行,如果用户名都没校验通过,显然不需要执行密码的校验。

/**
 * @Title: AccessPasswordZuulFilter
 * @ProjectName springcloud_zuul_api_gateway
 * @Description: 校验密码的Zuul过滤器
 * @Author WeiShiHuai
 * @Date 2018/9/14 15:37
 */

@Component
public class AccessPasswordZuulFilter extends ZuulFilter {

    private static Logger logger = LoggerFactory.getLogger(AccessPasswordZuulFilter.class);
    private static final String PASSWORD = "123456";

    /**
     * 过滤器类型
     * pre:在路由之前执行  routing:在路由的时候执行  post:在路由完成之后执行  error:路由发生错误时执行
     *
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 过滤器的执行顺序
     * 数字越大,优先级越低
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * 是否需要过滤,true表示过滤,false表示不过滤
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        //获取上一个过滤器保存的过滤器状态,如果返回false,则说明上一个过滤器没有成功,则无需执行后面的过滤器,直接返回结果
        return (boolean) requestContext.get("isSuccess");
    }

    /**
     * 过滤器具体校验逻辑,如权限校验等等
     * 实际项目中可以进行数据库查询权限数据库等等
     *
     * @return
     */
    @Override
    public Object run() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        //获取请求url中的password
        String password = request.getParameter("password");

        //说明密码不为空且正确
        if (null != password && PASSWORD.equals(password)) {
            //表示让Zuul进行路由跳转
            requestContext.setSendZuulResponse(true);
            //设置响应码
            requestContext.setResponseStatusCode(200);
            //记录这个过滤器的状态
            requestContext.set("isSuccess", true);
            logger.info("welcome,the password is valid!!");
            return null;
        } else { //用户名不正确
            //表示让Zuul过滤这个请求,不进行路由跳转
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(401);
            //记录这个过滤器的状态
            requestContext.set("isSuccess", false);
            logger.info("sorry,the password is not valid, please try again!!");
            requestContext.setResponseBody("sorry,the password is not valid, please try again!!");
            return null;
        }
    }
}

九、启动类注入过滤器Bean

@SpringBootApplication
@EnableDiscoveryClient
//@EnableZuulProxy注解用于开启Zuul路由功能(反向代理)
@EnableZuulProxy
public class SpringcloudApiGatewayApplication {

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

    @Bean
    AccessUsernameZuulFilter accessUsernameZuulFilter() {
        return new AccessUsernameZuulFilter();
    }

    @Bean
    AccessPasswordZuulFilter accessPasswordZuulFilter() {
        return new AccessPasswordZuulFilter();
    }
}

十、启动项目

分别启动eureka-server、zuul-service、zuul-api-gateway

我们浏览器访问http://localhost:3333/zuul-service-url/getInfo/weixiaohuai或者http://localhost:3333/zuul-service-serviceId/getInfo/weixiaohuai,如下图

可以看到,如果url中没有传入username或者password参数的话,返回我们自定义的错误信息

a. 如果我们访问http://localhost:3333/zuul-service-url/getInfo/weixiaohuai?username=weixiaohuai,这时候校验用户名的过滤器已经通过,被校验密码的过滤器拦截了,提示密码不合法

b. 如果我们访问http://localhost:3333/zuul-service-url/getInfo/weixiaohuai?password=123456,同样确实username参数,如下图返回用户名不合法的错误

c. 如果我们访问http://localhost:3333/zuul-service-url/getInfo/weixiaohuai?username=weixiaohuai&password=123456,这时候路由就成功跳转到zuul-service中

至此,zuul路由过滤功能已经实现完成。在实际项目中,需要结合权限验证框架进行实现,这里只是演示zuul过滤器的一些用法。本文是作者在学习过程中的一些总结,文章仅供参考,希望大家一起学习,共同进步。

猜你喜欢

转载自blog.csdn.net/Weixiaohuai/article/details/82717679