该笔记为本人学习SpringCloud时总结的,对应视频来自尚硅谷
视频地址为:https://www.bilibili.com/video/BV18E411x7eT
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行其独立的进程中,服务与服务间采用轻量级的通信机制互相协作(通常是基于Http协议的RESTful API)。每个服务都围绕着基本业务进行构建,并且能够被独立的部署到生产环境、类环境等。另外,应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建
SpringCloud=分布式微服务架构的一站式解决方案,是多种微服务架构落地技术的集合体
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vrJL9ElM-1595411658264)(https://gitee.com/CainLong/images/raw/master/NSGK%@_M6K8PZZMGQE}0ESM.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zEFWkSVj-1595411658267)(file:///D:\QQ\1422234087\Image\C2C\VAB7MP{B19[XXMA%L)]PNY$Y.png)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E1IzArjl-1595411658269)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200622184811932.png)]
微服务调试最好在Run Dashboard空间,不在Run空间里
开发过程开启热部署功能,代码修正。
子工程添加依赖
org.springframework.boot
spring-boot-devtools
runtime
true
父工程添加插件
org.springframework.boot
spring-boot-maven-plugin
true
true
idea setting
按下 ctrl+shift+alt+/ 点击注册
只需要编写Controller层代码和实体类代码,那最初先靠什么来完成两个模块间的传输的?答案是HttpClient,不过我们使用RestTemplate来进行调用,其实他就是封装了一层HttpClient,它提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
那么RestTemplate的使用方式是怎样的呢?使用RestTemplate访问restful接口非常的简单粗暴无脑。(url,requestMap,ResponseBean.class)这三个参数分别代表REST请求地址、请求参数、HTTP响应转换被转换成的对象类型
测试出现问题,在支付模块中,参数没加@RequestBody的话,其中的数据是为空的
系统中有重复部分,重构,相似的地方拿出来
新建一个子工程,供多个工程调用
为什么要有服务注册中心?方便服务之间的调度、协调和管理
Spring Cloud封装了Netflix公司开发的Eureka模块来实现服务治理
在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要服务治理,管理服务与服务之间的依赖,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
Eureka采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息,比如服务地址通讯地址等以别名方式注册到注册中心中。另一方(消费者|提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理没个服务于服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0iS2E1ax-1595411658277)(https://gitee.com/CainLong/images/raw/master/20200623154826.png)]
单点故障,所以能配多个的就配多个集群
Eureka包含两个组件:Eureka Server 和 Eureka Client
Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
EurekaClient通过注册中心进行访问
是一个java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7KQLecfq-1595411658278)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200623164011308.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMPCPcb5-1595411658279)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200623164145461.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5AlLLdmg-1595411658280)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200623165304276.png)]
问题:微服务RPC远程服务调用最核心的是什么?
那必然就是高可用呀,试想你的注册中心就只有一个的话,加入它出故障了,那么整个系统的服务环境就都不能用了,所以
解决办法就是:搭建Eureka注册中心集群,实现负载均衡+故障容错
集群搭建的一个原理就是:互相注册,相互守望。
对内互相注册,对外暴露出一个整体
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QL84MYqs-1595411658280)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200623171721227.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cREAkA5q-1595411658281)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200623171736890.png)]
两个Eureka注册中心集群搭建完成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h5ZrYEjD-1595411658283)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200623172316359.png)]
将两个微服务注册进注册中心
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GHpoqG3F-1595411658284)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200623174946459.png)]
服务模块集群搭建完成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1FtegDxw-1595411658285)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200623175015950.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8u3jADAp-1595411658286)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200623175032135.png)]
负载均衡算法,挨个轮询
去掉主机名称,服务名称的修改
访问信息有ip信息提示
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
故障现象:
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护,一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。
导致原因:
一句话:某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该服务的信息进行保存,属于CAP里面的AP分支
为什么会产生Eureka自我保护机制?
为了防止EurekaClient可以正常运行,但是与EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除
什么是自我保护模式?
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)是,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了–因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题–当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uOj6pbpQ-1595411658287)(C:\Users\14222\Desktop\gqGr\typera\QQ�VGr20200624103742.png)]
怎么禁止自我保护
在eureka yml中配置[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x5daQfmP-1595411658288)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200624105630830.png)]
在客户端 yml中配置[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5uMCwdG3-1595411658289)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200624105701109.png)]
SpringCloud整合Zookeeper代替Eureka
zookeeper是一个分布式协调工具,可以实现注册中心功能
Consul是一套开源的分布式服务发现和配置管理系统,由HashiCorp公司用Go语言开发
提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。
它具有很多优点。包括:基于raft协议,比较简洁;支持健康检查,同时支持Http和DNS协议 支持跨数据中心的WAN集群 提供图形界面 跨平台,支持Linux、Mac、Windows
组件名 | 语言 | CAP | 服务健康检查 | 对外暴露接口 | Springcloud集成 |
---|---|---|---|---|---|
Eureka | Java | AP | 可配支持 | HTTP | 已集成 |
Consul | Go | CP | 支持 | HTTP/DNS | 已集成 |
Zookeeper | Java | CP | 支持 | 客户端 | 已集成 |
最多只能同时较好的满足两个。
CAP理论核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,因此,根据CAP原理将NoSql数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:
CA - 单点集群,满足一致性、可用性的系统,通常在可扩展性上不太强大
CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。Zookeeper/Consul
AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。Eureka
CAP理论关注粒度是数据,而不是整体系统设计的策略
AP:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vXiVz8I0-1595411658290)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200628131230999.png)]
CP:当网络分区出现后,为了保证一致性,就必须拒接请求,否则无法保证一致性,结论:违背了可用性A的要求,只满足一致性和分区容错,即CP
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iov7so2z-1595411658291)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200628131522736.png)]
## Ribbon入门介绍
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套**客户端** 负载均衡的工具
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时、重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。
做负载均衡
LB负载均衡(Load Balance)是什么?
简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。
常见的负载均衡有软件Nginx、LVS、硬件F5等。
Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡区别?
Nginx是服务器负载均衡,客户端所有请求都会交给Nginx,然后由Nginx实现转发请求。即负载均衡是由服务端实现的。
Ribbon本地负载均衡,在调用微服务接口的时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
Nginx就比作医院的大门,Ribbon就比作科室,就比如你牙疼,挂牙科,但是牙科不可能就一个医生,所以Ribbon的作用就是“你挂哪一个牙科医生”
集中式LB
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如Nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;
进程内LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从地址中选择出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取提供方的地址。
Ribbon就是负载均衡+RestTemplate调用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KtQr1NLU-1595411658292)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200628154112413.png)]
Ribbon在工作的时候分两步:
返回对象为响应体中数据转化成的对象,基本可以理解为Json
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YnbSXRmS-1595411658294)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200628155817091.png)]
返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BRQJOBVn-1595411658295)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200628155824069.png)]
###IRule:根据特定算法中从服务列表中选取一个要访问的服务
IRule是一个接口
/**
* Interface that defines a "Rule" for a LoadBalancer. A Rule can be thought of
* as a Strategy for loadbalacing. Well known loadbalancing strategies include
* Round Robin, Response Time based etc.
*
* @author stonse
*
*/
public interface IRule{
/*
* choose one alive server from lb.allServers or
* lb.upServers according to key
*
* @return choosen Server object. NULL is returned if none
* server is available
*/
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
它有哪些落地实现类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jzpujDY-1595411658296)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200628162203603.png)]
com.netflix.loadbalancer.RoundRobinRule:轮询
com.netflix.loadbalancer.RandomRule:随机
com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule的策略来获取服务,如果获取服务失败则在指定时间内会进行重试
WeightResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
BestAvailableRule:会先过滤由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例
ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器
写一个配置类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fdX2N6d7-1595411658297)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200628164033541.png)]
注意是在与启动类不同的包下声明的一个配置类,因为官方文档明确说明不要放在ComponentScan能扫描的本包下以及子包下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CIFTh3yl-1595411658297)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200628164201622.png)]
再看启动类,name表示我要去注册中心找的服务名称,configuration表示我自己配置的负载均衡实现类,不用默认的。
负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启动后rest接口计数从1开始。
List instances = disconveryClient.getInstances(“CLOUD-PAYMENT-SERVICE”);
如:List [0] instances = 127.0.0.1:8002
List [1] instances = 127.0.0.1:8001
8001+8002组合成为集群,它们共计2台机器,集群总数为2,按照轮询算法原理:
当总请求数为1时:1 % 2 = 1 对应下标位置为1,则获得服务地址为127.0.0.1:8001
当总请求数为2时:2 % 2 = 0 对应下标位置为0,则获得服务地址为127.0.0.1:8002
当总请求数为1时:3 % 2 = 1 对应下标位置为1,则获得服务地址为127.0.0.1:8001
当总请求数为1时:4 % 2 = 1 对应下标位置为0,则获得服务地址为127.0.0.1:8002
如此类推…
import com.netflix.client.config.IClientConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* The most well known and basic load balancing strategy, i.e. Round Robin Rule.
*
* @author stonse
* @author Nikos Michalakis
*
*/
public class RoundRobinRule extends AbstractLoadBalancerRule {
private AtomicInteger nextServerCyclicCounter;
private static final boolean AVAILABLE_ONLY_SERVERS = true;
private static final boolean ALL_SERVERS = false;
private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);
public RoundRobinRule() {
//原子整形类 初始值为0
nextServerCyclicCounter = new AtomicInteger(0);
}
public RoundRobinRule(ILoadBalancer lb) {
this();
setLoadBalancer(lb);
}
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
}
//注册中心的服务
Server server = null;
//重试次数
int count = 0;
while (server == null && count++ < 10) {
//活着的服务实例 也就是在注册中心显示up状态的
List<Server> reachableServers = lb.getReachableServers();
//所有服务实例
List<Server> allServers = lb.getAllServers();
//活着的服务实例的数量
int upCount = reachableServers.size();
//所有服务实例的数量
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
//下一次服务实例的位置下标,这里内部采用CAS+自旋锁来对索引下标定位
int nextServerIndex = incrementAndGetModulo(serverCount);
//根据索引下标得到服务实例
server = allServers.get(nextServerIndex);
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}
/**
* Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
*
* @param modulo The modulo to bound the value of the counter.
* @return The next value.
*/
private int incrementAndGetModulo(int modulo) {
for (;;) {
//初始为0
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQ20yT3r-1595411658298)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200628180157312.png)]
package com.markus.springcloud.lb;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Author:markusZhang
* Date:Create in 2020/6/28 17:31
* todo:
*/
@Component
public class MyLB implements LoadBalancer {
private AtomicInteger atomicInteger = new AtomicInteger(0);
public final int getAndIncrement(){
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= 2147483647 ? 0:current+1;
}while(!this.atomicInteger.compareAndSet(current,next));
System.out.println("*****第几次访问,次数next: "+next);
return next;
}
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}
Feign是一个声明式WebService客户端。使用Feign能让编写WebService客户端更加简单。
它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7vuM6x2t-1595411658299)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200628181120528.png)]
Feign旨在使编写Java Http客户端变得更容易
前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多出调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由它来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign相关注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。
Feign集成了Ribbon
利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FwlgPhRQ-1595411658300)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200628182711948.png)]
OpenFeign天生自带负载均衡
一句话:接口+注解
服务端模块故意写一个超过3秒的业务,而OpenFeign客户端默认是只等待一秒钟,超时就直接返回报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MDlcdnDW-1595411658301)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200629152512701.png)]
为了避免这种情况,有时候我们需要设置Fiegn客户端的超时控制
###yml文件中开启配置
OpenFeign天生自带Ribbon,那么我们在超时控制的时候就交由Ribbon来解决
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ASZ297H3-1595411658302)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200629153353520.png)]
Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。
说白了就是对Feign接口的调用情况进行监控和输出
日志级别
yml配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xT2XuhsD-1595411658303)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200629154342975.png)]
还需要再写一个配置类,注册Bean
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
停止更新,但出道即巅峰。它的服务降级、服务熔断、服务限流、服务隔离等等设计思想特别优秀
###分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败,比如网络卡顿、调用超时、程序出错等等
服务雪崩:多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的"扇出"。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的"雪崩效应"。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便多个依赖关系的失败,不能取消整个应用程序或系统。
所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
"断路器"本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个服务预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
服务降级
服务熔断
接近实时的监控
限流、隔离。。。
服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback。也就是要有一个兜底的方案
哪些情况会触发降级:
类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
就是保险丝:服务的降级->进而熔断->恢复调用链路
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
###服务降级
有两个接口响应服务,一个是立即返回,一个是等待3秒返回。正常情况下是ok的
在高并发情况下,观测结果,立即返回的那个请求也要转圈了。
原因就是:tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。
结论:上面还是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死
上述结论:正因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生
如何解决?解决的要求
超时导致服务器变慢(转圈)–>超时不再等待
出错(宕机或程序运行出错)–>出错要有兜底
解决:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wviTkzOg-1595411658304)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200629182926510.png)]
需要在启动类上方添加一个注解用于激活Hystrix 服务降级
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MA7D2YAg-1595411658305)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200629183026021.png)]
在客户端和服务端都做了服务降级之后,会出现一个问题,每个业务方法对应一个兜底的方法,会造成代码膨胀。兜底方法和业务方法放在一起会造成耦合度特别高,混乱
统一和自定义的分开
代码膨胀:
代码混乱:
就可以类比家里的保险丝
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。
当检测到该节点微服务调用响应正常后,恢复调用链路。
在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand。
实操:
//=====服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),//失败率达到多少后跳闸
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if (id<0){
throw new RuntimeException("******id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id")Integer id){
return "id 不能负数,请稍后再试 o(╥﹏╥)o id:"+id;
}
熔断的三种状态:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ftcvszp-1595411658310)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630143154799.png)]
涉及到断路器的三个重要参数:快照时间窗、请求总数阈值、错误百分比阈值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vBTc8CiC-1595411658312)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630144623906.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-maR0c5hd-1595411658314)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630144636666.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-11Yd4v9N-1595411658315)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630144717967.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O87jKESP-1595411658316)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630144734057.png)]
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c2Qb8SML-1595411658318)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630151604383.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VxGDtdYq-1595411658319)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630151619027.png)]
实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减
该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大,该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实例中快速的发现故障实例和高压力实例。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vMpM30O3-1595411658320)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630152108872.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zBNVWXXQ-1595411658321)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630152116406.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xNXNRmCO-1595411658322)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630152140817.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UMC2wIXs-1595411658323)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630152223808.png)]
不再讲
SpringCloud Gateway是SpringCloud的一个全新项目,基于spring5.0+springboot2.0和Project Reactor等技术开发的网关,它旨在为微服务提供一种简单有效的统一的API路由管理方式。
SpringCloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Zuul,在spring Cloud 2.0以上版本中,没有对新版本的Zuul2.0以上最新高性能版本进行集成,仍然还是使用Zuul1.x非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
SpringCloud Gateway的目标提供统一的路由方式且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SuqxZws4-1595411658324)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630155325573.png)]
Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true,则匹配该路由
Predicate(断言):参考的是Java8的java.util.function.Predicate,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t0bHu9Eh-1595411658328)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630161306716.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NoDaRpzD-1595411658329)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630161826431.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pqjjqni1-1595411658330)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630161949881.png)]
核心逻辑:路由转发+执行过滤器链
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c5GZh4WR-1595411658331)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630164730944.png)]
第一种就是上面的yml配置
第二种是硬编码,通过Bean配置
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_markus",
r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
默认情况下Gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pZryKxbB-1595411658332)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630172047754.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-huicH2tm-1595411658333)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630172744601.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YVGmnD5V-1595411658334)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630172756975.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9S13Am0d-1595411658335)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630172958325.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KUoaQ5De-1595411658336)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630174713168.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MVHSrE3G-1595411658336)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200630175220751.png)]
路由过滤器可用于修改进入的Http响应,路由过滤器只能指定路由进行调用
springcloud Gateway内置了多种过滤器,他们都由Gateway Filter的工厂来产生
它的生命周期只有两个,pre–>post,在业务逻辑前和在业务逻辑后
种类有两个:单一过滤器31种之多,全局过滤器有10种
其实在过滤器这块,常用的还是自定义过滤器。下面记录下怎么使用它
两个主要接口 Global Filter,Order
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H9p2JLm4-1595411658337)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200701144214214.png)]
全局日志记录、统一网关鉴权、等等
分布式配置中心
分布式系统面临的问题:配置问题,系统中必定会出现大量的重复的配置文件
是什么:
怎么玩:
能干嘛:
配置读取规则:
label:分支(branch)
name:服务名
profiles:环境(dev/test/prod)
application.yml:是用户级的资源配置项
bootstrap.yml是系统级的,优先级更加高
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vJDOwDin-1595411658339)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200701160006315.png)]
新建一个模块,编写bootstrap.yml
server:
port: 3355
spring:
application:
name: cloud-client
cloud:
#Config客户端配置
config:
label: master # 分支名称
name: config # 配置文件名称
profile: dev # 读取后缀名称 上述3个综合:master分支上的config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml
uri: http://localhost:3344 #配置中心地址
#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka
成功试下了客户端3355访问SpringCloud Config3344通过GitHub获取配置信息
问题来了,分布式配置的动态刷新问题
##Config客户端之动态刷新
避免每次更新配置都要重启客户端微服务
在客户端yml中配置
# 暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
在业务Controller层上面打上@RefreshScope注解
测试发现没有生效?
解决:
没有重启客户端,刷新之后,发现生效了。
成功实现了客户端3355刷新到最新配置内容–避免了服务重启
想想还有什么问题?
消息总线
上一讲解的加深和扩充—分布式自动刷新配置功能
是什么:
能干嘛:
为何被称为总线:
先安装Erlang
##SpringCloud Bus动态刷新定点通知
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
前四个字母分别是Naming 和 Configuration,最后的s为Service
是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台
就是注册中心+配置中心的组合 ==== eureka+config+bus
替代Eureka做服务注册中心
替代Config做服务配置中心
服务注册与发现框架 | CAP模型 | 控制台管理 | 社区活跃度 |
---|---|---|---|
Eureka | AP | 支持 | 低(2.x版本闭源) |
Zookeeper | CP | 不支持 | 中 |
Consul | CP | 支持 | 高 |
Nacos | AP/CP | 支持 | 高 |
据说Nacos在阿里巴巴内部有超过10万的实例运行,已经过了类似双十一等各种大型流量的考验
注意观察表里的Nacos,它是支持AP和CP的,但是不能支持CAP,在AP和CP之间可以来回切换
我们先来看看Nacos全景图所示
C是所有节点在同一时间看到的数据是一致的; 而A的定义是所有的请求都会收到响应。
何时选择使用何种模式?
一般来说,如果不需要存储服务级别的信息且服务实例是通过nacos- client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如Spring doud和Dubbo服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性, 因此AP模式下只支持注册临时实例。如果需要在服务级别编辑或者存储配置信息,那么CP是必须, K8S服务和DNS服务则适用于CP模式。CP模式下则支持注册持久化实例,此时则是以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。
问题1:
实际开发中,通常一个 系统会准备
dev开发环境
test测试环境
prod生产环境。
如何保证指定环境启动时服务能正确读取到Nacos.上相应环境的配置文件呢?
问题2:
一个大型分布式微服务系统会有很多微服务子项目,
每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境…
那怎么对这些微服务配置进行管理呢?
三者关系?为什么这么设计?
NameSpace+Group+DataID
1.是什么
类似java里面的package名和类名
最外层的namespace是可以用于区分部署环境的,Group和DataID逻辑上区分两个目标对象
2.三者情况
默认情况
Namespace=pulic,Group=DEFAULT_GROUP,默认Cluster是DEFAULT
Nacos默认的命名空间是public, Namespace主要用来实现隔离。
比方说我们现在有三个环境:开发测试、铲环境,我们就可以创建= E个Namespace,不同的Namespace之间是隔离的。
Group默认是DEFAULT_ GROUP, Group可以把不同的微服务划分到同一个分组里面去
Service就是微服务;一个Service可以包含多 个Cluster (集群),Nacos默认Cluster是DEFAULT, Cluster是对指定微服务的一个虚拟划分。
比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房,
这时就可以给杭州机房的Service微服务起一个集群名称(HZ),
给广州机房的Service微服务起一个集群名称 (GZ) , 还可以尽量让同-个机房的微服务互相调用,以提升性能。
后是Instance,就是微服务的实例。
3.分组方案
4.Namespace
问题
默认Nacos使用嵌入式数据库实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一 致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。
找到nacos下的conf下的application.properities里面的注释去掉:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4RpPCyuy-1595411658356)(C:\Users\14222\AppData\Roaming\Typora\typora-user-images\image-20200722170002378.png)]
先去执行conf下的nacos-mysql.mysql语句,然后将上图这些原本注释掉的信息取消注释,重启nacos,成功。
推荐linux系统,预计需要1个Nginx+3个nacos注册中心+1个mysql
nacos下载linux版
集群配置步骤