Spring设计目标

Spring为开发者提供一个一站式轻量级应用开发平台

在JavaEE开发中,支持POJO和JavaBean开发方式,使应用面向接口开发,充分支持 OO(面向对象)设计方法;Spring通过IoC容器实现对象耦合关系的管理,并实现依赖反转,将对象之间的依赖关系交给IoC容器,实现解耦;

IoC容器和AOP模块。通过IoC容器管理POJO对象以及他们之间的耦合关系;通过AOP以动态非侵入的方式增强服务。

Java Spring注解

SpringBoot 和 Spring Cloud知识点_spring


Spring 缺点

Spring依赖反射,反射影响性能。

Spring 设计模式

SpringBoot 和 Spring Cloud知识点_spring_02

Spring框架中的事件

SpringBoot 和 Spring Cloud知识点_构造器_03

Spring 出现同名Bean

同一个配置文件内同名的Bean,以最上面定义的为准

不同配置文件中存在同名Bean,后解析的配置文件会覆盖先解析的配置文件

同文件中@ComponentScan和@Bean出现同名Bean。同文件下@Bean的会生效,@ComponentScan扫描进来不会生效。通过@ComponentScan扫描进来的优先级是最低的,原因就是它扫描进来的Bean定义是最先被注册的。

Spring Bean和Java Bean

(1)什么是JavaBean:

JavaBean是一种JAVA语言写的可重用组件。JavaBean符合一定规范写的Java类,是一种规范。它的方法命名。

1.所有属性为private

2.这个类必须具有一个公共的(public)无参构造函数。

3.private属性必须提供public的getter和setter来给外部访问,并且方法的命名也必须遵循一定的命名规范

4.这个类是可序列化的,要实现serializable接口

(2)什么是SpringBean

SpringBean是受Spring管理的对象 所有能受Spring容器管理的对象都可以成为SpringBean.

二者之间的区别:

用处不同:传统javabean更多地作为值传递参数,而spring中的bean用处几乎无处不在,任何组件都可以被称为bean。

写法不同:传统javabean作为值对象,要求每个属性都提供getter和setter方法;但spring中的bean只需为接受设值注入的属性提供setter方法。

生命周期不同:传统javabean作为值对象传递,不接受任何容器管理其生命周期;spring中的bean有spring管理其生命周期行为。

Spring 4种装配bean属性的方法

1.byName 名称

2.byType  类型

3.constructor  构造器自动注入

4.autodetect  自动装配

Spring Bean 依赖注入方式

依赖注入是时下最流行的IoC实现方式,依赖注入分为接口注入(Interface Injection),Setter方法注入(Setter Injection)和构造器注入(Constructor Injection)三种方式。其中接口注入由于在灵活性和易用性比较差,现在从Spring4开始已被废弃。

构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。

Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入

SpringBoot 和 Spring Cloud知识点_spring_04

Spring Bean 自动装配

SpringBoot 和 Spring Cloud知识点_构造器_05

注:@Autowired和@Resource之间的区别:

(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入

SpringBoot 和 Spring Cloud知识点_spring_06

自动装配的局限性是:

重写:你仍需用 和 配置来定义依赖,意味着总要重写自动装配。

基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。

模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配

Spring 应用上下文的生命周期 :

 1.刷新阶段 refresh

 2.启动阶段 start

 3.停止阶段 stop

 4.关闭阶段 close

Spring 提供配置方式 :

1.基于xml配置 2.基于注解配置  3.基于Java API 配置 比如@Configuration

Spring 的 @Qualifier注解

 当创建多个相同类型的bean 并希望仅使用属性装配其中一个bean时,您可以使用@Qualifier注解和@Autowired 通过指定应该装配哪个确切的bean来消除歧义

SpringBoot 和 Spring Cloud知识点_作用域_07

Spring中Bean的五种作用域

SpringBoot 和 Spring Cloud知识点_作用域_08

6、自定义scope,必须实现ScopeMetadataResolve接口提供的无参数构造器

Spring 四种依赖注入

1.set注入 2.构造器注入 3.静态工厂注入 4.实例工厂方法注入

Spring 中 ApplicationContext和BeanFactory的区别?

BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。


有状态对象 :有实例变量的对象,可以保存数据,就是非线程安全的。每个用户有自己特有的一个实例,在用户生存期内,bean保持了用户的信息,即“有状态”,一旦用户灭亡,bean的生命期也告结束。

无状态对象 : 就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的,一旦实例化就被加进会话池中。

无状态Bean : 线程中的操作不会对Bean的成员执行查询意外的操作,那么这个单例Bean是线程安全的

有状态Bean :一般提供了通过ThreadLocal去解决线程安全的方法,为每个线程保存线程私有的数据。

Spring的Java配置,是通过@Bean和@Configuration来实现 

Spring的连接点 : 是一个应用执行过程中,能够插入一个切面的点

连接点:可以是调用方法时,抛出异常时,甚至修改字段时,切面代码可以利用这些点插入到应用的正规流程中。使得程序执行过程中能够应用通知的所有点

动态代理 与 静态代理 : 最大的好处是接口中声明的所有方法,都被转移到调用处理器一个集中的方法处理,利于解耦和维护。

JDK动态代理是目标对象实现一个接口,CGLIB不需要

JDK动态代理是自带的,CGLIB需要引入第三方包

Spring IOC为了屏蔽构造细节 : 例如new出来的对象的生命周期中的所有细节对于使用端都是知道的,如果在没有IOC容器的前提下,ioc就没有必要存在

Spring 中什么是Aspect :

  aspect由pointcut和advice组成,既包含了横切逻辑的定义,也包括了连接点的定义,AOP就是负责实施切面的框架,它将切面所定义的横切逻辑编织到切面所指定的连接点中,AOP的工作重心在于如何将增强能力编织目标对象的连接点上,这里包含两个工作 

  1.如何通过pointcut和advice定位到特定的jointpoint

  2.如何advice中编写切面代码

Spring 框架中都用到了哪些设计模式?

  1. 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
  2. 单例模式:Bean默认为单例模式。
  3. 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
  4. 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
  5. 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。

什么是Spring IOC 容器?

控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。

Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。

Spring 中的 IoC 的实现原理就是工厂模式加反射机制。

IoC(控制反转)主要指将对象的创建权交给外界(例如Spring容器),这里的交给外界就是控制反转的过程,在对象创建过程中普遍会依赖其他的对象资源,所以需要外界对原始对象进行属性依赖的赋值操作,这个过程就是DI(依赖注入)。在这里我们要清楚是将手动创建对象的权利反转给Spring容器,Spring容器对创建的对象进行依赖注入。所以IoC和DI是相辅相成的搭档,IoC需要DI进行依赖的赋值,他们都是为实现解耦而服务的。

Spring IOC的功能

依赖注入  依赖检查  自动装配  支持集合 指定初始化方法和销毁方法

支持回调某些方法(但是需要实现 Spring 接口,略有侵入)

SpringBoot 和 Spring Cloud知识点_作用域_09

SpringBoot 和 Spring Cloud知识点_构造器_10

@Component和@Bean的区别

SpringBoot 和 Spring Cloud知识点_构造器_11

Spring IOC源码流程

IOC 容器中存放了程序需要的所有的对象,这些对象只会被创建一次(单例),容器将这些对象包装成一个个 bean(带有对象信息以及该对象的结构体),IOC 容器简单来说不过是一个以该对象名称为 key、对应 bean 为 value 的 map

BeanFactory 接口定义了容器的最基本功能,它可以读取类的配置文档,管理类的加载,实例化,维护类之间的依赖关系。实现了此接口的类才能叫容器

refresh重建工厂

refresh() 方法重建,refresh() 会将原来的容器销毁,然后再重新执行一次初始化操作

这个接口非常重要,配置文件被Springxml解析后,这里的 BeanDefinition 就是我们所说的 Spring 的 Bean,我们配置的一个个类的信息都会存放在一个个BeanDefinition中

读取完配置之后,会以 bean name 为键,bean definition 为值,存放在一个 map 中,这个过程叫注册 bean

使用 map 保持别名,遇到别名时,先把别名变成 beanName

bean 覆盖:指多个 bean 的名字相同,默认情况下 spring 支持 bean 覆盖,在注册 bean 遇到 bean 覆盖时会这样处理

getBean:

在以上这些结束后,Spring 遍历 map 集合,将所有的 bean 初始化,初始化的过程封装到了 getBean 方法里

getBean 应该是我们获取 bean 使用的方法(你也可以使用这个方法来获取对象),所以这个方法有一层判断,如果在单例池中存在这个 bean,直接返回这个 bean

createBean 实例化时分两种情况,存在方法覆写时使用 CGLIB 代理返回一个代理对象,没有就使用 java 反射直接创建一个 bean 对象实例(之间可能还会使用有参构造创建对象或者无参构造创建对象)

随后会进入 DI 的流程,在依赖注入的时候,如果发现需要注入的对象尚未初始化,还需要触发注入对象的初始化动作,同时在注入的时候也会分为按名称注入和按类型注入(除此之外还有构造器注入等方式)


Spring Bean生命周期

SpringBoot 和 Spring Cloud知识点_构造器_12

BeanFactory和ApplicationContext的区别

SpringBoot 和 Spring Cloud知识点_spring_13

SpringBoot 和 Spring Cloud知识点_spring_14

BeanFactory和FactoryBean区别

SpringBoot 和 Spring Cloud知识点_作用域_15

ApplicationContext的通常实现

FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。

ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。

WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。

控制反转(IoC)有什么作用

  • 管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
  • 解耦,由容器去维护具体的对象
  • 托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的

IOC的优点是什么?

  • IOC 或 依赖注入把应用的代码量降到最低。
  • 它使应用容易测试,单元测试不再需要单例和JNDI查找机制。
  • 最小的代价和最小的侵入性使松散耦合得以实现。
  • IOC容器支持加载服务时的饿汉式初始化和懒加载。

什么是Spring的依赖注入?

控制反转IoC是一个很大的概念,可以用不同的方式来实现。其主要实现方式有两种:依赖注入和依赖查找

依赖注入:相对于IoC而言,依赖注入(DI)更加准确地描述了IoC的设计理念。所谓依赖注入(Dependency Injection),即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。

依赖注入的基本原则

依赖注入的基本原则是:应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责,“查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。容器全权负责组件的装配,它会把符合依赖关系的对象通过属性(JavaBean中的setter)或者是构造器传递给需要的对象。

依赖注入有什么优势

依赖注入之所以更流行是因为它是一种更可取的方式:让容器全权负责依赖查询,受管组件只需要暴露JavaBean的setter方法或者带参数的构造器或者接口,使容器可以在初始化时组装对象的依赖关系。其与依赖查找方式相比,主要优势为:

  • 查找定位操作与应用代码完全无关。
  • 不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。
  • 不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器。

SpringBoot 和 Spring Cloud知识点_作用域_16

SpringBoot 和 Spring Cloud知识点_作用域_17


Spring框架中的单例bean是线程安全的吗?

不是,Spring框架中的单例bean不是线程安全的。

spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。

实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。

  • 有状态就是有数据存储功能。
  • 无状态就是不会保存数据。

在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声

明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。

ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。

ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

Spring中@Primary注解

Spring的@Primary注解,该注解是框架在3.0版中引入的。

其作用与功能,当有多个相同类型的bean时,使用@Primary来赋予bean更高的优先级

Spring xml配置和注解的区别

XML 配置可以和注解混合使用,但是混合使用的话,XML 配置会覆盖 annotation (注解),因此并不推荐混合使用。

Spring @ControllerAdvice

这是一个增强的 Controller,对controller层做异常处理、数据预处理、全局数据绑定, springboot 会自动扫描到,不需要调用。

SpringBoot 和 Spring Cloud知识点_作用域_18

BeanPostProcessor注册时机与执行顺序

SpringBoot 和 Spring Cloud知识点_作用域_19

SpringBoot 和 Spring Cloud知识点_spring_20

SpringBoot 和 Spring Cloud知识点_作用域_21

在Spring中,@Service默认都是单例的。用了私有全局变量,若不想影响下次请求,就需要用到原型模式,即@Scope(“prototype”)

所谓单例,就是Spring的IOC机制只创建该类的一个实例,每次请求,都会用这同一个实例进行处理,因此若存在全局变量,本次请求的值肯定会影响下一次请求时该变量的值。
原型模式,指的是每次调用时,会重新创建该类的一个实例,比较类似于我们自己自己new的对象实例。

Spring解决循环依赖

SpringBoot 和 Spring Cloud知识点_构造器_22

SpringBoot 和 Spring Cloud知识点_spring_23

SpringBoot 和 Spring Cloud知识点_构造器_24

SpringBoot 和 Spring Cloud知识点_构造器_25

  • 手动设置了 allowCircularReferences=false,则表示不允许循环依赖


spring内部有三级缓存:

成品:完整的Bean,是当一个Bean完全创建后(完成属性填充、彻底的完成初始化)才put进去, 所以,是成品。

半成品:只要通过 getObject() 方法从三级缓存的BeanFactory中取出Bean一次,原材料就变成半成品,就put到二级缓存 , 所以,二级缓存里边的bean,都是半成品。

原材料工厂: 三级缓存 singletonFactories ,用于保存bean的创建工厂但是是不完整的Bean的工厂Factory,是当一个Bean在new之后 (没有属性填充、初始化),就put进去。所以,是原材料工厂。

SpringBoot 和 Spring Cloud知识点_作用域_26

SpringBoot 和 Spring Cloud知识点_作用域_27

SpringBoot 和 Spring Cloud知识点_构造器_28

SpringBoot 和 Spring Cloud知识点_构造器_29

TestService1注入到TestService3又需要从第三级缓存中获取实例,而第三级缓存里保存的并非真正的实例对象,而是ObjectFactory对象。

说白了,两次从三级缓存中获取都是ObjectFactory对象,而通过它创建的实例对象每次可能都不一样的。

这样不是有问题?

为了解决这个问题,spring引入的第二级缓存。前一个图其实TestService1对象的实例已经被添加到第二级缓存中了,而在TestService1注入到TestService3时,只用从第二级缓存中获取该对象即可。

SpringBoot 和 Spring Cloud知识点_spring_30

SpringBoot 和 Spring Cloud知识点_构造器_31

Spring AOP

(1)连接点(Join point):指程序运行过程中所执行的方法。在Spring AOP中,一个连接点总代表一个方法的执行。

(2)切面(Aspect):被抽取出来的公共模块,可以用来会横切多个对象。Aspect切面可以看成 Pointcut切点 和 Advice通知 的结合,一个切面可以由多个切点和通知组成。

在Spring AOP中,切面可以在类上使用 @AspectJ 注解来实现。

(3)切点(Pointcut):切点用于定义 要对哪些Join point进行拦截。

切点分为execution方式和annotation方式。execution方式可以用路径表达式指定对哪些方法拦截,比如指定拦截add*、search*。annotation方式可以指定被哪些注解修饰的代码进行拦截。

(4)通知(Advice):指要在连接点(Join Point)上执行的动作,即增强的逻辑,比如权限校验和、日志记录等。通知有各种类型,包括Around、Before、After、After returning、After throwing。

(5)目标对象(Target):包含连接点的对象,也称作被通知(Advice)的对象。 由于Spring AOP是通过动态代理实现的,所以这个对象永远是一个代理对象。

(6)织入(Weaving):通过动态代理,在目标对象(Target)的方法(即连接点Join point)中执行增强逻辑(Advice)的过程。

(7)引入(Introduction):添加额外的方法或者字段到被通知的类。Spring允许引入新的接口(以及对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制

+++++++++++++++++++++++++++++++++

通过在代理类中包裹切面,Spring在运行期把切面织入到Spring管理的bean中。代理封装了目标类,

并拦截被通知方法的调用,再把调用转发给真正的目标bean。当代理拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。

(1)AspectJ是静态代理,也称为编译时增强,AOP框架会在编译阶段生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。

(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法

(3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。

SpringBoot 和 Spring Cloud知识点_spring_32

(1)没有异常情况下的执行顺序:

around before advice

before advice

target method 执行

after advice

around after advice

afterReturning advice

JDK动态代理和CGLIB动态代理

SpringBoot 和 Spring Cloud知识点_构造器_33

什么是切面

aspect 由 pointcount 和 advice 组成,切面是通知和切点的结合。 它既包含了横切逻辑的定义, 也包括 连接点的定义. Spring AOP 就是负责实施切面的框架, 它将切面所定义的横切逻辑编织到切面所指定的连接点中.

AOP 的工作重心在于如何将增强编织目标对象的连接点上, 这里包含两个工作:

如何通过 pointcut 和 advice 定位到特定的 joinpoint 上

如何在 advice 中编写切面代码.

SpringBoot 和 Spring Cloud知识点_构造器_34

AOP动态代理装配过程

SpringBoot 和 Spring Cloud知识点_构造器_35

AOP动态代理执行流程

SpringBoot 和 Spring Cloud知识点_构造器_36

执行被@Transactional注解的方法时,首先会被AOP切面代理拦截, 先执行 事务的动态代理,再执行业务方法。 在事务的动态代理动态代理对象中,进行事务的 开启、提交、或者回滚。

AOP事务执行流程

SpringBoot 和 Spring Cloud知识点_作用域_37


Spring事务管理

Spring为我们提供了两种的事务管理,编程式事务管理和声明式事务管理,因为spring提倡使用声明式事务管理,所以笔者本身也具体学习研究和应用声明式事务,这里只详细谈谈声明式事务,而编程式事务就一句概括;

编程式事务:使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。

声明式事务:spring的声明式事务的实现方式有两种,一种是基于AOP切面实现声明式事务,而第二种是基于注解@ Transacitional实现,但是两种都需要在sprig的xml配置才能启动事务管理

SpringBoot 和 Spring Cloud知识点_构造器_38

SpringBoot 和 Spring Cloud知识点_作用域_39

@Transactional注解使用注意

1. @Transactional 注解只能用在public 方法上,如果用在protected或者private的方法上,不会报错,但是该注解不会生效。

2. 默认情况下,@Transactional注解只能回滚非检查型异常,具体为RuntimeException及其子类和Error子类。

SpringBoot 和 Spring Cloud知识点_构造器_40

Spring事务源码分析

我们从 @EnableTransactionManagement 注解聊起

AutoProxyRegistrar.class:注册AOP处理器

ProxyTransactionManagementConfiguration.class:代理事务配置

我们在方法上写上 @EnableTransactionManagement 注解,Spring 会注册一个 BeanFactoryTransactionAttributeSourceAdvisor 的类

创建对应的方法 Bean 时,会和 AOP 一样,利用该 Advisor 类生成对应的代理对象

最终调用方法时,会调用代理对象,并通过环绕增强来达到事务的功能。

动态代理执行源码分析

代理对象运行时,会拿到所有的 Interceptor并进行排序,责任链递归运行

SpringBoot 和 Spring Cloud知识点_spring_41

SpringBoot 和 Spring Cloud知识点_构造器_42

从上面的看到

在业务方法前面,开启事务 ;业务方法后面,捕获异常,执行事务回滚 ;如果没有异常,提交事务

事务执行源码

SpringBoot 和 Spring Cloud知识点_构造器_43

SpringBoot 和 Spring Cloud知识点_构造器_44

SpringBoot 和 Spring Cloud知识点_作用域_45

提交事务源码

SpringBoot 和 Spring Cloud知识点_spring_46

SpringBoot 和 Spring Cloud知识点_构造器_47

SpringBoot 和 Spring Cloud知识点_作用域_48

回滚事务源码

SpringBoot 和 Spring Cloud知识点_spring_49

++++++++++++++++++++++++++++++++++++++++++

Spring Boot 有哪些优点?

Spring Boot 主要有如下优点:

  1. 容易上手,提升开发效率,为 Spring 开发提供一个更快、更广泛的入门体验。
  2. 开箱即用,远离繁琐的配置。
  3. 提供了一系列大型项目通用的非业务性功能,例如:内嵌服务器、安全管理、运行数据监控、运行状况检查和外部化配置等。
  4. 没有代码生成,也不需要XML配置。
  5. 避免大量的 Maven 导入和各种版本冲突。

为什么要用SpringBoot?

  • 为了解决java开发中的,繁多的配置、底下的开发效率,复杂的部署流程,和第三方技术集成难度大的问题,产生了spring boot。
  • springboot 使用 “习惯优于配置”的理念让项目快速运行起来,使用springboot很容易创建一个独立运行的jar,内嵌servlet容器
  • springboot的核心功能一:独立运行spring项目,springboot可以以jar包的形式独立运行,运行一个springboot项目只需要 java -jar xxx.jar 来运行
  • springboot的核心功能二:内嵌servlet容器,可以内嵌tomcat,接天jetty,或者undertow,这样我们就可以不用war包形式部署项目
  • springboot的核心功能三,提供starter简化maven配置,spring提供了一系列starter pom 来简化maven的依赖加载, 当使用了 spring-boot-starter-web时,会自动加载所需要的依赖包
  • springboot的核心功能三:自动配置spring sprintboot 会根据在类路径的jar包,类,为jar包中的类自动配置bean,这样会极大的减少使用的配置,会根据启动类所在的目录,自动配置bean;添加一个 springboot-starter-web 启动器就能拥有 web 的功能,无需其他配置。

Spring Boot集成了绝大部分目前流行的开发框架,就像Maven集成了所有的JAR包一样,Spring Boot集成了几乎所有的框架,使得开发者能快速搭建Spring项目。

(3)Spring Boot 是由 Pivotal 团队提供的基于 Spring 的全新框架,旨在简化 Spring 应用的初始搭建和开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。

Spring Boot 的核心设计思想是“约定优于配置”。基于这一设计原则,Spring Boot 极大地简化了项目和框架的配置。比如在使用 Spring 开发 Web 项目时,我们需要配置 web.xml、Spring 和 MyBatis 等,还需要将它们集成在一起。而使用 Spring Boot 一切将变得极其简单,它采用了大量的默认配置来简化这些文件的配置过程,只需引入对应的 Starters(启动器)。

SpringBoot 和 Spring Cloud知识点_作用域_50


什么是 Spring Boot Stater ?

启动器是一套方便的依赖描述符,它可以放在自己的程序中。你可以一站式的获取你所需要的 Spring 和相关技术,而不需要依赖描述符的通过示例代码搜索和复制黏贴的负载。

Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,可以一站式集成 Spring和其他技术,而不需要到处找示例代码和依赖包。Spring Boot Starter的工作原理是:

Spring Boot在启动时扫描项目所依赖的JAR包,寻找包含spring.factories文件的JAR包,根据spring.factories配置加载AutoConfigure类,根据@Conditional注解的条件,进行自动配置并将Bean注入Spring Context。

例如,如果你想使用 Sping 和 JPA 访问数据库,只需要你的项目包含 spring-boot-starter-data-jpa 依赖项,你就可以完美进行。

Spring JavaConfig 是 Spring 社区的产品,它提供了配置 Spring IoC 容器的纯Java 方法。因此它有助于避免使用 XML 配置。使用 JavaConfig 的优点在于:

(1)面向对象的配置。由于配置被定义为 JavaConfig 中的类,因此用户可以充分利用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean 方法等。

(2)减少或消除 XML 配置。基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在 XML 和 Java 之间来回切换。JavaConfig 为开发人员提供了一种纯 Java 方法来配置与 XML 配置概念相似的 Spring 容器。从技术角度来讲,只使用 JavaConfig 配置类来配置容器是可行的,但实际上很多人认为将JavaConfig 与 XML 混合匹配是理想的。

(3)类型安全和重构友好。JavaConfig 提供了一种类型安全的方法来配置 Spring容器。由于 Java 5.0 对泛型的支持,现在可以按类型而不是按名称检索 bean,不需要任何强制转换或基于字符串的查找。

SpringBoot 和 Spring Cloud知识点_作用域_51

SpringBoot的核心注解

@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。

@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源

自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。

@ComponentScan:Spring组件扫描。


Spring Boot 自动配置原理是什么?

注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,

@EnableAutoConfiguration 给容器导入META-INF/spring.factories 里定义的自动配置类。

筛选有效的自动配置类。

每一个自动配置类结合对应的 xxxProperties.java 读取配置文件进行自动配置功能

SpringBoot配置加载顺序

Spring Boot 中读取配置文件有以下 5 种方法:

使用 @Value 读取配置文件。

使用 @ConfigurationProperties 读取配置文件。

使用 Environment 读取配置文件。

使用 @PropertySource 读取配置文件。

使用原生方式读取配置文件

顺序如下

1)properties文件;

2)YAML文件;

3)系统环境变量;

4)命令行参数

