一、zuul是什么
-
Zuul是spring cloud中的微服务网关。
- 网关: 是一个网络整体系统中的前置门户入口。请求首先通过网关,进行路径的路由,定位到具体的服务节点上。
-
Zuul是一个微服务网关,首先是一个微服务。也是会在Eureka、nacos等注册中心中进行服务的注册和发现。也是一个网关,请求应该通过Zuul来进行路由。
-
Zuul网关不是必要的。是推荐使用的。
-
使用Zuul,一般在微服务数量较多(多于10个)的时候推荐使用,对服务的管理有严格要求的时候推荐使用,当微服务权限要求严格的时候推荐使用。
社区不再维护了,推荐使用Spring cloud gateway
1、有nginx了为什么还需要zuul
zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。
Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。
Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。
Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMIP服务器。
- 相同点:
- 可以实现负载均衡(zuul使用的是ribbon实现负载均衡)
- 可以实现反向代理(隐藏真实ip地址)
- 可以过滤请求,实现网关的效果
- 不同点:
- Nginx–c语言开发,Zuul–java语言开发(需要部署到tomcat等实现功能)
- 负载均衡实现:
- Zuul:采用ribbon+eureka(服务发现)实现本地负载均衡
- Nginx:采用服务器实现负载均衡
- Nginx适合于服务器端负载均衡,Zuul适合微服务中实现网关
- Nginx相比zuul功能会更加强大,因为Nginx整合一些脚本语言(Nginx+lua)
二、zuul特点
-
Zuul提供了动态路由、监控、弹性负载和安全功能
-
Zuul包含了对请求的路由和过滤两个最主要的功能:
- 路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础
- 过滤功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础
三、项目实现及路由配置
-
jar包
<!--spring-cloud-starter-netflix-eureka-client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- spring-cloud-starter-netflix-zuul --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
-
启动类
// 开启zuul @EnableZuulProxy @SpringBootApplication public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } }
-
yml路由配置
zuul: # 忽略指定微服务,*禁止所有服务通过服务名访问 ignoredServices: '*' host: # 超时时间 connect-timeout-millis: 60000 socket-timeout-millis: 60000 # 路由 routes: # 路由别名 xx-service: # 路由规则 /**:代表多个层级/api/user/detail,/* 是代表一层(/api/user/detail,不会被路由找到) path: /api/** # 注册中心上注册的服务名 serviceId: xx-web # 路由别名 xx-web: # 路由规则 path: /service/** # 注册中心上注册的服务名 serviceId: xx-service # 路由别名 xx-shop: # 路由规则 path: /shop/** # 注册中心上注册的服务名 serviceId: xx-shop
-
path:路由规则
通配符 含义 举例 ? 匹配任意单个字符 /api/a, /api/b * 匹配任意数量的字符 /api/asdd, /api/bdsfa ** 匹配任意数量的字符 /api/user/detail,/api/user/save
例如:
-
Zuul服务的端口号是80
-
访问“xx-web”,端口号8080,项目中“user/detail”接口
-
未使用zuul之前访问方式:localhost:8080/user/detail
-
使用后:localhost/api/user/detail
- 路由配置解析到 符合的路由规则
- 跳转到对应的服务(注册中心注册的服务)
四、过滤器
过滤器 (filter) 是zuul的核心组件,zuul大部分功能都是通过过滤器来实现的。 zuul中定义了4种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。
生命周期 | 说明 |
---|---|
PRE | 这种过滤器在请求被路由之前调用。可利用这种过滤器实现身份验证、在 集群中选择请求的微服务、记录调试信息等。 |
ROUTING | 这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用 Apache HttpClient或 Netfilx Ribbon请求微服务 |
POST | 这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准 的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。 |
ERROR | 在其他阶段发生错误时执行该过滤器。 |
1、pre 过滤器
@Component
public class AccessFilter extends ZuulFilter {
private static final MyLog _log = MyLog.getLog(ZuulFilter.class);
/**
* 路由类型
* @return
*/
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
/**
* 指定过滤器的执行顺序,不同的过滤器允许返回相同的数字
* @return
*/
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER;
}
/**
* 是否要执行, true表示执行, false表示不执行
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的具体逻辑
*/
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
// 输出请求方法,请求url
_log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
return null;
}
}
2、 禁用某个过滤器
zuul:
# 过滤器类
SendErrorFilter:
route:
disable: true
3、error 过滤器
@Component
public class ErrorFilter extends ZuulFilter {
private static final MyLog _log = MyLog.getLog(ZuulFilter.class);
/**
* 路由类型
* @return
*/
@Override
public String filterType() {
return FilterConstants.ERROR_TYPE;
}
/**
* 指定过滤器的执行顺序,不同的过滤器允许返回相同的数字
* @return
*/
@Override
public int filterOrder() {
return FilterConstants.SEND_ERROR_FILTER_ORDER;
}
/**
* 是否要执行, true表示执行, false表示不执行
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的具体逻辑
*/
@Override
public Object run() throws ZuulException {
try {
RequestContext context = RequestContext.getCurrentContext();
ZuulException exception = (ZuulException)context.getThrowable();
_log.info("进入系统异常拦截" + exception.getMessage());
HttpServletResponse response = context.getResponse();
response.setContentType("application/json; charset=utf8");
response.setStatus(exception.nStatusCode);
PrintWriter writer = null;
try {
writer = response.getWriter();
writer.print("{code:"+ exception.nStatusCode +",message:\""+
exception.getMessage() +"\"}");
} catch (IOException e) {
e.printStackTrace();
} finally {
if(writer!=null){
writer.close();
}
}
} catch (Exception e) {
ReflectionUtils.rethrowRuntimeException(e);
}
return null;
}
}
五、Zuul的熔断降级
1、服务都有hystrix,为什么还要用zuul的熔断降级呢
-
hystrix:在分布式系统中对服务间的调用进行控制
-
zuul是一个代理服务,但如果被代理的服务突然断了,这个时候zuul上面会有出错信息,例如,停止了被调用的微服务;
一般服务方自己会进行服务的熔断降级,但对于zuul本身,也应该进行zuul的降级处理,给调用方友好的提示;
我们需要有一个zuul的降级,实现如下:
2、代码实现
@Component
public class ProviderFallback implements FallbackProvider {
@Override
public String getRoute() {
//表明是为哪个微服务提供回退,*表示为所有微服务提供回退
//指定微服务,就用路由配置的名称,路由别名
return "*";
}
/**
* @param route :服务的路由
* @param cause : 异常
* @return ClientHttpResponse:熔断后的换回值
*/
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "text/html; charset=UTF-8");
return headers;
}
@Override
public InputStream getBody() throws IOException {
// 响应体
return new ByteArrayInputStream("服务正在维护,请稍后再试.".getBytes());
}
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.BAD_REQUEST;
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.BAD_REQUEST.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.BAD_REQUEST.getReasonPhrase();
}
@Override
public void close() {
// 可以不做处理
}
};
}
}