Spring Cloud 总结
微服务的概念
微服务是一种设计风格,它的主旨是将原本独立的系统拆分成多个小型服务,服务间通过RESTful API进行通信。每个小型服务都围绕着系统中的某项或一些耦合度高的业务功能进行构建。
微服务的优点
1.每个微服务都很小,这样能聚焦一个指定的业务功能或业务需求。
2.微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的。
3.当某个服务出现问题时,不会影响整合系统,不使用微服务,每个服务之间依赖太重,一个有问题往往会导致都出问题。
微服务的缺点
1..当服务数量增加,管理复杂性增加。
2.跟踪问题难
3.分布式系统复杂,不容易管理
注意事项
springcloud 版本更迭过快,本笔记主要是使用spring boot 1.5.4以及spring cloud 版本
笔记
spring cloud核心组件
Eureka:服务治理组件,包含服务注册中心,服务注册,与发现机制的实现
Hystrix:容错管理组件,实现断路器模式,帮助服务依赖中心出现的延迟和故障提供强大的容错能力
ribbon:客户端负载均衡的服务调用组件。
Feign:基于Ribbon和Hystrix的声明试服务调用组件
Zuul:网关组件,提供智能路由,访问过滤等功能
Aaius:外部化配置组件。
Eureka
服务治理组件
如何搭建服务注册中心
步骤
1.告诉应用这是一个注册中心
在springbootapplication注解的类中加上
@EnableEurekaServer
2.配置application.properties文件
server.port=1111
eureka.instance.hostname=localhost
#由于该应用为注册中心,所以设置false,代表不向注册中心注册自己
eureka.client.register-with-eureka=true
#由于注册中心的职责是维护实例,并不需要去检索服务,所以也设置为false
#eureka.client.fetch-registry=false
spring.application.name=hello-service
//代表服务将注册到那个注册中心
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
如何注册服务提供者
1.加入注解@EnableDiscoveryClient开启注册功能
在主类上加上@EnableDiscoveryClient注解,激活DiscoveryClient的自动配置。
2.设置eureka.client.register-with-eureka=true 代表向注册中心注册自己
server.port=1111
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=true
#由于注册中心的职责是维护实例,并不需要去检索服务,所以也设置为false
#eureka.client.fetch-registry=false
spring.application.name=hello-service //指定服务的名字
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/ //服务注册中心的地址
#####
如何实现高可用注册中心
将自己作为服务向其他服务注册中心注册自己,形成一组互相注册的服务注册中心,实现服务清单同步。
如何消费服务
通过不同端口启动服务
启动命令 java -jar xx.jar --server.port=8081, java -jar xx.jar --server.port=8082
这样便启动了两个服务,相当于是服务的集群
创建boot项目
在主类中添加注解@EnableDiscoveryClient,这样便可以去检索服务
//在主类中注入一个bean到spring中并带有负载均衡的效果
@Bean
//提供负载均衡
@LdBalanced
RestTemplate template(){
return
new RestTemplate() ;
}
在service层依赖注入RestTemplate
@Autowired RestTemplate template ; @Override public User find(Integer id) { return template.getForObject("http://USER-SERVICE/user/{1}",User.class,id); }
使用getForObject方法远程调用服务
重要概念
服务注册中心
服务提供者
服务消费者
服务提供者
服务注册
注册到服务注册中心 以 {服务名:{服务的实例名:服务的元信息}}存储
服务同步
由于服务注册中心相互注册,所以当服务提供者注册一个服务到一个注册中心时,它会将请求转发给集群中的其他注册中心。
服务续约
在注册完成后,服务者会维护一个心跳,持续发给Eureka Server“我们还活着”,以防止Eureka Server的剔除任务。
#####
服务消费者
获取服务
此时,服务注册中心已经注册了一个服务,并且该服务有两个实例。当我们启动服务消费者时,它会发送一个Rest请求给服务注册中心,来获取上面注册的服务清单。为了性能考虑,Euerka Server会维护一份只读的服务清单来返回客户端,同时该缓存清单会每隔30秒更新一次。
确保eureka.client.fetch-registry=true 来检索服务
服务调用
服务消费者在获取服务清单后,通过服务名可以获取具体实例,和该实例的元信息。因为有服务实例的信息,所以客户端可以根据自己的需求决定调用那个实例,在Ribbon中默认采用轮询的方式调用,从而实现负载均衡
对于访问实例的选择,Eureka有Region和zone的概念,一个Region中可以包含多个Zone,每个服务客户端需要注册到一个zone中,所以每个客户端对应一个region和一个zone。在进行服务调用的时候,优先访问同处一个zone中的服务提供方,若访问不到,就访问其他zone。
服务下线
系统运行会出现关闭或重启服务的情况,当服务实例正常关闭时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:"我要下线了“。服务端接收请求后,将服务状态设置为下线,并把下线事件传播出去。
服务注册中心
失效剔除
有些时候,我们的服务实例不一定会正常下线,服务注册中心没有收到"服务下线"的请求。为了将这些无效提供服务的实例剔除Eureka Server在启动时会创建一个定时任务,默认每隔一段时间(默认60s)将当前清单中超时(默认90s)没有续约的服务剔除。
自我保护
服务注册到server之后会维护一个心跳连接就,告诉Eureka server我还活着。eureka server在运行期间会统计心跳失败的比例在15分钟内少于85%,如果少于,Eureka server就会将这些实例信息保护起来。但是如果这段时间内实例出现问题,客户端会拿到实际已经不存在的实例,会出现调用失败的情况,所以客户端必须要有容错机制,比如请求重试,和断路器机制。
服务容错Hystrix
使用步骤
添加依赖
在主类添加注解
响应大于一秒就会进入回调方法
原理分析:
1.创建HystrixCommond 或者 HystrixObservableCommand对象,用来表示对依赖服务的操作请求,同时传递所有需要的参数。
2、执行命令
3.结果是否被缓存
4.断路器是否打开
如果断路器打开,那么hystrix不会执行命令,而是转接到fallback处理逻辑
如果断路器是关闭的执行第五步
5.线程池/请求队列/信号量是否占满
6.计算断路器的健康度
7.fallback处理
异常的获取
在fallback方法中添加参数 Throwable e
使用ignoreExceptions 注解来忽略异常,也就是如果使用了这个注解,并且注解了某个异常,那么当这个异常在Command执行中出现时,会忽略掉而不进入fallback中处理。直接抛给用户
请求缓存
注解实现方式
实现过滤器
在主类 添加注解
移除缓存
这里的请求缓存指的是一次请求中的缓存,当用户在此发起请求,缓存的生命周期就结束了,就使用新的缓存。
请求合并
当调用多次单个请求时,可以将该请求合并在一起,去访问批处理的接口,然后将结果单个返回
public class BookCollapseCommand extends HystrixCollapser<List<Book>, Book, Long> { private BookService bookService; private Long id; //为请求合并器设置时间,合并器会在设定时间内获取单个user的请求,并在时间窗结束时进行和并并组装成单个批量请求 public BookCollapseCommand(BookService bookService, Long id) { super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("bookCollapseCommand")).andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100))); this.bookService = bookService; this.id = id; } @Override //返回给定的单个请求参数 public Long getRequestArgument() { return id; } @Override //collapsedRequests该参数保存了设定时间内的所有单个请求,通过获取的参数来组织我们的command protected HystrixCommand<List<Book>> createCommand(Collection<CollapsedRequest<Book, Long>> collapsedRequests) { List<Long> ids = new ArrayList<>(collapsedRequests.size()); ids.addAll(collapsedRequests.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList())); BookBatchCommand bookBatchCommand = new BookBatchCommand(ids, bookService); return bookBatchCommand; } @Override //该方法在创建command并执行完成之后执行,batchResponse是批量执行的结果,collapsedRequests代表每个被合并的请求,遍历batchResponse到被和并发的请求中. protected void mapResponseToRequests(List<Book> batchResponse, Collection<CollapsedRequest<Book, Long>> collapsedRequests) { System.out.println("mapResponseToRequests"); int count = 0; for (CollapsedRequest<Book, Long> collapsedRequest : collapsedRequests) { Book book = batchResponse.get(count++); collapsedRequest.setResponse(book); } } }
controller中已异步的方式调用Command,就会把请求合并起来。
注解方式
使用请求合并会造成延时
所以具备以下特点可以使用请求合并
请求命令本身的延迟高
延迟窗内的并发量大
属性值优先级
全局默认值:最低
全局配置属性:通过配置文件中定义全局属性值,在应用启动时或在与spring cloud config和spring cloud bus实现的动态刷新配置功能配合下,可以实现对“全局默认值”的覆盖,以及在运行期对全局默认值动态修改
实例默认值:通过代码对实例定义的默认值。
实例配置属性:通过配置文件中为指定实例进行属性配置以覆盖前三个,可以配合spring cloud config和spring cloud bus实现动态刷新配置功能,可以实现对具体实例配置的覆盖
待续
碰到的问题
版本不兼容
springboot2.x 与springcloud版本不兼容,解决办法springboot 退版本到1.5.x springcloud使用低版本
服务注册失败
eureka.client.register-with-eureka=true这个属性一定要设为true
请求缓存无效
两个问题
第一个缓存指定的id必须指定cacheMethod 直接使用Cachekey注解无效,原因未知。
第二个缓存必须在一个HystrixContext上下文中才能生效,所以必须初始化一个HytrixContext,解决办法是加入Webfilter,在filter中初始化HystrixContext,并使用@ServletComponentScan注解使其生效。
请求合并的问题
两个问题
第一个问题是在写批量查询的接口时,传参的问题,参数要以List的形式获取,在URL地址栏中写参数。
解决的方法有两种一种是使用vo对象将List作为vo对象的参数。第二种就是使用@requestParam("ids")指定参数。
在url中传参有两种方式?ids=1,2,3,4 或者?ids=1&ids=2&ids=3
第二个问题是请求合并的command中方法mapResponseToRequests的第一个参数获取的不是User类型而是一个LinkedHashMap类型
解决方法在远程调用批量接口的方法中将返回值由List<User> 改为User[]
springboot导入依赖包的问题
打的jar包会多BOOT-INF和org目录导致,依赖不到这个jar的class
解决办法
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <skip>true</skip> </configuration>
<plugin>
在maven-plugin中加入
<configuration> <skip>true</skip> </configuration>