SpringBoot 和 Spring Cloud知识点_构造器_52

Spring profiles

Spring Profiles 允许用户根据不同配置文件(dev,test,prod 等)来注册 bean。因此,当应用程序 在开发中运行时,只有某些 bean 可以加载,而在 PRODUCTION中,某些其他 bean 可以加载。假设我们的要求是 Swagger 文档仅适用于 QA 环境,并且禁用所有其他文档。这可以使用配置文件来完成。

SpringBoot解决跨域问题

跨域可以在前端通过 JSONP 来解决,但是 JSONP 只可以发送 GET 请求,无法发送其他类型的请求,在 RESTful 风格的应用中,就显得非常鸡肋,因此我们推荐在后端通过 (CORS,Cross-origin resource sharing) 来解决跨域问题。

SpringBoot Actuator

Spring Boot 提供监视器端点以监控各个微服务的度量。这些端点对于获取有关应用程序的信息(如它们是否已启动)以及它们的组件(如数据库等)是否正常运行很有帮助。但是,使用监视器的一个主要

缺点或困难是,我们必须单独打开应用程序的知识点以了解其状态或健康状况。想象一下涉及 50 个应用程序的微服务,管理员将不得不击中所有 50 个应用程序的执行终端。为了帮助我们处理这种情况,

我们将使用位于的开源项目。 它建立在 Spring Boot Actuator 之上,它提供了一个 Web UI,使我们能够可视化多个应用程序的度。

SpringBoot 热部署

SpringBoot 和 Spring Cloud知识点_构造器_53

RequestMapping和GetMapping的不同之处在哪里

RequestMapping具有类属性的,可以进行GET、POST、PUT或者其他的注释中具有的请求方法。

GetMapping是Get请求方法中的一个特例,它只是RequestMapping的一个延伸,目的是为了提高清晰度。

SpringBoot parent作用

spring-boot-starter-parent是Spring Boot的一个parent pom,它包含许多通用的配置和依赖项,可以让我们更方便地创建基于Spring Boot的项目。在创建一个标准的Spring Boot项目时,一般都会继承spring-boot-starter-parent,来确保项目构建和依赖管理的效果更加稳定和一致。

SpringBoot2.x的新特性

SpringBoot 和 Spring Cloud知识点_构造器_54

SpringBoot 的jar包 无法被其他项目引用依赖

SpringBoot异常处理

使用注解@ExceptionHandler 可以将一个方法指定为异常处理方法。该注解只有一个可选属性 value,为一个 Class数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。

一般把异常处理方法专门定义在一个类中,作为全局的异常处理类。 需要使用注解@ControllerAdvice,字面理解就是“控制器增强”,是给控制器对象增强功能的。

+++++++++++++++++++++++++++++++++++++++++

Spring Cloud 总结

Rest和RPC区别

RPC 是一种通信协议,它允许应用程序在不同的计算机上相互调用远程服务。简单来说,它就是一种让客户端应用程序可以调用远程服务器的方式。在 RPC 中,客户端应用程序可以像调用本地函数一样调用远程服务器上的函数,而无需了解底层网络细节。RPC 通常使用 XML 或 JSON 等格式来传输数据,常见的实现包括 gRPC 和 Thrift 等。

REST 是一种基于 HTTP 协议的架构风格,它主要用于分布式系统中的 Web 服务。在 REST 中,每个资源都有一个唯一的标识符(URI),通过 HTTP 协议的 GET、POST、PUT、DELETE 等方法来对资源进行操作。REST 使用 JSON 或 XML 等格式来传输数据,并使用 HTTP 状态码来表示响应结果。

1.范式:RPC 是一种面向方法的范式,它强调远程调用函数,客户端和服务器之间传递的数据是通过序列化方法的参数和返回值来实现的。而 REST 是一种面向资源的范式,它强调对 URI 所代表的资源进行操作。

2.协议:RPC 通常使用自定义协议或基于 TCP 的协议,它的性能和效率较高。而 REST 使用 HTTP 协议,它的优点是易于扩展和与现有的基于 Web 的系统进行集成。

3.数据格式:RPC 通常使用二进制格式传输数据,如 Protobuf 和 MessagePack 等。而 REST 使用 JSON、XML 等文本格式传输数据,这些格式易于阅读和解析。

