什么是微服务架构
在系统架构与设计的实践中,有两种集中式系统和分布式系统。
集中式系统:集中式系统也叫单体应用,就是把所有的程序、功能、模块都集中到一个项目中,部署在一台服务器上,从而对外提供服务。
分布式系统:分布式系统就是把所有的程序、功能拆分成不同的子系统,部署在多台不同的服务器上,这些子系统相互协作共同对外提供服务。
分布式强调系统的拆分,微服务也是强调系统的拆分,微服务架构属于分布式架构的范畴。
微服务:
简单地说, 微服务是系统架构上的一种设计风格, 它的主旨是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间通过基于HTTP的RESTful API进行通信协作。每个服务都是一个独立的项目,可以进行独立的测试、开发和部署等。
由于各个独立的服务之间使用的是基于HTTP的作为数据通信协作的基础,所以这些微服务可以使用不同的语言来开发。
缺点:微服务架构增加了集成测试,系统维护、部署的难度,也会出现数据一致性的问题。
微服务技术栈
天上飞的理念和地上落地的实现(框架,中间件)
微服务条目 | 落地实现 |
---|---|
服务基础开发 | Spring MVC、Spring、SpringBoot |
服务注册与发现 | Netflix的Eureka、Apache的ZooKeeper,Consul,Nacos |
服务调用 | RPC调用有阿里巴巴的Dubbo,Rest方式调用有当当网Dubbo基础上扩展的Dubbox,以及Ribbon、OpenFeign(Feign),LoadBalancer等 |
配置管理 | 百度的Disconf、360的QConf、淘宝的Diamond、Netflix的Archaius等 |
负载均衡 | Ribbon,Nginx |
服务熔断 | Hystrix |
服务路由(API网关) | Zuul, gateway |
服务监控 | Zabbix,Nagios |
批量任务 | 当当网的Elastic-Job、Linkedln的Azkaban |
服务跟踪 | 京东的Hydra、Twitter的Zipkin等 |
消息队列 | RabittMQ,ActiveMQ |
服务总线 | Bus,Nacos |
服务部署 | Docker,OpenStack |
数据流操作开发包 | SpringCloud Stream |
认识Spring Cloud
Spring Cloud是一个一站式的开发分布式系统的框架,为开发者提供了一系列的构建分布式系统的工具集。
Spring Cloud基于Spring Boot框架构建微服务架构。
Spring Boot与Spring Cloud
Spring Cloud基于Spring Boot框架构建微服务架构。
Spring Boot是来构建微服务,Spring Cloud是来协调微服务的。
Spring Cloud是关注全局微服务协调整理治理的框架,它将Spring Boot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理,服务发现,路由,事件总线,全局锁等集成服务。
Dubbo和Spring Cloud技术选型
分布式+服务治理Dubbo
Spring Cloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式。这样牺牲了服务调用的性能,REST方式更灵活,服务方和调用方依靠约定,不存在代码级别的强依赖。
Dubbo就像组装机,自由度高,性能也高(花同样的钱)。Spring Cloud就像品牌机,兼容性高,稳定性高。
CAP理论
一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性在是分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡,在此Zookeeper保证的是CP, 而Eureka则是AP。
Zookeeper保证CP
在ZooKeeper中,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举,但是问题在于,选举leader需要一定时间, 且选举期间整个ZooKeeper集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得ZooKeeper集群失去master节点是大概率事件,虽然服务最终能够恢复,但是在选举时间内导致服务注册长期不可用是难以容忍的。
Eureka保证AP
Eureka优先保证可用性,Eureka各个节点是平等的,某几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。
所以Eureka在网络故障导致部分节点失去联系的情况下,只要有一个节点可用,那么注册和查询服务就可以正常使用,而不会像zookeeper那样使整个注册服务瘫痪,Eureka优先保证了可用性。
服务注册中心Eureka
服务注册:
将服务所在主机、端口、版本号、通信协议等信息登记到注册中心上
服务发现:
服务消费者向注册中心请求已经登记的服务列表,然后得到某个服务的主机、端口、版本号、通信协议等信息,从而实现对具体服务的调用
对于注册进Eureka里面的微服务,可以通过服务发现来获得该服务的信息
修改controller,加一个DiscoverClient类型的属性
主启动类加@EnableDiscoveryClient注解
Eureka是什么?
Eureka是一个服务治理组件,它主要包括服务注册和服务发现,Eureka 是一个基于 REST 的服务,用来定位服务。Netflix 公司开发的,Spring Cloud对Netflix Eureka 做了二次封装。
Eureka注册中心高可用集群
在微服务架构的这种分布式系统中,我们要充分考虑各个微服务组件的高可用性问题,不能有单点故障,由于注册中心eureka本身也是一个服务,如果它只有一个节点,那么它有可能发生故障,这样我们就不能注册与查询服务了,所以我们需要一个高可用的服务注册中心,这就需要通过注册中心集群来解决。
eureka服务注册中心它本身也是一个服务,它也可以看做是一个提供者,又可以看做是一个消费者,我们之前通过配置:
eureka.client.register-with-eureka=false 让注册中心不注册自己,但是我们可以向其他注册中心注册自己
Eureka Server的高可用实际上就是将自己作为服务向其他服务注册中心注册自己,这样就会形成一组互相注册的服务注册中心,进而实现服务清单的互相同步,往注册中心A上注册的服务,可以被复制同步到注册中心B上,所以从任何一台注册中心上都能查询到已经注册的服务,从而达到高可用的效果。
Eureka服务注册中心自我保护机制
在没有Eureka自我保护的情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例,但是当发生网络分区故障时,那么微服务与Eureka Server之间将无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是正常的,此时不应该注销这个微服务,如果没有自我保护机制,那么Eureka Server就会将此服务注销掉
当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么就会把这个微服务节点进行保护。一旦进入自我保护模式,Eureka Server就会保护服务注册表中的信息,不删除服务注册表中的数据(也就是不会注销任何微服务)。
它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务,使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
eureka.server.enable-self-preservation = false禁用自我保护模式。
例如,两个微服务客户端实例A和B之间有调用的关系,A是消费者,B是提供者,但是由于网络故障,B未能及时向Eureka发送心跳续约,这时候Eureka 不能简单的将B从注册表中剔除,因为如果剔除了,A就无法从Eureka 服务器中获取B注册的服务,但是这时候B服务是可用的;所以,Eureka的自我保护模式最好还是开启它。
负载均衡服务调用Ribbon
负载均衡是指将一个请求均匀地分摊到不同的节点单元上执行,负载均和分为硬件负载均衡和软件负载均衡
硬件负载均衡:比如 F5、深信服、Array 等;
软件负载均衡:比如 Nginx、LVS、HAProxy 等;
硬件负载均衡或是软件负载均衡,他们都会维护一个可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。当客户端发送请求到负载均衡设备的时候,该设备按某种算法(比如轮询、权重、 最小连接数等)从维护的可用服务端清单中取出一台服务端的地址,然后进行转发。
Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,是一个基于HTTP和TCP的客户端负载均衡工具。
Ribbon只是一个工具类框架,比较小巧,Spring Cloud对它封装后使用也非常方便,它不像服务注册中心、配置中心、API网关那样需要独立部署,Ribbon只需要在代码直接使用即可;
Ribbon是客户端的负载均衡工具,而客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置不同,在客户端负载均衡中,所有客户端节点下的服务端清单,需要自己从服务注册中心上获取,比如Eureka服务注册中心。
Ribbon 与 Nginx 的区别
Ribbon是客户端的负载均衡工具,而客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置不同,在客户端负载均衡中,所有客户端节点下的服务端清单,需要自己从服务注册中心上获取,比如Eureka服务注册中心。
Nginx相当于医院大门,对外的。Ribbon相当于前台,对内的。
Nginx集中式负载均衡(LB)
Ribbon进程内的负载均衡(LB)
一开始引入Eureka依赖时,带着Ribbon依赖
Ribbon核心组件IRule
IRule根据特定的算法从服务列表中选取一个要访问的服务
IRule实现类 | 算法 |
---|---|
RandomRule | 随机 |
RoundRobinRule | 轮询 |
WeightedResponseTimeRule | 根据平均响应时间计算所有服务的权重,响应时间越快服务权重就越大被选中的概率即越高,如果服务刚启动时统计信息不足,则使用RoundRobinRule策略,待统计信息足够会切换到该WeightedResponseTimeRule策略 |
RetryRule | 先按照RoundRobinRule策略分发,如果分发到的服务不能访问,则在指定时间内进行重试,分发其他可用的服务; |
BestAvailableRule | 先过滤掉由于多次访问故障的服务,然后选择一个并发量最小的服务; |
AvailabilityFilteringRule | 先过滤掉由于多次访问故障的服务,以及并发连接数超过阈值的服务,然后对剩下的服务按照轮询策略进行访问 |
ZoneAvoidanceRule | 综合判断服务节点所在区域的性能和服务节点的可用性,来决定选择哪个服务 |
如何替换?
默认时轮询。自定义配置类不能放在@ComponentScan所扫描的包以及子包下,否则这个自定义配置类会被所有的Ribbon客户端所共享,达不到定制的目的了。(@SpringBootApplication这个注解就是被扫描的包,所以跳出这个包)
1)定义配置类,注意上面的
2)在主启动类(有问题???)
@RibbonClient(name = "cloud-payment-service",configuration = MyRule.class)
负载均衡算法
轮询:rest接口第几次请求%服务器集群总数量=实际调用服务器位置下标,每次重启服务后,rest接口从1计数
List<ServiceInstances> instances=discoveryClient.getInstances("cloud-payment-service")
OpenFeign服务接口调用
之前是用Ribbon+RestTemplate,现在直接是OpenFeign,自定义一个接口,上面加上相关注解
@FeignClient使用在消费端
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
服务接口加注解,@FeignClient这个注解就是找暴漏在注册中心的服务
1)module,pom,yml,主启动(注解都是在哪使用,在哪激活)就在主启动类上激活@EnableFeignClients
2)新建一个service包,新建接口,相当于之前的controller,就是没有方法体罢了。加上两个注解@Component,@FeignClient,这个注解加上服务的名字
为什么找不到服务啊????
目测是order服务没写名字
注意啦!!!客户端服务接口中的的getmapping里面的值一定和服务端controller的一致
OpenFeign超时控制
服务降级和熔断Hystrix
服务之间通过远程调用实现信息交互,那么当某个服务的响应太慢或者故障,又或者因为网络波动或故障,则会造成调用者延迟或调用失败,当大量请求到达,则会造成请求的堆积,导致调用者的线程挂起,从而引发调用者也无法响应,调用者也发生故障。
为了解决此问题,微服务架构中引入了一种叫熔断器的服务保护机制。
微服务架构中的熔断器,就是当被调用方没有响应,调用方直接返回一个错误响应即可,而不是长时间的等待,这样避免调用时因为等待而线程一直得不到释放,避免故障在分布式系统间蔓延。
Hystrix具备服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能。
在微服务架构中,微服务之间的数据交互通过远程调用完成,微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,此时如果链路上某个微服务的调用响应时间过长或者不可用,那么对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,导致“雪崩效应”。
Hystrix重要概念
服务降级(fallback):
服务器忙,稍后再试,不让客户端等待,返回一个友好的提示
服务熔断(break):
服务熔断是应对雪崩效应的一种微服务链路保护机制.调用链路的某个微服务不可用或者响应时间太长时,会进行服务熔断,不再有该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。直接拒绝访问,拉闸,然后调用服务降级返回友好提示。
服务限流(flowlimit):严禁拥挤,线程一秒N个,有序进行
如何解决
超时不再等待,程序出错有兜底
解决:
1)服务方(8001)超时了,80方不能一直等待,服务降级
2)8001 down机了,80不能一直等待,服务降级
3)8001没事,只不过80要求自己等待的时间不超过自身限制,服务降级
8001自身找问题,如果业务处理超过3秒则服务降级步骤:
1)注解@HystrixCommad
在8001的业务层方法加上@HystrixCommand
2)这个注解两个属性
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
3)新增注解,启动类上加上启动注解@EnableCircuitBreaker
刚才是服务端自测,假设消费端认为2秒就不行了,就得服务降级
一般服务降级都放在客户端
步骤:
1)引入Hystrix依赖
2)yml,加上
feign: hystrix: enabled: true
3)启动类加上@EnableHystrix
4)controller层写上兜底的方法
这样写法,兜底方法和业务逻辑在一起了,耦合度极高,而且一个方法一个兜底,所以代码膨胀。
全局服务降级(解决代码膨胀问题)
配置过fallback的就是精确打击的,没有的就找全局的
@DefaultProperties(defaultFallback=“”)
1.类上加上一个@DefaultProperties,写个全局方法
2.注解@HystrixCommand加在方法上没有那俩属性了
通配服务降级(解决耦合度高问题)
因为是feign,所以有个业务接口,这个接口是用来暴露给服务端的,
解耦合可以写个类实现这个接口,在这个类里实现fallback
三步:
1)写个类实现找服务的那个接口,加上@Component
2)重写里面的方法,里面就是fallback方法
3)在之前接口的@FeignClient这个注解加上fallback = PaymentHystrixServiceImpl.class这个属性
注意:这样之前写的精确打击不管用了????条件还管用(超时),但是fallback的方法不是精确打击的了,而是重写的方法
服务熔断
一定时间内请求过多,但都失败,调用失败,就会自动启动熔断机制,当检测该节点响应正常后,恢复调用链路。
服务降级-----进而熔断------恢复链路
服务限流
Hystrix图形化Dashboard搭建
服务网关
Zuul和Gateway
SpringCloud Gateway作为Spring Cloud生态系统中的网关,目标代替zuul,没有对zuul2.0以上的版本进行集成,仍然还是使用1.x的非Reactor模式的老版本,为了提高性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层使用了高性能的Reactor模式通信框架Netty。
Gateway是基于异步非阻塞模型开发的
功能
反向代理,鉴权,流量监控,熔断,日志监控等
三大核心概念:
路由(Route):
路由是构建网关的基本模块,由ID,目标URI,一系列的断言和过滤器构成,如果断言为true则匹配该路由
断言(Predicate):
可以匹配HTTP请求中的所有内容(请求头,请求参数),如果请求与断言相匹配就路由
过滤(Filter):在请求被路由前或者后对请求进行修改
入门配置
1)依赖
2)yml,不太明白
3)启动类
4)网关如何做映射,在ym中写配置,也可以编码配置类
gateway配置动态路由
1)yml,加上开启发现
2)uri换了服务名lb://
通过以上服务网关可以负载均衡??之前的openfeign呢???
gateway的predicate
相当于where条件,匹配全是true才会访问
gateway的filter
自定义的过滤器
1)实现两个接口GlobalFilter, Ordered
2)实现相应的方法,一个是顺序的
分布式配置中心
配置文件yml,如果连接数据库的话,要改的话都得改,一处修改处处修改
配置中心分客户端和服务端
客户端的动态刷新
当github上的内容更改后,服务端的配置中心3344,会立即更改,但3355的配置得重启之后才生效
1)pom引actuator监控
2)修改yml,暴漏监控
3)业务类controller加注解@RefreshScope
4)还得curl发送post请求刷新3355
消息总线bus
优化上述的手动版动态刷新
bus配合config实现配置动态刷新
配合mq
SpringCloud Stream消息驱动
作用
屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型
通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件细节之间的隔离
发送者8801
1)module
2)pom加入与rabbit的整合
3)yml
4)主启动
5)业务类,接口,实现类,controller
但是这里的service实现类不用写@service了,因为这是和消息stream打交道呢,不再是增删改查,要换成@EnableBinding,不是操作的数据库,而是操作的消息中间件
接收者8802
1)module
2)pom加入与rabbit的整合
3)yml
4)主启动
5)业务类,消费者,只要一个controller就行了,自动监听,类上@EnableBinding(Sink.class)
方法上@StreamListener(Sink.INPUT)
两个问题
重复消费问题
消息持久化问题
重复消费问题
不同组可以全面消费(重复消费),同一组内会发生竞争关系,只有一个可以进行消费。
默认分组时不同的
我们要自定义配置分组,其实这里的分组就是绑定不同的队列,如果消息在一个队列里边就不存在重复消费了
修改分组:
1)改yml,加个group
组名xrh一样后,就避免重复消费了,轮询的
消息持久化问题
就是说如果8802把group标签删了,当8802宕机之后,再复活就不会收到xrh1里面的消息了,造成消息丢失,8803没删group,死了之后再活还是会收到xrh1里面的消息
SpringCloud Sleuth
链路追踪
zipkin dashboard
Sleuth负责收集微服务,ZipKin负责展现