Spring Cloud 微服务入门教程(八):Spring Cloud Zuul 服务网关动态路由和Cookie头信息传递和跨域

上一节我们讲了微服务只间通过消息队列实现异步通讯,本节将介绍微服务中的网关Spring Cloud Zuul,可以实现统一管理众多的接口、实现负载均衡等功能。

新建一个网关模块

新建一个Maven的网关模块,依赖spring-cloud-starter-netflix-zuul,修改POM文件:

<?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">
	    <parent>
	        <artifactId>cloud</artifactId>
	        <groupId>net.renfei</groupId>
	        <version>1.0.0</version>
	    </parent>
	    <modelVersion>4.0.0</modelVersion>
	    <groupId>net.renfei</groupId>
	    <artifactId>gateway</artifactId>
	    <version>1.0.0</version>
	    
	    <dependencies>
	        <dependency>
	            <groupId>org.springframework.cloud</groupId>
	            <artifactId>spring-cloud-starter-config</artifactId>
	        </dependency>
	        <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>
	</project>

添加一个程序启动类,作为启动入口,并增加@EnableZuulProxy注解:

package net.renfei.gateway;

	import org.springframework.boot.SpringApplication;
	import org.springframework.boot.autoconfigure.SpringBootApplication;
	import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
	import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

	@EnableZuulProxy
	@EnableEurekaClient
	@SpringBootApplication
	public class GatewayApplication {
	    public static void main(String[] args) {
	        SpringApplication.run(GatewayApplication.class, args);
	    }
	}

以及配置文件bootstrap.yml,可以实现动态获取配置,为了方便演示,我将配置直接写入bootstrap.yml,现实中是写到Git远程配置中心的:

spring:
	  application:
	    name: Gateway
	  cloud:
	    config:
	      discovery:
	        service-id: config
	        enabled: true
	      profile: dev
	eureka:
	  client:
	    service-url:
	      defaultZone: http://localhost:8761/eureka/
	server:
	  port: 8080
	zuul:
	  routes:
	    democlientroute:
	      path: /DC/**
	      service-id: DemoClient
	      sensitiveHeaders:
	    demoserviceroute:
	      path: /DS/**
	      service-id: DemoService
	  ignored-patterns:
	    - /admin
	    - /myadmin

zuul.routes后面的名称是可以自己定义的,path是指请求的路径匹配,service-id是对应路由到哪个服务的服务名称,sensitiveHeaders的作用是为了让Zuul将请求的头信息包括Cookie转发给后端服务,这个配置默认是在org.springframework.cloud.netflix.zuul.filters.ZuulProperties#sensitiveHeaders,我们可以看到默认的是private Set<String> sensitiveHeaders = new LinkedHashSet(Arrays.asList("Cookie", "Set-Cookie", "Authorization"));,如果你的头信息不是这三个,可以自己在配置文件中自定义。ignored-patterns的意思是忽略这些地址不做转发。

Zuul的过滤器

网关Zuul的核心其实就是一堆过滤器,所以在网关这里会写很多过滤器,由于有点多,我直接上一个表格:

类型顺序过滤器功能
pre-3ServletDetectionFilter标记处理 Servlet 的类型
pre-2Servlet30WrapperFilter包装 HttpServletRequest 请求
pre-1FormBodyWrapperFilter包装请求体
route1DebugFilter标记调试标志
route5PreDecorationFilter处理请求上下文供后续使用
route10RibbonRoutingFilterserviceId
route100SimpleHostRoutingFilterurl 请求转发
route500SendForwardFilterforward 请求转发
post0SendErrorFilter处理有错误的请求响应
post1000SendResponseFilter处理正常的请求响应

我们去继承com.netflix.zuul.ZuulFilter,看看都有哪些设置:

package net.renfei.gateway.filter;

	import com.netflix.zuul.ZuulFilter;
	import com.netflix.zuul.exception.ZuulException;
	import org.springframework.stereotype.Component;
	import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
	import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

	/**
	 * 授权过滤器,可以判断用户是否有权访问
	 *
	 * @author RenFei
	 */
	@Component
	public class AuthorizationFilter extends ZuulFilter {
	    @Override
	    public String filterType() {
	        return PRE_TYPE;
	    }
	    @Override
	    public int filterOrder() {
	        return PRE_DECORATION_FILTER_ORDER - 1;
	    }
	    @Override
	    public boolean shouldFilter() {
	        return true;
	    }
	    @Override
	    public Object run() throws ZuulException {
	        //在此处执行判断逻辑
	        return null;
	    }
	}

是不是很简单,先设置一个类型,然后设置执行的顺序,run()里面执行我们想要做的逻辑。

Zuul路由的动态刷新

网关会管理众多的服务接口,如果每次配置都重启非常影响生产,之前的章节我们已经搭建好SpringCloudBUS消息总线,实现了动态配置更新,我们就利用BUG总线消息刷新配置,POM中依赖spring-cloud-starter-config,然后我们新建一个配置类,加上@RefreshScope注解就可以实现动态刷新了。由于我是演示,就不去Git上面折腾配置文件了,给出参考代码:

package net.renfei.gateway.config;

	import org.springframework.boot.context.properties.ConfigurationProperties;
	import org.springframework.cloud.context.config.annotation.RefreshScope;
	import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
	import org.springframework.stereotype.Component;

	/**
	 * Zuul的配置实现动态从配置中心拉取
	 *
	 * @author RenFei
	 */
	@Component
	public class ZuulConfig {
	    @RefreshScope
	    @ConfigurationProperties("zuul")
	    public ZuulProperties zuulProperties() {
	        return new ZuulProperties();
	    }
	}

Zuul网关的跨域设置

我们新建一个配置类CorsConfig,注册一个Bean:CorsFilter然后进行设置,代码如下:

package net.renfei.gateway.config;

	import org.springframework.context.annotation.Bean;
	import org.springframework.stereotype.Component;
	import org.springframework.web.cors.CorsConfiguration;
	import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
	import org.springframework.web.filter.CorsFilter;
	import java.util.Arrays;

	/**
	 * 跨域配置
	 *
	 * @author RenFei
	 */
	@Component
	public class CorsConfig {
	    @Bean
	    public CorsFilter corsFilter() {
	        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
	        CorsConfiguration config = new CorsConfiguration();
	        //是否支持Cookie跨域
	        config.setAllowCredentials(true);
	        //原始域列表
	        config.setAllowedOrigins(Arrays.asList("*"));
	        //允许的头
	        config.setAllowedHeaders(Arrays.asList("*"));
	        //允许的方法,GET、POST....
	        config.setAllowedMethods(Arrays.asList("*"));
	        //允许跨域时间,时间段内不再检查跨域
	        config.setMaxAge(300L);
	        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", config);
	        return new CorsFilter(urlBasedCorsConfigurationSource);
	    }
	}

代码中已经有了注释,就不再啰嗦了,一个基本的网关就搭建好了。

分享此页面

Comments