4.编程模型:RPC 支持多种编程语言和平台,如 Java、C++、Python 等。而 REST 是基于 HTTP 协议的,只要能够发送 HTTP 请求和解析 HTTP 响应的语言都可以使用 REST。

5.适用场景:RPC 适用于对性能要求较高,且客户端和服务器之间的数据交互较为复杂的场景。而 REST 适用于对数据格式和安全性要求较高,且需要易于扩展和集成的场景。

微服务定义

微服务就是一个独立的职责单一的服务应用程序。在 intellij idea 工具里面就是用maven开发的一个个独立的module,具体就是使用springboot 开发的一个小的模块,处理单一专业的业务逻辑,一个模块只做一个事情。将传统的all in one应用,根据业务功能的不同,拆分成一个个的服务,去掉以往的严重耦合现象,每个服务提供单个业务功能。微服务强调的是服务大小,关注的是某一个点,具体解决某一个问题/落地对应的一个服务应用,可以看做是idea 里面一个 module。

SpringBoot 和 Spring Cloud知识点_spring_55

微服务优点

(1)模块化和组件化的设计,允许独立开发或更新某个服务,而不需要全面升级整个系统来实现;

(2)基于轻量级通信协议和API,允许各个服务之间相互协作,提高系统的整体性能和可扩展性;

(3)高度的可靠性和鲁棒性,某个服务出现故障不会影响整个系统正常运行;

(4)更加灵活的部署和伸缩能力,可以快速响应变化的业务需求。

SpringBoot 和 Spring Cloud知识点_作用域_56

微服务缺点

(1)优点:松耦合,聚焦单一业务功能,无关开发语言,团队规模降低。在开发中,不需要了解多有业务,只专注于当前功能,便利集中,功能小而精。微服务一个功能受损,对其他功能影响并不是太大,可以快速定位问题。微服务只专注于当前业务逻辑代码,不会和 html、css 或其他界面进行混合。可以灵活搭配技术,独立性比较舒服。

(2)缺点:随着服务数量增加,管理复杂,部署复杂,服务器需要增多,服务通信和调用压力增大,运维工程师压力增大,人力资源增多,系统依赖增强,数据一致性,性能监控

微服务架构特点:

(1)模块化和组件化的设计,允许独立开发或更新某个服务,而不需要全面升级整个系统来实现;

(2)基于轻量级通信协议和API,允许各个服务之间相互协作,提高系统的整体性能和可扩展性;

(3)高度的可靠性和鲁棒性,某个服务出现故障不会影响整个系统正常运行;

(4)更加灵活的部署和伸缩能力,可以快速响应变化的业务需求


Spring Cloud和dubbo区别

(1)服务调用方式:dubbo是RPC,Spring Cloud是Rest Api。

(2)注册中心:dubbo 是zookeeper,Spring Cloud可以是zookeeper或其他。

(3)服务网关:dubbo本身没有实现,只能通过其他第三方技术整合,springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持断路器,与git完美集成配置文件支持版本控制,事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。

(4)架构完整度:Spring Cloud包含诸多微服务组件要素,完整度上比Dubbo高。

微服务通信

同步:RPC ,REST等。

异步:消息队列,要考虑消息的可靠传输、高性能,以及编程模型的变化等。

微服务拆分原则

AKF 立方体也叫做scala cube,它在《The Art of Scalability》一书中被首次提出,旨在提供一个系统化的扩展思路。AKF 把系统扩展分为以下三个维度:

X 轴:直接水平复制应用进程来扩展系统。

Y 轴:将功能拆分出来扩展系统。

Z 轴:基于用户信息扩展系统。

SpringBoot 和 Spring Cloud知识点_作用域_57

SpringBoot 和 Spring Cloud知识点_构造器_58

SpringBoot 和 Spring Cloud知识点_构造器_59

SpringBoot 和 Spring Cloud知识点_构造器_60

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

优缺点

微服务的框架那么多比如:dubbo、Kubernetes,为什么就要使用Spring Cloud的呢?

优点:

产出于Spring大家族,Spring在企业级开发框架中无人能敌,来头很大,可以保证后续的更新、完善组件丰富,功能齐全。Spring Cloud 为微服务架构提供了非常完整的支持。例如、配置管理、服务发现、断路器、微服务网关等;Spring Cloud 社区活跃度很高,教程很丰富,遇到问题很容易找到解决方案服务拆分粒度更细,耦合度比较低,有利于资源重复利用,有利于提高开发效率可以更精准的制定优化服务方案,提高系统的可维护性减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发微服务可以是跨平台的,可以用任何一种语言开发适于互联网时代,产品迭代周期更短缺点:

微服务过多,治理成本高,不利于维护系统分布式系统开发的成本高(容错,分布式事务等)对团队挑战大总的来说优点大过于缺点,目前看来Spring Cloud是一套非常完善的分布式框架,目前很多企业开始用微服务、Spring Cloud的优势是显而易见的。因此对于想研究微服务架构的同学来说,学习Spring Cloud是一个不错的选择。

Dubbo使用的是RPC通信,二进制传输,占用带宽小 ;擅长短连接大数据量的服务提供方式

Spring Cloud使用的是Http Restful方式;很多基于Spring Boot来实现

Spring Cloud主要的子项目有哪些

Spring Cloud的子项目,大致可分成两类,一类是对现有成熟框架"Spring Boot化"的封装和抽象,也是数量最多的项目;第二类是开发了一部分分布式系统的基础设施的实现,如Spring Cloud Stream扮演的就是kafka, ActiveMQ这样的角色。

Spring Cloud Config

Spring Cloud Config 是一个解决分布式系统的配置管理方案。它包含 Client和 Server 两个部分,Server 提供配置文件的存储、以接口的形式将配置文件的内容提供出去,Client 通过接口获取数据、并依据此数据初始化自己的应用。Spring cloud 使用 git 或 svn 存放配置文件,默认情况下使用 git



Spring Cloud Netflix

Netflix OSS 开源组件集成,包括Eureka、Hystrix、Ribbon、Feign、Zuul等核心组件。

Eureka:服务治理组件,包括服务端的注册中心和客户端的服务发现机制;Ribbon:负载均衡的服务调用组件,具有多种负载均衡调用策略;Hystrix:服务容错组件,实现了断路器模式,为依赖服务的出错和延迟提供了容错能力;Feign:基于Ribbon和Hystrix的声明式服务调用组件;Zuul:API网关组件,对请求提供路由及过滤功能。

Spring Cloud Bus

用于传播集群状态变化的消息总线,使用轻量级消息代理链接分布式系统中的节点,可以用来动态刷新集群中的服务配置。

Spring Cloud Bus 是 Spring Cloud 的一个组件,用于在分布式系统中使用轻量级消息代理来传递消息。它使用轻量级消息代理(如 RabbitMQ 或 Kafka)来传递消息,并为各个服务之间的配置变更、路由信息等提供一种简单的分布式发布/订阅模式。

Spring Cloud Bus 是一个全局事件总线,通过 AMQP(高级消息队列协议)消息代理或 Redis 来链接 Spring Boot 应用程序。这使得在多个节点上运行的 Spring Boot 应用程序之间的通信变得简单而可靠,从而消除了重复代码和复杂的配置。

使用场景

配置刷新:通过消息广播机制,实现配置的动态刷新,避免了每个节点都需要手动刷新配置的问题。

事件驱动:通过消息代理的发布-订阅模式,实现事件的传递和驱动,使得各个节点能够响应和处理特定的事件。

监控与管理:可以通过消息总线来收集系统的监控数据和指标,实现分布式系统的集中管理和监控。

Spring Cloud Consul

Consul是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其它分布式服 务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实 现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 等)。 使用起来也较为简单。Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和 Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。

使用 Raft 算法来保证一致性, 比复杂的 Paxos 算法更直接. 相比较而言, zookeeper 采用的是 Paxos, 而 etcd 使用的则是 Raft。

支持多数据中心,内外网的服务采用不同的端口进行监听。 多数据中心集群可以避免单数据中心 的单点故障,而其部署则需要考虑网络延迟, 分片等情况等。 zookeeper 和 etcd 均不提供多数据中心功能的支持。

支持健康检查。 etcd 不提供此功能。

支持 http 和 dns 协议接口。 zookeeper 的集成较为复杂, etcd 只支持 http 协议。 官方提供 web 管理界面, etcd 无此功能。

Consul 是一个分布式高可用的系统,它包含多个组件,但作为一个整体在微服务架构中提供服务发现和服务配置的工具。Spring Cloud Consul 项目是针对Consul的服务治理实现。

特性:

  1. 服务发现
  2. 健康检查
  3. key/value 存储
  4. 多数据中心

SpringBoot 和 Spring Cloud知识点_spring_61

当 Producer 启动的时候,会向 Consul 发送一个 post 请求,告诉 Consul 自己的 IP 和 Port

Consul 接收到 Producer 的注册后,每隔10s(默认)会向 Producer 发送一个健康检查的请求,检验Producer是否健康

当 Consumer 发送 GET 方式请求 /api/address 到 Producer 时,会先从 Consul 中拿到一个存储服务 IP 和 Port 的临时表,从表中拿到 Producer 的 IP 和 Port 后再发送 GET 方式请求 /api/address

该临时表每隔10s会更新,只包含有通过了健康检查的 Producer

consul与eureka比较

1.Consul 强一致性(C)

服务注册相比 Eureka 会稍慢一些。因为 Consul 的 raft 协议   要求必须过半数的节点都写入成功才认为注册成功  ;raft 协议:Raft

 Leader 挂掉时,重新选举期间整个 Consul 不可用。保证了强一致性但牺牲了可用性。

Eureka 保证高可用(A)和最终一致性:

服务注册相对要快,因为不需要等注册信息复制replicate到其他节点,也不保证注册信息是否 replicate 成功

当数据出现不一致时,虽然A, B上的注册信息不完全相同,但每个Eureka节点依然能够正常对外提供服务,这会出现查询服务信息时如果请求A查不到,但请求B就能查到。如此保证了可用性但牺牲了一致性。

2,Consul实现了服务端的均衡负载功能

3,eureka 就是个 servlet 程序,跑在 servlet 容器中; Consul 则是 go 编写而成。

SpringBoot 和 Spring Cloud知识点_spring_62

consul组件

agent

组成 consul 集群的每个成员上都要运行一个 agent,可以通过 consul agent 命令来启动。agent可以运行在 server 状态或者 client 状态。自然的, 运行在 server 状态的节点被称为 server 节点,运行在 client 状态的节点被称 为 client 节点。

server 节点

负责组成 cluster 的复杂工作(选举server 自行选举一个 leader、状态维 护、转发请求到 leader),以及 consul 提供的服务(响应RPC 请求),以及存放和复制数据。考虑到容错和可用性,一般部署 3 ~ 5 个比较合适。

client 节点

负责转发所有的 RPC 到 server 节点。本身无状态,且轻量级,因此,可以部署大量的client 节点。

Spring Cloud Security

安全工具包,对Zuul代理中的负载均衡OAuth2客户端及登录认证进行支持。

Spring Cloud Security提供了一组用于构建安全应用程序和服务的原语,最小化。可以从外部(或集中)高度配置的声明式模型适用于通常使用中央契约管理服务的大型合作远程组件系统的实现。在像Cloud Foundry这样的服务平台上也很容易使用。基于Spring Boot和Spring安全性OAuth2,我们可以快速创建实现常见模式的系统,如单点登录,令牌中继和令牌交换。


Spring Cloud Sleuth

Spring Cloud应用程序的分布式请求链路跟踪,支持使用Zipkin、HTrace和基于日志(例如ELK)的跟踪。 

Trace ID和Span ID

Trace ID是一个唯一的标识,用于跟踪一个请求的整个处理过程。在一个分布式系统中,一个请求可能会经过多个服务节点,每个服务节点都会生成一个Span ID来表示它自己的处理过程,同时将Trace ID传递给下一个服务节点。因此,通过Trace ID和Span ID的组合,我们就可以将整个请求的处理过程进行链路追踪。

Span

Span是一个跨度,它代表了一个请求在一个服务节点的处理过程。Span包含了开始时间、结束时间、Span ID、父Span ID、Span名称、Span标签等信息。在Spring Cloud Sleuth中,每个Span都可以看作是一次调用,每个调用都有自己的Span ID和父Span ID,因此可以将整个调用链路构建出来。

Tracer

Tracer是Spring Cloud Sleuth中的核心组件,它用于创建和管理Span。在一个请求到来时,Tracer会生成一个Trace ID和一个Root Span,并将Trace ID和Root Span传递给下一个服务节点。在每个服务节点处理请求时,Tracer会生成一个新的Span,并将父Span ID设置为上一个服务节点的Span ID,然后将Trace ID和新生成的Span ID传递给下一个服务节点。因此,通过Tracer,我们可以实现整个请求链路的追踪和监控。

Span Exporter

Span Exporter是将Span发送到Zipkin或其他分布式追踪系统的组件。在Spring Cloud Sleuth中,我们可以配置Span Exporter来将Span发送到指定的追踪系统。



Spring Cloud Stream

轻量级事件驱动微服务框架,可以使用简单的声明式模型来发送及接收消息,主要实现为Apache Kafka及RabbitMQ。

Spring Cloud Task

用于快速构建短暂、有限数据处理任务的微服务框架,用于向应用中添加功能性和非功能性的特性。

Spring Cloud Zookeeper

基于Apache Zookeeper的服务治理组件。

SpringBoot Actuator

可以帮助你监控和管理SpringBoot应用,比如健康检查,审计,统计和HTTP追踪

微服务网关的作用

SpringBoot 和 Spring Cloud知识点_作用域_63


API网关

API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。

API 网关基本功能包含了 统一接入、协议适配、流量管理与容错、以及安全防护四大基本功能。

API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。

使用API网关的最大优点是,它封装了应用程序的内部结构。客户端只需要同网关交互,而不必调用特定的服务。API网关为每一类客户端提供了特定的API。这减少了客户端与应用程序间的交互次数,还简化了客户端代码。API网关也有一些不足,它增加了一个我们必须开发、部署和维护的高可用组件。为了暴露每个微服务的端点,开发人员必须更新API网关。

拥有强大的自定义插件系统可以自行扩展,提供友好的图形化配置界面,能够快速帮助企业进行 API 服务治理、提高 API 服务的稳定性和安全性。

