- 架构知识+分布式微服务+高并发高可用高性能知识序幕就此拉开(一:总览篇)
- 网关开了个头
- 你请求来了,我网关把你拦截住,验明正身,加以控制,协助你调用服务,完成请求的调用。但是这个过程中,为了解耦和或者说为了方便集群中多个节点的协调管理。得叫下面的帮手们来帮忙:
比如咱们作为客户端进行购物时,那么多服务提供者【服务提供者有很多实例,可能人家已经搞了拆分模块后的分布式集群,那实例就不少啦】,如果用非技术的眼光看就是,你提供多个,我挑一个买,咱们的访问过程应该是这样的。
话说回来,除了用东方神秘的力量之外,那么多超市服务,我咋知道实际调哪一个呢、这么多怎么管理呢…?【重要概念,微服务中一个服务既可以是服务消费者又可以是服务提供者,服务这个概念是相对的】
针对这么多问题就需要注册中心来帮助我们
- 为什么要这个服务注册与发现机制
- 这个哥们,我觉得他就是一个中间商。还拿之前的XXX高速项目来说事,我目前有三个服务模块,数据管理(历史数据、实时数据、预测数据)、监控模块以及预警管理。问题来了
- 如果除了这三个模块之外,我想扩展其他的功能,那我去原项目上改代码?改配置文件?,你服务提供者变多了你不该改配置文件或者代码能行?
- 我把这三个在不同的服务器上部署为单独的节点后,由于下位机请求量太大,我对他们三个分别做了扩展,成了集群【后期由于同一模块中不同小模块的访问量,可能对他们再做两方面的拆分…】,如果某一个节点撂挑子,说我不干了或者说由于天灾人祸干不了了,我客户端楞头巴脑的还不停地访问他【因为我客户端此时也不知道谁好谁坏呀】,我不得统计一下你行不行呀?不行了让我改改配置文件和代码,然后重启一下你?这要是多了还不累死我?
- 所以,有了服务注册与发现机制之后,就不需要这么麻烦了,
集群中每个节点在启动运行时会将自己的显著信息【IP、端口以及其他服务名称等信息】上报给服务注册中心保管,由注册中心负责维护一个可用服务的列表【记录下各个服务的IP和Port】,然后服务消费者就可以通过注册中心动态获取可用服务的地址信息进行调用。并且这个服务消费者心眼也多,会在自己本地缓存一份服务提供者的显著信息,以便在服务注册中心拉垮了后我依旧可以找到要调用的服务进行调用
。如果服务信息发生变更,注册中心会将变更推送给相关联的服务,更新服务地址信息,无需手动更新,也不需要重启服务
, 这些对开发者来说完全是无感的。服务注册与发现可以帮助我们实现服务的优雅上下线,从而实现服务的弹性扩缩容。- 除此之外,
服务注册与发现机制还有一个非常重要的功能:你哪个服务提供者拉垮了或者宕机了,我可以通过心跳机制来检测服务是否可用,从而从服务列表中把你给剔除掉
。并且注册中心会将变更推送给相关联的服务,更新服务地址信息
。
- 这个哥们,我觉得他就是一个中间商。还拿之前的XXX高速项目来说事,我目前有三个服务模块,数据管理(历史数据、实时数据、预测数据)、监控模块以及预警管理。问题来了
- 常见的服务注册与发现中心:Zookeeper、Eureka、Nacos、Consul和Etcd
- SpringCloud的注册中心Eureka
- Eureka:Eureka 是Netflix公司开源的一个注册中心,Netflix公司配套的还有Feign、Ribbon、 Zuul、 Hystrix 等知名的微服务系统构建所必须的组件。
对于CAP理论来说,Eureka保证的是AP【这不是刚好吗】
。Eureka 集群只要有一台Eureka正常服务,整个注册中心就可用的,只是查询到的数据可能是过期的(集群中的各个节异步方式同步数据,不保证强一致性)
。- 在2018年的时候,Netflix 宣布其开源的核心组件Hystrix、Ribbon、 Zuul、Eureka 等进入维护状态,不再进行新特性开发,只修BUG。于是,吓得Spring不得不考虑移除Netflix的组件。
- Eureka很容易形成各个节点之间的数据不一致的情况
- Eureka的自我保护机制:当网络分区产生后【允许认情况下,Eureka Server在一定时间内,没有接收到某个微服务心跳,会将某个微服务注销(90S)。】或者说是当网络故障时,Eureka保证系统的可用性,但不保证系统里面数据的一致性。微服务与Server之间无法正常通信,上述行为就非常危险,因为微服务正常,不应该注销。Eureka Server通过自我保护模式来解决整个问题,当Server在短时间内丢失过多客户端时,那么Server会进入自我保护模式,会保护注册表中的微服务不被注销掉。当网络故障恢复后,退出自我保护模式
- Eureka手里有几员大将,为了攒战功,Eureka分别把自己的大将派往不同地方。其中这个eureka-server这个将军属于上将之一。Eureka的大将之一eureka-server,作为大管家,新官上任三把火。【可以到Github或者Gitee看代码,结合图一块看:】
- 第一把火,你们market-service但凡要提供shopping服务,必须在你们market-service服务实例启动后到我这里来注册你们的服务信息【到eureka-server进行服务注册】【每个需要被注册中心管理的服务的配置文件中,有Eureka的配置,包括defalutZone=…,表示你这个服务要注册到哪里呀,这个哪里就指的是eureka-server注册中心所在】
- 第二把火,我这里给购物的消费者(服务)保存服务实例地址列表,并把列表提供给消费者让他们选择,然后拉取market-service的信息选择出心仪的那一个【people-service根据服务名称列表进行服务发现或者拉取】
- 【服务发现其实指的是,咱们服务不是已经注册在Eureka中了嘛,咱们界面上可以看到哪些注册进来了,然后,咱们可以通过DiscoveryClient这个类的getService()方法获得微服务列表List,以及getInstances(服务ID)方法获得一个具体的微服务信息。然后在主程序类加上一个@EnableDiscoveryClient开启服务发现。当然那个actuator导包之后并在application.xml中配置info配置之后,可以配置一些监控信息,在Eureka的页面中点链接也可以显示一下信息,但是这个不好用,一般显示一些author信息这些,主要的还是通过DiscoveryClient】
- Eureka的自我保护机制:
- 某个微服务不可用了,没发来心跳,eureka会90秒后立即清理注销这个实例【有时候由于网络分区存在,微服务可能本身非常健康,不应该被注销】。但是Eurake通过自我保护机制不会注销服务,当网络故障恢复后自动退出保护模式【宁可错误保留,也不盲目注销】
- 第三把火,为了防止market-service哪个由于疫情或者其他原因搞不下去了宕机了【我又不能说是你都不卖东西了,人家购物的人啥啥坐在在看你家的东西,搞了半天下架你也不吭声,这不是坑人嘛】,market-service这边的服务们必须每隔一段时间(默认30)向eureka-server发起请求报告自己的心跳状态【超过一定时间没发心跳则eureka-server就会认为这个微服务发生故障,剔除,你给我除去】
- Eureka说,eureka-server,你烧的好,再奖励你一把火,于是,eureka-server再次下令,但是是一把小火,就是你们people从eureka-server提供的实例列表中选择一个心仪的实例(地址)时,需要通过“负载均衡算法”选择,然后向该实例发起远程调用
- 第一把火,你们market-service但凡要提供shopping服务,必须在你们market-service服务实例启动后到我这里来注册你们的服务信息【到eureka-server进行服务注册】【每个需要被注册中心管理的服务的配置文件中,有Eureka的配置,包括defalutZone=…,表示你这个服务要注册到哪里呀,这个哪里就指的是eureka-server注册中心所在】
- 【Eureka是基于REST(代表性状态转移)的服务,主要在 AWS 云中用于定位服务,以实现负载均衡和中间层服务器的故障转移。我们称此服务为Eureka服务器。】【
可以充当服务发现的组件有很多:Zookeeper ,Consul , Eureka 等
】- 这里面ctrl+F搜餐馆:
- 我们依旧那拿上面的餐馆为例,我们要找合适的餐馆吃饭,但是我又不知道哪个餐馆有啥菜,有这个菜的饭馆在哪里…,所以此时,作为微服务中的 Consumer的咱们以及作为微服务中的Provider。【此时我们作为Consumer需要调用提供者Provider提供的一些服务,就是找到他们的餐馆吃饭】。要是光咱们双方在交涉,不管是餐馆随地贴小广告还是咱们傻乎乎的找,就会出现几个问题(效率是很低的:不是所有人都能看到小广告,所以会出现
资源消耗问题
)。所以搞个中介,中介为我们提供统一房源的餐馆,我们只需要找中介就可以找到餐馆+参观这个服务提供者也只需要把餐馆所在信息发布或者叫注册在中介那里就行
。然后就得考虑餐馆有新的旧的咱们是不是应该增删注册表+服务消费者是不是也得让中介知道自己的信息+一个中介生病了退休了怎么办,你不搞个中介集群 服务发现
:其实就是一个“中介”,整个过程中有三个角色:服务提供者(餐馆)、服务消费者(干饭人)、服务中介。服务中介
:其实就是服务提供者和服务消费者之间的“桥梁”,服务提供者可以把自己注册到服务中介那里,而服务消费者如需要消费一些服务(使用一些功能)就可以在服务中介中寻找注册在服务中介的服务提供者
。- 服务注册 Register:当 Eureka 客户端向 Eureka Server 注册时,它提供自身的元数据,比如 IP 地址、端口,运行状况指示符 URL,主页等。
餐馆 (提供者 Eureka Client Provider)把自己餐馆所在信息以及可以做的菜等信息在中介 (服务器 Eureka Server) 那里登记下来
- 服务续约 Renew:Eureka 客户会每隔 30 秒(默认情况下)发送一次心跳来续约。 通过续约来告知 Eureka Server 该 Eureka 客户仍然存在,没有出现问题。 正常情况下,如果 Eureka Server 在 90 秒没有收到 Eureka 客户的续约,它会将实例从其注册表中删除。【
餐馆 (提供者 Eureka Client Provider)定期告诉中介 (服务器 Eureka Server) 我的餐馆还提供伙食(续约) ,中介 (服务器Eureka Server) 收到之后继续保留该餐馆的信息
。】 - 获取注册列表信息 Fetch Registries:Eureka 客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每 30 秒钟)更新一次。每次返回注册列表信息可能与 Eureka 客户端的缓存信息不同, Eureka 客户端自动处理。如果由于某种原因导致注册列表信息不能及时匹配,Eureka 客户端则会重新获取整个注册表信息。 Eureka 服务器缓存注册列表信息,整个注册表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka 客户端和 Eureka 服务器可以使用 JSON / XML 格式进行通讯。在默认的情况下 Eureka 客户端使用压缩 JSON 格式来获取注册列表的信息。【
咱们干饭人(消费者 Eureka Client Consumer) 去中介 (服务器 Eureka Server) 那里获取所有的餐馆信息列表 (客户端列表 Eureka Client List) ,而且干饭人为了获取最新的信息会定期向中介 (服务器 Eureka Server) 那里获取并更新本地列表
。】 - 服务下线 Cancel:Eureka 客户端在程序关闭时向 Eureka 服务器发送取消请求。 发送请求后,该客户端实例信息将从服务器的实例注册表中删除。该下线请求不会自动完成,它需要调用以下内容:DiscoveryManager.getInstance().shutdownComponent();【
餐馆 (提供者 Eureka Client Provider)告诉中介 (服务器 Eureka Server) 我的餐馆不卖饭了,中介之后就将注册的房屋信息从列表中剔除。
】 - 服务剔除 Eviction:在默认的情况下,当 Eureka 客户端连续 90 秒(3 个续约周期)没有向 Eureka 服务器发送服务续约,即心跳,Eureka 服务器会将该服务实例从服务注册列表删除,即服务剔除。【
餐馆 (提供者 Eureka Client Provider)会定期联系 中介 (服务器 Eureka Server) 告诉他我的餐馆还卖饭(续约),如果中介 (服务器 Eureka Server) 长时间没收到提供者的信息,那么中介会将他的餐馆信息给下架(服务剔除)。别人就不到这家买饭了
】
- 服务注册 Register:当 Eureka 客户端向 Eureka Server 注册时,它提供自身的元数据,比如 IP 地址、端口,运行状况指示符 URL,主页等。
服务提供者
: 就是提供一些自己能够执行的一些服务给外界。就是餐馆,可以给干饭人提供伙食服务消费者
:就是需要使用一些服务的“用户”
- Eureka的搭建或者实践过程:三步可以上篮
- pom.xml中导入依赖+编写application.xml配置文件【端口号、服务名称,你要被哪个中间件管理,配置中心?注册中心?…】+在主程序类加个@EnableEurekaServer注解,老版本要加,新版本不需要加
- 其实比如你要搞Eureka集群,就是1的application.xml中的defaultZone中原本放一个http://…,现在放集群中除他自己之外的所有http://…,其余的节点一样,自己的application.xml中的defaultZone中原本放一个http://…,现在放集群中除他自己之外的所有http://…
- RestTemplate:微服务之间的调用经常是使用的 RestTemplate,RestTemplate是Spring提供的一个访问 Http 服务的客户端类,比如这个时候我们 消费者 B 需要调用 提供者 A 所提供的服务我们就需要这么写。
@Autowired private RestTemplate restTemplate; // 这里是提供者A的ip地址,但是如果使用了 Eureka 那么就应该是提供者A的名称 private static final String SERVICE_PROVIDER_A = "http://localhost:8081"; @PostMapping("/judge") public boolean judge(@RequestBody Request request) { String url = SERVICE_PROVIDER_A + "/service1"; return restTemplate.postForObject(url, request, Boolean.class); }
@RestController public class UserInfoController { /** * RestTemplate用来提供多种便捷访问远程http服务的方法,简单的restful服务模板 * 消费者这边是不应该有service的,借助RestTemplate,有很多方法供我们直接调用,比如利用GET/POST方式获得实体,好多方法都跟网络有关【参数是url之类的】 */ @Autowired private RestTemplate restTemplate; private static final String REST_URL_PREFIX = "http://localhost:8081"; @RequestMapping("/consumer/userinfo/get/{id}") public UserInfo get(@PathVariable("id") Long id){ return restTemplate.getForObject(REST_URL_PREFIX + "/userinfo/get/" + id, UserInfo.class); } ... }
- Eureka:Eureka 是Netflix公司开源的一个注册中心,Netflix公司配套的还有Feign、Ribbon、 Zuul、 Hystrix 等知名的微服务系统构建所必须的组件。
- Zookeeper也可以做注册中心:
- zookeeper主要看这里【https://blog.csdn.net/m0_52436398/article/details/125193033】
- zookeeper是强一致性的,数据一致性好,但是zookeeper的注册中心可用性不好,当发现Leader选举以及过半节点不可用时那么这个请求的可用性就不能保证了
- 注册中心又不需要强一致性,注册中心一般需要的是可用性,所以从这个角度来说Zookeeper不是很专业
- zookeeper主要看这里【https://blog.csdn.net/m0_52436398/article/details/125193033】
- Nacos:
既可以用来做注册中心,又可以用来做配置中心的优秀项目
。- Nacos借鉴吸收了其他注册中心的有点,与Spring Boot、Dubbo、Spring Cloud、Kubernetes无缝对接,兼容性很好。
并且Nacos不仅支持CP也支持AP【能同时满足CAP理论的大牛,咱不用还等啥】
。Nacos性能强悍(比Eureka能支持更多的服务实例) ,易用性较强(文档丰富、数据模型简单且自带后台管理界面) ,支持99.9%可用。
- Nacos借鉴吸收了其他注册中心的有点,与Spring Boot、Dubbo、Spring Cloud、Kubernetes无缝对接,兼容性很好。
- SpringCloud的注册中心Eureka
- 注册中心中间件们的区别:
- 极力推荐:楼仔老师的注册中心原理和选型:Zookeeper、Eureka、Nacos、Consul和Etcd
Zookeeper保证的是CP(降低可用性)
。ZooKeeper 的处理方式,它保证了CP(数据一致性)
【 ZooKeeper、HBase 就是 CP 架构】- 因为选举新主机过程中,整个集群是不可用的
Eureka保证的是AP(降低数据一致性,允许返回旧数据,不能接受服务直接宕机)
。【Cassandra、Eureka 就是 AP 架构】- 几个节点宕机其余的依旧可以提供注册和查询服务。只要有一台Eureka还在就可以继续保证注册服务的可用性
- Eureka 的处理方式证了
AP(可用性)
- Nacos 不仅支持 CP 架构也支持 AP 架构。
- 补充:
- CAP定理:
- 设计一个分布式系统必定会遇到一个问题——
因为分区容忍性(partition tolerance)的存在,就必定要求我们需要在系统可用性(availability)和数据一致性(consistency)中做出权衡 。这就是著名的 CAP 定理(布鲁尔定理(Brewer’s theorem))
。【比如上面咱们那个连锁店饭店的事情,整个连锁店为整个系统,而里面的小门店是一个个独立的子系统,这个时候有个门店创作了一个新菜式,其他门店还不知道,不就出现了数据不一致的情况嘛;咱们必须保证一致性,需要所有店都会这道菜才能提供这道菜的服务,但是有些店死活提供不了这个菜的服务,就相当于出现了系统可用性问题】 - CAP也就是 Consistency(一致性)、Availability(可用性)、Partition Tolerance(分区容错性) 这三个单词首字母组合。CAP 理论的提出者布鲁尔在提出 CAP 猜想的时候,并没有详细定义 Consistency、Availability、Partition Tolerance 三个单词的明确定义。
- Consistency:一致性就是
在客户端任何时候看到各节点的数据都是一致的
。例如一个数据在某个分区节点更新之后,在其他分区节点读出来的数据也是更新之后的数据。 - Availability:可用性就是
在任何时刻都可以提供读写
。可用性是指系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。这里的重点是"有限时间内"和"返回结果"。 - Partition Tolerance:分区容错性是在网络故障、某些节点不能通信的时候系统仍能继续工作【也就是
分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务
】。 具体地讲在分布式系统中,在任何数据库设计中,一个Web应用最多只能同时支持上面的两个属性。显然,任何横向扩展策略都要依赖于数据分区。因此,设计人员必须在一致性与可用性之间做出选择。
- Consistency:一致性就是
- CAP 【特别注意,CAP的讨论是建立在
系统发生“分区”的情况下
,CAP 理论只能满足 CP 或者 AP。要注意的是,这里的前提是系统发生了“分区”
。如果系统没有发生“分区”的话,节点间的网络连接通信正常的话,也就不存在 P 了。这个时候,我们就可以同时保证 C 和 A 了。或者说如果系统发生“分区”,我们要考虑选择 CP 还是 AP。如果系统没有发生“分区”的话,我们要思考如何保证 CA
。】【ACID 是数据库事务完整性的理论,CAP 是分布式系统设计理论,BASE 是 CAP 理论中 AP 方案的延伸。】
- 网络分区?
- 分布式系统中,多个节点之前的网络本来是连通的,但是
因为某些故障(比如部分节点网络出了问题)某些节点之间不连通了,整个网络就分成了几块区域
,这就叫 网络分区 - 发生网络分区的时候,
如果我们要继续服务,那么强一致性和可用性只能 2 选 1
。也就是说当网络分区之后 P 是前提,决定了 P 之后才有 C 和 A 的选择
。也就是说分区容错性(Partition tolerance)P我们是必须要实现的
。简而言之就是:CAP 理论中分区容错性 P 是一定要满足的,在此基础上,只能满足可用性 A 或者一致性 C
。【选择 CP 还是 AP 的关键在于当前的业务场景,没有定论,比如对于需要确保强一致性的场景如银行一般会选择保证 CP。如果网络分区正常的话(系统在绝大部分时候所处的状态),也就说不需要保证 P 的时候,C 和 A 能够同时保证
】
- 分布式系统中,多个节点之前的网络本来是连通的,但是
- 网络分区?
- 设计一个分布式系统必定会遇到一个问题——
- BASE 理论:
指的是系统发生分区后选择AP,然后系统分区故障恢复后系统应该达到最终一致性【虽然无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。】
。【如果系统发生“分区”,我们要考虑选择 CP 还是 AP。如果系统没有发生“分区”的话,我们要思考如何保证 CA。因此,AP 方案只是在系统发生分区的时候放弃一致性,而不是永远放弃一致性。在分区故障恢复后,系统应该达到最终一致性。这一点其实就是 BASE 理论延伸的地方】`。- CAP中,如果系统没有发生“分区”的话,节点间的网络连接通信正常的话,也就不存在 P 了。这个时候,我们就可以同时保证 C 和 A 了。因此,
如果系统发生“分区”,我们要考虑选择 CP 还是 AP。如果系统没有发生“分区”的话,我们要思考如何保证 CA。因此,AP 方案只是在系统发生分区的时候放弃一致性,而不是永远放弃一致性。在分区故障恢复后,系统应该达到最终一致性。这一点其实就是 BASE 理论延伸的地方。
- CAP中,如果系统没有发生“分区”的话,节点间的网络连接通信正常的话,也就不存在 P 了。这个时候,我们就可以同时保证 C 和 A 了。因此,
- BASE 是 Basically Available(基本可用) 、Soft-state(软状态) 和 Eventually Consistent(最终一致性) 三个短语的缩写。BASE 理论是对 CAP 中一致性 C 和可用性 A 权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于 CAP 定理逐步演化而来的,它大大降低了我们对系统的要求。BASE 理论本质上是对 CAP 的延伸和补充,更具体地说,
BASE 理论是对 CAP 中 AP 方案的一个补充,也就是对于我们的业务系统,我们考虑牺牲一致性来换取系统的可用性和分区容错性。
。- Basically Available(基本可用):基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性【允许损失部分可用性有两部分含义:】。但是,这绝不等价于系统不可用。
- 响应时间上的损失: 正常情况下,处理用户请求需要 0.5s 返回结果,但是由于系统出现故障,处理用户请求的时间变为 3 s。
- 系统功能上的损失:正常情况下,用户可以使用系统的全部功能,但是由于系统访问量突然剧增,
系统的部分非核心功能无法使用
- Soft-state(软状态):软状态指允许系统中的数据存在中间状态(CAP 理论中的数据不一致),并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时,
也就是状态可以有一段时间不同步
。 - Eventually Consistent(最终一致性):最终一致性强调的是
系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态
。 因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性
。分布式一致性的 3 种级别如下:
- 强一致性 :系统写入了什么,实时的读出来的就是什么【
某些对数据一致要求十分严格的场景比如银行转账还是要保证强一致性
。】 - 弱一致性 :
不一定可以读取到最新写入的值,也不保证多少时间之后读取到的数据是最新的,只是会尽量保证某个时刻达到数据一致的状态
- 最终一致性 :弱一致性的升级版,
系统会保证在一定时间内达到数据一致的状态。
- 强一致性 :系统写入了什么,实时的读出来的就是什么【
- Basically Available(基本可用):基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性【允许损失部分可用性有两部分含义:】。但是,这绝不等价于系统不可用。
- CAP定理:
巨人的肩膀:
Dubbo中文文档
SpringCloud中文文档
极客时间
凤凰架构