微服务知识点
Eureka:服务注册中心
微服务可能有多个服务注册中心集群,注册中心集群是 “互相注册,相互守望” 你中有我,我中有你
有注册中心集群,有多个服务提供者 provider
@LoadBalanced 注解赋予RestTemplate负载均衡的能力.
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
Eureka的自我保护理论
一句话:某时刻某一个微服务分支不可用了,Eureka不会立刻清理,依旧会对改微服务的信息进行保存
在自我保护模式中,Eureka Server会保护服务注册表中的信息,不会再注销任何服务实例。
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目的注销任何可能健康的服务实例。
怎么禁止Eureka自我保护
注册中心.yml配置
eureka
server:
## 关闭自我保护机制,保证不可用服务被及时剔除
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
服务提供者provider --.yml配置
eureka
instance
# Eureka客户端向服务器端发送心跳的时间间隔,单位为秒(默认30秒)
lease-renewal-interval-in-seconds: 1
# Eureka服务端在收到最后一次心跳后等待时间上限,单位秒(默认90秒)超时将剔除服务
lease-expiration-duration-in-seconds: 2
SpringCloud整合zookeeper代替eureka
1.启动出现问题,zookeeper的jar包冲突问题
jar包是3.5.3,我们用的的zookeeper是3.4.9版本,造成了冲突。
解决办法:先排除自带的3.5.3-beta版本zookeeper,再引入我们自己使用的3.4.9版本
<!-- springboot整合zookeeper客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!-- 先排除自带的zookeeper3.5.3 -->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加zookeeper3.4.9版本-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
zookeeper注册节点是临时节点 ,一段时间请求没有反应后会被删除。
zookeeper在.yml中的配置:
spring:
application:
name: cloud-consumer-order
cloud:
zookeeper:
connect-string: 192.168.119.178:2181 #zookeeper的服务器地址
Consul服务注册与发现
简介:Cunsul是一套开源的分布式服务发现和配置管理系统,由HashiCorp公司用Go语言开发。
提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。
它具有很多优点。包括:基于raft协议,比较简洁;支持健康检查,同时支持HTTP和DNS协议 支持跨数据中心的WAN集群,提供图形界面,跨平台,支持Linux、mac、Windows。
总结:1.服务发现–提供HTTP和DNS两种发现方式。
2.健康检测–支持多种方式,HTTP、TCP、Docker、Shell脚本定制化
3.KV存储—Key、Value的存储方式
4.多数据中心----Consul支持多数据中心
5.可视化Web界面
consul 下载安装教程
下载cmd安装包,解压之后,cmd命令安装
使用开发模式启动 consul agent -dev
然后访问localhost:8500访问consul页面
consul在yml的配置
server:
port: 8006
spring:
application:
name: consul-provider-payment
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
组件名 | 语言 | CAP | 服务健康检查 | 对外暴漏接口 | spring Cloud集成 |
---|---|---|---|---|---|
Eureka | Java | AP | 可配支持 | HTTP | 已集成 |
Consul | Go | CP | 支持 | HTTP/DNS | 已集成 |
zookeeper | java | CP | 支持 | 客户端 | 已集成 |
C 强一致性 A 可用性 P分区容错性
Spring Cloud Ribbon是基于NetFlix Ribbon实现的一套客户端 负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer 后面所有的机器,Ribbon会帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。
Ribbon在工作时分为两步:
第一步选择EurekaServer,它优先选择在同一个区域内负载较少的server.
第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。(比较空闲的ip)
其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权重。
面试询问,除了轮询,你换过其他的负载均衡方法吗?
LRule: 根据特定算法中从服务列表中选取一个要访问的服务。
Ribbon 默认自带方法:
警告:自定义配置类,即@Configuration注解的类 不能放在@ComponentScan所扫描的当前包以及子包下,否则我们自定义的这个配置类会被所有的Ribbon客户端共享,达不到特殊化定制的目的了。
如何替换?
所以要写自定义配置类,重新建一个包,如下
然后写自定义配置类
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
// 定义为随机
return new RandomRule();
}
}
最后在启动类添加@RibbonClient注解,使用配置类
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}
Ribbon 轮询负载均衡算法的原理:
rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务器重启后rest接口计数从1开始。
List instances = discoveryClient.getInstances(“CLOUD-PAYMENT-SERVICE”);
如 List[0] instances = 127.0.0.1:8002
List[1] instances =127.0.0.1:8001
8001+8002组合成为集群,它们共计2台机器,集群总数为2,按照轮询算法原理
- 当前请求数为1时: 1%2=1 对应下标为1,则获得服务器地址为127.0.0.1:8001
- 当前请求数为2时: 2%2=0 对应下标为0,则获得服务器地址为127.0.0.1:8002
- 当前请求数为3时: 3%2=1 对应下标为1,则获得服务器地址为127.0.0.1:8001
- 当前请求数为4时: 4%2=0 对应下标为0,则获得服务器地址为127.0.0.1:8002
- 如此类推…
Feign是一个声明式的web服务客户端,让编写Web服务客户端变得非常容易,只需创建一个接口并在接口上面添加注解即可。
Feign自带负载均衡配置项,集成了Ribbon
Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。
说白了就是对Feign接口的调用情况进行监控和输出。
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调试失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
”断路器“本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝)向调用方返回一个符合预期的、可处理的备选响应(fallBack)
,而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要的占用,从而避免了故障在分布式系统的蔓延,乃至雪崩。
Hystrix重要概念
-
服务降级
服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback
哪些情况会发出服务降级?
- 程序运行异常
- 服务超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
-
服务熔断
类似保险丝,服务达到最大访问量后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
服务的降级–>进而熔断–>恢复调用链路
熔断机制的概述:熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。
当检测到该节点微服务调用响应正常后,恢复调用链路。
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", //服务兜底返回错误信息方法 commandProperties = { @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),// 死否开启断路器 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),// 请求次数 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),// 时间窗口期 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),//60%失败率达到多少后跳闸 }) public String paymentCircuitBreaker(@PathVariable("id") Integer id) { if (id < 0) { throw new RuntimeException("******id 不能为负数"); } String uuid = IdUtil.simpleUUID(); return Thread.currentThread().getName() + "\t" + "调用成功,流水号:" + uuid; } public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) { return "id 不能为负数,请稍后再试,/(ToCVoT)/~~id" + id; }
涉及到断路器的三个重要参数:快照时间窗、请求总数阀值、错误百分比阀值
1.快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒。
2.请求总数阀值:在快照时间窗内,必须满足请求总数阀值才有资格触发熔断。默认20次,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所以请求都超时或其他原因失败,断路器都不会打开。
3.错误百分比阀值:当请求总数在快照时间窗内超过了阀值,比如发生了30次调用,如果在这30次调用中,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阀值情况下,这时候就会将断路器打开。
当断路器开启的时候,所有请求都不会进行转发
一段时间后(默认是5秒),这时候断路器是半开状态,会让其中一个请求进行转发,如果成功,断路器会关闭,若失败,继续开启。
当断路器开启之后
1.再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallback.通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果。
2.原来的主逻辑要如何恢复?
对于这一问题,hystrix也为我们实现了自动恢复功能。
当断路器打开,对主逻辑进行熔断之后,hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的成为主逻辑。
当休眠时间窗到期,熔断器将进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将闭合,主逻辑恢复;如果此次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。
-
服务限流
秒杀高并发等操作,严禁一蜂窝的过来拥挤,大家排队,一秒N个,有序进行
-
解决故障和响应缓慢的问题
1.超时导致服务器变慢,响应等待转圈 ----超时不再等待,返回预期结果
2.出错(宕机或程序运行出错)---------出错要有兜底,不能返回错误信息,要返回一个兜底信息
3.解决:
- 对方服务(8001)超时了,调用者(8088)不能一直卡死等待,必须有服务降级
- 对方服务(8001)宕机了,调用者(8088) 不能一直卡死等待,必须有服务降级
- 对方服务(8001)Ok,调用者(8088)自己出故障或者有自我要求 (自己的等待时间小于服务提供者),自己处理降级
DashboardHystrix图形化监控;
Gateway新一代网关
概述简介:Cloud全家桶中有个很重要的组件就是网关,在1.x版本都是采用Zuul网关
但在2.x版本中,Zuul的升级一直跳票,springCloud最后自己研发了一个网关替代Zuul,那就是springCloud Gateway.一句话Gateway就是Zuul的替代
Gateway是在Spring生态系统之上构建的API网关服务,基于Spring 5,Spring Boot2和Project Reactor等技术。
Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤功能,例如:熔断、限流、重试等。
SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty.
Spring cloud Gateway的目标提供统一的路由方式基于Filter链的方式提供了网关的基本功能,例如:安全,监控/指标,和限流。
(Route)路由是构建网关的基本模块,它由ID,目标URL,一系列的断言和过滤器组成,如果断言为True则匹配该路由。
GateWay网关路由两种配置方式:1.在配置文件yml中配置;2.代码中注入RouteLocator的Bean
1.在配置文件yml中配置
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh # payment_route #路由的Id,没有固定规则但要求唯一建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh #payment_route
uri: http://localhost:8001
predicates:
- Path=/payment/lb/**
eureka:
instance:
hostname: cloud-gateway-service
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
2.代码中注入RouteLocator的Bean
package com.guigu.springcloud.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Date 2023/2/13 18:34
*/
@Configuration
public class GateWayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_guigu", r -> r.path("/guonei").uri("http://baidu.com/guonei")).build();
return routes.build();
}
}
yml配置GateWay动态路由
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh # payment_route #路由的Id,没有固定规则但要求唯一建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb:cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh #payment_route
# uri: http://localhost:8001
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/**
eureka:
instance:
hostname: cloud-gateway-service
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
GateWay网关组成部分:1.路由;2.断言;3.过滤;
SpringCloud Config分布式配置中心
SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。
application.yml配置
server:
port: 3344
spring:
application:
name: cloud-config-center
cloud:
config:
server:
git:
uri: https://gitee.com/xingtu0427/springcloud-config.git
strict-host-key-checking: false
search-paths:
- springcloud-config
label: master
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka
application.yml是用户级别的资源配置项;
bootstrap.yml是系统级的,优先级更高。
SpringCloud Bus
Spring Cloud Bus能管理和传播分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以当作微服务间的通信通道。
什么是总线?
在微服务架构的系统中,通常会使用轻量化的消息代理来构建一个共用的消息主题,并让所有微服务实例都连接上来。用于该主题中产生的消息会被所有实例监听和消费,所以称他为消息总线。在总线上的各个实例,都可以方便地广播一些需要让其他连接在该主题上的实例都知道的消息。
基本原理:
ConfigClient实例都监听MQ中同一个topic(默认是SpringCloud Bus)。当一个服务刷新数据的时候,它会把这个信息放入到Topic中,这样其他监听同一个Topic的服务就能得到通知,然后去更新自身的配置。
Bus支持两种消息代理:RabbitMQ和Kafka
RibbitMq环境配置
命令启动mq: rabbitmq-plugins enable rabbitmq_management
http://localhost:15672/ username: guest password: guest
SpringCloud Stream
是什么
屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型。
为什么要引入
MQ(消息中间件)
ActiveMQ 让我们不再关注具体MQ的细节
RabbitMQ 我们只要用一种适配绑定的方式,自动的给我们在
RocketMQ =======>>> 有没有一种技术诞生 各种MQ内切换。
Kafka
SpringCloud Stream 是用于构建与共享消息传递系统连接的高度可伸缩的事件驱动微服务框架,该框架提供了一个灵活的编程模型,它建立在已经建立和熟悉的Spring熟语和最佳实践上,包括支持持久化的发布/订阅、消费组以及消息分区这三个核心概念。
目前只支持rabbitMq和Kafka。
在没有绑定器这个概念的情况下,我们的SpringBoot应用要直接与消息中间件进行信息交互的时候,用于各种消息中间件构建的初衷不同,它们的实现细节会有较大的差异性
通过定义绑定器作为中间层,完美实现了应用程序与消息中间件细节之间的隔离。
通过向应用程序暴露统一的Channel通道,使得应用程序不需要再考虑各种不同的消息中间件实现。
通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件细节之间的隔离。
Stream编码常用注解简介
组成 | 说明 |
---|---|
Middleware | 中间件,目前只支持RabbitMQ和Kafka |
Binder | Binder是应用于消息中间件之间的封装,目前实行了Kafka和RabbitMQ的Binder,通过Binder(绑定器)可以很方便的连接中间件,可以动态的改变消息类型(对应于Kafka的topic,RabbitMq的exchange),这些都可以通过配置文件来实现。 |
@Input | 注解标识输入通道,通过该输入通道接收到的消息进入应用程序 |
@Output | 注解标识输出通道,发布的消息将通过该通道离开应用程序 |
@Streamlistener | 监听队列,用于消费者的队列的消息接收 |
@EnableBinding | 指信道channel和exchange绑定在一起 |
微服务信息发送和消息传递,有可能会造成重复消费,一次发送,多个服务接受,导致重复消费。
解决方法: 微服务应用放置于同一个group中,就能保证消息只会被其中一个应用消费一次。
不同的组是可以消费的,同一个组内会发生竞争关系,只有其中一个可以消费。
Nacos知识点
Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos:就是注册中心+配置中心的组合。Nacos=Eureka + Config +Bus
何时选择使用何种模式?
一般来说,如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如SpringCloud 和Dubbo服务,都适合用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。
如果需要在服务级别编辑或者存储配置信息,那么CP是必须。K8S服务和DNS服务则适用于CP模式。
CP模式下则支持注册持久化实例,此时则是以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。
Sentinel知识点
Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
流量控制有以下几个角度:
资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
运行指标,例如 QPS、线程池、系统负载等;
控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
Seata术语
分布式事务处理过程的一ID+三组件模型
Transaction ID XID全局唯一的事务ID
三组件模型如下:
TC-交易协调员
维护全局和分支事务的状态,驱动全局提交或回滚。
TM-事务管理器
定义全局事务的范围:开始全局事务,提交或回滚全局事务。
RM-资源经理
管理分支事务处理的资源,与TC对话以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
Seata是一个开源分布式事务解决方案,致力于提供高性能和易于使用的分布式事务服务。Seata将为用户提供AT、TCC、SAGA和XA事务模型,为用户创建一站式分布式解决方案。
生成集群下分布式全局唯一ID的业务需求
硬性要求是:1.全局唯一
2.趋势递增
3.单调递增
4.信息安全
5.时间戳
性能要求:高可用— 发一个获取分布式ID的请求,服务器就要保证99.999%的情况下给我创建一个唯一分布式ID
低延迟---- 发一个获取分布式ID的请求,服务器就要快,极速
高QPS—假如并发一口气10w个创建分布式ID的请求同时杀过来,服务器要顶得住并且一下子成功创建10万ID
1.UUID 可以达到要求,但是对数据库入库和修改不友好,不推荐
为什么无序的UUID会导致入库性能变差呢?
因为无序的UUID会导致索引变得无序,这会影响查询、更新和插入操作的性能。索引是用来提高查询性能的,但是无序的UUID会使得索引变得无序,从而导致索引的性能变差。这样就会导致入库性能变差,因为它必须首先扫描整个表格才能找到正确的记录,而不是通过索引进行快速查找。
使用UUID:UUID是通用唯一识别码,也叫GUID,由一个128位数字组成,一般由32位的16进制数字表示。UUID主要由以下组成部分:
a. 当前时间:占用60bits,最大可以表示到2039年
b. 机器标识:占用10bits,可以表示1024台机器
c. 进程标识:占用18bits,可以表示262144个进程
d. 序列号:占用18bits,同一毫秒内可以表示262144个序列号
2.mysql自增主键:唯一性,递增
问题:单机可以实现要求,集群不合适。
3.基于Redis生成全局ID的策略
单机版:因为redis是单线的天生保证了原子性,可以使用原子操作incr和incrBy来实现。
优点:集群分布式:现在redis支持集群,也可以实现,并且相当符合我们的要求
缺点:维护成本高,配置麻烦;投入成本大,性能产出不高
4.使用雪花算法生成唯一全局ID
雪花算法(Snowflake Algorithm)是Twitter在2010年发布的一种开源的分布式ID生成算法。该算法生成一个64位的数字ID,它由一个毫秒级时间戳、一个机器标识符和一个计数器组成。雪花算法能够为每一个节点提供唯一的ID,可以按照时间有序生成,并且ID本身就包含了更多的信息。
优点:
毫秒数在高位,自增序列在低位,整个id都是趋势递增的。
不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成的ID性能也是非常高的。
可以根据自身业务特性分配bit位,非常灵活。
缺点:
依赖机器时钟,如果机器时钟回拨,会导致重复ID生成,
在单机上是递增的,但是由于设计到分布式环境,每个机器上的时钟不可能完全同步,有时候会出现不是全局递增的情况(此缺点可以认为无所谓,一般分布式ID只是要求趋势递增,并不会严格要求递增,90%的需求都只要求趋势递增)
其他补充:百度开源的分布式唯一ID生成器UidGenerator;Leaf—美团点评分布式ID生成系统。