将内部系统中重复的组件抽取出来并放置在网关上运行,如进行用户授权、访问控制、防火墙、数据转换等;

提供服务编排的功能,让企业可以快速从各类服务上获取需要的数据,对业务实现快速响应。

网关和过滤器的区别

传统的方式是使用过滤器拦截用户会话信息,只能用于单个服务。

微服务网关是整个微服务API请求的入口,可以实现API过滤,包括:用户登录验证、跨域解决、日志拦截、权限控制、限流、熔断、负载均衡、黑名单与白名单等。可隐藏具体业务的真实性,还可以解决代码冗余。

过滤器--局部拦截

网关--全局拦截


Zuul和Nginx对比

SpringBoot 和 Spring Cloud知识点_构造器_64

Zuul的工作原理

Zuul是通过Servlet来实现的,Zuul通过自定义的ZuulServlet(类似于Spring MVC的DispatcherServlet)来对请求进行控制。

Zuul的核心是一系列过滤器,可以在Http请求的发起和响应返回期间执行一系列的过滤器。Zuul包括以下4种过滤器:

PRE过滤器:它是在请求路由道具体的服务之前执行的,这种类型的过滤器可以做安全验证,例如身份验证、参数验证等。

ROUTING过滤器:它用于将请求路由到具体的微服务实例。在默认情况下,它使用Http Client进行网络请求。

POST过滤器:它是请求已被路由到微服务后执行的。一般情况下,用作收集统计信息、指标,以及将响应传送到客户端。

ERROR过滤器:它是在其他过滤器发生错误时执行的。

Zuul采取了动态读取、编译和运行这些过滤器。过滤器之间不能直接相互通信,而是通过RequestContext对象来共享数据,每个请求都会创建一个RequestContext对象。

继承zuulFilter需要实现四个方法,filterType、filterOrder、shouldFilter、run

filterType():过滤器执行类型。有以下四种类型pre、route、post、error

filterOrder():过滤器执行顺序,数字越大,优先级越低

shouldFilter():是否执行该过滤器,此处为true,则会执行run方法

run():过滤器具体执行逻辑

Zuul重要概念

动态路由表:Zuul支持Eureka路由,手动配置路由,这俩种都支持自动更新

路由定位:根据请求路径,Zuul有自己的一套定位服务规则以及路由表达式匹配

反向代理:客户端请求到路由网关,网关受理之后,在对目标发送请求,拿到响应之后在给客户端

Zuul如何限流

方式一:可以通过继承ZuulFilter抽象类自定义pre过滤器,加上限流算法,来实现

方式二:可以通过hystrix的资源隔离模式,设置线程池最大连接数或者最大信号量来实现

方式三:常用,Ratelimit,使用令牌桶算法

什么是Spring Cloud Gateway?

Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的控制,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制,监控,缓存,负载均衡等作用。

Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。它不能在传统的 servlet 容器中工作,也不能构建成 war 包。

Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等

它使用了一个RouteLocatorBuilder的bean去创建路由,除了创建路由,RouteLocatorBuilder也可以让你添加各种predicates和filters,predicates断言的意思,顾名思义就是根据具体的请求的规则,由具体的route去处理,filters是各种过滤器,用来对请求做各种判断和修改。

使用网关好处 :客户端如果和多个服务端通信

 1.客户端存在跨域请求,处理相对复杂

 2.认证复杂,每个服务都需要独立认证

 

zuul和Spring Cloud Gateway的比较

Zuul基于Servlet2.x构建,使用阻塞的API,没有内置限流过滤器,不支持长连接。

Spring Cloud Gateway基于Spring 5、Project Reactor、Spring Boot 2,使用非阻塞式的API,内置限流过滤器,支持长连接(比如 websockets),在高并发和后端服务响应慢的场景下比Zuul1的表现要好

gateway线程开销少,支持各种长连接、websocket,spring官方支持,但运维复杂,

zuul编程模型简单,开发调试运维简单,有线程数限制,延迟堵塞会耗尽线程连接资源

gateway对比zuul多依赖了spring-webflux,在spring的支持下,功能更强大,内部实现了限流、负载均衡等,扩展性也更强,但同时也限制了仅适合于Spring Cloud套件

 zuul则可以扩展至其他微服务框架中,其内部没有实现限流、负载均衡等。

SpringBoot 和 Spring Cloud知识点_构造器_65

Spring Cloud Gateway 底层使用了高性能的通信框架Netty

WebFlux 模块的名称是 spring-webflux,名称中的 Flux 来源于 Reactor 中的类 Flux。Spring webflux 有一个全新的非堵塞的函数式 Reactive Web 框架,可以用来构建异步的、非堵塞的、事件驱动的服务,在伸缩性方面表现非常好。使用非阻塞API。 Websockets得到支持,并且由于它与Spring紧密集成,所以将会是一个更好的 开发 体验。

 Zuul 1.x,是一个基于阻塞io的API Gateway。Zuul已经发布了Zuul 2.x,基于Netty,也是非阻塞的,支持长连接,但Spring Cloud暂时还没有整合计划

SpringBoot 和 Spring Cloud知识点_构造器_66


Spring Cloud OpenFeign

基于Ribbon和Hystrix的声明式服务调用组件,可以动态创建基于Spring MVC注解的接口实现用于服务调用,在Spring Cloud 2.0中已经取代Feign成为了一等公民。

Feign远程调用,核心就是通过一系列的封装和处理,将以JAVA注解的方式定义的远程调用API接口,最终转换成HTTP的请求形式,然后将HTTP的请求的响应结果,解码成JAVA Bean,返回给调用者。Feign远程调用的基本流程,大致如下图所示。

SpringBoot 和 Spring Cloud知识点_构造器_67

从上图可以看到,Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的 Request 请求。通过Feign以及JAVA的动态代理机制,使得Java 开发人员,可以不用通过HTTP框架去封装HTTP请求报文的方式,完成远程服务的HTTP调用 Feign 远程调用的重要组件

在微服务启动时,Feign会进行包扫描,对加@FeignClient注解的接口,按照注解的规则,创建远程接口的本地JDK Proxy代理实例。然后,将这些本地Proxy代理实例,注入到Spring IOC容器中。当远程接口的方法被调用,由Proxy代理实例去完成真正的远程访问,并且返回结果。

为了清晰的介绍SpringCloud中Feign运行机制和原理,在这里,首先为大家梳理一下Feign中几个重要组件。

SpringBoot 和 Spring Cloud知识点_构造器_68

注意,上面的代码中,在DemoClient 接口上,加有@FeignClient 注解。也即是说,Feign在启动时,会为其创建一个本地JDK Proxy代理实例,并注册到Spring IOC容器。

如何使用呢?可以通过@Resource注解,按照类型匹配(这里的类型为DemoClient接口类型),从Spring IOC容器找到这个代理实例,并且装配给需要的成员变量。

 调用处理器 InvocationHandler

大家知道,通过 JDK Proxy 生成动态代理类,核心步骤就是需要定制一个调用处理器,具体来说,就是实现JDK中位于java.lang.reflect 包中的 InvocationHandler 调用处理器接口,并且实现该接口的 invoke(…) 抽象方法。

为了创建Feign的远程接口的代理实现类,Feign提供了自己的一个默认的调用处理器,叫做 FeignInvocationHandler 类,该类处于 feign-core 核心jar包中。当然,调用处理器可以进行替换,如果Feign与Hystrix结合使用,则会替换成 HystrixInvocationHandler 调用处理器类,类处于 feign-hystrix 的jar包中。

默认的调用处理器 FeignInvocationHandler 是一个相对简单的类,有一个非常重要Map类型成员 dispatch 映射,保存着远程接口方法到MethodHandler方法处理器的映射。

以前面示例中DemoClient 接口为例,其代理实现类的调用处理器 FeignInvocationHandler 的dispatch 成员的内存结构图如图3所示。

SpringBoot 和 Spring Cloud知识点_作用域_69

为何在图3中的Map类型成员 dispatch 映射对象中,有两个Key-Value键值对呢?

原因是:默认的调用处理器 FeignInvocationHandle,在处理远程方法调用的时候,会根据Java反射的方法实例,在dispatch 映射对象中,找到对应的MethodHandler 方法处理器,然后交给MethodHandler 完成实际的HTTP请求和结果的处理。前面示例中的 DemoClient 远程调用接口,有两个远程调用方法,所以,其代理实现类的调用处理器 FeignInvocationHandler 的dispatch 成员,有两个有两个Key-Value键值对。

源码很简单,重点在于invoke(…)方法,虽然核心代码只有一行,但是其功能是复杂的:

(1)根据Java反射的方法实例,在dispatch 映射对象中,找到对应的MethodHandler 方法处理器;

(2)调用MethodHandler方法处理器的 invoke(...) 方法,完成实际的HTTP请求和结果的处理。

补充说明一下:MethodHandler 方法处理器,和JDK 动态代理机制中位于 java.lang.reflect 包的 InvocationHandler 调用处理器接口,没有任何的继承和实现关系。MethodHandler 仅仅是Feign自定义的,一个非常简单接口。

MethodHandler 的invoke(…)方法,主要职责是完成实际远程URL请求,然后返回解码后的远程URL的响应结果。Feign提供了默认的 SynchronousMethodHandler 实现类,提供了基本的远程URL的同步请求处理。有关 SynchronousMethodHandler类以及其与MethodHandler的关系,大致如图4所示。

SpringBoot 和 Spring Cloud知识点_spring_70

 Feign 客户端组件 feign.Client

客户端组件是Feign中一个非常重要的组件,负责端到端的执行URL请求。其核心的逻辑:发送request请求到服务器,并接收response响应后进行解码。

由于不同的feign.Client 实现类,内部完成HTTP请求的组件和技术不同,故,feign.Client 有多个不同的实现。这里举出几个例子:

(1)Client.Default类:默认的feign.Client 客户端实现类,内部使用HttpURLConnnection 完成URL请求处理;

(2)ApacheHttpClient 类:内部使用 Apache httpclient 开源组件完成URL请求处理的feign.Client 客户端实现类;

(3)OkHttpClient类:内部使用 OkHttp3 开源组件完成URL请求处理的feign.Client 客户端实现类。

(4)LoadBalancerFeignClient 类:内部使用 Ribben 负载均衡技术完成URL请求处理的feign.Client 客户端实现类。

此外,还有一些特殊场景使用的feign.Client客户端实现类,也可以定制自己的feign.Client实现类。下面对上面几个常见的客户端实现类,进行简要介绍

SpringBoot 和 Spring Cloud知识点_作用域_71


二:ApacheHttpClient类

ApacheHttpClient 客户端类的内部,使用 Apache HttpClient开源组件完成URL请求的处理。

从代码开发的角度而言,Apache HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,它不仅使客户端发送Http请求变得容易,而且也方便开发人员测试接口。既提高了开发的效率,也方便提高代码的健壮性。

从性能的角度而言,Apache HttpClient带有连接池的功能,具备优秀的HTTP连接的复用能力。关于带有连接池Apache HttpClient的性能提升倍数,具体可以参见后面的对比试验。

ApacheHttpClient 类处于 feign-httpclient 的专门jar包中,如果使用,还需要通过Maven依赖或者其他的方式,倒入配套版本的专门jar包。

三:OkHttpClient类

OkHttpClient 客户端类的内部,使用OkHttp3 开源组件完成URL请求处理。OkHttp3 开源组件由Square公司开发,用于替代HttpUrlConnection和Apache HttpClient。由于OkHttp3较好的支持 SPDY协议(SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。),从Android4.4开始,google已经开始将Android源码中的 HttpURLConnection 请求类使用OkHttp进行了替换。也就是说,对于Android 移动端APP开发来说,OkHttp3 组件,是基础的开发组件之一。

四:LoadBalancerFeignClient 类

LoadBalancerFeignClient 内部使用了 Ribben 客户端负载均衡技术完成URL请求处理。在原理上,简单的使用了delegate包装代理模式:Ribben负载均衡组件计算出合适的服务端server之后,由内部包装 delegate 代理客户端完成到服务端server的HTTP请求;所封装的 delegate 客户端代理实例的类型,可以是 Client.Default 默认客户端,也可以是 ApacheHttpClient 客户端类或OkHttpClient 高性能客户端类,还可以其他的定制的feign.Client 客户端实现类型。

Feign基本原理

Feigin是一种模板化,声明式的http客户端,feign可以通过注解绑定到接口上来简化Http请求访问。当然我们也可以在创建Feign对象时定制自定义解码器(xml或者json等格式解析)和错误处理。

Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,封装了http调用流程。

SpringBoot 和 Spring Cloud知识点_spring_72

SpringBoot 和 Spring Cloud知识点_构造器_73

Feign流程源码实现

首先通过@EnableFeignCleints注解开启FeignCleint。

根据Feign的规则实现接口,并加@FeignCleint注解。

程序启动后,会进行包扫描,扫描所有的@FeignCleint的注解的类,并将这些信息注入到ioc容器中。

当接口的方法被调用,通过jdk的代理,来生成具体的RequesTemplate。

RequesTemplate在生成Request

Request交给Client去处理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp

最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。

Feign的工作原理分为主要的三个步骤:

创建远程接口的本地代理实例

要使用Feign就必须在启动类上加上注解@EnableFeignCleints,这个注解就相当于Feign组件的一个入口,当使用@EnableFeignCleints后,程序启动后,会进行包扫描,扫描所有被@FeignCleint注解修饰的接口,通过JDK底层的动态代理来为远程接口创建代理实例,并且注册到IOC容器中。

封装Request对象并进行编码

当调用ProductClient接口中的getProductInfo()方法时,底层通过JDK动态代理交由Feign的代理类FeignInvocationHandler进行处理。

发送请求并对获取结果进行解码

feign配合Ribbon使用时提供的默认实现类LoadBalancerFeignClient,内部使用 Ribben 负载均衡技术完成URL请求处理的feign.Client客户端实现类。

后一步是针对不同的http状态码来做不同的处理,如果http状态码为成功,再判断是否有返回值,如果有返回值就对响应对象进行解码操作,无返回值直接返回null。




Eureka 和 Nacos 对比

Eureka是什么

Eureka 是Spring Cloud 微服务框架默认的也是推荐的服务注册中心,

由Netflix公司与2012将其开源出来,Eureka基于REST服务开发,主要用于实现AWS云的中服务定位,以实现中间层服务器的负载均衡和故障转移,遵循着CAP理论中的A(可用性)P(分区容错性)

