整体概念
Zuul是Netflix开源的微服务网关,核心是一系列过滤器。这些过滤器可以完成以下功能。
-
是所有微服务入口,进行分发。
-
身份认证与安全。识别合法的请求,拦截不合法的请求。
-
监控。在入口处监控,更全面。
-
动态路由。动态将请求分发到不同的后端集群。
-
压力测试。可以逐渐增加对后端服务的流量,进行测试。
-
负载均衡。也是用ribbon。
-
限流。
准备
新建项目 eureka-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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>eureka-zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka-zuul</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR3</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-netflix-eureka-client</artifactId>
</dependency>
<!--zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!--Zuul限流的相关依赖-->
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>1.5.0.RELEASE</version>
</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>
配置文件
#是否将自己注册到其他Eureka Server,默认为true 需要
eureka.client.register-with-eureka=true
#是否从eureka server获取注册信息, 需要
eureka.client.fetch-registry=true
#设置服务注册中心的URL,用于client和server端交流
eureka.client.serviceUrl.defaultZone=http://eureka-service1.com:7901/eureka/
spring.application.name=eureka-zuul
#web端口,服务是由这个端口处理rest请求的
server.port=7903
相关配置
# 负载均衡策略
order-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RoundRobinRule
# 暴露端点 (访问 http://localhost:7903/actuator 可看到一些需要的信息)
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
management.endpoint.health.enabled=true
management.endpoint.routes.enabled=true
# 通过服务名配置
zuul.routes.order-service=/zuul-order/**
# 自定义命名配置(将order.path 的内容重定向到 order.url)
zuul.routes.order.path=/xx/**
zuul.routes.order.url=http://baidu.com
# 忽略微服务
# zuul.ignored-services=order-service
# 正则忽略
# zuul.ignored-patterns=
# 去掉前缀,访问时带上前缀,实际请求会将前缀去掉
#zuul.prefix=/api
#zuul.strip-prefix=true
# 敏感信息不传播到下游
# 表示忽略下面的值向微服务传播,以下配置为空表示:所有请求头都透传到后面微服务
zuul.sensitive-headers=token
# 开启限流
zuul.ratelimit.enabled=true
# 针对服务进行限流 (下面配置表示:60s内允许3个访问且总请求时间小于30s)
# 时间窗口内对应的请求数量限制
zuul.ratelimit.policies.shop-service.limit=300
# 刷新时间窗口的时间
zuul.ratelimit.policies.shop-service.refresh-interval=600
# 时间窗口内总请求时间小于30s
zuul.ratelimit.policies.shop-service.quota=1
# 全局限流
#zuul.ratelimit.default-policy.limit=3
#zuul.ratelimit.default-policy.refresh-interval=60
#zuul.ratelimit.default-policy.quota=30
# 限流存储方式
# in_memory 使用ConcurrentHashMap作为数据存储(在内存中),作为默认值
# redis,consul,jpa
zuul.ratelimit.repository=in_memory
# 限流类型 origin - ip url user
zuul.ratelimit.default-policy.type=origin
接口容错
为服务提供容错回退
@Component
public class Fallback implements FallbackProvider {
/**
* 表明为哪个微服务提供回退
* 服务Id ,若需要所有服务调用都支持回退,返回null 或者 * 即可
*/
@Override
public String getRoute() {
return "*";
//return "shop-service";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT);
} else {
return response(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private ClientHttpResponse response(final HttpStatus status) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
//return status;
return HttpStatus.BAD_REQUEST;
}
@Override
public int getRawStatusCode() throws IOException {
//return status.value();
return HttpStatus.BAD_REQUEST.value();
}
@Override
public String getStatusText() throws IOException {
//return status.getReasonPhrase();
//return HttpStatus.BAD_REQUEST.name();
return HttpStatus.BAD_REQUEST.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
String msg = "{\"msg\":\"网关信息:服务故障\"}";
return new ByteArrayInputStream(msg.getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
过滤器
PRE: 在请求被路由之前调用,可利用这种过滤器实现身份验证。选择微服务,记录日志。
ROUTING:在将请求路由到微服务调用,用于构建发送给微服务的请求,并用http clinet(或者ribbon)请求微服务。
POST:在调用微服务执行后。可用于添加header,记录日志,将响应发给客户端。
ERROR:在其他阶段发生错误是,走此过滤器。
自定义过滤器
extends ZuulFilter
注意实现下面4个方法
filterType:pre,routing,post,error
filterOrder:执行顺序,在谁前,在谁后,可以+1,-1
shouldFilter:此过滤器是否执行,true false,可以写过滤器是否执行的判断条件。
run:具体执行逻辑。
@Component
public class LoginFilter extends ZuulFilter {
public LoginFilter() {
super();
}
@Override
public boolean isStaticFilter() {
return super.isStaticFilter();
}
@Override
public String disablePropertyName() {
return super.disablePropertyName();
}
@Override
public boolean isFilterDisabled() {
return super.isFilterDisabled();
}
@Override
public ZuulFilterResult runFilter() {
return super.runFilter();
}
@Override
public int compareTo(ZuulFilter filter) {
return super.compareTo(filter);
}
// 指定过滤器类型
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
// 指定优先级,越小级别越高
@Override
public int filterOrder() {
return 0;
}
// 是否启动过滤
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
System.out.println("------ 进入了自定义filter, 可以做一些事情了...");
return null;
}
}
限流
令牌桶可以看作是一个存放一定数量令牌的容器。系统按设定的速度向桶中放置令牌。当桶中令牌满时,多出的令牌溢出,桶中令牌不再增加。在使用令牌桶对流量规格进行评估时,是以令牌桶中的令牌数量是否足够满足报文的转发为依据的。每个需要被转发的报文,都要从令牌桶中领取一定数量的令牌(具体数量视报文大小而定),才可以被正常转发。如果桶中存在足够的令牌可以用来转发报文,称流量遵守或符合约定值,否则称为不符合或超标。