Zuul是什么
Zuul 即微服务网关,主要有两个功能
- 请求路由,也可以说请求转发
- 请求过滤
通过路由(将外部请求转发到具体的微服务实例上),完成微服务统一入口。
过滤器则负责对请求进行处理,比如请求校验等
Zuul的使用
Zuul需要和Eureka配合使用,Zuul将自身注册到Eureka服务治理中,在能够获取其他微服务的信息时,还能够保障自身的高可用。
- 新建Zuul模块
- pom.xml引入依赖
<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>
- 修改application.yml
server:
port: 9000
spring:
application:
name: zuul
eureka:
client:
serviceUrl:
# 随机找到一台注册,而不是三台一起注册,注册成功就不注册了
defaultZone: http://eureka001:8761/eureka/
instance:
# statusPageUrlPath: ${server.servletPath}/info
# healthCheckUrlPath: ${server.servletPath}/health
instance-id: zuul #此实例注册到eureka服务端的唯一的实例ID
prefer-ip-address: true #是否显示IP地址
leaseRenewalIntervalInSeconds: 10 #eureka客户需要多长时间发送心跳给eureka服务器,表明它仍然活着,默认为30 秒 (与下面配置的单位都是秒)
leaseExpirationDurationInSeconds: 30 #Eureka服务器在接收到实例的最后一次发出的心跳后,需要等待多久才可以将此实例删除,默认为90秒
# 配置路由
zuul:
prefix: /api
ignored-services: "*"
strip-prefix: false # 默认为true,如需要保留/client则需要修改为false, 也可以单独给某个服务配置
routes:
myconsumer:
serviceId: eureka-consumer
path: /client/**
myproduct:
serviceId: eureka-product
path: /product/** # /* 只有一层, /product/select能到,/product/xxx/select就不行
- 启动类加上
@EnableZuulProxy
或者@EnableZuulServer
@SpringBootApplication
@EnableZuulProxy
public class AppZuul {
public static void main(String[] args) {
SpringApplication.run(AppZuul.class);
}
}
@EnableZuulServer和@EnableZuulProxy的区别
官网解释:Spring Cloud Netflix installs a number of filters, depending on which annotation was used to enable Zuul.
@EnableZuulProxy
is a superset of@EnableZuulServer
. In other words,@EnableZuulProxy
contains all the filters installed by@EnableZuulServer
. The additional filters in the “proxy” enable routing functionality. If you want a “blank” Zuul, you should use@EnableZuulServer
.
简单来说Spring Cloud Netflix安装了许多过滤器,而@EnableZuulProxy
是 @EnableZuulServer
的超集,@EnableZuulProxy
包含 @EnableZuulServer
的所有过滤器。如何抉择,需要看你是否需要启用附加的过滤器。
具体可以参考官网:https://docs.spring.io/spring-cloud-netflix/docs/2.2.8.RELEASE/reference/html/#router-and-filter-zuul 9.18.4 - 9.18.5
或者博客:
路由
路由配置:
zuul:
prefix: /api
ignored-services: "*" #禁止微服务名调用,避免多重地址
strip-prefix: false # 默认为true,如需要保留/client则需要修改为false, 也可以单独给某个服务配置
routes:
myconsumer:
serviceId: eureka-consumer
path: /client/**
myproduct:
serviceId: eureka-product
path: /product/** # /* 只有一层, /product/select能到,/product/xxx/select就不行
过滤器
类型 | 触发时机 | 使用场景 |
PRE | 在请求被路由之前调用 | 身份验证、记录调试信息等 |
ROUTING | 在请求被路由之后 | 构建发送给微服务的请求 |
POST | 微服务执行之后 | 收集统计信息和指标 |
ERROR | 发生错误时 | 错误日志 |
触发时机图:
过滤器如何使用:
@Component
public class LogFilter extends ZuulFilter {
@Override
public String filterType() {
//过滤器的类型
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
// 过滤器优先级 数字越小优先级越高
return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
}
@Override
public boolean shouldFilter() {
//shouldFilter:返回一个 boolean值来判断该过滤器是否要执行, true表示执行, false表示不执行。
return true ;
}
@Override
public Object run() throws ZuulException {
//run:过滤器的具体逻辑。
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String remoteAddr = request.getRemoteAddr();
System.out.println("访问者IP:"+remoteAddr+"访问地址:"+request.getRequestURI());
return null;
}
}
Zuul 容错和回退
Zuul默认已经整合了Hystrix和Ribbon
如何进行降级
// 官方Demo
class MyFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
// 声明哪个微服务进行降级回退 return *则对所有服务进行降级
return "customers";
}
@Override
public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT);
} else {
return response(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private ClientHttpResponse response(final HttpStatus status) {
//这里返回一个ClientHttpResponse对象 并实现其中的方法,关于回退逻辑的详细,便在下面的方法中
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
//返回一个HttpStatus对象 这个对象是个枚举对象, 里面包含了一个status code 和reasonPhrase信息
return status;
}
@Override
public int getRawStatusCode() throws IOException {
//返回status的code 比如 404,500等
return status.value();
}
@Override
public String getStatusText() throws IOException {
//返回一个HttpStatus对象的reasonPhrase信息
return status.getReasonPhrase();
}
@Override
public void close() {
//降级信息响应之后调用的方法
}
@Override
public InputStream getBody() throws IOException {
//把降级信息响应回前端
return new ByteArrayInputStream("fallback".getBytes());
}
@Override
public HttpHeaders getHeaders() {
//需要对响应报头设置的话可以在此设置
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
Zuul 高可用的实现方式
Zuul怎么来实现高可用呢,这里需要分不同的情况
- 同一注册中心,服务间调用,这里之间可以使用Ribbon即可(Zuul就相当于其中的注册中心的服务)。
- 不同注册中心,或者说不同微服务体系,甚至,单体架构,如何进行调用?
这种情况同样有很多选择,需要根据内部情况来抉择
- Nginx代理,一般来说公司都会有Nginx集群,这里可以直接使用Nginx来代理即可。
- HA代理,Haproxy同样可以实现。但HA同样需要集群搭建。
- vip,通过vip漂移应该也可以实现。
这里只是提出方案,就不具体写出配置了。