Eureka详解

一个Eureka中分为eureka server和eureka clint,其中eurka server是作为服务的注册与发现中心,eureka client既可以作为服务的生产者,又可以作为服务的消费者, Eureka 2.0 后已经停止开源

SpringBoot 和 Spring Cloud知识点_构造器_74

Eureka的集群中,只要有一台Eureka还在,就能保证注册服务可用(保证A), 但是查到的信息可能不是最新的(无法保证C)。 如果15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障;进入自我保护模式,保护注册信息,不再删除注册数据。

Eviction 服务剔除当 Eureka Client 和 Eureka Server 不再有心跳时,Eureka Server 会将该服务实例从服务注册列表中删除,即服务剔除。

Cancel: 服务下线Eureka Client 在程序关闭时向 Eureka Server 发送取消请求。发送请求后,该客户端实例信息将从 Eureka Server 的实例注册表中删除。

远程调用:

当 Eureka Client 从注册中心获取到服务提供者信息后,就可以通过 Http 请求调用对应的服务;服务提供者有多个时,Eureka Client 客户端会通过 Ribbon 自动进行负载均衡。

Eureka保证AP

Eureka Server 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而 Eureka Client 在向某个 Eureka 注册时,如果发现连接失败,则会自动切换至其它节点。只要有一台 Eureka Server 还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。 无法保证CP。

Zookeeper保证CP

在ZooKeeper中,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举,但是问题在于,选举leader需要一定时间, 且选举期间整个ZooKeeper集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得ZooKeeper集群失去master节点是大概率事件,虽然服务最终能够恢复,但是在选举时间内导致服务注册长期不可用是难以容忍的。

所以Eureka在网络故障导致部分节点失去联系的情况下,只要有一个节点可用,那么注册和查询服务就可以正常使用,而不会像zookeeper那样使整个注册服务瘫痪,Eureka优先保证了可用性

Eureka 工作流程

SpringBoot 和 Spring Cloud知识点_spring_75

Eureka自我保护机制

Eureka 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务

Eureka 仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)

当网络稳定时,当前实例新的注册信息会被同步到其它节点中

Eureka 自我保护机制是为了防止误杀服务而提供的一个机制。当个别客户端出现心跳失联时,则认为是客户端的问题,剔除掉客户端;当 Eureka 捕获到大量的心跳失败时,则认为可能是网络问题,进入自我保护机制;当客户端心跳恢复时,Eureka 会自动退出自我保护机制。

如果在保护期内刚好这个服务提供者非正常下线了,此时服务消费者就会拿到一个无效的服务实例,即会调用失败。对于这个问题需要服务消费者端要有一些容错机制,如重试,断路器等。


Nacos是什么

Nacos支持多种协议,其中包括HTTP、Dubbo、gRPC和DNS等

Nacos是阿里巴巴最新开源的项目,提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。更敏捷和容易地构建、交付和管理微服务平台。

Nacos支持基于DNS和基于RPC的服务发现,动态配置服务(配置中心),动态DNS服务

SpringBoot 和 Spring Cloud知识点_作用域_76

Provider APP:服务提供者

Consumer APP:服务消费者

Name Server:通过VIP(Virtual IP)或DNS的方式实现Nacos高可用集群的服务路由

Nacos Server:Nacos服务提供者,里面包含的Open API是功能访问入口,Conig Service、Naming Service 是Nacos提供的配置服务、命名服务模块。Consitency Protocol是一致性协议,用来实现Nacos集群节点的数据同步,这里使用的是Raft算法(Etcd、Redis哨兵选举)

Nacos Console:控制台


SpringBoot 和 Spring Cloud知识点_作用域_77

Nacos的实现原理

1.客户端provider向nacos server的open api发起调用,把自己的服务地址链接,服务名称注册上去

2.nacos server与服务提供者provider建立心跳机制,用来检测服务状态

3.服务消费者consumer查询出提供服务实例列表

4.并且默认10s去nacos server拉取服务实例列表

5.当服务消费者检测到服务异常,基于UDP协议推送更新

6.服务消费者即可调用了


eureka只支持AP

nacos支持CP和AP两种

nacos是根据配置识别CP或AP模式,如果注册Nacos的client节点注册时是ephemeral=true即为临时节点,那么Naocs集群对这个client节点效果就是AP,反之则是CP,即不是临时节点

Nacos支持服务端主动检测提供者状态,临时实例采用心跳模式,非临时实例采用主动检测模式,临时实例心跳不正常会被剔除,非临时实例则不会被剔除

Nacos的主要功能

1、服务发现与服务健康检查

Nacos使服务更容易注册,并通过DNS或者http接口发现其他服务,Nacos还提供服务的实时健康检查,防止向不健康的主机或者服务实例发送请求。

2、动态配置管理

动态配置服务允许在所有的环境中以集中和动态的方式管理所有的服务配置。Nacos消除了在更新配置使重新部署应用程序,使配置的更改更加高效和灵活。

3、动态NDS服务

Nacos 提供基于DNS协议的服务发现能力,旨在支持异构语言服务发现,支持将注册在Nacos服务以域名的方式暴露端点,让第三方应用方便查阅和发现。

4、服务和元数据管理

Nacos能从微服务平台建设的角度管理数据中心的所有服务和元数据,包括服务的描述,生命周期、服务的静态依赖分析、服务的健康状态、流量管理、路由及安全策略。

Nacos命名空间namespace

Nacos 引入命名空间 Namespace 的概念来进行多环境配置和服务的管理及隔离。例如,你可能存在本地开发环境dev、测试环境test、生产环境prod 三个不同的环境,那么可以创建三个不同的 Namespace 区分不同的环境。

成功创建新命名空间后,就可以在 springboot 的配置文件配置命名空间的 id 切换到对应的命名空间,并获取对应空间下的配置文件,但在没有指定命名空间配置的情况下,默认的配置都是在 public 空间中

Nacos服务分组

Group 也可以实现环境隔离的功能,但 Group 设计的目的主要是做同一个环境中的不同服务分组,把不同的微服务的配置文件划分到同一个分组里面去,Nacos 如果不指定 Group,则默认的分组是 DEFAULT_GROUP。

如果没有 Group,试想一下这个场景:有两个微服务,一个是订单系统,一个是用户系统,但是他们有着相同的配置,比如 datasource-url,那么如何区分呢?这时候 Group 就派上用场了。上述场景中订单系统、用户系统可以单独分为一个组,比如 ORDER_GROUP、USER_GROUP,当然这是比较细粒度的分组,根据企业的业务也可以多个微服务分为一组。

Nacos负载均衡

Nacos的负载均衡指的是,在进行服务发现时进行负载均衡,正常情况下,在进行服务发现时,会根据服务名从Nacos中拉取所有的实例信息,但是Nacos中提供了一个功能,就是可以在拉取实例时,可以根据随机策略只拉取到所有实例中的某一个,这就是Nacos中的负载均衡。

Nacos就近访问

首先,在Nacos中,一个服务可以有多个实例,并且可以给实例设置cluster-name,就是可以再进一步的给所有实例划分集群,那如果现在某个服务A想要调用服务B,那么Naocs会看调用服务A的实例是属于哪个集群的,并且调用服务B时,那就会调用同样集群下的服务B实例,根据cluster-name来判断两个实例是不是同一个集群,这就是Nacos的就近访问。

Nacos的CAP

Nacos中的配置中心其实没什么CP或AP,因为配置中心的数据是存在一个Mysql中的,只有注册中心的数据需要进行集群节点之间的同步,从而涉及到是CP还是AP,如果注册的节点是临时节点,那么就是AP,如果是非临时节点,那么就是CP,默认是临时节点。

Nacos自我保护机制

在Nacos集群中,当有大量服务实例下线或网络故障时,Nacos会开启自我保护机制,保证服务注册和发现的正常运行。

Nacos连接方式

  1. nacos使用的是netty和服务直接进行连接,属于长连接
  2. eureka是使用定时发送和服务进行联系,属于短连接

nacos:提供了nacos console可视化控制话界面,可以对实例列表进行监听,对实例进行上下线,权重的配置,并且config server提供了对服务实例提供配置中心,且可以对配置进行CRUD,版本管理

eureka:仅提供了实例列表,实例的状态,错误信息,相比于nacos过于简单

Nacos与Eureka相比优势如下:

nacos在自动或手动下线服务,使用消息机制通知客户端,服务实例的修改很快响应;Eureka只能通过任务定时剔除无效的服务。

nacos可以根据namespace命名空间,DataId,Group分组,来区分不同环境(dev,test,prod),不同项目的配置。

自我保护机制

相同点:保护阈值都是个比例,0-1 范围,表示健康的 instance 占全部instance 的比例。

不同点:

1)保护方式不同

Eureka保护方式:当在短时间内,统计续约失败的比例,如果达到一定阈值,则会触发自我保护的机制,在该机制下,Eureka Server不会剔除任何的微服务,等到正常后,再退出自我保护机制。自我保护开关(eureka.server.enable-self-preservation: false)

Nacos保护方式:当域名健康实例 (Instance) 占总服务实例(Instance) 的比例小于阈值时,无论实例 (Instance) 是否健康,都会将这个实例 (Instance) 返回给客户端。这样做虽然损失了一部分流量,但是保证了集群的剩余健康实例 (Instance) 能正常工作。


Nacos的阈值是针对某个具体Service的,而不是针对所有服务的,但Eureka的自我保护阈值是针对所有服务的。

Nacos有自己的配置中心,提供管理界面,eureka需要配合config实现配置中心,且不提供管理界面

SpringBoot 和 Spring Cloud知识点_作用域_78


Spring Cloud Ribbon

Ribbon是一个客户端负载均衡器,它可以按照一定规则来完成多台服务器负载均衡调用,这些规则还支持自定义。在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这次机器。我们也很容易使用Ribbon实现自定义的负载均衡算法

Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的接口模板(上面标的有访问地址),通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。而Feign则会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。Feign整合了Ribbon和Hystrix(关于Hystrix我们后面再讲),可以让我们不再需要显式地使用这两个组件

Eureka作为服务注册中心。Ribbon在Feign与Eureka之间充当中介,为客户端提供软件负载均衡算法。在Ribbon这一层中可以实现一个所谓的智能路由。

ribbon利用了RestTemplate的拦截器机制,在拦截器中实现ribbon的负载均衡。负载均衡的基本实现就是利用applicationName从服务注册中心获取可用的服务地址列表,然后通过一定算法负载,决定使用哪一个服务地址来进行http调用。



负载均衡算法

① RoundRobinRule:轮询选择,轮询下标,选择下标对应位置的Server;

② Random:随机选择Server;

③ RetryRule:对选定的负载均衡策略机上重试机制;在一个配置时间段内选择的Server不成功时,则一直尝试使用 subRule 的方式选择一个可用的Server;

④ AvailabilityFilteringRule:过滤掉一直连接失败的被标记为 circuit tripped (断路器状态)的后端Server,并过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个Server的运行状态。对剩下的使用轮询策略去访问。

⑤ BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态(tripped)的Server,然后选择一个并发量最小的Server

⑥ WeightedResponseTimeRule:根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低;根据平均响应时间计算所有服务的权重,响应时间越快的服务权重越大被选中的概率越大。刚启动时如果统计信息不足,则使用RoundRobinRule(轮询)策略,等统计信息足够,会切换到WeightedResponseTimeRule。

⑦ ZoneAvoidanceRule(默认):复合判断Server所在Zone的性能和Server的可用性选择Server,在没有Zone的情况下类是轮询。

Ribbon和Feign的区别

Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。Feign需要将调用的方法定义成抽象方法即可。

Nginx和Ribbon区别

Nginx:是客户端把所有的请求统一交给nginx,由nginx进行实现负载请求转发,属于服务器端负载均衡,既请求有nginx服务器端进行转发。

ribbon : 是从eureka注册中心服务器端上获取服务注册信息列表,缓存到本地,然后本地实现负载均衡策略达到均衡分发服务的目的。

Ribbon如何监控失效的实例

1、通过IPing检测实例,如果检测到某服务实例不存在/一定时间未响应,则会从持有服务列表中及时移除

2、保留zone的统计数据,ribbon可以避免可能访问失效的zone(剔除无实例、实例故障率大于阈值)


Hystrix 熔断降级限流

 1.降级(Fallback)

  当一个依赖的服务发生故障或超时时,Hystrix可以提供一个备用的、降级的响应,而不是返回错误或抛出异常。这有助于保持系统的一部分功能继续可用,而不会因依赖服务的问题而受到影响。

  2.熔断(Circuit Breaker)

  Hystrix引入了熔断器的概念,类似于电路中的熔断器。如果某个依赖的故障率超过了一定的阈值,Hystrix会打开熔断器,暂时阻止对该依赖的请求,以减轻负载和避免连锁故障。在一段时间后,熔断器会尝试半开状态,允许一部分请求通过,如果成功,就继续关闭熔断器,否则保持打开。

  3.资源隔离(Thread Pooling and Request Batching)

  Hystrix允许为不同的依赖服务配置独立的线程池,以确保某个依赖服务的问题不会影响到整个系统的线程资源。此外,Hystrix还支持请求批处理,可以将多个请求合并为一个,减少对依赖服务的负载。

  4.实时监控和度量

  Hystrix提供了实时监控和度量功能,可以通过仪表板查看依赖服务的性能指标,如请求成功率、失败率、响应时间等。这有助于运维人员及时发现和解决问题。

  5.自动恢复

  一旦依赖服务的故障率降低到可接受水平,Hystrix会自动恢复对该服务的正常请求处理,不再触发熔断机制。

  6.超时处理

  Hystrix可以配置每个依赖服务的超时时间,如果请求超时,它会被视为失败,并根据熔断策略进行处理。

  总的来说,Hystrix通过熔断、降级、资源隔离等策略,以及实时监控和度量来实现容错。它允许开发者在分布式系统中更好地处理依赖服务的故障,提高系统的可用性和稳定性。然而,需要注意的是,Hystrix在Netflix的官方GitHub仓库中已经宣布停止维护,推荐使用更先进的容错和断路器库,如Resilience4j或Sentinel。


Hystrix工作流程

【入口】通过HystrixCommand和HystrixCollapser注解修饰的方法,会被HystrixCommandAspect进行aop处理

2、【缓存】如RequestCache打开,则判断缓存是否命中

3、【熔断】判断是否有被开关、熔断降级,若被降级,则走降级逻辑;若未被降级,则走正常逻辑

