概述

Spring Cloud为开发人员提供了快速构建分布式系统中的一些常见模式的工具(例如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导层选举、分布式会话、集群状态)。分布式系统的协调导致了样板模式,使用Spring Cloud开发人员可以快速建立实现这些模式的服务和应用程序。它们在任何分布式环境中都能很好地工作,包括开发人员自己的笔记本电脑、裸机数据中心和云计算等托管平台。

尽管Spring Cloud带有”Cloud”的字样,但它并不是云计算解决方案,而是Spring Boot的基础上构建的,用于快速构建分布式系统的通用模式的工具集。

Netflix与Spring的恩怨情仇

原文链接
为什么spring cloud 2020.0 删除了netflix的很多组件?

  • 蜜月期
    2013: Netflix推出以zuul1为代表开的源项目
    2014: SpringBoot诞生,引入Netflix开源组件
    2015: SpringCloud诞生,引入Netflix开源组件
    2016: 发现Zuul1存在性能问题,Netflix官宣进行优化
  • 争执期
    2016-2017: Netflix跳票
    2018-05: Netflix发布Zuul 2.x,Spring未引入
    2018-06: Netflix宣告Eureka 2.0 开源工作停止
  • 分手
    2018-11: Netflix宣告Hystrix开源工作停止
    2018-11: Spring推出新一代网关Gateway、一些列孵化项目
    2018-12:Spring官方宣布Netflix的相关项目进入维护模式
    2018-12::Netflix 宣布 Spring 系列技术栈进入维护模式
  • SpringCloud因分手受影响的模块
    spring-cloud-netflix-archaius
    spring-cloud-netflix-hystrix-contract
    spring-cloud-netflix-hystrix-dashboard
    spring-cloud-netflix-hystrix-stream
    spring-cloud-netflix-hystrix
    spring-cloud-netflix-ribbon
    spring-cloud-netflix-turbine-stream
    spring-cloud-netflix-turbine
    spring-cloud-netflix-zuul

总之一句话:爱过

SpringCloud Netflix

Netflix通过自动配置和绑定到Spring环境和其他Spring编程模型的习惯方式来为Spring Boot应用程序提供Netflix OSS集成。通过几个简单的注释,您可以快速启用和配置应用程序中的常见模式,并通过经过测试的Netflix组件构建大型分布式系统。提供的模式包括服务发现(Eureka),断路器(Hystrix),智能路由(Zuul)和客户端负载均衡(Ribbon)。

Eureka(服务发现)

Eureka是基于REST(代表性状态转移)的服务,主要在AWS云中用于定位服务,以实现负载均衡和中间层服务器的故障转移。我们称此服务为Eureka服务器。Eureka还带有一个基于Java的客户端组件Eureka Client,它使与服务的交互变得更加容易。客户端还具有一个内置的负载平衡器,可以执行基本的循环负载平衡。在Netflix,更复杂的负载均衡器将Eureka包装起来,以基于流量,资源使用,错误条件等多种因素提供加权负载均衡,以提供出色的弹性。

  • Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
  • Eureka Server之间通过复制的方式完成数据的同步
  • Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就别一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
  • Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。
  • 综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性

配置参考

对启动类使用@EnableEurekaServer注解

1
2
3
4
5
6
7
8
9
10
server:
port: 8001
eureka:
instance:
hostname: enreka-server
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:${server.port}/eureka/

对启动类使用@EnableEurekaClient注解

1
2
3
4
5
6
7
8
9
10
11
server:
port: 8002
spring:
application:
name: eureka-client
eureka:
client:
service-url:
defaultZone: http://localhost:8001/eureka/
instance:
prefer-ip-address: true

Ribbon(负载均衡)

负载均衡(Load Balance)

负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。
负载均衡(Load Balance)其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。
负载均衡详解

负载均衡

Ribbon(客户端负载均衡器)

Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。

Ribbon工作时分为两步:
第一步 选择 Eureka Server, 它优先选择在同一个Zone且负载较少的Server。
第二步 根据用户指定的策略,在从Server取到的服务注册列表中选择一个地址。其中Ribbon提供了多种策略:

策略 描述
随机策略
RandomRule
随机选择server
轮询策略
RoundRobinRule
轮询选择, 轮询index,选择index对应位置的Server;
重试策略
RetryRule
对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的server;
最低并发策略
BestAvailableRule
逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server
可用过滤策略
AvailabilityFilteringRule
过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值)或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个Server的运行状态;
响应时间加权重策略
ResponseTimeWeightedRule
根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。响应时间越短,权重越高,被选中的概率越高,这个策略很贴切,综合了各种因素,比如:网络,磁盘,io等,都直接影响响应时间
区域权重策略
ZoneAvoidanceRule
综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server

