一:Zuul介绍
Zuul是Netflix开源的微服务网关,他可以和Eureka,Ribbon,Hystrix等组件配合使用。Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:
# 身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求
# 审查与监控:
# 动态路由:动态将请求路由到不同后端集群
# 压力测试:逐渐增加指向集群的流量,以了解性能
# 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
# 静态响应处理:边缘位置进行响应,避免转发到内部集群
# 多区域弹性:跨域AWS Region进行请求路由,旨在实现ELB(ElasticLoad Balancing)使用多样化
二:创建Zuul模块
我们基于上一篇的工程,在RediaMallCloud中新增一个模块Mall_ZuulCenter,新增的步骤请参考《Sping Boot多模块项目的创建和配置》。其中要注意的是我们在建立新模块其中一个勾选zuul网关依赖。
新建完成之后,我们得修改父工程的pom和子工程的pom文件
父工程里面的module变成了四个
<!--在父pom文件当中添加模块的名称,子模块pom中<name>标签当中的值-->
<modules>
<module>Mall_EurekaCenter</module>
<module>Mall_ManagerService</module>
<module>Mall_WechatService</module>
<module>Mall_ZuulCenter</module>
</modules>
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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.smartisan</groupId>
<artifactId>mallzuulcenter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Mall_ZuulCenter</name>
<description>Demo project for Spring Boot</description>
<!-- 把原有的parent的信息注释掉,继承我们的父工程-->
<parent>
<!--<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>--> <!-- lookup parent from repository -->
<groupId>com.smartisan</groupId>
<artifactId>RediaMallCloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- JSON Configuration -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.6</version>
</dependency>
<!--标签友好化-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>、
配置:application.yml文件
eureka:
client:
serviceUrl:
defaultZone: http://admin:password@localhost:8761/eureka/
spring:
application:
name: zuul_center
cloud:
loadbalancer:
retry:
enabled: true
thymeleaf:
mode: LEGACYHTML5
cache: false
http:
encoding:
charset: UTF-8
force: true
enabled: true
server:
port: 8050
tomcat:
uri-encoding: UTF-8
zuul:
# max:
# host:
# connections: 500
host:
socket-timeout-millis: 180000
connect-timeout-millis: 180000
routes:
mall-wechatservice:
path: /wechat/**
sensitiveHeaders:
mall-managerservice:
path: /manager/**
sensitiveHeaders:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 180000
ribbon:
ConnectTimeout: 60000
ReadTimeout: 60000
MaxAutoRetriesNextServer: 0
MaxAutoRetries: 1
在zuul主类上加上注解:@EnableZuulProxy,开启zuul的功能。@EnableEurekaClient注解向注册中心注册。
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;
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class MallZuulCenterApplication {
public static void main(String[] args) {
SpringApplication.run(MallZuulCenterApplication.class, args);
}
}
我们分别在mall-wechatservice和mall-managerservice编写两个接口对外提供服务。
mall-managerservice:
@Controller
public class ManagerController {
private final Logger logger= LoggerFactory.getLogger(this.getClass());
@Value("${server.port}")
String port;
//get请求
@ResponseBody
@RequestMapping("/helloA")
public String sayHelloA(){
Map<String,Object> map = new HashMap<String,Object>();
map.put("name","king james");
map.put("age",33);
map.put("team","Cleveland Cavaliers");
map.put("port",port);
logger.info(JSON.toJSONString(map));
return JSON.toJSONString(map);
}
}
@Controller
public class WechatController {
private final Logger logger= LoggerFactory.getLogger(this.getClass());
@Value("${server.port}")
String port;
//get请求
@ResponseBody
@RequestMapping("/helloA")
public String sayHelloA(){
Map<String,Object> map = new HashMap<String,Object>();
map.put("name","kobe");
map.put("age",36);
map.put("team","Los Lakers");
map.put("port",port);
logger.info(JSON.toJSONString(map));
return JSON.toJSONString(map);
}
}
现在整个工程目录如下:
启动顺序:先启动Mall_EurekaCenter,再启动Mall_ZuulCenter,然后再启动Mall_ManagerService/Mall_WechatService
分别请求http://localhost:8050/manager/helloA和http://localhost:8050/wechat/helloA,页面显示如下:
说明Zuul服务网关已经能够正确的路由到我们的微服务上面去了。
三:zuul服务过滤
编写服务过滤器
@Component
public class MyZuulFilter extends ZuulFilter {
private final Logger logger= LoggerFactory.getLogger(this.getClass());
/**
* 前置过滤器。
* 但是在 zuul 中定义了四种不同生命周期的过滤器类型:
* 1、pre:可以在请求被路由之前调用;
* 2、route:在路由请求时候被调用;
* 3、post:在route和error过滤器之后被调用;
* 4、error:处理请求时发生错误时被调用;
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤的优先级,数字越大,优先级越低。
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 是否执行该过滤器。
* true:说明需要过滤;
* false:说明不要过滤;
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的具体逻辑。
*/
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
logger.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter("token");
if(accessToken == null) {
logger.warn("token is empty");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
}catch (Exception e){}
return null;
}
logger.info("ok");
return null;
}
}
再次访问http://localhost:8050/manager/helloA,页面显示如下:已经能够正确的过滤信息了。