4、【隔离】根据配置走线程池隔离或信号量隔离,任务满则走降级逻辑

5、【执行】执行任务,若任务执行失败或异常,则进入降级逻辑

6、【超时】通过定时器延时任务检测业务调用执行是否超时,若超时则取消业务执行的线程,进入降级逻辑;若未超时,则正常返回

7、【降级】若进入降级逻辑,根据getFallback()方法,返回降级处理的数据,若未实现该方法,则返回异常

8、【统计】业务调用执行结果成功、失败、超时等均会进入统计模块,通过健康统计结果来决定熔断器打开或关闭


Hystrix实现熔断的过程如下:

1. 当服务调用失败或超时时,Hystrix会记录这个事件,并根据预设的阈值进行判断。

2. 如果失败或超时的事件达到了预设的阈值,Hystrix会自动打开断路器,停止对该服务的调用。

3. 在断路器打开的状态下,Hystrix会自动切换到备用的服务或者返回预设的默认值。

4. 在一段时间内,Hystrix会定期地尝试调用服务,如果调用成功,则会关闭断路器,否则继续保持打开状态。

SpringBoot 和 Spring Cloud知识点_作用域_79

SpringBoot 和 Spring Cloud知识点_作用域_80

Hystrix 线程池隔离与信号量隔离

一、信号量隔离:

信号量隔离是一种资源隔离方式,它通过设置每个资源(或接口)的许可证数量来控制并发访问。当并发请求到达时,如果资源的许可证数量已经用尽,新的请求将被阻塞,以保护资源不被过度访问。信号量隔离适用于需要控制并发访问的场景,例如数据库连接、外部API调用等。

信号量模式从始至终都只有请求线程自身,是同步调用模式,不支持超时调用,不支持直接熔断,由于没有线程的切换,开销非常小。 不支持主动超时,不支持异步调用。

二、线程池隔离(默认):

线程池模式可以支持异步调用,支持超时调用,支持直接熔断,存在线程切换,开销大。

线程池开销大,在高并发下可能线程数不够用;优点是线程池是异步,对于网络访问请求,若发生超时,可以避免调用线程阻塞住。

线程隔离策略:开销比较大的时候,或者是请求比较耗时的时候,并发低时,我们最好是使用线程隔离策略。

信号量隔离策略:超大并发量的场景下,每个服务实例每秒都几百的QPS场景用信号量;另外请求缓存的这些服务的时候,我们可以使用信号量隔离策略,因为这类服务的返回通常会非常的快,不会占用容器线程太长时间,而且也减少了线程切换的一些开销,提高了缓存服务的效率。

THREAD(线程隔离):使用该方式,HystrixCommand将会在单独的线程上执行,并发请求受线程池中线程数量的限制。

SEMAPHORE(信号量隔离):使用该方式,HystrixCommand将会在调用线程上执行,开销相对较小,并发请求受信号量的个数的限制。

Sentinel 高可用治理

工作流程

  • NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;
  • ClusterBuilderSlot 则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;
  • StatisticSlot 则用于记录、统计不同纬度的 runtime 指标监控信息;
  • FlowSlot 则用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制;
  • AuthoritySlot 则根据配置的黑白名单和调用来源信息,来做黑白名单控制;
  • DegradeSlot 则通过统计信息以及预设的规则,来做熔断降级;
  • SystemSlot 则通过系统的状态,例如 load1 等,来控制总的入口流量;


SpringBoot 和 Spring Cloud知识点_作用域_81

SpringBoot 和 Spring Cloud知识点_构造器_82

流量控制

熔断降级

Sentinel 提供以下几种熔断策略:

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

网关流量控制

SpringBoot 和 Spring Cloud知识点_作用域_83

热点参数限流

SpringBoot 和 Spring Cloud知识点_构造器_84

SpringBoot 和 Spring Cloud知识点_构造器_85

SpringBoot 和 Spring Cloud知识点_作用域_86

SpringBoot 和 Spring Cloud知识点_构造器_87

SpringBoot 和 Spring Cloud知识点_作用域_88

来源访问控制

很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的黑白名单控制的功能。黑白名单根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

调用方信息通过 ContextUtil.enter(resourceName, origin) 方法中的 origin 参数传入。



分布式配置中心

1)web管理系统:后台可以使用图形管理界面管理配置文件,阿波罗有图形管理界面,springcloud config没有管理界面。

2)存储分布式配置文件的服务器(持久存储服务器):springcloud config 存储在版本控制器中,阿波罗存储在数据库中。

3)ConfigServer 缓存配置文件服务器(临时存储服务器)

4)ConfigClient 读取ConfigServer 中的配置文件信息

Spring Cloud Consul

consul是google开源的一个使用go语言开发的服务发现、配置管理中心服务。内置了服务注册与 发现框架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其 他工具(比如ZooKeeper等)。服务部署简单,只有一个可运行的二进制的包。每个节点都需要 运行agent,他有两种运行模式server和client。每个数据中心官方建议需要3或5个server节点以 保证数据安全,同时保证server-leader的选举能够正确的进行。

也是CP模式

可以用于服务注册、健康检查、负载均衡、故障恢复等方面。

1:服务发现: 解决在分布式环境中,如何找到可用的服务地址的问题,支持通过DNS和HTTP查询服务地址。

2:健康检查: 定时监控服务是否正常,对于异常的服务会主动下线。

3:键值存储: 配置中心解决方案,是一种key/value存储结构,区别就是key是以目录树结构形式组织的,可以用来存储系统配置信息。

4:多数据中心: 支持多数据中心部署。

使用 Raft 算法来保证一致性, 比复杂的 Paxos 算法更直接. 相比较而言, zookeeper 采用的是 ZAB, 而 etcd 使用的则是 Raft。

支持多数据中心,内外网的服务采用不同的端口进行监听。 多数据中心集群可以避免单数据中心 的单点故障,而其部署则需要考虑网络延迟, 分片等情况等。 zookeeper 和 etcd 均不提供多数据中心功能的支持。

Consul 工作原理

SpringBoot 和 Spring Cloud知识点_构造器_89


SpringBoot 和 Spring Cloud知识点_构造器_90


Consul组件

Consul主要有server和client两种组件组成。

1:server负责核心数据的存储和处理请求,server可以部署多个实例(通常推荐3-5个),server只有一个实例是leader实例,就是主节点,主节点是自动选举产生的,主节点负责处理数据的写入处理,同时将数据同步至其他server节点。

2:client负责跟server通信,处理转发服务注册、服务发现请求到server节点,client还负责服务的健康检查,client节点也可以部署多个实例,甚至每个微服务节点都部署一个client实例。

Consul键值存储

consul提供一个键值存储(k/v)数据库,我们使用这个特性,存储我们的应用配置、应用元数据和实现分布式锁。

consul支持命令方式和http api两种方式读写键值数据。


Consul 服务注册

1 Consul 的服务注册是通过 Agent 进程实现的。

2 当一个服务启动时,它会向 Consul 的 Agent 发送一个注册请求,Agent 会将服务的元数据存储在本地,并将服务的信息发送到 Consul 的 Server 上。

3 当服务停止时,它会向 Agent 发送一个注销请求,Agent 会将服务的元数据从本地删除,并将服务的信息从 Consul 的 Server 上删除。

Consul Agent

Consul安装之后,代理必须运行。 代理可以在服务器或客户端模式下运行。 每个数据中心都必须至少有一台服务器,但推荐使用3台或5台服务器。 一个单一的服务器部署是非常不推荐的,因为在故障情况下数据丢失是不可避免的。 所有其他代理以客户端模式运行。 客户端是一个非常轻量级的进程,它注册服务,运行健康检查,并将查询转发给服务器。 代理程序必须在集群中的每个节点上运行。

Consul 健康检查

Consul 的健康检查是通过 Agent 进程实现的。

当一个服务注册后,它会向 Consul 的 Agent 发送一个健康检查请求,Agent 会定期向服务发送健康检查请求,并根据服务的响应结果来判断服务的健康状态。

如果服务的健康状态发生变化,Agent 会将服务的状态信息发送到 Consul 的 Server 上,以便其他服务可以及时发现和处理。

Consul故障恢复

Consul 的故障恢复是通过 Agent 进程实现的。

当一个服务的健康状态发生变化时,Agent 会将服务的状态信息发送到 Consul 的 Server 上,并通知其他服务进行故障恢复。

如果一个服务无法访问其他服务,它会向 Consul 的 Agent 发送一个故障恢复请求,Agent 会返回一个可用的服务地址列表,并根据负载均衡算法选择一个地址进行访问。

Consul 负载均衡

Consul 的负载均衡是通过 Service Mesh 实现的。

当一个服务需要访问其他服务时,它会向 Consul 的 Agent 发送一个服务发现请求,Agent 会返回一个可用的服务地址列表,并根据负载均衡算法选择一个地址进行访问。

Consul 支持多种负载均衡算法,包括轮询、随机、加权轮询、加权随机等。

SpringBoot 和 Spring Cloud知识点_作用域_91


Spring Cloud Security

Spring Cloud Security提供了一组用于构建安全应用程序和服务的原语,最小化。可以从外部(或集中)高度配置的声明式模型适用于通常使用中央契约管理服务的大型合作远程组件系统的实现。在像Cloud Foundry这样的服务平台上也很容易使用。基于Spring Boot和Spring安全性OAuth2,我们可以快速创建实现常见模式的系统,如单点登录,令牌中继和令牌交换。

Spring Security是一套提供了完整的声明式安全访问控制的解决方案。

Spring Security是基于Spring AOP和Servlet过滤器的,它只能服务基于Spring的应用程序。

除常规认证和授权外,它还提供ACL,LDAP,JAAS,CAS等高级安全特性以满足复杂环境中的安全需求。

JWT

JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简介的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公钥/私钥对来签名,防止被篡改。

JWT令牌的优点:

jwt基于json,非常方便解析。

可以在令牌中自定义丰富的内容,易扩展。

通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。

资源服务使用JWT可不依赖授权服务即可完成授权。

缺点:JWT令牌较长,占存储空间比较大。

Dubbo基本理论和架构

SpringBoot 和 Spring Cloud知识点_spring_92

SpringBoot 和 Spring Cloud知识点_spring_93

  • 服务治理控制面。服务治理控制面不是特指如注册中心类的单个具体组件,而是对 Dubbo 治理体系的抽象表达。控制面包含协调服务发现的注册中心、流量管控策略、Dubbo Admin 控制台等,如果采用了 Service Mesh 架构则还包含 Istio 等服务网格控制面。
  • Dubbo 数据面。数据面代表集群部署的所有 Dubbo 进程,进程之间通过 RPC 协议实现数据交换,Dubbo 定义了微服务应用开发与调用规范并负责完成数据传输的编解码工作。
  • 服务消费者 (Dubbo Consumer),发起业务调用或 RPC 通信的 Dubbo 进程
  • 服务提供者 (Dubbo Provider),接收业务调用或 RPC 通信的 Dubbo 进程
  • RPC 服务定义、开发范式。比如 Dubbo 支持通过 IDL 定义服务,也支持编程语言特有的服务开发定义方式,如通过 Java Interface 定义服务。
  • RPC 服务发布与调用 API。Dubbo 支持同步、异步、Reactive Streaming 等服务调用编程模式,还支持请求上下文 API、设置超时时间等。
  • 服务治理策略、流程与适配方式等。作为服务框架数据面,Dubbo 定义了服务地址发现、负载均衡策略、基于规则的流量路由、Metrics 指标采集等服务治理抽象,并适配到特定的产品实现。

通信协议

Dubbo 从设计上不绑定任何一款特定通信协议,HTTP/2、REST、gRPC、JsonRPC、Thrift、Hessian2 等几乎所有主流的通信协议,Dubbo 框架都可以提供支持。 这样的 Protocol 设计模式给构建微服务带来了最大的灵活性,开发者可以根据需要如性能、通用型等选择不同的通信协议,不再需要任何的代理来实现协议转换,甚至你还可以通过 Dubbo 实现不同协议间的迁移。

Dubbo Protocol 被设计支持扩展,您可以将内部私有协议适配到 Dubbo 框架上,进而将私有协议接入 Dubbo 体系,以享用 Dubbo 的开发体验与服务治理能力。比如 Dubbo3 的典型用户阿里巴巴,就是通过扩展支持 HSF 协议实现了内部 HSF 框架到 Dubbo3 框架的整体迁移。

SpringBoot 和 Spring Cloud知识点_作用域_94

SpringBoot 和 Spring Cloud知识点_作用域_95



Dubbo服务治理

SpringBoot 和 Spring Cloud知识点_作用域_96


Dubbo服务端加载过程

SpringBoot 和 Spring Cloud知识点_构造器_97

Dubbo服务提供者provider初始化和注册

服务端provider启动,根据dubbo配置



Dubbo 服务注册与发现流程

SpringBoot 和 Spring Cloud知识点_构造器_98

SpringBoot 和 Spring Cloud知识点_spring_99

SpringBoot 和 Spring Cloud知识点_作用域_100



Dubbo支持的协议

SpringBoot 和 Spring Cloud知识点_spring_101

SpringBoot 和 Spring Cloud知识点_作用域_102

Dubbo协议通信

因 dubbo 协议采用单一长连接,假设网络为千兆网卡(1024Mbit=128MByte), 根据测试经验数据每条连接最多只能压满 7MByte(不同的环境可能不一样,供参考),理论上 1 个服务提供者需要 20 个服务消费者才能压满网卡。

因 dubbo 协议采用单一长连接, 如果每次请求的数据包大小为 500KByte,假设网络为千兆网卡(1024Mbit=128MByte),每条连接最大 7MByte(不同的 环境可能不一样,供参考), 单个服务提供者的 TPS(每秒处理事务数)最大为:128MByte / 500KByte = 262。 单个消费者调用单个服务提供者的 TPS(每秒处理事务数)最大为:7MByte / 500KByte = 14。 如果能接受,可以考虑使用,否则网络将成为瓶颈。

Dubbo采用异步长连接

如果采用常规的 hessian 服务,服务提供者很容易就被压跨, 通过单一连接,保证单一消费者不会压死提供者, 长连接,减少连接握手验证等, 并使用异步 IO,复用线程池,防止 C10K 问题。

Dubbo服务调用缓存

为了提高数据访问的速度。Dubbo 提供了声明式缓存,以减少用户加缓存的工作量。其实比普通的配置文件就多了一个标签 cache="true"

