文章目录
- Mybatis 框架
- Spring 框架
- Spring 的两大核心是什么?谈一谈你对IOC 的理解? 谈一谈你对DI 的理解? 谈一谈你对 AOP 的理解?
- Spring 的生命周期?
- Spring 支持 bean 的作用域有几种吗? 每种作用域是什么样的?
- BeanFactory 和 ApplicationContext 有什么区别
- Spring 框架中都用到了哪些设计模式?
- Spring 事务的实现方式和实现原理
- 你知道的 Spring 的通知类型有哪些,分别在什么时候执行?
- Spring 的对象默认是单例的还是多例的? 单例 bean 存不存在线程安全问题呢?
- @Resource 和@Autowired 依赖注入的区别是什么? @Qualifier 使用场景是什么?
- Spring 的常用注解
- Spring 的事务传播行为
- Spring 中的隔离级别
- SpringMVC 框架
- Dubbo 框架
- Zookeeper 框架
- SpringBoot 框架
- SpringCloud 框架
Mybatis 框架
谈一谈你对 Mybatis 框架的理解
MyBatis 是一款优秀的持久层框架,一个半 ORM(对象关系映射)框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
在 mybatis 中,${} 和 #{} 的区别是什么?
- #{} 是占位符,预编译处理,${}是字符串替换。
- Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;Mybatis 在处理$ {}时,就是把${}替换成变量的值。
- 使用#{}可以有效的防止 SQL 注入,提高系统安全性。
MyBatis 编程步骤是什么样的?
- 创建 SqlSessionFactory
- 通过 SqlSessionFactory 创建 SqlSession
- 通过 sqlsession 执行数据库操作
- 调用 session.commit()提交事务
- 调用 session.close()关闭会话
在 mybatis 中,resultType 和 ResultMap 的区别是什么?
- 如果数据库结果集中的列名和要封装实体的属性名完全一致的话用 resultType 属性.
- 如果数据库结果集中的列名和要封装实体的属性名有不一致的情况用 resultMap 属性,通过 resultMap 手动建立对象关系映射,resultMap 要配置一下表和类的一一对应关系,所以说就算你的字段名和你的实体类的属性名不一样也没关系,都会给你映射出来
在 Mybatis 中你知道的动态 SQL 的标签有哪些?作用分别是什么?
- < if > : if 是为了判断传入的值是否符合某种规则,比如是否不为空.
- < where > : where 标签可以用来做动态拼接查询条件,当和 if 标签配合的时候,不用显示的声明类型 where 1 = 1 这种无用的条件
- < foreach > : foreach 标签可以把传入的集合对象进行遍历,然后把每一项的内容作为参数传到 sql 语句中.
- < include > : include 可以把大量的重复代码整理起来,当使用的时候直接 include 即可,减少重复代码的编写;
- < set >适用于更新中,当匹配某个条件后,才会对该字段进行跟新操作;
谈一下你对 mybatis 缓存机制的理解?
Mybatis 有两级缓存,一级缓存是 SqlSession 级别的,默认开启,无法关闭;二级缓存是 Mapper 级别的,二级缓存默认是没有开启的,但是可以手动开启。
- 一级缓存:基础 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session, 当 Session flush 或 close 之后,Session 中的所有 Cache 就将清空
- 二级缓存其存储作用域为 Mapper(Namespace),使用二级缓存属性类需要实现Serializable 序列化接口
- 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了C(增加)/U(更新)/D(删除)操作后,默认该作用域下所有 select 中的缓存将被clear.需要在 setting 全局参数中配置开启二级缓存,如下 conf.xml 配置:
当我们的配置文件配置了 cacheEnabled=true 时,就会开启二级缓存,二级缓存是
mapper 级别的,如果你配置了二级缓存,那么查询数据的顺序应该为:二级缓存→一级缓存→数据库。
Spring 框架
Spring 的两大核心是什么?谈一谈你对IOC 的理解? 谈一谈你对DI 的理解? 谈一谈你对 AOP 的理解?
- Spring 的两大核心是:IOC(控制反转)和 AOP(面向切面编程)
- IOC 的意思是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到 Spring 容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。最直观的表达就是,IOC 让对象的创建不用去 new 了,可以由 spring 根据我们提供的配置文件自动生产,我们需要对象的时候,直接从 Spring 容器中获取即可.
Spring 的配置文件中配置了类的字节码位置及信息, 容器生成的时候加载配置文件识别字节码信息, 通过反射创建类的对象.
Spring 的 IOC 有三种注入方式 :构造器注入, setter 方法注入, 根据注解注入。 - DI 的意思是依赖注入,和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖 Io c 容器来动态注入对象需要的外部资源。
- AOP,一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect). SpringAOP 使用的动态代理,所谓的动态代理就是说 AOP 框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个 AOP 对象,这个 AOP 对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理:
- JDK 动态代理只提供接口代理,不支持类代理,核心 InvocationHandler 接口和Proxy 类,InvocationHandler 通过 invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起,Proxy 利用 InvocationHandler 动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
- 如果代理类没有实现 InvocationHandler 接口,那么 Spring AOP 会选择使用CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库, 可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现 AOP。CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final, 那么它是无法使用 CGLIB 做动态代理的。
Spring 的生命周期?
- 实例化一个 Bean,也就是我们通常说的 new.
- 按照 Spring 上下文对实例化的 Bean 进行配置,也就是 IOC 注入
- 如果这个 Bean 实现 dao 了 BeanNameAware 接口,会调用它实现的setBeanName(String beanId)方法,此处传递的是 Spring 配置文件中 Bean 的 ID.
- 如果这个 Bean 实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory(), 传递的是 Spring 工厂本身(可以用这个方法获取到其他 Bean)。
- 如果这个Bean实现了 ApplicationContextAware 接口,会调用setApplicationContext(ApplicationContext)方法,传入 Spring 上下文,该方式同样可以实现步骤 4,但比 4 更好,因为 ApplicationContext 是 BeanFactory 的子接口,有更多的实现方法。
- 如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor 经常被用作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用 After 方法,也可用于内存或缓存技术。
- 如果这个Bean 在Spring 配置文件中配置了init-method 属性会自动调用其配置的初始化方法。
- 如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用postAfterInitialization(Object obj, String s)方法。
注意:以上工作完成以后就可以用这个 Bean 了,那这个 Bean 是一个 single(单例) 的,所以一般情况下我们调用同一个 ID 的 Bean 会是在内容地址相同的实例。 - 当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 接口,会调用其实现的 destroy 方法。
- 最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的销毁方法 。
Spring 支持 bean 的作用域有几种吗? 每种作用域是什么样的?
Spring 支持如下 5 种作用域:
- singleton:默认作用域,单例 bean,每个容器中只有一个 bean 的实例。
- prototype:每次请求都会为 bean 创建实例。
- request:为每一个 request 请求创建一个实例,在请求完成以后,bean 会失效并被垃圾回收器回收。
- session:与 request 范围类似,同一个 session 会话共享一个实例,不同会话使用不同的实例。
- global-session:全局作用域,所有会话共享一个实例。如果想要声明让所有会 话共享的存储变量的话,那么这全局变量需要存储在 global-session 中。
BeanFactory 和 ApplicationContext 有什么区别
- BeanFactory:Spring 最顶层的接口,实现了 Spring 容器的最基础的一些功能, 调用起来比较麻烦, 一般面向 Spring 自身使用BeanFactory 在启动的时候不会去实例化 Bean,从容器中拿 Bean 的时候才会去实例化。
- ApplicationContext:是 BeanFactory 的子接口,扩展了其功能, 一般面向程序员身使用ApplicationContext 在启动的时候就把所有的 Bean 全部实例化了。
Spring 框架中都用到了哪些设计模式?
- 工厂模式:BeanFactory 就是简单工厂模式的体现,用来创建对象的实例
- 单例模式:Bean 默认为单例模式
- 代理模式:Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 字节码生成技术
- 模板模式: 用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate
- 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都会得到通知被制动更新, 如 Spring 中 listener 的实现–ApplicationListener
Spring 事务的实现方式和实现原理
Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring 是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过 binlog 或者 redo log 实现的。
spring 事务实现主要有两种方法
- 编程式,beginTransaction()、commit()、rollback()等事务管理相关的方法
- 声明式,利用注解 Transactional 或者 aop 配置
你知道的 Spring 的通知类型有哪些,分别在什么时候执行?
Spring 的通知类型有四种,分别为:
- 前置通知[before]:在切点运行之前执行
- 后置通知[after-returning]:在切点正常结束之后执行
- 异常通知[after-throwing]:在切点发生异常的时候执行
- 最终通知[after]:在切点的最终执行
- 环绕通知:环绕通知运行程序员以编码的方式自己定义通知的位置, 用于解决其他通知时序问题
Spring 的对象默认是单例的还是多例的? 单例 bean 存不存在线程安全问题呢?
- 在 spring 中的对象默认是单例的,但是也可以配置为多例。
- 单例 bean 对象对应的类存在可变的成员变量并且其中存在改变这个变量的线程时, 多线程操作该 bean 对象时会出现线程安全问题。
原因:多线程操作如果改变成员变量,其他线程无法访问该 bean 对象,造成数据混乱。
解决办法:在 bean 对象中避免定义可变成员变量;在 bean 对象中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中。
@Resource 和@Autowired 依赖注入的区别是什么? @Qualifier 使用场景是什么?
@Resource:
- 只能放在属性上,表示先按照属性名匹配 IOC 容器中对象 id 给属性注入值若没有成功,会继续根据当前属性的类型匹配 IOC 容器中同类型对象来注入值,若指定了 name 属性@Resource(name = “对象 id”),则只能按照对象 id 注入值。
@Autowird:
- 放在属性上:表示先按照类型给属性注入值如果 IOC 容器中存在多个与属性同类型的对象,则会按照属性名注入值也可以配合@Qualifier(“IOC 容器中对象 id”)注解直接按照名称注入值。
- 放在方法上:表示自动执行当前方法,如果方法有参数,会自动从 IOC 容器中寻找同类型的对象给参数传值也可以在参数上添加@Qualifier(“IOC 容器中对象 id”)注解按照名称寻找对象给参数传值。
@Qualifier 使用场景:
- @Qualifier(“IOC 容器中对象 id”)可以配合@Autowird 一起使用, 表示根据指定的 id 在 Spring 容器中匹配对象。
Spring 的常用注解
- @Component(任何层)、@Controller、@Service、@Repository(dao): 用于实例化对象
- @Scope : 设置 Spring 对象的作用域
- @PostConstruct、@PreDestroy : 用于设置 Spring 创建对象在对象创建之后和销毁之前要执行的方法
- @Value: 简单属性的依赖注入
- @Autowired: 对象属性的依赖注入
- @Qualifier: 要和@Autowired 联合使用,代表在按照类型匹配的基础上,再按照名称匹配。
- @Resource 按照属性名称依赖注入
- @ComponentScan: 组件扫描
- @Bean: 表在方法上,用于将方法的返回值对象放入容器
- @PropertySource: 用于引入其它的 properties 配置文件
- @Import: 在一个配置类中导入其它配置类的内容
- @Configuration: 被此注解标注的类,会被 Spring 认为是配置类。Spring 在启动的时候会自动扫描并加载所有配置类,然后将配置 类中 bean 放入容器
- @Transactional 此注解可以标在类上,也可以表在方法上,表示当前类中的方法具有事务管理功能。
Spring 的事务传播行为
spring 事务的传播行为说的是,当多个事务同时存在的时候,spring 如何处理这些事务的行为。
- PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
- PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务, 如果当前不存在事务,就以非事务执行。
- PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务, 就把当前事务挂起。
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按 REQUIRED 属性执行。
Spring 中的隔离级别
- ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别, 使用数据库默认的事务隔离级别。
- ISOLATION_READ_UNCOMMITTED:读未提交,允许另外一个事务可以看到这个事务未提交的数据。
- ISOLATION_READ_COMMITTED:读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新。解决脏读问题。
- ISOLATION_REPEATABLE_READ:可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新。
- ISOLATION_SERIALIZABLE:一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。
SpringMVC 框架
谈一下你对 SpringMVC 框架的理解(了解)
SpringMVC 是一个基于 Java 的实现了 MVC 设计模式的请求驱动类型的轻量级 Web 框架,通过把 Model,View,Controller 分离,将 web 层进行职责解耦,把复杂的 web 应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。在我看来,SpringMVC 就是将我们原来开发在 servlet 中的代码拆分了, 一部分由SpringMVC 完成,一部分由我们自己完成。
SpringMVC 主要组件
- 前端控制器 DispatcherServlet : 接收请求、响应结果, 相当于转发器, 有了DispatcherServlet 就减少了其它组件之间的耦合度。
- 处理器映射器 HandlerMapping:根据请求的 URL 来查找 Handler
- 处理器适配器 HandlerAdapter:负责执行 Handler
- 处理器 Handler:处理业务逻辑的 Java 类
- 视图解析器 ViewResolver:进行视图的解析,根据视图逻辑名将 ModelAndView 解析成真正的视图(view)
- 视图 View:View 是一个接口, 它的实现类支持不同的视图类型,如 jsp,freemarker,pdf 等等
谈一下 SpringMVC 的执行流程以及各个组件的作用
- 用户发送请求到前端控制器(DispatcherServlet)
- 前端控制器( DispatcherServlet )收到请求调用处理器映射器(HandlerMapping),去查找处理器(Handler)
- 处理器映射器(HandlerMapping)找到具体的处理器(可以根据 xml 配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给前端控制器( DispatcherServlet)。
- 前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)
- 处理器适配器(HandlerAdapter)去调用自定义的处理器类(Controller,也叫后端控制器)。
- 自定义的处理器类(Controller,也叫后端控制器)将得到的参数进行处理并返回结果给处理器适配器(HandlerAdapter)
- 处理器适配器( HandlerAdapter ) 将得到的结果返回给前端控制器(DispatcherServlet)
- DispatcherServlet( 前 端 控 制 器 ) 将 ModelAndView 传 给 视 图 解 析 器(ViewReslover)
- 视图解析器(ViewReslover)将得到的参数从逻辑视图转换为物理视图并返回给前端控制器(DispatcherServlet)
- 前端控制器(DispatcherServlet)调用物理视图进行渲染并返回
- 前端控制器(DispatcherServlet)将渲染后的结果返回
说一下 SpringMVC 支持的转发和重定向的写法
转发:
forward 方式:在返回值前面加"forward:“,比如"forward:user.do?name=method4”
重定向:
redirect 方式:在返回值前面加 redirect:, 比如"redirect:http://www.baidu.com"
SpringMVC 的常用注解
- @RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。
- @RequestBody:注解实现接收 http 请求的 json 数据,将 json 转换为 java 对象。
- @ResponseBody:注解实现将 conreoller 方法返回对象转化为 json 对象响应给客户。
- @PathVariable 用户从 url 路径上获取指定参数,标注在参数前 @PathVariabel(" 要获取的参数名")。
- @RequestParam: 标注在方法参数之前,用于对传入的参数做一些限制,支持三个属性:
- value:默认属性,用于指定前端传入的参数名称
- required:用于指定此参数是否必传
- defaultValue:当参数为非必传参数且前端没有传入参数时,指定一个默认值
- @ControllerAdvice 标注在一个类上,表示该类是一个全局异常处理的类。
- @ExceptionHandler(Exception.class) 标注在异常处理类中的方法上,表示该方法可以处理的异常类型。
谈一下 SpringMVC 统一异常处理的思想和实现方式
使用 SpringMVC 之后,代码的调用者是 SpringMVC 框架,也就是说最终的异常会抛到框架中,然后由框架指定异常处理类进行统一处理
方式一: 创建一个自定义异常处理器(实现HandlerExceptionResolver 接口),并实现里面的异常处理方法,然后将这个类交给 Spring 容器管理
方式二: 在类上加注解(@ControllerAdvice)表明这是一个全局异常处理类在方法上加注解(@ExceptionHandler), 在 ExceptionHandler 中有一个value 属性,可以指定可以处理的异常类型.
在 SpringMVC 中, 如果想通过转发将数据传递到前台,有几种写法?
- 方式一:直接使用 request 域进行数据的传递
- 方式 二:使用 Model 进行传值,底层会将数据放入 request 域进行数据的传递
- 方式 三:使用 ModelMap 进行传值,底层会将数据放入 request 域进行数据的传递
- 方式四:借用 ModelAndView 在其中设置数据和视图
在 SpringMVC 中拦截器的使用步骤是什么样的?
-
定义拦截器类: SpringMVC 为 我 们 提 供 了 拦 截 器 规 范 的 接 口 , 创 建 一 个 类 实 现
HandlerInterceptor,重写接口中的抽象方法;- preHandle 方法:在调用处理器之前调用该方法,如果该方法返回 true 则请求继续向下进行,否则请求不会继续向下进行,处理器也不会调用
- postHandle 方法:在调用完处理器后调用该方法
- afterCompletion 方法:在前端控制器渲染页面完成之后调用此方法
-
注册拦截器:
在 SpringMVC 核心配置文件中注册自定义的拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="拦截路径规则"/>
<mvc:exclude-mapping path="不拦截路径规则"/>
<bean class="自定义拦截器的类全限定名"/>
</mvc:interceptor>
</mvc:interceptors>
在 SpringMVC 中文件上传的使用步骤是什么样的? 前台三要素是什么?
文件上传步骤:
- 加入文件上传需要的 commons-fileupload 包
- 配置文件上传解析器,springmvc 的配置文件的文件上传解析器的id 属性必须为multipartResolver
- 后端对应的接收文件的方法参数类型必须为 MultipartFile,参数名称必须与前端的 name 属性保持一致
文件上传前端三要素:
- form 表单的提交方式必须为 post
- enctype 属性需要修改为:multipart/form-data
- 必须有一个type 属性为file 的input 标签,其中需要有一个name 属性;如果需要上传多个文件需要添加 multiple 属性
SpringMVC 中如何解决 GET|POST 请求中文乱码问题?
- 解决 post 请求乱码问题:在 web.xml 中配置一个 CharacterEncodingFilter 过滤器,设置成 utf-8;
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</fil ter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- get 请求中文参数出现乱码解决方法有两个:
- 修改 tomcat 配置文件添加编码与工程编码一致
<ConnectorURIEncoding="utf-8" connectionTimeout="20000" port="8080" p rotocol="HTTP/1.1" redirectPort="8443"/>
- 另外一种方法对参数进行重新编码
String userName= new String(request.getParamter("userName").getBytes("ISO885 9-1"),"utf-8")
Dubbo 框架
什么是 dubbo?
- 工作在soa 面向服务分布式框架中的服务管理中间件。Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案。
- 它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。从服务模型的角度来看,Dubbo 采用的是一种非常简单的模型,要么是提供方提供服务,要么是消费方消费服务,所以基于这一点可以抽象出服务提供方(Provider)和服务消费方(Consumer)两个角色。关于注册中心、协议支持、服务监控等内容。
- Dubbo 使用的是缺省协议, 采用长连接和nio 异步通信, 适合小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。反之, dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
Dubbo 的实现原理
- 容器(Container)启动服务提供者(Service Provider),Dubbo启动时注册服务地址。
- 服务消费者(Service Consumer)启动时订阅服务地址 。
- Dubbo Registry 变更时推送服务地址列表。
- 服务消费者(Service Consumer)随机调用一个服务地址,失败重试另一个地址。
- 后台定时采集服务调用次数和调用时间等信息。
节点角色说明
- Provider: 暴露服务的服务提供方。
- Consumer: 调用远程服务的服务消费方。
- Registry: 服务注册与发现的注册中心。
- Monitor: 统计服务的调用次调和调用时间的监控中心。
- Container: 服务运行容器。
调用关系说明
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用, 如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Zookeeper 框架
Zookeeper 是什么
- Zookeeper 是一个分布式协调服务的开源框架, 主要用来解决分布式集群中应用系统的一致性问题, 例如怎样避免同时操作同一数据造成脏读的问题.
- ZooKeeper 本质上是一个分布式的小文件存储系统. 提供基于类似于文件系统的目录树方式的数据存储, 并且可以对树中的节点进行有效管理. 从而用来维护和监控你存储的数据的状态变化. 通过监控这些数据状态的变化,从而可以达到基于数据的集群管理.
- 在大数据生态系统里,很多组件的命名都是某种动物,比如 hadoop 就是大象, hive 就是蜜蜂, 而 Zookeeper 就是动物管理员.
Zookeeper 的数据模型
- Zookeeper 本质上是一个分布式的小文件存储系统.
- Zookeeper 表现为一个分层的文件系统目录树结构, 既能存储数据, 而且还能像目录一样有子节点. 每个节点可以存最多 1M 左右的数据.
- Zookeeper 每个节点称做一个 Znode, 每个 Znode 都可以通过其路径唯一标识.
- Zookeeper 客户端还能给节点添加 watch, 也就是监听器, 可以监听节点的变化, 这个功能常在实际开发中作为监听服务器集群机器上下线操作.
节点结构:
图中的每个节点称为一个 Znode。 每个 Znode 由 3 部分组成:
- stat:此为状态信息, 描述该 Znode 的版本, 权限等信息
- data:与该 Znode 关联的数据
- children:该 Znode 下的子节点
节点类型:
Znode 有 2 大类 4 小类, 两大类分别为永久节点和临时节点.
2大类:
- 永久节点(Persistent): 客户端和服务器端断开连接后,创建的节点不会消失, 只有在客户端执行删除操作的时候, 他们才能被删除.
- 临时节点(Ephemeral): 客户端和服务器端断开连接后,创建的节点会被删除.
4小类:
Znode 还有一个序列化的特性, 这个序列号对于此节点的父节点来说是唯一的, 这样便会记录每个子节点创建的先后顺序.
- 永久节点(Persistent)
- 永久_序列化节点(Persistent_Sequential)
- 临时节点(Ephemeral)
- 临时_序列化节点(Ephemeral_Sequential)
Zookeeper 的 watch 监听机制
- 在ZooKeeper 中还支持一种watch(监听)机制, 它允许对ZooKeeper 注册监听, 当监听的对象发生指定的事件的时候, ZooKeeper 就会返回一个通知.
- Watcher 分为以下三个过程:客户端向 ZooKeeper 服务端注册 Watcher、服务端事件发生触发 Watcher、客户端回调 Watcher 得到触发事件情况.
- 触发事件种类很多,如:节点创建,节点删除,节点改变,子节点改变等。
- Watcher 是一次性的. 一旦被触发将会失效. 如果需要反复进行监听就需要反复进行注册.
监听器原理:
- 首先要有一个 main()线程
- 在 main 线程中创建 Zookeeper 客户端, 这时就会创建两个线程, 一个复制网络连接通信(connect), 一个负责监听(listener)
- 通过 connect 线程将注册的监听事件发送给 Zookeeper , 常见的监听有监听节点数据的变化、监听节点状态的变化、监听子节点增减的变化
- 将注册的监听事件添加到 Zookeeper 的注册的监听器列表中
- 监听到有数据或路径变化, 就会将这个消息发送给 listener 线程.
- listener 线程内部调用了 process()方法.此方法是程序员自定义的方法, 里面可以写明监听到事件后做如何的通知操作.
Zookeeper 的应用场景
ZK 提供的服务包括: 统一命名服务, 统一配置管理, 统一集群管理, 集群选主, 服务动态上下线, 分布式锁等.
统一命名服务
: 统一命名服务使用的是 zookeeper的 node 节点全局唯一的这个特点.在分布式环境下,经常需要对应用/服务进行统一命名,便于识别。统一配置管理
: 统一配置管理, 使用的是 Zookeeper 的 watch 机制- 需求: 分布式环境下, 要求所有节点的配置信息是一致的
- 方案: 可以把所有的配置都放在一个配置中心, 然后各个服务分别去监听配置中心, 一旦发现里面的内容发生变化, 立即获取变化的内容, 然后更新本地配置即可.
- 实现: 配置管理可交由 Zookeeper 实现(可将配置信息写入 Zookeeper 上的一个 Znode,各个客户端服务器监听这个 Znode,一旦 Znode 中的数据被修改, Zookeeper 将通知各个客户端服务器.)
统一集群管理
:统一集群管理使用的是 Zookeeper 的 watch 机制- 需求: 分布式环境中, 实时掌握每个节点的状态是必要的, 可以根据节点实时状态做出一些调整.
- 方案: Zookeeper 可以实现实时监控节点状态变化(可将节点信息写入 Zookeeper 上的一个 Znode,监听这个 Znode 可获取它的实时状态变化,)
集群选主
:集群选主使用的是 zookeeper 的临时节点.- 需求: 在集群中, 很多情况下是要区分主从节点的, 一般情况下主节点负责数据写入, 从节点负责数据读取。
- 实现:使用 Zookeeper 的临时节点可以轻松实现这一需求, 我们把上面描述的这个过程称为集群选主的过程, 首先所有的节点都认为是从节点, 都有机会称为主节点, 然后开始选主。(所有参与选主的主机都去 Zookeeper 上创建同一个临时节点, 那么最终一定只有一个客户端请求能够创建成功,成功创建节点的客户端所在的机器就成为了 Master,其他没有成功创建该节点的客户端,成为从节点,所有的从节点都会在主节点上注册一个子节点变更的Watcher,用于监控当前主节点是否存活,一旦发现当前的主节点挂了,那么其他客户端将会重新进行选主。)
分布式锁
:分布式锁使用的是 Zookeeper 的临时有序节点- 需求: 在分布式系统中, 很容出现多台主机操作同一资源的情况, 比如两台主机同时往一个文件中追加写入文本, 如果不去做任何的控制, 很有可能出现一个写入操作被另一个写入操作覆盖掉的状况.
- 方案: 此时我们可以来一把锁, 哪个主机获取到了这把锁, 就执行写入, 另一台主机等待; 直到写入操作执行完毕,另一台主机再去获得锁,然后写入。这把锁就称为分布式锁, 也就是说:分布式锁是控制分布式系统之间同步访问共享资源的一种方式。
- 实现: 使用 Zookeeper 的临时有序节点可以轻松实现这一需求(所有需要执行操作的主机都去 Zookeeper 上创建一个临时有序节点,然后获取到 Zookeeper 上创建出来的这些节点进行一个从小到大的排序,判断自己创建的节点是不是最小的, 如果是, 自己就获取到了锁; 如果不是, 则对最小的节点注册一个监听,如果自己获取到了锁, 就去执行相应的操作. 当执行完毕之后, 连接断开, 节点消失, 锁就被释放了,如果自己没有获取到锁, 就等待, 一直监听节点是否消失,锁被释放后, 再重新执行抢夺锁的操作.)
Zookeeper 集群
Zookeeper 集群介绍: Zookeeper 在一个系统中一般会充当一个很重要的角色, 所以一定要保证它的高可用, 这就需要部署 Zookeeper 的集群. Zookeeper 有三种运行模式: 单机模式, 集群模式和伪集群模式.
单机模式: 使用一台主机不是一个 Zookeeper 来对外提供服务, 有单点故障问题, 仅适合于开发、测试环境.
集群模式: 使用多台服务器, 每台上部署一个 Zookeeper 一起对外提供服务, 适合于生产环境.
伪集群模式:在服务器不够多的情况下, 也可以考虑在一台服务器上部署多个Zookeeper 来对外提供服务.
Zookeeper 集群角色有哪些?
- Leader: 负责投票的发起和决议, 更新系统状态, 是事务请求(写请求) 的唯一处理者, 一个 ZooKeeper 同一时刻只会有一个 Leader.对于 create 创建/setData 修改/delete 删除等有写操作的请求, 则需要统一转发给 leader 处理, leader 需要决定编号和执行操作, 这个过程称为一个事务.
- Follower: 接收客户端请求, 参与选主投票. 处理客户端非事务(读操作)请求,转发事务请求(写请求)给 Leader;
- Observer: 针对访问量比较大的 zookeeper 集群, 为了增加并发的读请求. 还可新增观察者角色.可以接受客户端请求, 把请求转发给leader, 不参与投票, 只同步leader 的状态.
Zookeeper 的特性有哪些?
- Zookeeper: 一个领导者(Leader), 多个跟随者(Follower)组成的集群.
- 集群中只要有半数以上节点存活, Zookeeper 集群就能正常服务.
- 全局数据一致: 每个Server 保存一份相同的数据副本, Client 无论连接到哪个Server, 数据都是一致的.
- 更新请求顺序性: 从同一个客户端发起的事务请求,最终会严格按照顺序被应用到zookeeper 中.
- 数据更新原子性: 一次数据更新要么成功, 要么失败。
- 实时性,在一定时间范围内,Client 能读到最新数据。
Zookeeper如何保证分布式数据一致性,对这个协议的了解?
- Zookeeper 采用 ZAB(Zookeeper Atomic Broadcast)协议来保证分布式数据一致性 。
- ZAB 并不是一种通用的分布式一致性算法,而是一种专为 Zookeeper 设计的崩溃可恢复的原子消息广播算法。
- ZAB 协议包括两种基本模式: 崩溃恢复 模式和 消息广播 模式:
- 消息广播模式主要用来进行事务请求的处理
- 崩溃恢复模式主要用来在集群启动过程,或者 Leader 服务器崩溃退出后进行新的Leader 服务器的选举以及数据同步.
Zookeeper 集群写数据流程
- Client 向 Zookeeper 的 Server1 上写数据, 发送一个写请求.
- 如果 Server1 不是 Leader, 那么 Server1 会把接受的请求进一步转发给 Leader, 因为每个Zookeeper 的Server 里面有一个是Leader. 这个Leader 会将写请求广播给各个Server, 比如 Server1 和 Server2, 各个 Server 会将该写请求加入待写队列, 并向Leader 发送成功信息(ack 反馈机制).
- 当Leader 收到半数以上Server 的成功信息, 说明该写操作可以执行. Leader 会向各个Server 发送事务提交信息, 各个 Server 收到信息后会落实队列里面的写请求, 此时写成功.
- Server1 会进一步通知 Client 数据写成功了, 这是就认为整个写操纵成功.
为什么 zookeeper 集群的数目,一般为奇数个?
- 容错:
由于在增删改操作中需要半数以上服务器通过。
- 2 台服务器,至少 2 台正常运行才行(2 的半数为 1,半数以上最少为 2),正常运行
1 台服务器都不允许挂掉 - 3台服务器,至少 2 台正常运行才行(3 的半数为 1.5,半数以上最少为 2),正常运行可以允许 1 台服务器挂掉
- 4台服务器,至少 3 台正常运行才行(4 的半数为 2,半数以上最少为 3),正常运行可以允许 1 台服务器挂掉
- 5台服务器,至少 3 台正常运行才行(5 的半数为 2.5,半数以上最少为 3),正常运行可以允许 2 台服务器挂掉
- 6台服务器,至少 3 台正常运行才行(6 的半数为 3,半数以上最少为 4),正常运行可以允许 2 台服务器挂掉
通过以上可以发现,3 台服务器和 4 台服务器都最多允许 1 台服务器挂掉,5 台服务器和 6 台服务器都最多允许 2 台服务器挂掉,但是明显 4 台服务器成本高于 3 台服务器成本,6 台服务器成本高于 5 服务器成本。这是由于半数以上投票通过决定的。
- 防脑裂
一个 zookeeper 集群中,可以有多个 follower.observer 服务器,但是必需只能有一个 leader 服务器。如果 leader 服务器挂掉了,剩下的服务器集群会通过半数以上投票选出一个新的leader 服务器。
- 一个集群 3 台服务器,全部运行正常,但是其中 1 台裂开了,和另外 2 台无法通讯。3 台机器里面 2 台正常运行过半票可以选出一个 leader。
- 一个集群 4 台服务器,全部运行正常,但是其中 2 台裂开了,和另外 2 台无法通讯。4 台机器里面 2 台正常工作没有过半票以上达到 3,无法选出 leader 正常运行。
- 一个集群 5 台服务器,全部运行正常,但是其中 2 台裂开了,和另外 3 台无法通讯。5 台机器里面 3 台正常运行过半票可以选出一个 leader。
- 一个集群 6 台服务器,全部运行正常,但是其中 3 台裂开了,和另外 3 台无法通讯。6 台机器里面 3 台正常工作没有过半票以上达到 4,无法选出 leader 正常运行。
通过以上分析可以看出,为什么 zookeeper 集群数量总是单出现,主要原因还是在于第 2 点,防脑裂,对于第 1 点,无非是正本控制,但是不影响集群正常运行。但是出现第 2 种裂的情况,zookeeper 集群就无法正常运行了。
SpringBoot 框架
SpringBoot 是什么?
SpringBoot是 Spring 的子项目,主要简化 Spring 开发难度,去掉了繁重配置,提供各种启动器,可以让程序员很快上手,节省开发时间.
SpringBoot 的优点
SpringBoot 基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。
- 版本锁定:解决 maven 依赖版本容易冲突的问题,集合了常用的并且测试过的所有版本,使用了 Starter(启动器)管理依赖并能对版本进行集中控制,如下的父工程带有版本号, 就是对版本进行了集中控制.
<!--引入父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
- 起步依赖: 解决了完成某一个功能要整合的 jar 包过多的问题,集合了常用的 jar 包。
- 自动配置:解决了整合框架或者技术的配置文件过多,集合了所有的约定的默认配置。
- 内置 Tomcat:通过内置的 tomcat,无需再用其他外置的 Tomcat 就直接可以运行 javaEE 程序。
运行 SpringBoot 项目的方式
- 可以打包
- 可以使用 Maven 插件直接运行.
- 直接运行 main 方法.
对SpringBoot 的启动器 starter的了解
- 什么是 starter?
starter 启动器,可以通过启动器集成其他的技术,比如说: web, mybatis, redis 等等.可以提供对应技术的开发和运行环境. - starter 执行原理?
SpringBoot 在启动时候会去扫描 jar 包中的一个名为 spring.factories,根据文件中的配置,去加载自动配置类. 配置文件格式是 key=value, value 中配置了很多需要 Spring 加载的类,Spring 会去加载这些自动配置类, Spring 读取后,就会创建这些类的对象,放到 Spring 容器中.后期就会从 Spring 容器中获取这些类对象. - SpringBoot 中常用的启动器
spring-boot-starter-web, 提供 web 技术支持、spring-boot-starter-test,提供测试支持、spring-boot-starter-jdbc、spring-boot-starter-jpa、spring-boot-starter-redis
SpringBoot 运行原理是什么情况
(一)SpringApplication 类作用及 run()方法作用:
- SpringApplication 这个类整合了其他框架的启动类, 只要运行这一个类,所有的整合就都完成了.
- 调用 run 函数 , 将当前启动类的字节码传入(主要目的是传入@SpringBootApplication 这个注解), 以及 main 函数的 args 参数.
- 通过获取当前启动类的核心信息, 创建 IOC 容器.
(二)当前启动类@SpringBootApplication 详细剖析:
- run 函数传入的当前启动类字节码, 最重要的是传入了@SpringBootApplication, 点开该注解源码, 会发现有多个注解组成。
(1) 第一类: JDK 原生注解 4 个
@Target(ElementType.TYPE) //当前注解的使用范围
@Retention(RetentionPolicy.RUNTIME) //生命周期
@Documented //声明在生成 doc 文档时是否带着注解
@Inherited //声明是否子类会显示父类的注解
(2)第二类: @SpringBootConfiguration
@SpringBootConfiguration本质是@Configuration,定义该类是个配置类功能等同于xml 配置文件.提到@Configuration 就要提到他的搭档@Bean, 使用这两个注解就可以创建一个简单的 Spring 配置类, 可以用来替代相应的 xml 配置文件.可以理解为创建了 IOC 容器了.
(3)第三类: @ComponentScan, 包扫描功能
这个注解对应 Spring 的 XML 配置中的@ComponentScan,其实就是自动扫描并加载符合条件的组件(比如@Component 和@Repository 等)或者 bean 定义, 最终将这些bean 定义加载到 IoC 容器中.也可以通过 basePackages 等属性来细粒度的定制@ComponentScan 自动扫描的范围, 如果不指定, 则默认扫描@ComponentScan 所在类的 package 及子包进行扫描。
(4)第四类: @EnableAutoConfiguration
- @EnableAutoConfiguration本质是@import, 自动导入功能。@EnableAutoConfiguration 也是借助@Import 的帮助,将所有符合自动配置条件的 bean 定义加载到IoC容 器 . @EnableAutoConfiguration 会根据类路径中的 jar 依赖为项目进行自动配置, 如:添加了spring-boot-starter-web 依赖, 会自动添加 Tomcat 和 SpringMVC 的依赖, SpringBoot 会对 Tomcat 和 SpringMVC 进行自动配置.
- SpringBoot 自动配置的注解是 @EnableAutoConfiguration,我们用的时候是在启动类上加@SpringBootApplication,这个注解是复合注解,内部包含 @EnableAutoConfiguration, @EnableAutoConfiguration 内部有一个@Import, 这个注解才是完成自动配置的关键,@Import 导入一个类(AutoConfigurationImportSelector)这个类内部提供了一个方法(selectImports). 这个方法会扫描导入的所有 jar 包下的 spring.factories 文件. 解析文件中自动配置类 key=value, 将列表中的类创建,并放到 Spring 容器中.
SpringBoot 热部署
导入 spring-boot-devtools 这个 jar 包: 就可以完成热部署了.
SpringBoot 中的配置文件
(1) 有哪些配置文件?
application.yml 或 application.properties 或 bootstrap.yml 或 bootstrap.properties
(2) 上面两种配置文件有什么区别?
- bootstrap 由父 ApplicationContext 加载, 比 application 配置文件优先被加载.
- bootstarp 里的属性不能被覆盖.
- application: springboot 项目中的自动化配置.
- bootstrap: 使用 spring cloud config 配置中心时, 需要加载连接配置中心的配置属性的, 就可以使用 bootstrap 来完成.
(3) 读取配置文件的方式?
- 读取默认配置文件
- 需要注入 Environment 类, 使用 environment.getProperty(peorperties 中的 key), 这样就能获得 key 对应的 value 值或者使用
- @value(${key.value}) 直接读取
- 读取自定义配置文件
- 自定义配置文件后缀必须是.propeties
- 编写和自定义配置文件对应的 java 类, 类上放 3 个注解
- @ConfigurationProperties(“前缀”)
- @PropertySource(“指定配置文件”)
- @Component 包扫描
- 读取的时候就跟读取默认配置文件一样.
SpringBoot 支持哪些日志框架
- Java Utils logging
- Log4j2
- Lockback
如果你使用了启动器,那么 springboo 默认将 Lockback 作为日志框架.
SpringBoot 常用注解
- @SpringBootApplication: 它封装了核心的@SpringBootConfiguration+@EnableAutoConfiguration +@ComponentScan 这三个类,大大节省了程序员配置时间,这就是 SpringBoot 的核心设计思想.
- @EnableScheduling 是通过@Import 将Spring 调度框架相关的bean 定义都加载到IoC 容器
- @MapperScan:spring-boot 支持mybatis 组件的一个注解,通过此注解指定mybatis 接口类的路径,即可完成对 mybatis 接口的扫描
- @RestController 是 @Controller 和 @ResponseBody的结合 , 一个类被加上@RestController 注解,数据接口中就不再需要添加@ResponseBody,更加简洁。
- @RequestMapping,我们都需要明确请求的路径.
- @GetMappping,@PostMapping,@PutMapping,@DeleteMapping结合@RequestMapping 使用, 是 Rest风格的, 指定更明确的子路径.
- @PathVariable:路径变量注解,用{}来定义 url 部分的变量名.
- @Service 这个注解用来标记业务层的组件,我们会将业务逻辑处理的类都会加上这个注解交给 spring 容器。事务的切面也会配置在这一层。当让 这个注解不是一定要用。有个泛指组件的注解,当我们不能确定具体作用的时候 可以用泛指组件的注解托付给spring 容器.
- @Component 和 spring 的注解功能一样, 注入到 IOC 容器中.
- @ControllerAdvice 和 @ExceptionHandler 配合完成统一异常拦截处理.
SpringCloud 框架
SOA 和微服务的区别?
-
集中式架构
项目功能简单, 一个项目只需一个应用, 将所有功能部署在一起, 这样的架构好处是减少了部署节点和成本.
缺点: 代码耦合,开发维护困难, 无法水平扩展, 单点容错率低,并发能力差 -
垂直拆分架构
当访问量逐渐增大,单一应用无法满足需求,此时为了应对更高的并发和业务需求,我们根据业务功能对系统进行拆分。
优点:- 系统拆分实现了流量分担,解决了并发问题
- 可以针对不同模块进行优化, 方便水平扩展,负载均衡,容错率提高
缺点:
- 系统间相互独立,会有很多重复开发工作,影响开发效率
-
分布式服务
当垂直应用越来越多, 随着项目业务功能越来越复杂, 并非垂直应用这一条线进行数据调用, 应用和应用之间也会互相调用, 也就是完成某一个功能,需要多个应用互相调用, 这就是将功能拆完来完成的分布式架构.
服务治理架构 SOA:
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键, 而最初的服务治理基石是 Dubbo 服务治理。
优点:
- 服务注册中心,实现服务自动注册和发现,无需人为记录服务地址.
- 服务自动订阅,服务列表自动推送,服务调用透明化,无需关心依赖关系
- 动态监控服务状态监控报告,人为控制服务状态
缺点:
- 服务间依然会有依赖关系,一旦某个环节出错会影响较大(容错机制)
- 服务关系复杂,运维、测试部署困难,不符合开发-运维一站式维护的思想.
微服务:
微服务的特点:
- 单一职责:微服务中每一个服务都对应唯一的业务能力,做到单一职责.
- 微:微服务的服务拆分粒度很小,例如一个用户管理就可以作为一个服务。每个服务虽小,但“五脏俱全”。
- 面向服务:面向服务是说每个服务都要对外暴露 Rest 风格服务接口 API。并不关心服务的技术实现,做到与平台和语言无关,也不限定用什么技术实现,只要提供Rest 的接口即可。
- 自治:自治是说服务间互相独立,互不干扰
- 团队独立:每个服务都是一个独立的开发团队,人数不能过多。
- 技术独立:因为是面向服务,提供 Rest 接口,使用什么技术没有别人干涉
- 前后端分离:采用前后端分离开发,提供统一 Rest 接口,后端不用再为 PC、移动段开发不同接口
- 数据库分离:每个服务都使用自己的数据源
- 部署独立,服务间虽然有调用,但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件,可复用,可替换,降低耦合,易维护.
*综上, 无论是 SOA 还是微服务, 都需要进行服务调度, 目前主流的服务调度是 RPC 和HTTP 两种协议, 而 Dubbo 基于 RPC 的远程调度机构, SpringCloud 是基于 Rest 风格(基于 http 协议实现的)的 Spring 全家桶微服务服务治理框架. *
SpringCloud 是什么?
SpringCloud 是一系列框架的集合,集成 SpringBoot,提供很多优秀服务:服务发现和注册,统一配置中心, 负载均衡,网关, 熔断器等的一个微服务治理框架.
SpringCloud 的优势?
- 因为 SpringCloud 源于 Spring,所以它的质量,稳定性,持续性都是可以保证的。
- SpringCloud 天热支持 SpringBoot 框架,就可以提高开发效率,能够实现需求。
- SpringCloud 更新很快,后期支持很给力。
- SpringCloud 可以用来开发微服务。
SpringCloud 有哪些核心组件?
- Eureka: 注册中心, 服务注册和发现
- 提供服务注册和发现, 是注册中心. 有两个组件: Eureka 服务端和 Eureka 客户端
- Eureka 服务端: 作为服务的注册中心, 用来提供服务注册, 支持集群部署.
- Eureka 客户端: 是一个 java 客户端, 将服务注册到服务端, 同时将服务端的信息缓存到本地, 客户端和服务端定时交互.
- Eureka-Server:就是服务注册中心(可以是一个集群),对外暴露自己的地址。
- 提供者:启动后向 Eureka 注册自己信息(地址,服务名称等),并且定期进行服务续约。
- 消费者:服务调用方,会定期去 Eureka 拉取服务列表,然后使用负载均衡算法选出一个服务进行调用。
- 心跳(续约):提供者定期通过 http 方式向 Eureka 刷新自己的状态
- 服务的注册和发现都是可控制的,可以关闭也可以开启。默认都是开启
- 注册后需要心跳,心跳周期默认 30 秒一次,超过 90 秒没发心跳认为宕机
- 服务拉取默认 30 秒拉取一次.
- Eureka 每个 60 秒会剔除标记为宕机的服务
- Eureka 会有自我保护,当心跳失败比例超过阈值(默认 85%),那么开启自我保护,不再剔除服务。
- Eureka 高可用就是多台 Eureka 互相注册在对方上.
原理:
- Ribbon: 负载均衡, 实现服务调用的负载均衡
- Ribbon 是 Netflix 发布的云中服务开源项目. 给客户端提供负载均衡, 也就是说Ribbon 是作用在消费者方的.
- 简单来说, 它是一个客户端负载均衡器, 它会自动通过某种算法去分配你要连接的机器.
- SpringCloud 认为 Ribbon 这种功能很好, 就对它进行了封装, 从而完成负载均衡.
- Ribbon 默认负责均衡策略是轮询策略.
- Hystrix: 熔断器
- 有时候可能是网络问题, 一些其它问题, 导致代码无法正常运行, 这是服务就挂了, 崩溃了. 熔断器就是为了解决无法正常访问服务的时, 提供的一种解决方案.
- 解决因为一个服务崩溃而引起的一系列问题, 使问题只局限于这个服务中,不会影响其他服务.
- Hystrix 提供了两种功能, 一种是服务降级:
- Hystrix 为每个服务分配了小线程池, 当用户发请求过来, 会通过线程池创建线程来执行任务, 当创建的线程池已满或者请求超时,则启动服务降级功能
- 降级指的请求故障时, 不会阻塞, 会返回一个友好提示(可以自定义, 例如网站维护中请稍后重试), 也就是说不会影响其他服务的运行.
- 另外一种是服务熔断:
- Closed:关闭状态(断路器关闭),所有请求都正常访问。
- Open:打开状态(断路器打开),所有请求都会被降级。Hystix 会对请求情况计数, 当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全关闭。默认失败比例的阈值是 50%,请求次数最少不低于 20 次。
- Half Open:半开状态,open 状态不是永久的,打开后会进入休眠时间(默认是 5S)。随后断路器会自动进入半开状态。此时会释放 1 次请求通过,若这个请求是健康的,则会关闭断路器,否则继续保持打开,再次进行 5 秒休眠计时。
- Feign: 远程调用
- 后台系统中, 微服务和微服务之间的调用可以通过 Feign 组件来完成.
- Feign 组件集成了 Ribbon 负载均衡策略(默认开启的, 使用轮询机制), Hystrix 熔断器(默认关闭的, 需要通过配置文件进行设置开启)
- 被调用的微服务需要提供一个接口, 加上@FeignClient(“url”)注解
- 调用方需要在启动类上加上@EnableFeignClients, 开启 Feign 组件功能.
- Zuul: 网 关
- Gateway: 路由/网关
- 对于项目后台的微服务系统, 每一个微服务都不会直接暴露给用户来调用的, 但是如果用户知道了某一个服务的ip:端口号:url:访问参数, 就能直接访问你. 如果再是恶意访问, 恶意攻击, 就会击垮后台微服务系统.因此, 需要一个看大门的大 boss, 来保护我们的后台系统.
- Gateway 支持过滤器功能,对请求或响应进行拦截,完成一些通用操作。(pre 过滤器,在转发之前执行,可以做参数校验、权限校验、流量监控、日志输出、协议转换等。post 过滤器,在后端微服务响应之后并且给前端响应之前执行,可以做响应内容、响应头的修改,日志的输出,流量监控等。)
- Gateway 还提供了两种类型过滤器(局部过滤器和全局过滤器)
- 对于局部过滤器(GatewayFilter):GatewayFilter 局部过滤器,是针对单个路由的过滤器。在 Spring Cloud Gateway 组件中提供了大量内置的局部过滤器,对请求和响应做过滤操作。遵循约定大于配置的思想,只需要在配置文件配置局部过滤器名称,并为其指定对应的值,就可以让其生效.
- 对于全局过滤器(GlobalFilter ): GlobalFilter 全局过滤器,不需要在配置文件中配置,系统初始化时加载,并作用在每个路由上。Spring Cloud Gateway 核心的功能也是通过内置的全局过滤器来完成。通过定义类实现 GlobalFilter 和 Ordered 接口然后复写方法完成逻辑出来来实现全局过滤器。
- Spring Cloud Config: 配置中心
- 在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在 Spring Cloud 中,有分布式配置中心组件 spring Cloud Config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git 仓库中.-
SpringBoot 和 SpringCloud 的关系
- SpringBoot 是为了解决 Spring 配置文件冗余问题, 简化开发的框架.
- SpringCloud 是为了解决微服务之间的协调和配置问题, 还有服务之间的通信, 熔断, 负载均衡远程调度任务框架.
- SpringCloud 需要依赖 SpringBoot 搭建微服务, SpringBoot 使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,SpringCloud 很大的一部分是基于 SpringBoot 来实现。
- SpringBoot 不需要依赖 SpringCloud 就可以独立开发. SpringBoot 也可以集成Dubbo 进行开发.
SpringCloud 和 Dubbo 的区别
- SpringCloud 和 Dubbo 都是主流的微服务架构.
- Dubbo 是 Apache 下的 Spring 体系下的微服务解决方案.
- SpringCloud 是阿里系统中分布式微服务治理框架.
- 技术方面对比
- SpringCloud 功能远远超过 Dubbo, Dubbo 只实现了服务治理(注册和发现). 但是 SpringCloud 提供了很多功能, 有 21 个子项目
- Dubbo 可以使用 Zookeeper 作为注册 中心, 实现服务 的注册和发现,SpringCloud 不仅可以使用 Eureka 作为注册中心, 也可以使用 Zookeeper 作为注册中心.
- Dubbo 没有实现网关功能, 只能通过第三方技术去整合. 但是 SpringCloud 有zuul 路由网关, 对请求进行负载均衡和分发. 提供熔断器, 而且和 git 能完美集成.
- 性能方面对比
- 由于 Dubbo 底层是使用 Netty 这样的 NIO 框架,是基于 TCP 协议传输的,配合以 Hession 序列化完成 RPC。 而 SpringCloud 是基于 Http 协议+Rest 接口调用远程过程的,相对来说,Http 请求会有更大的报文,占的带宽也会更多。
- 使用 Dubbo 时, 需要给每个实体类实现序列化接口, 将实体类转化为二进制进行RPC 通信调用.而使用 SpringCloud 时, 实体类就不需要进行序列化.
Eureka 和 Zookeeper 的区别
在谈这个问题前我们先看下 CAP 原则: C(Consistency)-数据一致性; A(Availability)- 服务可用性; P(Partition tolerance)-服务对网络分区故障的容错性, 这三个特性在任何分布式系统中不能同时满足, 最多同时满足两个, 而且 P(分区容错性)是一定要满足的.
- Eureka 满足 AP(服务可用性和容错性), Zookeeper 满足 CP(数据一致性和容错性)
- Zookeeper 满足 CP, 数据一致性, 服务的容错性. 数据在各个服务间同步完成后才返回用户结果, 而且如果服务出现网络波动导致监听不到服务心跳, 会立即从服务列表中剔除, 服务不可用.
- Eureka 满足 AP, 可用性, 容错性. 当因网络故障时, Eureka 的自我保护机制不会立即剔除服务, 虽然用户获取到的服务不一定是可用的, 但至少能够获取到服务列表. 用户访问服务列表时还可以利用重试机制, 找到正确的服务. 更服务分布式服务的高可用需求.
- Eureka 集群各节点平等, Zookeeper 集群有主从之分.(如果 Zookeeper 集群中有服务宕机,会重新进行选举机制,选择出主节点, 因此可能会导致整个集群因为选主而阻塞, 服务不可用. Eureka 集群中有服务宕机,因为是平等的各个服务器,所以其他服务器不受影响.)
- Eureka 的服务发现者会主动拉取服务, Zookeeper 服务发现者是监听机制(Eureka 中获取服务列表后会缓存起来, 每隔 30 秒重新拉取服务列表. Zookeeper 则是监听节点信息变化, 当服务节点信息变化时, 客户端立即就得到通知.)
欢迎java热爱者了解文章,作者将会持续更新中,期待各位友友的关注和收藏。。。