使用方法

Feign(服务调用)

RestTemplate

RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。RestTemplate 继承自 InterceptingHttpAccessor 并且实现了 RestOperations 接口,其中 RestOperations 接口定义了基本的 RESTful 操作,这些操作在 RestTemplate 中都得到了实现。

RestTemplate实现了以下6个主要的HTTP meshod:

HTTP method RestTemplate methods
DELETE delete
GET getForObject,getForEntity
HEAD headForHeaders
OPTIONS optionsForAllow
PUT put
ANY exchange,execute

使用方式

首先我们假设已经有了一个客户端并且彼此都已经注册到了eureka服务器
这个客户端的注册名为CLIENT,访问地址为 http://localhost:8888/msg,可以返回一个字符串Hello,RestTemplate!

  • 第一种方式(直接使用restTemplate, url写死)
    1
    2
    3
    4
    5
    6
    7
    8
    @RestController
    public class TestController {
    @GetMapping("/msg")
    public String msg() {
    RestTemplate restTemplate = new RestTemplate();
    return restTemplate.getForObject("http://localhost:8888/msg", String.class);
    }
    }
  • 第二种方式(利用loadBalancerClient通过应用名获取url, 然后再使用restTemplate)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @RestController
    public class TestController {
    @Autowired
    private LoadBalancerClient loadBalancerClient;
    @GetMapping("/msg")
    public String msg() {
    RestTemplate restTemplate = new RestTemplate();
    ServiceInstance serviceInstance = loadBalancerClient.choose("CLIENT");
    return restTemplate.getForObject(String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort()) + "/msg", String.class);
    }
    }
  • 第三种方式(利用配置类加入@LoadBalanced, 可在restTemplate里使用应用名字)
    1
    2
    3
    4
    5
    6
    7
    8
    @Configuration
    public class RestTemplateConfiguration {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
    return new RestTemplate();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @RestController
    public class TestController {
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping("/msg")
    public String msg() {
    return restTemplate.getForObject("http://CLIENT/msg", String.class);
    }
    }

FeignClient

  • 创建一个springcloud项目并添加spring-cloud-starter-openfeign依赖
  • 对启动类使用@EnableFeignClients注解
  • 创建业务层(接口),使用@FeignClient(value = "应用名称")注解
    1
    2
    3
    4
    5
    @FeignClient("CLIENT")
    public interface TestService {
    @GetMapping("/msg")
    String msg();
    }
  • 控制器直接注入业务层并调用即可

Zuul(微服务网关)

Zuul是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。
Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。

Zuul的核心是一系列的过滤器,这些过滤器可以完成以下功能。

  • 身份认证与安全;识别每个资源的验证要求,并拒绝那些与要求不符的请求。
  • 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产视图。
  • 动态路由:动态地将请求路由到不同的后端集群。
  • 压力测试:逐渐增加指向集群的流量,以了解性能。
  • 负载分配:为每一种负载类型分配对应容量,并弃用超岀限定值的请求。
  • 静态响应处理:在边缘位置直接建立部分响应,从而避免其转发到内部集群。
  • 多区域弹性:跨越AWS Region进行请求路由,旨在实现ELB ( Elastic Load Balancing) 使用的多样化,以及让系统的边缘更贴近系统的使用者。

使用

  • 创建一个springcloud项目并添加spring-cloud-starter-netflix-zuul依赖
  • 对启动类使用@EnableZuulProxy注解
  • application.yml中配置路由地址
    1
    2
    3
    4
    5
    6
    zuul:
    routes:
    随便起个名:
    path: /dao/**
    service-id: CLIENT1 #要路由到的应用名称,也可以单独映射url
    #url: http://localhost:8002/
  • 理论上可以直接按下面这种方式配置,不过博主未尝试,因为现在版本的springboot不支持zuul…
    1
    2
    3
    4
    zuul:
    routes:
    CLIENT1:
    path: /dao/**

ZuulFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

@Component
@Slf4j
public class RouteFilter extends ZuulFilter {

@Override
public String filterType() {
// 在路由之前进行过滤
return FilterConstants.PRE_TYPE;
}

@Override
public int filterOrder() {
return -5;
}

// 过滤通过后要执行的方法
@Override
public Object run() throws ZuulException {
log.info("通过过滤");
return null;
}

// 过滤的核心逻辑
@Override
public boolean shouldFilter() {
// 获取请求上下文
RequestContext context = RequestContext.getCurrentContext();
// 获取到请求
HttpServletRequest request = context.getRequest();

String user = request.getParameter("user");
String uri = request.getRequestURI();

// 若请求中包含/abc8080路径,且没有user请求参数,则无法通过过滤
if(uri.contains("/abc8080") && StringUtils.isEmpty(user)) {
log.warn("user用户为空");
// 指定当前请求未通过zuul过滤,默认值为true
context.setSendZuulResponse(false);
context.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
return false;
}
return true;
}
}

Hystrix(服务降级、熔断器)

雪崩效应

服务雪崩效应(Avalanche effect)是一种因“服务提供者的不可用”(原因)导致“服务调用者不可用”(结果),并将不可用逐渐放大的现象。
谈谈服务雪崩、降级、熔断

{"t":"list_item","d":2,"p":{"lines":[0,1]},"v":"服务雪崩原因","c":[{"t":"list_item","d":4,"p":{"lines":[1,2]},"v":"服务不可用","c":[{"t":"list_item","d":6,"p":{"lines":[2,3]},"v":"程序bug"},{"t":"list_item","d":6,"p":{"lines":[3,4]},"v":"缓存击穿"},{"t":"list_item","d":6,"p":{"lines":[4,5]},"v":"用户大量请求"}]},{"t":"list_item","d":4,"p":{"lines":[5,6]},"v":"重试加大流量","c":[{"t":"list_item","d":6,"p":{"lines":[6,7]},"v":"用户重试"},{"t":"list_item","d":6,"p":{"lines":[7,8]},"v":"代码逻辑重试"}]},{"t":"list_item","d":4,"p":{"lines":[8,9]},"v":"服务调用者不可用","c":[{"t":"list_item","d":6,"p":{"lines":[9,10]},"v":"同步等待造成的资源耗尽"}]}]}
{"t":"list_item","d":2,"p":{"lines":[0,1]},"v":"应对策略","c":[{"t":"list_item","d":4,"p":{"lines":[1,2]},"v":"应用扩容","c":[{"t":"list_item","d":6,"p":{"lines":[2,3]},"v":"增加机器数量"},{"t":"list_item","d":6,"p":{"lines":[3,4]},"v":"升级硬件规格"}]},{"t":"list_item","d":4,"p":{"lines":[4,5]},"v":"流量控制","c":[{"t":"list_item","d":6,"p":{"lines":[5,6]},"v":"限流"},{"t":"list_item","d":6,"p":{"lines":[6,7]},"v":"关闭重试"}]},{"t":"list_item","d":4,"p":{"lines":[7,8]},"v":"缓存","c":[{"t":"list_item","d":6,"p":{"lines":[8,9]},"v":"缓存预加载"}]},{"t":"list_item","d":4,"p":{"lines":[9,10]},"v":"服务降级","c":[{"t":"list_item","d":6,"p":{"lines":[10,11]},"v":"服务接口拒绝服务"},{"t":"list_item","d":6,"p":{"lines":[11,12]},"v":"页面拒绝服务"},{"t":"list_item","d":6,"p":{"lines":[12,13]},"v":"延迟持久化"},{"t":"list_item","d":6,"p":{"lines":[13,14]},"v":"随机拒绝服务"}]},{"t":"list_item","d":4,"p":{"lines":[14,15]},"v":"服务熔断"},{"t":"list_item","d":4,"p":{"lines":[15,16]},"v":"请求合并"}]}

使用

结合Ribbon使用

  • 添加spring-cloud-starter-openfeign依赖
  • 对启动类使用@EnableCircuitBreaker@EnableHystrix(前者的封装)注解
  • 在Service的方法上加上@HystrixCommand注解(该注解对该方法创建了熔断器的功能),并指定fallbackMethod熔断方法。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Service
    public class TestService {

    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "sayError")
    public String say() {
    return restTemplate.getForObject("http://CLIENT/say",String.class);
    }

    public String sayError() {
    return "服务暂时不可用";
    }
    }

Feign中使用

  • 添加spring-cloud-starter-openfeign以及spring-cloud-starter-netflix-hystrix依赖
  • 配置文件中添加
    1
    2
    3
    feign:
    hystrix:
    enabled: true
  • Feign业务层添加fallback并添加实现类
    TestService.java
    1
    2
    3
    4
    5
    @FeignClient(value = "CLIENT", fallback = TestServiceImpl.class)
    public interface TestService {
    @GetMapping("say")
    String say();
    }
    TestServiceImpl.java
    1
    2
    3
    4
    5
    6
    public class TestServiceImpl {
    @Override
    public String say(){
    return "服务暂时不可用";
    }
    }
  • 这样,在调用其他服务失败的时候会直接调用实现类的方法