Dubbo优雅关机

(1)收到 kill PID 进程退出信号,Spring 容器会触发容器销毁事件。

(2)provider 端会注销服务元数据信息(删除ZK节点)。

(3)consumer 会拉取最新服务提供者列表。

(4)provider 会发送 readonly 事件报文通知 consumer 服务不可用。

(5)服务端等待已经执行的任务结束并拒绝新任务执行。


Dubbo节点角色

1:Dubbo是一个分布式开发框架

2:Provider: 暴露服务的服务提供方。

3:Consumer: 调用远程服务的服务消费方。

4:Registry: 服务注册与发现的注册中心。

5:Monitor: 统计服务的调用次调和调用时间的监控中心。

6:Container: 服务运行容器。

Dubbo配置方式

SpringBoot 和 Spring Cloud知识点_作用域_103


Dubbo注册中心

ZooKeeper: ZooKeeper是Dubbo默认支持的服务注册中心之一。它是一个分布式的协调服务,可以用于存储服务的元数据和提供者信息。ZooKeeper提供了高可用性和稳定性,支持观察者模式,当服务状态发生变化时,可以及时通知消费者。适用于中小规模的分布式系统,提供可靠的服务注册和发现能力。

Consul: Consul是一个轻量级的服务发现和配置工具,Dubbo也支持使用Consul作为服务注册中心。Consul提供了健康检查、多数据中心支持等特性,适用于需要强大的服务发现和健康检查能力的场景。

Nacos: Nacos是一个全新的服务发现和配置管理平台,支持Dubbo作为服务注册中心。Nacos提供了服务发现、配置管理、动态路由等功能,适用于微服务架构下的注册中心需求。

Eureka: Eureka是Netflix开源的服务发现组件,Dubbo也支持使用Eureka作为服务注册中心。Eureka提供了高可用性、自我保护机制等特性,适用于需要高可用性的服务注册和发现场景。

Etcd: Etcd是一个分布式键值存储系统,Dubbo可以使用Etcd作为服务注册中心。Etcd提供了一致性、高可用性的特性,适用于需要分布式存储的服务注册和发现需求。

注册中心集群挂掉,如何正常消费

可以的,消费者在启动时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。 每次调用时,按照本地存储的地址进行调用。

消费者本地有一个生产者的列表,他会按照列表继续工作,倒是无法从注册中心去同步最新的服务列表,短期的注册中心挂掉是不要紧的,但一定要尽快修复。

挂掉是不要紧的,但前提是你没有增加新的服务(节点没有变动),如果你要调用新的服务,则是不能办到的。

Dubbo容错方案

SpringBoot 和 Spring Cloud知识点_spring_104

Dubbo序列化

SpringBoot 和 Spring Cloud知识点_构造器_105

Dubbo通信框架

Dubbo默认使用Netty作为底层通信框架来实现高效、稳定的网络通讯。Netty是一个高性能的异步事件驱动的网络编程框架,它支持各种传输协议和应用层协议,并且提供了很多高级特性,比如内存池技术、零拷贝技术、异步I/O等,使其非常适合分布式应用场景下的高并发、高吞吐量的网络通讯。此外,Dubbo还支持其他的RPC通信框架,比如mina、grizzly、jetty等,可以通过简单配置在Dubbo应用中选择不同的通信框架。

Dubbo集群容错

(1)Failover Cluster:失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但 重试会带来更长延迟。

(2)Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写 操作,比如新增记录。

(3)Failsafe Cluster:失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

(4)Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操 作。

(5)Forking Cluster:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较 高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

(6)Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错 通常用于通 知所有提供者更新缓存或日志等本地资源信息。

Dubbo 的默认集群容错方案? Failover Cluster

Dubbo负载均衡

1.Random LoadBalance 有概率按权重设置(默认)

2.RoundRobin LoadBalance 设置轮询比率需要按公约权重

3.LeastActive LoadBalance 活跃数少,相同的数量是随机的

4.ConsistentHash LoadBalaclava Hash 一致性的前提下,同样的参数请求会发给同一个提供者。


Dubbo服务请求流程

SpringBoot 和 Spring Cloud知识点_构造器_106

SpringBoot 和 Spring Cloud知识点_spring_107

SpringBoot 和 Spring Cloud知识点_构造器_108

SpringBoot 和 Spring Cloud知识点_作用域_109

Dubbo服务暴露流程

SpringBoot 和 Spring Cloud知识点_spring_110

1.通过ServiceConfig对象解析标签,并创建dubbo的标签解析器对象来解析dubbo标签,随后通过触发ContextRefreshEvent事件的回调方法开始暴露服务的动作。

2.通过调用proxyFactory对象的getInvoker方法,并用javassist或DdkProxyFactory来进行动态代理,把服务暴露接口封装成invoker对象,在该对象里包含需要执行的方法名、参数和对应的URL地址。

3.通过DubboProtocol的实现类,把包装后的invoker转换成exporter对象。随后启动服务器端的server来监听端口,等待服务调用的到来

通过调用Exchangers.bind(url, requestHandler) 来创建出server,会创建出一个HeaderExchangeServer

4.通过RegistryProtocol对象,保存URL地址和invoker之间的映射关系,同时把这层映射关系注册到服务中心,比如Zookeeper里。

一般情况下,URL指的是统一资源定位符,标准格式如下:

protocol://host:port/path?key1=value1&key2=value2

Dubbo就是用这种URL的方式来作为约定的参数类型,服务之间也是用URL来进行交互。

SpringBoot 和 Spring Cloud知识点_作用域_111

Dubbo客户端启动流程

根据配置从注册中心拉取要调用的服务配置,找到服务提供invoker列表

从注册中心拉取后,缓存一份到客户端本地,万一连不上可以备用

也是通过proxy生成invoker,然后包装成客户端client


SPI机制

SPI (Service Provider Interface),主要用于扩展的作用。举个例子来说,假如有一个框架有一个接口,他有自己默认的实现类,但是在代码运行的过程中,你不想用他的实现类或者想扩展一下他的实现类的功能,但是此时你又不能修改别人的源码,那么此时该怎么办?这时spi机制就有了用武之地。一般框架的作者在设计这种接口的时候不会直接去new这个接口的实现类,而是在Classpath路径底下将这个接口的实现类按作者约定的格式写在一个配置文件上,然后在运行的过程中通过java提供的api,从所有jar包中读取所有的这个指定文件中的内容,获取到实现类,用这个实现类,这样,如果你想自己替换原有的框架的实现,你就可以按照作者规定的方式配置实现,这样就能使用你自己写的实现类了。


Java SPI机制

SPI 全称是 Service Provider Interface,是一种 JDK 内置的动态加载实现扩展点的机制,通过 SPI 技术我们可以动态获取接口的实现类,不用自己来创建。

在面向对象的设计原则中,一般推荐模块之间基于接口编程,通常情况下调用方模块是不会感知到被调用方模块的内部具体实现。一旦代码里面涉及具体实现类,就违反了开闭原则。如果需要替换一种实现,就需要修改代码。

为了实现在模块装配的时候不用在程序里面动态指明,这就需要一种服务发现机制。Java SPI 就是提供了这样一个机制:为某个接口寻找服务实现的机制。这有点类似 IOC 的思想,将装配的控制权移交到了程序之外。

SPI 将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。

SPI原理

SpringBoot 和 Spring Cloud知识点_作用域_112

使用场景

JDBC驱动,加载不同数据库的驱动类

Spring中大量使用了SPI,比如:对servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等

Dubbo中也大量使用SPI的方式实现框架的扩展, 不过它对Java提供的原生SPI做了封装,允许用户扩展实现Filter接口

Tomcat 加载 META-INF/services下找需要加载的类

SpringBoot项目中 使用@SpringBootApplication注解时,会开始自动配置,而启动配置则会去扫描META-INF/spring.factories下的配置类


java中最常见的spi机制应用就是数据库驱动的加载,java其实就是定义了java语言跟数据库交互的接口,但是具体的实现得交给各大数据库厂商来实现,那么java怎么知道你的数据库厂商的实现了?这时就需要spi机制了,java好约了定在 Classpath 路径下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,然后内容是该数据库厂商的实现的接口的全限定名,这样数据库厂商只要按照这个规则去配置,java就能找到。

SpringBoot 和 Spring Cloud知识点_作用域_113

SpringBoot 和 Spring Cloud知识点_构造器_114

源码实现

SpringBoot 和 Spring Cloud知识点_构造器_115

SpringBoot 和 Spring Cloud知识点_构造器_116

跟踪load()方法发现其本质是创建了一个ServiceLoader对象,其共有两个参数,分别是代加载的类父类(接口)Class类对象和类加载器。在构造方法中完成了两件事,一个是变量赋值,一个是调用reload()方法。reload()方法则根据接口类型(父类)和类加载器初始化LazyIterator

会先去providers查找已经加载的缓存实现,如果不存在,则会继续调用LazyIterator#hasNext()用于发现尚未加载的实现,最后的实现在LazyIterator#hasNextService()中

SpringBoot 和 Spring Cloud知识点_构造器_117

SpringBoot 和 Spring Cloud知识点_作用域_118

代码举例

SpringBoot 和 Spring Cloud知识点_构造器_119

SpringBoot 和 Spring Cloud知识点_构造器_120

SpringBoot 和 Spring Cloud知识点_构造器_121

如果不做任何的设置,Java应用的线程的上下文类加载器默认就是AppClassLoader。在核心类库使用SPI接口时,传递的类加载器使用线程上下文类加载器,线程上下文加载器,来打破双亲委派模型。即父类使用子类的类加载器来进行类加载。从而保证父子类由一个类加载器进行加载,可以成功的加载到SPI实现的类。线程上下文类加载器在很多SPI的实现中都会用到。

通常我们可以通过Thread.currentThread().getClassLoader()和Thread.currentThread().getContextClassLoader()获取线程上下文类加载器。

这里的上下文类加载器(ContextClassLoader ),它其实是破坏了双亲委派机制的,但是也为程序带来了巨大的灵活性和可扩展性。

寻找 META-INF/services/类,解析类的内容,构造 Class ,初始化,返回,就这么简单了。

SpringBoot 和 Spring Cloud知识点_spring_122


Spring SPI

spring扩展也是依赖spi机制完成的,只不过spring对于扩展文件约定在Classpath 路径下的 META-INF目录底下,所有的文件名都是叫spring.factories,文件里的内容是一个以一个个键值对的方式存储的,键为类的全限定名,值也为类的全限定名,如果有多个值,可以用逗号分隔。

在Spring中,SPI机制的实现主要依赖于Java标准库中的java.util.ServiceLoader类。Spring通过自定义的SpringFactoriesLoader类来封装和简化SPI机制的使用。通过SpringFactoriesLoader.loadFactories()方法,您可以加载并实例化配置文件中指定的接口实现类。

键仅仅是一种标识,代表一种场景,最常见的自动装配的注解,@EnableAutoConfiguration,也就是代表自动装配的场景,当你需要你的类被自动装配,就可以以这个注解的权限定名键,你的类为名,这样springboot在进行自动装配的时候,就会拿这个键,找到你写的实现类来完成自动装配。

这里其实就是通过@EnableAutoConfiguration的全限定名从spring.factories中加载这个键对应的所有的实现类的名称,这样就能拿到所有需要自动装配的类的全限定名了

SpringBoot 和 Spring Cloud知识点_spring_123

SpringFactoriesLoader的应用场景还有很多,大家可以去看一下SpringBoot中的启动引导类SpringApplication,里面多次使用到了这个

SpringFactoriesLoader这个类来获取各种实现。

SpringBoot 和 Spring Cloud知识点_构造器_124

SpringBoot 和 Spring Cloud知识点_spring_125

SpringBoot 和 Spring Cloud知识点_构造器_126

SpringBoot 和 Spring Cloud知识点_spring_127

SpringBoot 和 Spring Cloud知识点_spring_128

SpringBoot 和 Spring Cloud知识点_构造器_129

JavaSPI 与 SpringSPI有什么区别?

它们最大的区别就是配置文件了,JavaSPI 是一个接口一个配置文件,而Spring SPI是集中在一个配置文件里,也就是spring.factories,还有一个就是java spi是在遍历的时候才真正加载实现类,而spring spi是在loadFactories的时候就加载了。

Dubbo SPI机制

  • Dubbo SPI在Java SPI的基础上进行了改进,包括支持键值对配置方式、增强原本SPI的功能(如IOC和AOP)、通过ExtensionLoader来解析并加载指定的实现类。
  • Dubbo SPI适用于调用者根据实际使用需要,启用、扩展或替换框架的实现策略,如数据库驱动加载接口实现类的加载。
  • Dubbo SPI允许按需加载实现类,解决了Java SPI中一次性加载所有实现类导致资源浪费的问题。
  • 引入了@SPI注解,所有扩展接口都需要加此注解,否则在创建ExtensionLoader时会报错。
  • 提供了更灵活的配置文件约束,从多个目录读取文件,如META-INF/dubbo/internal/、META-INF/dubbo/、META-INF/services/、META-INF/dubbo/external/,文件名为接口的全限定名,内容为键值对。
  • 增加了IOC和AOP特性,支持setter方法注入依赖的扩展实例。
  • 引入了自适应扩展机制(Adaptive),允许在真正使用时从URL中获取相关参数来调用真正的扩展点实现类。

ExtensionLoader是dubbo的spi机制所实现的类,通过这个类来加载接口所有实现类,获取实现类的对象。同时每一个接口都会有一个自己的ExtensionLoader。

类加载默认都是先调用getExtensionClasses这个方法的,当cachedClasses没有的时,才会去加载实现类,然后再把实现类放到cachedClasses中。真正实现加载的是loadExtensionClasses 方法,接下来我们详细看这个方法的源码。

checkDestroyed();

方法没什么东西,其实就是一个检查的作用。

cacheDefaultExtensionName();

缓存默认实现类的短名称。其实很简单,就是从@SPI注解中取出名称,就是默认的实现类的名称,缓存起来,ExtensionLoader有个getDefaultExtension方法,其实就是通过这个短名称对应的实现类的对象。

接下来会遍历LoadingStrategy,根据LoadingStrategy加载指定目录的文件。

SpringBoot 和 Spring Cloud知识点_spring_130

