三、Dubbo
1. 如果注册中心集群都挂掉,发布者和订阅者之间还能通信么?
# 能,因为有缓存;注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯 新的服务将无法发现;服务下架后仍旧有被调用的可能;
2. dubbo连接注册中心和直连的区别
连接注册中心:消费者动态发现服务提供者,实现软负载均衡和集群容错; 直连:不能动态增减提供者;开发、测试环境可通过指定Url方式绕过注册中心直连指定的服务地址,快速测试;
3. dubbo服务的容错机制
# http://dubbo.io/books/dubbo‐user‐book/demos/fault‐tolerent‐strategy.html Failover Cluster:失败自动切换,当出现失败,重试其它服务器,默认两次;Failfast Cluster:失败立即报错 Failsafe Cluster:失败安全,出现异常时,直接忽略 Failback Cluster:失败自动恢复,后台记录失败请求,定时重发 Forking Cluster:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源 Broadcast Cluster:播调用所有提供者,逐个调用,任意一台报错则报错 # 要注意分布式事务
3.1 Failover Cluster
失败自动切换,缺省值,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。配置如下:生产者:
<dubbo:service retries="2" />
消费者:
<dubbo:reference retries="2" />
消费者具体到调用生产者的哪个方法:
<dubbo:reference> <dubbo:method name="findFoo" retries="2" /> </dubbo:reference>
3.2 Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录 。配置如下:生产者:
<dubbo:service cluster="failfast" />
消费者:
<dubbo:reference cluster="failfast" />
3.3 Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作 。配置如下:生产者:
<dubbo:service cluster="failsafe" />
消费者:
<dubbo:reference cluster="failsafe" />
3.4 Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作 。配置如下:生产者:
<dubbo:service cluster="failback" />
消费者:
<dubbo:reference cluster="failback" />
3.5 Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作 ,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。配置如下:生产者:
<dubbo:service cluster=“forking" />
消费者:
<dubbo:reference cluster=“forking" />
3.6 Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息 。配置如下:生产者:
<dubbo:service cluster="broadcast" />
消费者:
<dubbo:reference cluster="broadcast" />
4. 具体问题示例1:关于服务调用超时
# 超时是针对消费端还是服务端?
dubbo的超时是争对客户端的,由于是一种NIO模式,消费端发起请求后得到一个ResponseFuture,然后消费端一直轮询这个ResponseFuture直至超时或者收到服务端的返回结果。虽然超时了,但仅仅是消费端不再等待服务端的反馈并不代表此时服务端也停止了执行。
# 超时在哪设置?
消费端
- 全局控制
<dubbo:consumer timeout="1000"></dubbo:consumer>
- 接口控制
- 方法控制
服务端
- 全局控制
<dubbo:provider timeout="1000"></dubbo:provider>
- 接口控制
- 方法控制
可以看到dubbo针对超时做了比较精细化的支持,无论是消费端还是服务端,无论是接口级别还是方法级别都有支持。
# 优先级是什么?
优先顺序为:客户端方法级>服务端方法级>客户端接口级>服务端接口级>客户端全局>服务端全局
# 实现原理消费端发起远程请求后,线程不会阻塞等待服务端的返回,而是马上得到一个ResponseFuture(ReponseFuture的实现类:DefaultFuture),消费端通过不断的轮询机制判断结果是否有返回。因为是通过轮询,轮询有个需要特别注要的就是避免死循环,所以为了解决这个问题就引入了超时机制,只在一定时间范围内做轮询,如果超时时间就返回超时异常
# 设置超时主要是解决什么问题?如果没有超时机制会怎么样?
对调用的服务设置超时时间,是为了避免因为某种原因导致线程被长时间占用,最终出现线程池用完返回拒绝服务的异常。
5. 服务提供者能实现失效踢出是什么原理
服务失效踢出基于zookeeper的临时节点原理。
临时节点的生命周期和客户端会话绑定,也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉
源码参考 RegistryService, 文档参考https://www.jianshu.com/p/f42c69e4bd3e?fromApp=1
# redis ‐ 脏数据由监控中心删除 # zookeeper ‐ 的临时节点
6. dubbo通讯协议
# 什么是通讯协议客户端服务端之间进行数据流转的一种约定,将数据按照标准格式来组装;# 协议内容头部信息(调用控制协议16B) + body(序列化) head = 魔数(2) + 标识(1) + 响应状态(1) + 消息ID(8) + 内容长度(4) body = dubbo版本 + 服务接口路径 + 服务版本号 + 服务方法名 + 参数描述符 + 参数值序列化 + 隐式参数
7. 大概率的问题
# dubbo文档,仔细阅读一遍,面试官可能随便抽取一个。
7.1 dubbo配置的方式和属性
dubbo的官网入门使用demo
1. 服务提供者
1.1 定义服务接口DemoService.java
package com.alibaba.dubbo.demo; public interface DemoService { String sayHello(String name); }
1.2 在服务提供方实现接口DemoServiceImpl.java
package com.alibaba.dubbo.demo.provider; import com.alibaba.dubbo.demo.DemoService; public class DemoServiceImpl implements DemoService { public String sayHello(String name) { return "Hello " + name; } }
1.3 用Spring配置声明暴露服务provider.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!-- 提供方应用信息,用于计算依赖关系 --> <dubbo:application name="hello-world-app" /> <!-- 使用multicast广播注册中心暴露服务地址 --> <dubbo:registry address="multicast://224.5.6.7:1234" /> <!-- 用dubbo协议在20880端口暴露服务 --> <dubbo:protocol name="dubbo" port="20880" /> <!-- 声明需要暴露的服务接口 --> <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" /> <!-- 和本地bean一样实现服务 --> <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" /> </beans>
1.4 加载Spring配置Provider.java
import org.springframework.context.support.ClassPathXmlApplicationContext; public class Provider { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"http://10.20.160.198/wiki/display/dubbo/provider.xml"}); context.start(); System.in.read(); // 按任意键退出 } }
2. 服务消费者
2.1 通过Spring配置引用远程服务consumer.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 --> <dubbo:application name="consumer-of-helloworld-app" /> <!-- 使用multicast广播注册中心暴露发现服务地址 --> <dubbo:registry address="multicast://224.5.6.7:1234" /> <!-- 生成远程服务代理,可以和本地bean一样使用demoService --> <dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService" /> </beans>
2.2 加载Spring配置,并调用远程服务Consumer.java
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.alibaba.dubbo.demo.DemoService; public class Consumer { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"http://10.20.160.198/wiki/display/dubbo/consumer.xml"}); context.start(); DemoService demoService = (DemoService)context.getBean("demoService"); // 获取远程服务代理 String hello = demoService.sayHello("world"); // 执行远程方法 System.out.println( hello ); // 显示调用结果 } }
3. 总结
3.1 该接口需单独打包,在服务提供方和消费方共享 3.2 对服务消费方隐藏实现 3.3 也可以使用IoC注入
7.2 dubbo底层协议
底层的调用协议http、rmi、netty(dubbo)7.3 dubbo服务注册与发现流程
调用关系说明:
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败, 再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
总结:
生产者启动时把接口注册到注册中心,消费者启动时从注册中心获取生产者的接口列表放到本地,当有请求过来时,从消费者的本地获取生产者的接口列表选择一个生产者使用底层的调用协议(http、rmi、netty(dubbo))去调用,dubbo同时还能对服务进行监控
四、SpringBoot
1、SpringBoot与SpringMVC差异化在哪
1.首先这两者专注的领域不同,解决的问题也不一样 2.Spring MVC是基于 Servlet 的一个 MVC 框架,通过Dispatcher Servlet, ModelAndView解决 WEB 开发的问题.但是它的配置繁琐,大量 .xml、.properties文件。并且用maven管理项目的时候很容易出现jar包冲突。3.而Spring Boot 是基于Spring的一套快速开发整合包,它不仅仅包含了spring mvc ,还包括 spring JPA、spring security等实现自动配置,降低项目搭建的复杂度.而且自带tomcat.pom.xml 中可以使用 starter的方式配置,简化配置还能解决jar包冲 突问题。spring-boot-starter-web spring-boot-starter-data-jpa spring-boot-starter-redis 4.但他们的基础都是Spring 的 IOC 和 AOP 5.spring mvc 打包后是war包,spring boot 是jar包
2、为什么说Spring是一个容器
Spring通过IOC创建和管理bean实例,所以Spring也称为IOC容器
3、常用注解@Required @Autowired @Qualifier @Controller @RequestMapping
@Required
注解作用于Bean setter
方法上,用于检查一个Bean的属性的值在配置期间是否被赋予或设置(populated),否则,容器会抛出一个BeanInitializationException异常。
示例:
package com.jsoft.testspring.testannotationrequired;import org.springframework.beans.factory.annotation.Required;public class Student { private Integer age; private String name; @Required public void setAge(Integer age){ this.age = age; } public Integer getAge(){ return this.age; } public void setName(String name){ this.name = name; } public String getName(){ return this.name; } }
beans.xml:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean id="student" class="com.jsoft.testspring.testannotationrequired.Student"> <property name="name" value="Jim"/> </bean></beans>
App.java:
package com.jsoft.testspring.testannotationrequired;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * Hello world! * */public class App { public static void main( String[] args ) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student)applicationContext.getBean("student"); System.out.println(student.getAge()); System.out.println(student.getName()); } }
而此时的运行结果是出现了错误的,因为age的setter方法没有在bean中注入,而age的setter方法标记了@Required,也就是必须要输入,抛出的异常为:BeanInitializationException。
那么我们将beans.xml补全之后:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean id="student" class="com.jsoft.testspring.testannotationrequired.Student"> <property name="name" value="Jim"/> <property name="age" value="27"/> </bean></beans>
此时的运行结果一切正常:
@Autowired自动装配
@Autowired顾名思义,就是自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。
@Autowired默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的Bean 时,Spring将其注入@Autowired标注的变量中。
@Qualifier(指定注入Bean的名称)
如果容器中有一个以上匹配的Bean,则可以通过@Qualifier注解限定Bean的名称
使用 @Controller 定义一个 Controller 控制器
例1:
@Controllerpublic class MyController { @RequestMapping ( "/showView" ) public ModelAndView showView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( " 需要放到 model 中的属性名称 " , " 对应的属性值,它是一个对象 " ); return modelAndView; } }
例1所示@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器,这个接下来就会讲到。
单单使用@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类,因为这个时候Spring 还不认识它。那么要如何做Spring 才能认识它呢?这个时候就需要我们把这个控制器类交给Spring 来管理。
这个时候有两种方式可以把MyController 交给Spring 管理,好让它能够识别我们标记的@Controller 。
第一种方式是在SpringMVC 的配置文件中定义MyController 的bean 对象。
第二种方式是在SpringMVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。
< context:component-scan base-package = "com.host.app.web.controller" > < context:exclude-filter type = "annotation" expression = "org.springframework.stereotype.Service" /> </ context:component-scan >
注:上面 context:exclude-filter 标注的是不扫描 @Service 标注的类
使用 @RequestMapping 来映射 Request 请求与处理器
例1可以使用@RequestMapping 来映射URL 到控制器类,或者是到Controller 控制器的处理方法上。当@RequestMapping 标记在Controller 类上的时候,里面使用@RequestMapping 标记的方法的请求地址都是相对于类上的@RequestMapping 而言的;当Controller 类上没有标记@RequestMapping 注解时,方法上的@RequestMapping 都是绝对路径。这种绝对路径和相对路径所组合成的最终路径都是相对于根路径“/ ”而言的。
在这个控制器中,因为MyController 没有被@RequestMapping 标记,所以当需要访问到里面使用了@RequestMapping 标记的showView 方法时,就是使用的绝对路径/showView.do 请求就可以了。
例2:
@Controller @RequestMapping ( "/test" )public class MyController { @RequestMapping ( "/showView" ) public ModelAndView showView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( " 需要放到 model 中的属性名称 " , " 对应的属性值,它是一个对象 " ); return modelAndView; } }