JavaGuide学习记录
背景:学习过程中的笔记总结,并记录疑问的问题
Zookeeper
5.3 消息广播模式。zk中两个队列是干啥的,如何保证消息的顺序执行??
关注知识点
2pc和3pc,以及Paxos算法(Proposer提案者
、Acceptor表决者
、Learner学习者,2阶段提交
)
7.1. 选主
7.2. 分布式锁
Dubbo
RPC 的原理是什么?
为了能够帮助小伙伴们理解 RPC 原理,我们可以将整个 RPC的 核心功能看作是下面👇 6 个部分实现的:
- 客户端(服务消费端) :调用远程方法的一端。
- 客户端 Stub(桩) : 这其实就是一代理类。代理类主要做的事情很简单,就是把你调用方法、类、方法参数等信息传递到服务端。
- 网络传输 : 网络传输就是你要把你调用的方法的信息比如说参数啊这些东西传输到服务端,然后服务端执行完之后再把返回结果通过网络传输给你传输回来。网络传输的实现方式有很多种比如最近基本的 Socket或者性能以及封装更加优秀的 Netty(推荐)。
- 服务端 Stub(桩) :这个桩就不是代理类了。我觉得理解为桩实际不太好,大家注意一下就好。这里的服务端 Stub 实际指的就是接收到客户端执行方法的请求后,去指定对应的方法然后返回结果给客户端的类。
- 服务端(服务提供端) :提供远程方法的一端。
具体原理图如下,后面我会串起来将整个RPC的过程给大家说一下。
- 服务消费端(client)以本地调用的方式调用远程服务;
- 客户端 Stub(client stub) 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体(序列化):
RpcRequest
; - 客户端 Stub(client stub) 找到远程服务的地址,并将消息发送到服务提供端;
- 服务端 Stub(桩)收到消息将消息反序列化为Java对象:
RpcRequest
; - 服务端 Stub(桩)根据
RpcRequest
中的类、方法、方法参数等信息调用本地的方法; - 服务端 Stub(桩)得到方法执行结果并将组装成能够进行网络传输的消息体:
RpcResponse
(序列化)发送至消费方; - 客户端 Stub(client stub)接收到消息并将消息反序列化为Java对象:
RpcResponse
,这样也就得到了最终结果。over!
相信小伙伴们看完上面的讲解之后,已经了解了 RPC 的原理。
虽然篇幅不多,但是基本把 RPC 框架的核心原理讲清楚了!另外,对于上面的技术细节,我会在后面的章节介绍到。
最后,对于 RPC 的原理,希望小伙伴不单单要理解,还要能够自己画出来并且能够给别人讲出来。因为,在面试中这个问题在面试官问到 RPC 相关内容的时候基本都会碰到。
Dubbo一款高性能、轻量级的开源 Java RPC 框架。
Dubbo 提供了六大核心能力
- 面向接口代理的高性能RPC调用。
- 智能容错和负载均衡。
- 服务自动注册和发现。
- 高度可扩展能力。
- 运行期流量调度。
- 可视化的服务治理与运维。
不过,Dubbo 的出现让上述问题得到了解决。Dubbo 帮助我们解决了什么问题呢?
- 负载均衡 : 同一个服务部署在不同的机器时该调用哪一台机器上的服务。
- 服务调用链路生成 : 随着系统的发展,服务越来越多,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。Dubbo 可以为我们解决服务之间互相是如何调用的。
- 服务访问压力以及时长统计、资源调度和治理 :基于访问压力实时管理集群容量,提高集群利用率。
....
Dubbo 架构中的核心角色有哪些?
官方文档中的框架设计章节 已经介绍的非常详细了,我这里把一些比较重要的点再提一下。
上述节点简单介绍以及他们之间的关系:
- Container: 服务运行容器,负责加载、运行服务提供者。必须。
- Provider: 暴露服务的服务提供方,会向注册中心注册自己提供的服务。必须。
- Consumer: 调用远程服务的服务消费方,会向注册中心订阅自己所需的服务。必须。
- Registry: 服务注册与发现的注册中心。注册中心会返回服务提供者地址列表给消费者。非必须。
- Monitor: 统计服务的调用次数和调用时间的监控中心。服务消费者和提供者会定时发送统计数据到监控中心。 非必须。
Invoker
就是 Dubbo 对远程调用的抽象
Invoker
分为
- 服务提供
Invoker
- 服务消费
Invoker
Dubbo的工作原理
从上到下分为十层,单向调用。左边消费者,右边生产者,中轴线上是双方都用到的接口
- config 配置层:Dubbo相关的配置。支持代码配置,同时也支持基于 Spring 来做配置,以
ServiceConfig
,ReferenceConfig
为中心 - proxy 服务代理层:调用远程方法像调用本地的方法一样简单的一个关键,真实调用过程依赖代理类,以
ServiceProxy
为中心。 - registry 注册中心层:封装服务地址的注册与发现。
- cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以
Invoker
为中心。 - monitor 监控层:RPC 调用次数和调用时间监控,以
Statistics
为中心。 - protocol 远程调用层:封装 RPC 调用,以
Invocation
,Result
为中心。 - exchange 信息交换层:封装请求响应模式,同步转异步,以
Request
,Response
为中心。 - transport 网络传输层:抽象 mina 和 netty 为统一接口,以
Message
为中心。 - serialize 数据序列化层 :对需要在网络传输的数据进行序列化。
Dubbo的SPI机制
Dubbo 的 SPI 机制了解么? 如何扩展 Dubbo 中的默认实现?
SPI(Service Provider Interface) 机制被大量用在开源项目中,它可以帮助我们动态寻找服务/功能(比如负载均衡策略)的实现。
SPI 的具体原理是这样的:我们将接口的实现类放在配置文件中,我们在程序运行过程中读取配置文件,通过反射加载实现类。这样,我们可以在运行的时候,动态替换接口的实现类。和 IoC 的解耦思想是类似的。
Java 本身就提供了 SPI 机制的实现。不过,Dubbo 没有直接用,而是对 Java原生的 SPI机制进行了增强,以便更好满足自己的需求。
Dubbo 的微内核架构了解吗?
Dubbo 采用 微内核(Microkernel) + 插件(Plugin) 模式,简单来说就是微内核架构。微内核只负责组装插件。
何为微内核架构呢? 《软件架构模式》 这本书是这样介绍的:
微内核架构模式(有时被称为插件架构模式)是实现基于产品应用程序的一种自然模式。基于产品的应用程序是已经打包好并且拥有不同版本,可作为第三方插件下载的。然后,很多公司也在开发、发布自己内部商业应用像有版本号、说明及可加载插件式的应用软件(这也是这种模式的特征)。微内核系统可让用户添加额外的应用如插件,到核心应用,继而提供了可扩展性和功能分离的用法。
微内核架构包含两类组件:核心系统(core system) 和 插件模块(plug-in modules)。
核心系统提供系统所需核心能力,插件模块可以扩展系统的功能。因此, 基于微内核架构的系统,非常易于扩展功能。
我们常见的一些IDE,都可以看作是基于微内核架构设计的。绝大多数 IDE比如IDEA、VSCode都提供了插件来丰富自己的功能。
正是因为Dubbo基于微内核架构,才使得我们可以随心所欲替换Dubbo的功能点。比如你觉得Dubbo 的序列化模块实现的不满足自己要求,没关系啊!你自己实现一个序列化模块就好了啊!
通常情况下,微核心都会采用 Factory、IoC、OSGi 等方式管理插件生命周期。Dubbo 不想依赖 Spring 等 IoC 容器,也不想自己造一个小的 IoC 容器(过度设计),因此采用了一种最简单的 Factory 方式管理插件 :JDK 标准的 SPI 扩展机制 (java.util.ServiceLoader
)。
Dubbo 提供的负载均衡策略有哪些?
Dubbo 提供的负载均衡策略有哪些?
RandomLoadBalance
:根据权重随机选择(对加权随机算法的实现)。这是Dubbo默认采用的一种负载均衡策略。
LeastActiveLoadBalance
直译过来就是最小活跃数负载均衡。
Dubbo 就认为谁的活跃数越少,谁的处理速度就越快,性能也越好,这样的话,我就优先把请求给活跃数少的服务提供者处理。
如果有多个服务提供者的活跃数相等怎么办?
很简单,那就再走一遍 RandomLoadBalance
。
ConsistentHashLoadBalance
即一致性Hash负载均衡策略。 ConsistentHashLoadBalance
中没有权重的概念,具体是哪个服务提供者处理请求是由你的请求的参数决定的,也就是说相同参数的请求总是发到同一个服务提供者。
另外,Dubbo 为了避免数据倾斜问题(节点不够分散,大量请求落到同一节点),还引入了虚拟节点的概念。通过虚拟节点可以让节点更加分散,有效均衡各个节点的请求量。
RoundRobinLoadBalance加权轮询负载均衡。
轮询就是把请求依次分配给每个服务提供者。加权轮询就是在轮询的基础上,让更多的请求落到权重更大的服务提供者上。比如假如有两个提供相同服务的服务器 S1,S2,S1的权重为7,S2的权重为3。
如果我们有 10 次请求,那么 7 次会被 S1处理,3次被 S2处理。
但是,如果是 RandomLoadBalance
的话,很可能存在10次请求有9次都被 S1 处理的情况(概率性问题)
Dubbo序列化协议
大白话介绍下RPC中序列化的概念,可以简单理解为对象-->字节的过程,同理,反序列化则是相反的过程。为什么需要序列化?因为网络传输只认字节。所以互信的过程依赖于序列化。有人会问,FastJson转换成字符串算不算序列化?对象持久化到数据库算不算序列化?没必要较真,广义上理解即可。
Dubbo 支持多种序列化方式:JDK自带的序列化、hessian2、JSON、Kryo、FST、Protostuff,ProtoBuf等等。
Dubbo 默认使用的序列化方式是 hession2。Dubbo 官网的一篇文章中提到说推荐使用 Kryo 作为生产环境的序列化方式(平均响应和tps均优于其它的序列化方式)
一般我们不会直接使用 JDK 自带的序列化方式。主要原因有两个:
- 不支持跨语言调用 : 如果调用的是其他语言开发的服务的时候就不支持了。
- 性能差 :相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。
既有 HTTP ,为啥用 RPC 进行服务调用?
RPC 只是一种设计而已
RPC 只是一种概念、一种设计,就是为了解决 不同服务之间的调用问题, 它一般会包含有 传输协议 和 序列化协议 这两个。
但是,HTTP 是一种协议,RPC框架可以使用 HTTP协议作为传输协议或者直接使用TCP作为传输协议,使用不同的协议一般也是为了适应不同的场景。
HTTP 和 TCP
可能现在很多对计算机网络不太熟悉的朋友已经被搞蒙了,要想真正搞懂,还需要来简单复习一下计算机网络基础知识:
我们通常谈计算机网络的五层协议的体系结构是指:应用层、传输层、网络层、数据链路层、物理层。
应用层(application-layer)的任务是通过应用进程间的交互来完成特定网络应用。 HTTP 属于应用层协议,它会基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。HTTP协议工作于客户端-服务端架构上。浏览器作为HTTP客户端通过 URL 向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。HTTP协议建立在 TCP 协议之上。
传输层(transport layer)的主要任务就是负责向两台主机进程之间的通信提供通用的数据传输服务。TCP是传输层协议,主要解决数据如何在网络中传输。相比于UDP,TCP 提供的是面向连接的,可靠的数据传输服务。
RPC框架功能更齐全
成熟的 RPC框架还提供好了“服务自动注册与发现”、"智能负载均衡"、“可视化的服务治理和运维”、“运行期流量调度”等等功能,这些也算是选择 RPC 进行服务注册和发现的一方面原因吧!
2、Dubbo 支持哪些协议,每种协议的应用场景,优缺点?
1、 dubbo:单一长连接和 NIO 异步通讯,适合大并发小数据量的服务调用,以及消费者远大于提供者。传输协议 TCP,异步, Hessian 序列化;
2、 rmi:采用 JDK 标准的 rmi 协议实现,传输参数和返回参数对象需要实现Serializable 接口,使用 java 标准序列化机制,使用阻塞式短连接,传输数据包大小混合,消费者和提供者个数差不多,可传文件,传输协议 TCP。多个短连接, TCP 协议传输,同步传输,适用常规的远程服务调用和 rmi 互操作。在依赖低版本的 Common-Collections 包, java 序列化存在安全漏洞;
3、 http:基于 Http 表单提交的远程调用协议,使用 Spring 的 HttpInvoke 实现。多个短连接,传输协议 HTTP,传入参数大小混合,提供者个数多于消费者,需要给应用程序和浏览器 JS 调用;
4、 webservice:基于 WebService 的远程调用协议,集成 CXF 实现,提供和原生 WebService 的互操作。多个短连接,基于 HTTP 传输,同步传输,适用系统集成和跨语言调用;
5、 hessian:集成 Hessian 服务,基于 HTTP 通讯,采用 Servlet 暴露服务,Dubbo 内嵌 Jetty 作为服务器时默认实现,提供与 Hession 服务互操作。多个短连接,同步 HTTP 传输, Hessian 序列化,传入参数较大,提供者大于消费者,提供者压力较大,可传文件;
6、 Redis:基于 Redis 实现的 RPC 协议
Spring cloud
ps:springCloud的总览图,里面的概念和模块可以通读以下
构建分布式系统不需要复杂和容易出错。Spring Cloud 为最常见的分布式系统模式提供了一种简单且易于接受的编程模型,帮助开发人员构建有弹性的、可靠的、协调的应用程序。Spring Cloud 构建于 Spring Boot 之上,使得开发者很容易入手并快速应用于生产中。
官方果然官方,介绍都这么有板有眼的。
我所理解的 Spring Cloud
就是微服务系统架构的一站式解决方案,在平时我们构建微服务的过程中需要做如 服务发现注册 、配置中心 、消息总线 、负载均衡 、断路器 、数据监控 等操作,而 Spring Cloud 为我们提供了一套简易的编程模型,使我们能在 Spring Boot 的基础上轻松地实现微服务项目的构建。
Spring Cloud 的服务发现框架——Eureka
ps:相关概念参考上面的链接
负载均衡之 Ribbon
RestTemplate
是Spring
提供的一个访问Http服务的客户端类
怎么说呢?就是微服务之间的调用是使用的 RestTemplate
。比如这个时候我们 消费者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); }
Ribbon
是 Netflix
公司的一个开源的负载均衡 项目,是一个客户端/进程内负载均衡器,运行在消费者端。
提到 负载均衡 就不得不提到大名鼎鼎的 Nignx
了,而和 Ribbon
不同的是,它是一种集中式的负载均衡器。
何为集中式呢?简单理解就是 将所有请求都集中起来,然后再进行负载均衡。如下图。
解决RestRemplate
API的使用麻烦,面向接口编程
RestRemplate
的 API
是否太麻烦,我能不能像调用原来代码一样进行各个服务间的调用呢?通过映射,实现面向接口的无缝开发
什么是 Hystrix之熔断和降级
什么是 Hystrix之熔断和降级
在分布式环境中,不可避免地会有许多服务依赖项中的某些失败。Hystrix是一个库,可通过添加等待时间容限和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点,停止服务之间的级联故障并提供后备选项来实现此目的,所有这些都可以提高系统的整体弹性。
总体来说 Hystrix
就是一个能进行 熔断 和 降级 的库,通过使用它能提高整个系统的弹性。
服务雪崩:阻塞会崩溃。因为这些请求会消耗占用系统的线程、IO 等资源,消耗完你这个系统服务器不就崩了么
熔断:熔断 就是服务雪崩的一种有效解决方案。当指定时间窗内的请求失败率达到设定阈值时,系统将通过 断路器 直接将此请求链路断开
降级:降级是为了更好的用户体验,当一个方法调用异常时,通过执行另一种代码逻辑来给用户友好的回复。
Spring Cloud Config
既能对配置文件统一地进行管理,又能在项目运行时动态修改配置文件
能进行配置管理的框架不止 Spring Cloud Config
一种,大家可以根据需求自己选择(disconf
,阿波罗等等)。而且对于 Config
来说有些地方实现的不是那么尽人意。
Spring Cloud Config
就是能将各个 应用/系统/模块 的配置文件存放到 统一的地方然后进行管理(Git 或者 SVN)。
我们会使用 Bus
消息总线 + Spring Cloud Config
进行配置的动态刷新。
你可以简单理解为 Spring Cloud Bus
的作用就是管理和广播分布式系统中的消息,也就是消息引擎系统中的广播模式。当然作为 消息总线 的 Spring Cloud Bus
可以做很多事而不仅仅是客户端的配置刷新功能。
而拥有了 Spring Cloud Bus
之后,我们只需要创建一个简单的请求,并且加上 @ResfreshScope
注解就能进行配置的动态修改了,下面我画了张图供你理解。
这篇文章中我带大家初步了解了 Spring Cloud
的各个组件,他们有
Eureka
服务发现框架Ribbon
进程内负载均衡器Open Feign
服务调用映射Hystrix
服务降级熔断器Zuul
微服务网关Config
微服务统一配置中心Bus
消息总线
如果你能这个时候能看懂文首那张图,也就说明了你已经对 Spring Cloud
微服务有了一定的架构认识。
Redis
Redis(6)删除策略(定时删除、惰性删除、定期删除)和数据逐出策略
Redis 6.0前为什么使用单线程?之后为什么又引入了多线程
Redis 选择使用单线程模型处理客户端的请求主要还是因为 CPU 不是 Redis 服务器的瓶颈,所以使用多线程模型带来的性能提升并不能抵消它带来的开发成本和维护成本,系统的性能瓶颈也主要在网络 I/O 操作上;而 Redis 引入多线程操作也是出于性能上的考虑,对于一些大键值对的删除操作,通过多线程非阻塞地释放内存空间也能减少对 Redis 主线程阻塞的时间,提高执行的效率。
Redis6.0 引入多线程主要是为了提高网络 IO 读写性能,因为这个算是 Redis 中的一个性能瓶颈(Redis 的瓶颈主要受限于内存和网络)。