通过URL打开一个输入流,然后读取文件内容,取出每一行,以 = 进行分割(因为规定的是以键值对存的),键就是短名称,值就是实现类的名称,然后再进入loadClass方法,这个方法很重要,其实是对实现类进行一个分类,后面dubbo的特性实现的前提就是对这些实现类的分类操作。

如果你加了@Adaptive注解,那么就将赋值到cachedAdaptiveClass属性上。我们叫这个类为自适应类。什么是自适应,其实说白了这个类本身并没有实际的意义,它是根据你的入参动态来实现找到真正的实现类来完成调用。getAdaptiveExtension其实就是获取到这个自适应实现类对应的对象。

如果你的实现类是有一个该类型为参数的构造方法,那么就将这个实现类放到cachedWrapperClasses中,并且我们称这个类为包装类,什么叫包装,其实跟静态代理有点像,就是将目标对象进行代理,可以增强功能。

这处标红的意思是判断是不是实现类是不是加了@Activate注解,是的话就将短名称和注解放入cachedActivates中,我们称这类实现类为自动激活的类,所谓的自动激活,就是可以根据你的入参,动态选择实现一批符合条件的实现类。

saveInExtensionClass就是将这个实现类放入extensionClasses中,该目录下的实现类就加载完成了。

接下来会继续循环,加载不同的目录底下,都会进行分类,并放到extensionClasses中。

当LoadingStrategy循环完成之后,最后将extensionClasses放入cachedClasses中,此时就完成了对于指定目录下实现类的加载和分类。

实现类对象构造

获取实现类对象的方法是getExtension方法,传入的name参数就是短名称,也就是spi文件的键,wrap是是否包装的意思,true的意思就是对你获取的目标对象进行包装(具体什么是包装,如何包装后面会讲),wrap默认是true

先从实现类的缓存中获取到短名称对应的实现类,上面提到,实现类加载之后会放到内部的一个缓存中。

这个if条件判断一般肯定是false的,但是有些情况,就比如第一次构建对象抛出异常,此时第二次来构建这个对象,那么不用说肯定也会有问题,dubbo为了快速知道哪些实现类对象构造的时候会出异常,就在第一次构建对象抛异常的时候缓存了实现类的短名称到unacceptableExceptions中,当第二次来构建的时候,能够快速知道,抛出异常,减少资源的浪费。

接下来就会从extensionInstances中获取实例,这个实例是没有包装的实例,也就是说如果你获取的不带包装的实例,就是这个实例。

构造其实很简单,就是当前instance当做包装类的构造参数通过反射构造,然后进行依赖注入,然后将构造出来的对象复制给instance,instance再进行回调之后再赋值给instance,这样往往复复就形成了一个链条。这里我画个图,让大家看看最后构造出来的对象是什么样。

SpringBoot 和 Spring Cloud知识点_spring_131

很多人可能不清楚,为什么需要包装,其实很好理解,就是起到动态增强目标对象的作用。可以理解为spring中的aop,但是dubbo因为不像spring那样有完整的ioc和aop的实现,dubbo就通过这种包装的方式来实现动态增强目标对象功能的作用。


SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能

协议扩展,集群扩展,路由扩展,序列化扩展

SpringBoot 和 Spring Cloud知识点_作用域_132

实现代码如下

public class TestiNterface {
    public static void main(String[] args) {
        ExtensionLoader<InterceptorAuthentication> extensionLoader = 
            ExtensionLoader.getExtensionLoader(InterceptorAuthentication.class);
        InterceptorAuthentication defaultHandle = 
            extensionLoader.getExtension("defaultHandle");
    }
}

@SPI
public interface InterceptorAuthentication {

    boolean authentication(HttpServletRequest request, HttpServletResponse response, Object handler);

}


public class DefaultPreHandle implements InterceptorAuthentication {

    private JwtTokenUtil jwtTokenUtil = SpringBeanUtils.getBean(JwtTokenUtil.class);

    @Override
    public boolean authentication(HttpServletRequest request, HttpServletResponse response, Object handler) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        Authority authority = method.getDeclaredAnnotation(Authority.class);
        if (null == authority) {
            authority = method.getDeclaringClass().getDeclaredAnnotation(Authority.class);
        }

        String token = request.getHeader("Authorization");

        if (null != authority && authority.needLogin()) {
            //check if 'authorization' is empty to prevent NullPointException
            if (StringUtils.isEmpty(token)) {
                //While authentication is required and 'Authorization' string is missing in the request headers,
                //reject this request(http403).
                AuthInterceptor.authRejectedResponse(response);
                return false;
            }
            if (jwtTokenUtil.canTokenBeExpiration(token)) {
                return true;
            }
            //while user not found, or token timeout, reject this request(http401).
            AuthInterceptor.loginFailResponse(response);
            return false;
        } else {
            return true;
        }
    }
}

SpringBoot 和 Spring Cloud知识点_构造器_133

获取扩展类加载器实现如下

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null) {
        // 首先判断传入的扩展点是否为空
            throw new IllegalArgumentException("Extension type == null");
        } else if (!type.isInterface()) {
        // 判断扩展点是否为接口
            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
        } else if (!withExtensionAnnotation(type)) {
        // 判断扩展点某类是否使用SPI注解
            throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        } else {
        // 查看该扩展点是否已经加载
            ExtensionLoader<T> loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
            // 未被加载则,进行加载并且放入到缓存中,key为扩展点,value为
            if (loader == null) {
            // 主要实现在new ExtensionLoader(type);
                EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
                loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
            }
			//最后返回
            return loader;
        }
    }

获取扩展实现代码如下

public T getAdaptiveExtension() {
 // 这里她首先从ExtensionFactory扩展类里面查找缓存的扩展实例
        Object instance = this.cachedAdaptiveInstance.get();
        // 缓存扩展实例为空就进行处理,那么我们首次进行调用时,缓存实例肯定是为空
        if (instance == null) {
            if (this.createAdaptiveInstanceError != null) {
                throw new IllegalStateException("Failed to create adaptive instance: " + this.createAdaptiveInstanceError.toString(), this.createAdaptiveInstanceError);
            }
			// 这里使用了双检索机制确保创建过程
            synchronized(this.cachedAdaptiveInstance) {
            // 再次进行空指针判断 
                instance = this.cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                  		// 调用cachedAdaptiveInstance()方法
                        instance = this.createAdaptiveExtension();
                        // 把扩展类塞入到缓存中
                        this.cachedAdaptiveInstance.set(instance);
                    } catch (Throwable var5) {
                        this.createAdaptiveInstanceError = var5;
                        throw new IllegalStateException("Failed to create adaptive instance: " + var5.toString(), var5);
                    }
                }
            }
        }
		// 最后返回
        return instance;
    }

Dubbo 的 SPI 机制主要基于 ExtensionLoader 实现,它是一个单例工厂类,负责加载和管理扩展点实现类。Dubbo 的 SPI 机制包括以下几个步骤:

获取扩展点名称:根据接口类获取 SPI 扩展点名称,默认为接口的全限定名。

获取扩展点实现类:从缓存中获取扩展点实现类,如果不存在则通过反射机制实例化扩展点实现类。

缓存扩展点实现类:将实例化的扩展点实现类缓存到本地,避免重复实例。

Dubbo 的 Protocol 实现类就是通过 SPI 机制加载的,可根据配置动态切换不同的协议。

Dubbo 的 Cluster 实现类就是通过 SPI 机制加载的,可根据配置动态切换不同的集群容错策略。

Dubbo 的 LoadBalance 实现类就是通过 SPI 机制加载的,可根据配置动态切换不同的负载均衡策略。

Java SPI 缺点

SpringBoot 和 Spring Cloud知识点_spring_134

DUBBO SPI 改进

SpringBoot 和 Spring Cloud知识点_构造器_135

1.对 Dubbo 进行扩展, 不需要改动 Dubbo 的源码.

2.延迟加载, 可以一次只加载自己想要加载的扩展实现。

3. 增加了对扩展点 IOC 和 AOP 的支持, 一个扩展点可以直接 setter 注入其 它扩展点。

4.Dubbo 的扩展机制能很好的支持第三方 IoC 容器, 默认支持 Spring Bean


Dubbo泛化调用

实现一个通用的服务测试框架,可通过GenericService 调用所有服务实现

泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 GenericService 调用所有服务实现。

通过 Spring 使用泛化调用 ,在 Spring 配置申明 generic="true":

SpringBoot 和 Spring Cloud知识点_构造器_136


SpringBoot 和 Spring Cloud知识点_作用域_137

SpringBoot 和 Spring Cloud知识点_作用域_138

打破双亲委派加载模式

例如: 加载mysql驱动

在加载第三方类库时,加载Driver类的实现类时,一开始就不是系统类加载器去加载的,而是直接使用启动类加载器进行加载

因为本身这个类就是java 基类中提供的,它仅仅是去找实现类而已;但是它找不到,因为java基类只提供了接口标准,没有提供实现类

由于驱动仅仅是一个标准,由各个厂商实现,需要运行时动态获取,那么启动类无法加载,最终能加载的就只能是应用类加载器。

相当于之前的双亲委派模式顺序变了,父类先尝试加载未加载成功,然后子类去加载了。

但他不是直接去找了子类,而是通过一个叫做线程上下文类加载器,但这个类加载器,实际上就是应用类加载器。

在某些情况下,需要跳出双亲委派机制,以便自定义类加载器可以加载某些特定的类,从而实现某些特定的功能。Spring框架就是一个典型的例子,它需要跳出双亲委派机制,以便在某些特定的情况下,自定义类加载器可以加载Spring的某些特定的类,从而实现Spring的一些特定的功能。

例如: springboot 中yml 没有mysql的相关配置,那么就不会报错,当我写入了mysql配置,开始验证驱动包是否存在,用户名密码是否正确~,这也是springboot实现热插拔的主要手段。

Dubbo与Istio

SpringBoot 和 Spring Cloud知识点_构造器_139

SpringBoot 和 Spring Cloud知识点_spring_140

Dubbo超高性能

Dubbo 内置支持 Dubbo2、Triple 两款高性能通信协议。其中

  • Dubbo2 是基于 TCP 传输协议之上构建的二进制私有 RPC 通信协议,是一款非常简单、紧凑、高效的通信协议。
  • Triple 是基于 HTTP/2 的新一代 RPC 通信协议,在网关穿透性、通用性以及 Streaming 通信上具备优势,Triple 完全兼容 gRPC 协议。




++++++++++++++++++++++++++++++++

RPC协议

SpringBoot 和 Spring Cloud知识点_作用域_141

SpringBoot 和 Spring Cloud知识点_spring_142

SpringBoot 和 Spring Cloud知识点_构造器_143

Rest RPC SOA区别

RPC 通信代价比较低,因为 RPC 是直接基于 TCP 进行调用的,并且传输的数据都是基于 TCP 进行的,所以效率更高,更优。但是由于是基于 TCP 的所以实现起来更为的复杂,更为的难以维护。

而 RESTful API 由于是直接基于 HTTP 的所以实现更为简单,维护更为容易。

RPC 快,效率高,但是不够通用,就好比地方方言,HTTP通用是普通话,但是效率不够高,传输的字节内容冗余多。

REST 相对更规范,更标准,更通用,简单易用,维护性和扩展性都比较好。

RPC+Protobuf 采用的是 TCP 做传输协议,REST 直接使用 HTTP 做应用层协议,这种区别导致 REST 在调用性能上会比 RPC+Protobuf 低。

SOAP是一种数据交换协议规范,是一种轻量的、简单的、基于XML的协议的规范。而SOAP能够看着是一个重量级的协议,基于XML、SOAP在安全方面是经过使用XML-Security和XML-Signature两个规范组成了WS-Security来实现安全控制的


RPC关键技术

一、动态代理

生成Client Stub(客户端存根)和Server Stub(服务端存根)的时候须要用到Java动态代理技术,可使用JDK提供的原生的动态代理机制,也可使用开源的:CGLib代理,Javassist字节码生成技术。

二、序列化和反序列化

在网络中,全部的数据都将会被转化为字节进行传送,因此为了可以使参数对象在网络中进行传输,须要对这些参数进行序列化和反序列化操做。

序列化:把对象转换为字节序列的过程称为对象的序列化,也就是编码的过程。

反序列化:把字节序列恢复为对象的过程称为对象的反序列化,也就是解码的过程。

目前比较高效的开源序列化框架:如Kryo、FastJson和Protobuf等。

三、NIO通讯

出于并发性能的考虑,传统的阻塞式 IO 显然不太合适,所以咱们须要异步的 IO,即 NIO。Java 提供了 NIO 的解决方案,Java 7 也提供了更优秀的 NIO.2 支持。能够选择Netty或者MINA来解决NIO数据传输的问题。

四、服务注册中心

可选:Redis、Zookeeper、Consul 、Etcd。通常使用ZooKeeper提供服务注册与发现功能,解决单点故障以及分布式部署的问题(注册中心)。

gRPC

gRPC是一个高性能、开源和通用的RPC框架,由Google开发。

它使用Protocol Buffers作为接口描述语言,可以在多种语言中使用,包括Java、Python、C++等。

gRPC支持多种传输协议和序列化协议,可以在不同的环境中使用,如云、移动设备、浏览器等。

grpc的原理是基于HTTP/2和protobuf协议,利用protobuf序列化和反序列化技术,实现远程过程调用。

protobuf是一种轻量级、高效、可扩展的数据序列化格式,由谷歌开发并开源。它允许在不同的平台和语言之间传递和解析数据,支持类型定义和版本控制,具有数据压缩效率高和序列化和反序列化速度快的优点。

gRPC通过在客户端和服务器之间建立一个protobuf序列化/反序列化通道,实现远程过程调用。客户端将请求序列化为字节流并发送到服务器,服务器将响应反序列化为字节流并发送给客户端。gRPC还支持负载均衡、服务发现机制、认证和授权、监控和日志等功能,提高了RPC框架的可靠性和可扩展性。

SpringBoot 和 Spring Cloud知识点_构造器_144

因此,gRPC 更适合作为底层的通信协议规范或编解码包,而 Dubbo 则可用作微服务整体解决方案。对于 gRPC 协议,我们推荐的使用模式 Dubbo + gRPC 的组合,这个时候,gRPC 只是隐藏在底层的一个通信协议,不被微服务开发者感知,开发者基于 Dubbo 提供的 API 和配置开发服务,并基于 dubbo 的服务治理能力治理服务,在未来,开发者还能使用 Dubbo 生态和开源的 IDL 配套工具管理服务定义与发布。