基于Netflix
的开源框架zuul
实现的 各个微服务之间都不存在单点,而且都注册于 Eureka ,基于此进行服务的注册于发现,再经过 Ribbon 进行服务调用,并具备客户端负载功能。 问题点?java
针对上述问题:SpringCloud 全家桶天然也有对应的解决方案: Zuul
。 Spring Cloud Zuul 将自身注册为 Eureka 服务治理下的应用,从 Eureka 中获取服务实例信息,从而维护路由规则和服务实例。nginx
咱们在全部的请求进来以前抽出一层网关应用,将服务提供的全部细节都进行了包装,这样全部的客户端都是和网关进行交互(统一入口)
,简化了客户端开发git
客户端负载
:Zuul 注册于 Eureka 并集成了 Ribbon 因此天然也是能够从注册中心获取到服务列表进行客户端负载。动态路由
:解放运维。监控与审查
身份认证与安全
压力测试
: 逐渐增长某一个服务集群的流量,以了解服务性能;金丝雀测试
服务迁移
负载剪裁
: 为每个负载类型分配对应的容量,对超过限定值的请求弃用;静态应答处理
添加依赖github
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
复制代码
spring-cloud-starter-zuul自己已经集成了hystrix和ribbon
注意点:正则表达式
(传统路由)
当使用path与url的映射关系来配置路由规则时,对于路由转发的请求则不会采用HystrixCommand来包装,因此这类路由请求就没有线程隔离和断路器保护功能,而且也不会有负载均衡的能力(服务路由)
使用Zuul的时候尽可能使用**path和serviceId
**的组合进行配置,这样不只能够保证API网关的健壮和稳定,也能用到Ribbon的客户端负载均衡功能。开启服务注册spring
@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
复制代码
路由配置顺序注意:api
YAML(文件格式,相似propeties)
propeties
文件,则会丢失顺序。# 项目配置
spring.application.name=sbc-gateway-zuul
server.port=8383
# 简化路由配置[zuul.routes.微服务Id = 指定路径]
zuul.routes.user = /api/**(指定正则表达式来匹配路径)
# 忽略指定微服务
zuul.ignored-services=微服务Id1,微服务Id2...
# 指定多个微服务负载
zuul.routes.user.path: /user/**
zuul.routes.user.serviceId: user
ribbon.eureka.enabled=false
user.ribbon.listOfServers: 微服务1, 微服务2...
# forward跳转本地
zuul.routes.user.path=/user/**
zuul.routes.user.url=forward:/user
# 对指定路由开启自定义敏感头
zuul.routes.[route].customSensitiveHeaders=true
zuul.routes.[route].sensitiveHeaders=[这里设置要过滤的敏感头]
# 全局
zuul.sensitiveHeaders=[这里设置要过滤的敏感头]
# --Zuul的Http客户端支持Apache Http、Ribbon的RestClient和OkHttpClient,默认使用Apache HTTP客户端。--
# 启用Ribbon的RestClient
ribbon.restclient.enabled=true
# 启用OkHttpClient
ribbon.okhttp.enabled=true
# eureka地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
复制代码
粒度
是微服务
,而不是某个API
ZuulFallbackProvider
接口(最新版本建议直接继承类FallbackProvider
,新版增长异常处理
),实现该接口就能够为Zuul实现回退功能/** * @program: spring-cloud * @description: zuul容错与回退 * @author: Mr.Tang * @create: 2018-06-20 14:25 **/
@Component
public class ProductServiceFallbackProvider implements FallbackProvider {
protected final Logger logger = LoggerFactory.getLogger(ProductServiceFallbackProvider.class);
@Override
public String getRoute() {
// 注意: 这里是route的名称,不是服务的名称,不然没法起到回退做用
return "ribbon-consumer";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return headers;
}
@Override
public InputStream getBody() throws IOException {
logger.info("ribbon-consumer: 服务不能够");
return new ByteArrayInputStream("该服务暂不可用,请稍后重试!".getBytes());
}
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
};
}
@Override
public ClientHttpResponse fallbackResponse(Throwable cause) {
if (cause != null && cause.getCause() != null) {
String reason = cause.getCause().getMessage();
logger.info("Excption {}",reason);
}
return fallbackResponse();
}
}
复制代码
路由重试(或者启动备用服务来分散压力) 网络等缘由致使服务不可用,须要进行重试,zuul结合Spring Retry
添加Spring Retry依赖安全
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
复制代码
开启重试 不使用重试,就必须考虑到是否可以接受单个服务实例关闭和eureka刷新服务列表之间带来的短期的熔断(在未刷新以前仍是会访问到熔断中的服务降级方法)服务器
#是否开启重试功能
zuul.retryable=true
#对当前服务的重试次数
ribbon.MaxAutoRetries=2
#切换相同Server的次数
ribbon.MaxAutoRetriesNextServer=0
复制代码
注意 断路器的其中一个做用就是防止故障或者压力扩散。当压力过大是,一个服务的宕机,路由将压力转向其它服务时有可能也会压垮其它服务。断路器的形式更像是提供一种友好的错误信息,提升服务之间的容错性。网络
Zuul大部分功能都是经过过滤器来实现的。Zuul中定义了四种标准过滤器类型
PRE
: 路由以前。咱们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。ROUTING
: 路由之时。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。POST
: 路由以后。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。ERROR
: 在其余阶段发生错误时执行该过滤器。Zuul中默认实现的Filter
类型 | 顺序 | 过滤器 | 功能 |
---|---|---|---|
pre | -3 | ServletDetectionFilter | 标记处理Servlet的类型 |
pre | -2 | Servlet30WrapperFilter | 包装HttpServletRequest请求 |
pre | -1 | FormBodyWrapperFilter | 包装请求体 |
route | 1 | DebugFilter | 标记调试标志 |
route | 5 | PreDecorationFilter | 处理请求上下文供后续使用 |
route | 10 | RibbonRoutingFilter | serviceId请求转发 |
route | 100 | SimpleHostRoutingFilter | url请求转发 |
route | 500 | SendForwardFilter | forward请求转发 |
post | 0 | SendErrorFilter | 处理有错误的请求响应 |
post | 1000 | SendResponseFilter | 处理正常的请求响应 |
除了默认的过滤器类型,Zuul还容许咱们建立自定义的过滤器类型。自定义(PRE)
以下: ZuulFilter
是Zuul中核心组件,经过继承该抽象类,覆写几个关键方法达到自定义调度请求的做用
/** * @program: spring-cloud * @description: token验证 * @author: Mr.Tang * @create: 2018-06-21 10:08 **/
public class TokenFilter extends ZuulFilter{
protected final Logger logger = LoggerFactory.getLogger(TokenFilter.class);
@Override
public String filterType() {
//pre:路由以前
//routing:路由中
//post:路由后
//error:发生错误时
return "pre";
}
@Override
public int filterOrder() {
// filter执行顺序,经过数字指定 ,优先级为0,数字越大,优先级越低
return 0;
}
@Override
public boolean shouldFilter() {
// 是否执行该过滤器,此处为true,说明须要过滤
return true;
}
@Override
public Object run() {
this.logger.info("This is pre-type zuul filter.");
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String token = request.getParameter("token");
if(StringUtils.isNotBlank(token)){
requestContext.setSendZuulResponse(true); //对请求进行路由
requestContext.setResponseStatusCode(200);
requestContext.set("isSuccess", true);
return null;
}else{
requestContext.setSendZuulResponse(false); //不对其进行路由
requestContext.setResponseStatusCode(400);
requestContext.setResponseBody("token is empty");
requestContext.set("isSuccess", false);
return null;
}
}
}
复制代码
filterType()
方法是该过滤器的类型;filterOrder()
方法返回的是执行顺序;shouldFilter()
方法则是判断是否须要执行该过滤器;run()
则是所要执行的具体过滤动做。过滤器之间并不会直接进行通讯,而是经过RequestContext
来共享信息,RequestContext
是线程安全的。 开启过滤器 在程序的启动类 添加 Bean
@Bean
public TokenFilter tokenFilter() {
return new TokenFilter();
}
复制代码
禁用过滤器 格式为:zuul.[filter-name].[filter-type].disable=true
如:
zuul.FormBodyWrapperFilter.pre.disable=true
复制代码
@EnableZuulServer和@EnableZuulProxy
@EnableZuulProxy
包含@EnableZuulServer
的功能,不会自动加载任何代理过滤器。@EnableZuulServer
替代 @EnableZuulProxy
。 这时候咱们能够添加任何 ZuulFilter
类型实体类都会被自动加载,Zuul 如今既然做为了对外的第一入口,那确定不能是单节点,对于 Zuul 的高可用有如下两种方式实现。
Eureka 高可用
:部署多个 Zuul 节点,而且都注册于 Eureka(有一个严重的缺点:那就是客户端也得注册到 Eureka 上才能对 Zuul 的调用作到负载,这显然是不现实的。)基于 Nginx 高可用
:在调用 Zuul 以前使用 Nginx 之类的负载均衡工具进行负载,这样 Zuul 既能注册到 Eureka ,客户端也能实现对 Zuul 的负载
在github
上有关于Spring Cloud
完整的部署。
其它相关文章
Spring cloud(1)-简介以及选择
Spring cloud(2)-服务发现(Eureka,Consul)
Spring cloud(3)-负载均衡(Feign,Ribbon)
Spring cloud(4)-熔断(Hystrix)
Spring cloud(5)-路由网关(Zuul)
Spring cloud(6)-配置管理及刷新(Config,Bus)
最后,给个 star 吧~
我的博客~
简书~