SpringCloud学习
整理于:尚硅谷Java
SpringCloud学习
一、面试题
-
什么是微服务
略
-
微服务之间是如何独立通讯的
略
-
SpringCloud和Dubbo有哪些区别
-
通信机制
Dubbo是基于RPC远程过程调用
SpringCloud是基于HTTP的RESTful API
-
-
SpringBoot和SpringCloud,请你谈谈对他们的理解
略
-
什么是服务熔断?什么是服务降级
略
-
微服务的优缺点分别是什么?说下你在项目开发中碰到的坑
优点
- 每个服务足够内聚,足够小,代码容易理解这样能聚焦一个指定的业务功能或业务需求;
- 开发简单、开发效率提高、一个服务可能就是专一的只做一件事
- 微服务能够被小团队单独开发,这个小团队是2~5人的开发人员组成
- 微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的
- 微服务能使用不同的语言开发
- 易于和第三方集成,微服务允许容易且灵活的方式集成自动化部署,通过持续集成工具,如Jenkins,Hudson,bamboo
- 微服务易于被一个开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果。无需通过合作才能体现价值
- 微服务允许你利用融合最新技术
- 微服务只是业务逻辑的代码,不会和HTML,css或其他页面组件混合。
- 每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一数据库
缺点
- 开发人员要处理分布式系统的复杂性
- 多服务运维难度,随着服务的增加,运维的压力也在增大
- 系统部署依赖,比如:系统依赖多个服务,若其中某些服务出问题,造成系统发生错误
- 服务间的通讯成本
- 数据一致性
- 系统集成测试
- 性能监控
-
你说知道的微服务技术栈有哪些?请列举一二
微服务技术栈:多种技术的集合体
维度(微服务条目) 落地的技术 服务开发 SpringBoot、Spring、SpringMVC 服务配置与管理 Netflix公司的Archaius,阿里的Diamond等 服务注册与发现 Eureka、Consul、Zookeeper等 服务调用 Rest、RPC、gRPC 服务熔断器 Hystrix、Envoy等 负载均衡 Ribbon、Nginx等 服务接口调用(客户端调用服务的简化工具) Feign等 消息队列 Kafka、RabbitMQ、ActiveMQ等 服务配置中心管理 SpringCloudConfig、Chef等 服务路由(API网关) Zuul等 服务监控 Zabbix、Nagios、Metrics、Spectator等 全链路追踪 Zipkin,Brave、Dapper等 服务部署 Docker、OpenStack、Kubernetes等 数据流操作开发包 SpringCloud Stream(封装与Redis,Rabbit、Kafka等发送接收消息) 事件消息总线 Spring Cloud Bus … -
eureka和zookeeper都可以提供服务注册于发现的功能,请你说下两个的区别
二、微服务概述
-
什么是微服务
微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底的去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事。从技术角度看就是一种小而独立的处理过程,类似进程概念,能够自行单独启动或销毁,拥有自己独立的数据库。
-
微服务与微服务架构
微服务
强调的是服务的大小,它关注的是某一个点,是具体解决某一个问题、提供落地的对应服务的一个服务应用。
微服务架构
简而言之,微服务架构风格[1]是一种将单个应用程序开发为一套小型服务的方法,每个小型服务都在自己的流程中运行,并与轻量级机制(通常是HTTP资源API)进行通信。这些服务围绕业务功能构建,可通过全自动部署机制独立部署。这些服务至少集中管理,可以用不同的编程语言编写,并使用不同的数据存储技术。
-
微服务优缺点
优点**
- 每个服务足够内聚,足够小,代码容易理解这样能聚焦一个指定的业务功能或业务需求;
- 开发简单、开发效率提高、一个服务可能就是专一的只做一件事
- 微服务能够被小团队单独开发,这个小团队是2~5人的开发人员组成
- 微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的
- 微服务能使用不同的语言开发
- 易于和第三方集成,微服务允许容易且灵活的方式集成自动化部署,通过持续集成工具,如Jenkins,Hudson,bamboo
- 微服务易于被一个开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果。无需通过合作才能体现价值
- 微服务允许你利用融合最新技术
- 微服务只是业务逻辑的代码,不会和HTML,css或其他页面组件混合。
- 每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一数据库
缺点
- 开发人员要处理分布式系统的复杂性
- 多服务运维难度,随着服务的增加,运维的压力也在增大
- 系统部署依赖,比如:系统依赖多个服务,若其中某些服务出问题,造成系统发生错误
- 服务间的通讯成本
- 数据一致性
- 系统集成测试
- 性能监控
-
微服务的技术栈有哪些
微服务技术栈:多种技术的集合体
维度(微服务条目) 落地的技术 服务开发 SpringBoot、Spring、SpringMVC 服务配置与管理 Netflix公司的Archaius,阿里的Diamond等 服务注册与发现 Eureka、Consul、Zookeeper等 服务调用 Rest、RPC、gRPC 服务熔断器 Hystrix、Envoy等 负载均衡 Ribbon、Nginx等 服务接口调用(客户端调用服务的简化工具) Feign等 消息队列 Kafka、RabbitMQ、ActiveMQ等 服务配置中心管理 SpringCloudConfig、Chef等 服务路由(API网关) Zuul等 服务监控 Zabbix、Nagios、Metrics、Spectator等 全链路追踪 Zipkin,Brave、Dapper等 服务部署 Docker、OpenStack、Kubernetes等 数据流操作开发包 SpringCloud Stream(封装与Redis,Rabbit、Kafka等发送接收消息) 事件消息总线 Spring Cloud Bus … -
为什么选择SpringCloud作为微服务架构
选型依据
- 整体解决方案和框架成熟度
- 社区热度
- 可维护性
- 学习曲线
功能点/服务框架 Netflix/SpringCloud Motan(新浪) gRPC(谷歌) Thrift(facebook) Dubbo/DubboX(阿里,当当网) 功能定位 完整的微服务框架 RPC框架,但整合了Zookeeper或Consul,实现集群环境的基本的服务注册/发现 RPC框架 RPC框架 服务框架 支持Rest 是,Ribbon支持多种可插拔的序列化选择 否 否 否 否 支持RPC 否 是(Hession2) 是 是 是 支持多语言 是(Rest形式) 否 是 是 否 服务注册/发现 是(Eureka)Eureka服务注册表,karyon服务端框架支持服务自注册和监控检查 是(Zookeeper/consul) 否 否 是 负载均衡 是(服务端zuul + 客户端Ribbon)Zuul - 服务,动态路由,云端负载均衡Eureka(针对中间层服务器) 是(客户端) 否 否 是(客户端) 配置服务 Netflix Archaius SpringCloud Config Server集中配置 是(zookeeper提供) 否 否 否 服务调用链监控 是(zuul)Zuul提供边缘服务,API网关 否 否 否 否 高可用/容错 是(服务端Hystrix + 客户端Ribbon) 是(客户端) 否 否 是(客户端) 典型应用案例 Netflix Sina Google Facebook 社区活跃程度 高 一般 高 一般 2012-2017年没有在维护 学习难度 中等 低 高 高 低 文档丰富度 高 一般 一般 一般 高 其他 pringCloud Bus为我们的应用程序带来了更多管理端点 支持降级 Netflix内部在开发集成gRPC IDL定义 实践的公司比较多
三、SpringCloud入门概述
1.SpringCloud
SpringCloud,基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于Netflix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。
SpringCloud利用SpringBoot的开发便利性巧妙的简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式回话等等,它们都可以用SpringBoot的开发风格做到一键启动和部署。
SpringBoot并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过SpringBoot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
SpringCloud,分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶
2.SpringCloud和SpringBoot是什么关系
SpringBoot专注于快速方便的开发单个个体微服务。
SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式回话等等集成服务。
SpringBoot可以离开SpringCloud独立使用开发项目,但是SpringCloud离不开SpringBoot,属于依赖管理。
结论:SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud是关注全局的服务治理框架
3.Dubbo是怎么到SpringCloud的?哪些优缺点让你去技术选型?
目前成熟的互联网架构(分布式+服务治理Dubbo)
Dubbo | SpringCloud | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netflix Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
断路器 | 不完善 | Spring Cloud Netflix Hystrix |
服务网关 | 无 | Spring Cloud Netflix Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总线 | 无 | Spring Cloud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
… |
最大的区别:SpringCloud抛弃了Dubbo的RPC通信,采用的是基于Http的Rest方式。两种方式各有优劣,虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更加合适。
SpringCloud的功能比Dubbo更加强大,涵盖面更广。SpringCloud社区支持与更新力度更好。
四、Eureka服务注册与发现
1.Eureka是什么
Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。
SpringCloud封装了Netflix公司开发的Eureka模块来实现服务注册和发现
Eureka采用了C/S的架构。Eureka Server作为服务注册功能的服务器,它是服务注册中心
而系统中的其他微服务,使用Eureka客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。SpringCloud的一些其他模块(比如Zuul)就可以通过Eureka Server来发现系统中的其他微服务,并执行相关的逻辑。
Eureka包含两个组件:Eureka Server和Eureka Client
- Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到
- Eureka Client是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,会将向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)
2.配置(SpringBoot版本2.1.4,SpringCloud版本Greenwich.SR1)
-
Eureka Server
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
server: port: 7001 eureka: instance: hostname: localhost #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己 fetch-registry: false #false表示自己就是注册中心,职责就是维护服务实例,并不需要去检索服务 service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka #设置与Eureka Server交互的地址查询服务和注册服务都需要依靠这个地址
// SpringBoot启动类 @EnableEurekaServer // Eureka Server服务器端启动类,接受其它微服务注册进来 @SpringBootApplication ...
-
Eureka Client
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
server: port: 8001 spring: application: name: client7001 #Eureka Client名称 重要 eureka: client: service-url: defaultZone: http://localhost:7001/eureka/
// SpringBoot启动类 @EnableEurekaClient // 启动Eureka Client @SpringBootApplication ...
-
Service Consumer
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
server: port: 9001 spring: application: name: consumer9001 eureka: client: service-url: defaultZone: http://localhost:7001/eureka/
// Configuration @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); } // Controller @GetMapping("/consumer/discovery") public Object getService() { return restTemplate.getForObject("http://{服务名}/discovery", Object.class); } //SpringBootApplication @EnableDiscoveryClient @SpringBootApplication ...
注意:
服务名 Eureka Client实例 provider UP(3):localhost:8000、localhost:8001、localhost:8002 consumer UP(1):localhost:9000 -
如果只是简单使用RestTemplate调用服务,不需要引入eureka-client依赖、不需要yml配置eureka,RestTemplate不需要加@LoadBalanced,只需要指定完整的服务url
restTemplate.getForObject("http://localhost:8000/discovery", Object.class);
-
如果要通过服务名调用服务,参照上面配置,一定要@LoadBalance注解修饰的restTemplate才能实现服务名的调用,没有修饰的restTemplate是没有该功能的。
@LoadBalance是通过服务名进行负载均衡,同一个服务名会有多个Eureka Client实例。
没有@LoadBalance就是一个简单的调用Rest接口,需要指定完整的url
restTemplate.getForObject("http://provider/discovery", Object.class);
-
3.actuator与注册微服务信息完善
-
主机名称:服务名称修改
eureka: client: service-url: defaultZone: http://localhost:7001/eureka/ instance: instance-id: eurekaClient7001 # 自定义实例名称
-
访问信息有IP信息提示
eureka: client: service-url: defaultZone: http://localhost:7001/eureka/ instance: instance-id: eurekaClient7001 # 自定义实例名称 prefer-ip-address: true # 访问路径可以显示IP
-
微服务info内容详细信息
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
# 新增 info: #自定义微服务info内容详细信息 app.name: eurekaClient8001 project.name: springcloud
点击微服务实例链接后,就会对应实例信息info的json数据
4.eureka的自我保护
某时刻某一个微服务不可用了,eureka不会立即清理,依旧会对该服务的信息进行保存。
默认情况下,如果Eureka Server在一定时间内没有接受到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区发生故障时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该Eureka Server节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。
使用自我保护模式,可以让Eureka集群更加健壮、稳定。
Eureka自我保护机制,通过配置 eureka.server.enable-self-preservation
来true打开/false禁用自我保护机制,默认打开状态,建议生产环境打开此配置,开发环境可以关闭。
5.服务发现
对于注册进Eureka里面的微服务,可以通过服务发现获得该服务的信息。
-
内部查看:通过Eureka Server页面发现服务及查看服务的信息
-
外部查看:服务消费者Consumer通过服务提供者Provider对外提供的接口发现获得该服务信息
// 服务提供者需要编写 @GetMapping("discovery") public Object discovery() { List<String> list = discoveryClient.getServices(); System.out.println("******" + list); List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("client7001"); //不区分大小写 for (ServiceInstance e : serviceInstanceList) { System.out.println("serviceId:"+ e.getServiceId()); System.out.println("host:" + e.getHost()); System.out.println("instanceId:" + e.getInstanceId()); System.out.println("scheme:" + e.getScheme()); System.out.println("metadata:" + e.getMetadata()); System.out.println("port:" + e.getPort()); System.out.println("uri:" + e.getUri()); System.out.println("*******************"); } return this.discoveryClient; }
// SpringBoot启动类 @EnableDiscoveryClient //开启服务发现 @SpringBootApplication ...
6.集群搭建
-
Eureka Server
例如有三个server示例分别对应端口为7001,7002,7003
server: port: 7001 eureka: instance: hostname: server7001 #eureka server 的主机名必须不同才可互相注册 同时配置hosts 例如 127.0.0.1 server7001 client: register-with-eureka: true fetch-registry: true serviceUrl: defaultZone: http://server7002:7002/eureka,http://server7003:7003/eureka --- server: port: 7002 eureka: instance: hostname: server7002 #eureka server 的主机名必须不同才可互相注册 同时配置hosts 例如 127.0.0.1 server7002 client: register-with-eureka: true fetch-registry: true serviceUrl: defaultZone: http://server7001:7001/eureka,http://server7003:7003/eureka --- server: port: 7003 eureka: instance: hostname: server7003 #eureka server 的主机名必须不同才可互相注册 同时配置hosts 例如 127.0.0.1 server7003 client: register-with-eureka: true fetch-registry: true serviceUrl: defaultZone: http://server7002:7002/eureka,http://server7001:7001/eureka
注意:
- fetch-registry:表示是否从eureka server获取注册信息,如果是单一节点,不需要同步其他eureka server节点,则可以设置为false,但此处为集群,应该设置为true,默认为true,可不显示设置。
- register-with-eureka:表示是否将自己注册到eureka server,因为要构建集群环境,需要将自己注册到集群中,所以应该开启。默认为true,可不显式设置。
这两个参数设为true后在
available-replicas
中才会显示有效的集群实例,否则在unavailable-replicas
中显示无效集群实例 -
Eureka Client
将服务提供者注册入3个server实例中,修改配置文件
eureka: client: service-url: defaultZone: http://server7001:7001/eureka/,http://server7002:7002/eureka,http://server7003:7003/eureka
7.作为服务注册中心,Eureka比Zookeeper好在哪里
著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性),A(可用性),P(分区容错性)。由于分区容错性P在分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。
Zookeeper保证的是CP,例如当master节点发生网络网络故障与其他节点失去联系是,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120秒,且选举期间整个zk集群都是不可用的。
Eureka则是AP,Eureka各个节点都是平等的。
Eureka认为客户端与注册中心出现网络故障后,此时会出现以下几种情况:
- Eureka不再从注册列表中移除因为长时间没有收到心跳而应该过期的服务。
- Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当当前节点依然可用)
- 当网络稳定时,当前实例新的注册信息会被同步到其他节点中。
因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像Zookeeper那样使整个注册服务瘫痪
五、Ribbon负载均衡
1.概述
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。
Ribbon默认轮询算法
Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
LB,即负载均衡算法(Load Balance),在微服务或分布式集群中经常用的一种应用。
目前主流的LB方案可分成两类:
- 集中式LB, 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
- 进程内LB,将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon就属于后者,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
2.Ribbon配置初步及负载均衡
要在项目中包含Ribbon,请使用组org.springframework.cloud
和工件ID spring-cloud-starter-ribbon
的起始器。
服务消费者Consumer,Ribbon需要与Eureka客户端整合,通过微服务名从Eureka上找到并访问
// 包含Ribbon
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
server:
port: 9001
spring:
application:
name: consumer9001
eureka:
client:
service-url:
defaultZone: http://server7001:7001/eureka/,http://server7002:7002/eureka/,http://server7003:7003/eureka/
@LoadBalanced // Ribbon负载均衡
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
// Controller
@GetMapping("/consumer/discovery")
public Object getService() {
return restTemplate.getForObject("http://{服务名}/discovery", Object.class);
}
// 启动类
@EnableDiscoveryClient
@SpringBootApplication
...
总结:Ribbon和Eureka整合后Consumer可以直接调用服务而不用关心地址和接口
3.Ribbon核心组件IRule
IRule根据特定算法中从服务列表中选取一个要访问的服务。
Ribbon在工作是分成两步:
- 第一步先选择Eureka Server,它优先选择在同一个区域内负载较少的server
- 第二步再根据用户指定的策略,再从server取到的服务注册列表中选择一个地址。
其中Ribbon提供了多种策略:比如轮询、随机、根据响应时间加权、哈希、一致性哈希。
Ribbon默认自带的七种策略:
- RoundRobinRule,轮询,依次执行每个执行一次(默认)
- RandomRule,随机
- AvailabilityFilteringRule,会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阀值得服务,然后对剩余的服务列表安排
- 轮询策略进行访问
- WeightedResponseTimeRule,根据平均响应时间计算所有的服务的权重,响应时间越快服务权重越大,容易被选中的概率就越高。刚启动时,如果统计信息不足,则使用RoundRobinRule(轮询)策略,等统计的信息足够了会自动的切换到WeightedResponseTimeRule
- RetryRule,先按照RoundRobinRule(轮询)的策略获取服务,如果获取的服务失败则在指定的时间会进行重试,进行获取可用的服务。如果多次获取某个服务失败,则不会再次获取该服务(如高德地图上某条道路堵车,司机不会走那条道路)
- BestAvailableRule,会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- ZoneAvoidanceRule,默认规则,复合判断Server所在区域的性能和Server的可用性选择服务器
4.自定义Ribbon策略
java配置Ribbon策略
-
在服务消费者Consumer端加上注解@RibbonClient(name=“对外曝光微服务的名称”,configuration=自定义的Rlue配置类.class)
// 启动类 @EnableDiscoveryClient @SpringBootApplication @RibbonClient(name="CLIENT", configuration = ClientRuleConfig.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
-
自定义Ribbon规则类
注意:
ClientRuleConfig必须是@Configuration,但请注意,它不在主应用程序上下文的@ComponentScan中,否则将由所有@RibbonClients共享。如果您使用@ComponentScan(或@SpringBootApplication),则需要采取措施避免包含(例如将其放在一个单独的,不重叠的包中,或者指定要在@ComponentScan)。
cc.jasonwang.springboot //SpringBoot启动类所在package,自定义Ribbon规则类就不能在这个包及其子包下 cc.jasonwang.rule //创建自定义Rlue配置类所在package
package cc.jasonwang.rule; import com.netflix.loadbalancer.IRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ClientRuleConfig { @Bean public IRule iRule() { return new CustomRibbonRule(); } }
功能实现:每个服务调用5次后,再调用下一个服务
package cc.jasonwang.rule; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import java.util.List; public class CustomRibbonRule extends AbstractLoadBalancerRule { // total = 0 // 当total==5以后,我们指针才能往下走, // index = 0 // 当前对外提供服务的服务器地址, // total需要重新置为零,但是已经达到过一个5次,我们的index = 1 // 分析:我们5次,但是微服务只有8001 8002 8003 三台,OK? private int total = 0; // 总共被调用的次数,目前要求每台被调用5次 private int currentIndex = 0; // 当前提供服务的机器号 public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } List<Server> upList = lb.getReachableServers(); List<Server> allList = lb.getAllServers(); int serverCount = allList.size(); if (serverCount == 0) { /* * No servers. End regardless of pass, because subsequent passes only get more * restrictive. */ return null; } *************************** //逻辑实现 if(total < 5) { server = upList.get(currentIndex); total++; } else { total = 0; currentIndex++; if(currentIndex >= upList.size()) { currentIndex = 0; } } *********************************** if (server == null) { /* * The only time this should happen is if the server list were somehow trimmed. * This is a transient condition. Retry after yielding. */ Thread.yield(); continue; } if (server.isAlive()) { return (server); } // Shouldn't actually happen.. but must be transient or a bug. server = null; Thread.yield(); } return server; } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig iClientConfig) { } }
配置文件的方式, 自定义ribbon策略
从版本1.2.0开始,Spring Cloud Netflix现在支持使用属性与Ribbon文档兼容来自定义Ribbon客户端。
这允许您在不同环境中更改启动时的行为。
支持的属性如下所示,应以.ribbon.为前缀:
- NFLoadBalancerClassName:应实施ILoadBalancer
- NFLoadBalancerRuleClassName:应实施IRule
- NFLoadBalancerPingClassName:应实施IPing
- NIWSServerListClassName:应实施ServerList
- NIWSServerListFilterClassName应实施ServerListFilter
注意:在这些属性中定义的类优先于使用@RibbonClient(configuration=MyRibbonConfig.class)定义的bean和由Spring Cloud Netflix提供的默认值。 配置的优先级: 配置文件 > java代码 > springcloud default
client:
ribbon:
NFLoadBalancerRuleClassName: cc.jasonwang.rule.CustomRibbonRule
六、Feign负载均衡
Feign是一个声明式的Web服务客户端。这使得Web服务客户端的写入更加方便,要使用Feign创建一个界面并对其进行注释。它具有可插入注释支持,包括Feign注释和JAX-RS注释。Feign还支持可插拔编码器和解码器。Spring Cloud增加了对Spring MVC注释的支持,并使用Spring Web中默认使用的HttpMessageConverters
。Spring Cloud集成Ribbon和Eureka以在使用Feign时提供负载均衡的http客户端。
总起来说,Feign具有如下特性:
- 可插拔的注解支持,包括Feign注解和JAX-RS注解;
- 支持可插拔的HTTP编码器和解码器;
- 支持Hystrix和它的Fallback;
- 支持Ribbon的负载均衡;
- 支持HTTP请求和响应的压缩。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
server:
port: 9002
spring:
application:
name: consumerFeign9002
eureka:
client:
service-url:
defaultZone: http://server7001:7001/eureka/,http://server7002:7002/eureka/,http://server7003:7003/eureka/
// @FeignClient创建feign接口
@FeignClient(name = "client") // name服务名
public interface ClientService {
@GetMapping("/user/list")
List<User> getUserList();
@GetMapping("/discovery/{serviceId}")
Object discovery(@PathVariable("serviceId") String serviceId);
}
// @EnableFeignClients启动Feign
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class MicroserviceEurekaConsumerFeignApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceEurekaConsumerFeignApplication.class, args);
}
}
Feign通过接口的方法调用Rest服务
Feign融合了Ribbon技术,所以支持负载均衡
问题:
问题一:使用@Autowired注入@FeignClient接口,IDEA红线(could not autowire)
这个问题实际和MyBatis的@Mapper一样,在正常运行后都是可以注入的。若不想看到红色波浪线可以添加@Component。
个人理解:虽然有IDEA红线(could not autowire)错误,但在启动运行后,会有对应的处理类将带有相应注解的接口基于JDK动态代理的方式,添加到Spring容器中。
问题二:Feign java.lang.IllegalStateException: PathVariable annotation was empty on param 0.
使用Feign的时候,如果参数中带有@PathVariable形式的参数,则要用value=""标明对应的参数,否则会抛出IllegalStateException异常
七、Hystrix断路器
1.分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时刻不可避免的失败。
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和C,微服务B和C又调用其他的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。
2.什么是Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
3.服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或相应时间太长时,会进行服务降级,进而熔断该节点微服务的调用,快速返回“错误“的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务之间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand
服务提供者Provider
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
// provider
server:
port: 8004
spring:
application:
name: client
eureka:
client:
service-url:
defaultZone: http://server7001:7001/eureka/,http://server7002:7002/eureka,http://server7003:7003/eureka
instance:
instance-id: eureka_client_hystrix_8004
prefer-ip-address: true
// Controller层
//定义异常错误熔断备选响应
@HystrixCommand(fallbackMethod = "defaultList")
@GetMapping("/user/list")
public List<User> list(Map<String, Object> params) {
throw new RuntimeException("未知异常");
//return userMapper.selectList(null);
}
// 参数列表要与list()一致
private List<User> defaultList(Map<String, Object> params) {
return new ArrayList<>();
}
//在启动类上添加@EnableHystrix或@EnableCircuitBreaker;
@EnableCircuitBreaker //开启断路器
//@EnableHystrix // 该注解包含@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
...
4.服务降级
转载:https://blog.csdn.net/weixin_39165464/article/details/83109125
4.1服务降级特征和方式(完整概念)
-
服务降级的特征:
原因:整体负荷超出整体负载承受能力。
目的:保证重要或基本服务正常运行,非重要服务延迟使用或暂停使用
大小:降低服务粒度,要考虑整体模块粒度的大小,将粒度控制在合适的范围内
可控性:在服务粒度大小的基础上增加服务的可控性,后台服务开关的功能是一项必要配置(单可配置文件,其他可领用数据库和缓存),可分为手动控制和自动控制。
次序:一般从外围延伸服务开始降级,需要有一定的配置项,重要性低的优先降级,比如可以分组设置等级1-10,当服务需要降级到某一个级别时,进行相关配置
-
降级的方式:
- 延迟服务:比如发表了评论,重要服务,比如在文章中显示正常,但是延迟给用户增加积分,只是放到一个缓存中,等服务平稳之后再执行。
- 在粒度范围内关闭服务(片段降级或服务功能降级):比如关闭相关文章的推荐,直接关闭推荐区
- 页面异步请求降级:比如商品详情页上有推荐信息/配送至等异步加载的请求,如果这些信息响应慢或者后端服务有问题,可以进行降级;
- 页面跳转(页面降级):比如可以有相关文章推荐,但是更多的页面则直接跳转到某一个地址
- 写降级:比如秒杀抢购,我们可以只进行Cache的更新,然后异步同步扣减库存到DB,保证最终一致性即可,此时可以将DB降级为Cache。
- 读降级:比如多级缓存模式,如果后端服务有问题,可以降级为只读缓存,这种方式适用于对读一致性要求不高的场景;
-
降级预案
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级
-
降级分类
降级按照是否自动化可分为:自动开关降级和人工开关降级。
降级按照功能可分为:读服务降级、写服务降级。
降级按照处于的系统层次可分为:多级降级。
-
自动降级分类
-
超时降级:主要配置好超时时间和超时重试次数和机制,并使用异步机制探测回复情况
-
失败次数降级:主要是一些不稳定的api,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况
-
故障降级:比如要调用的远程服务挂掉了(网络故障、DNS故障、http服务返回错误的状态码、rpc服务抛出异常),则可以直接降级。降级后的处理方案有:默认值(比如库存服务挂了,返回默认现货)、兜底数据(比如广告挂了,返回提前准备好的一些静态页面)、缓存(之前暂存的一些缓存数据)
-
限流降级
当我们去秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时开发者会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。
-
实现服务降级需要考虑几个问题
- 那些服务是核心服务,哪些服务是非核心服务
- 那些服务可以支持降级,那些服务不能支持降级,降级策略是什么
- 除服务降级之外是否存在更复杂的业务放通场景,策略是什么?
4.2Hystrix+Feign服务降级处理
整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来。
服务降级处理是在客户端实现完成的,与服务端没有关系,让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器。
个人认为这是作为客户端的服务熔断处理
服务端熔断的处理方式,会发现这种方式是很可怕的。第一如果有多个方法,对应的FallBack方法也会随之膨胀;第二异常处理与业务逻辑高耦合,完全不符合Spring AOP面向切面的思想。
服务消费者Consumer
@Component // 不能缺少@Component注解
public class ClientServiceFallBackFactory implements FallbackFactory<ClientService> {
@Override
public ClientService create(Throwable throwable) {
return new ClientService() {
@Override
public List<User> getUserList() {
return null;
}
@Override
public Object discovery(String serviceId) {
return null;
}
};
}
}
@FeignClient(name = "client", fallbackFactory = ClientServiceFallBackFactory.class)
public interface ClientService {
@GetMapping("/user/list")
List<User> getUserList();
@GetMapping("/discovery/{serviceId}")
Object discovery(@PathVariable("serviceId") String serviceId);
}
server:
port: 9002
spring:
application:
name: consumerFeign9002
feign:
hystrix:
enabled: true
eureka:
client:
service-url:
defaultZone: http://server7001:7001/eureka/,http://server7002:7002/eureka/,http://server7003:7003/eureka/
5.服务监控Hystrix Dashboard
除了隔离依赖服务的调用以外,Hystrix还提供了近实时的监控,Hystrix会实时、累加地记录所有关于HystrixCommand的执行信息,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
@EnableHystrixDashboard //开启监控仪表盘
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
...
访问http://localhost:9002/hystrix
输入需要监控的路径地址:服务提供者http://localhost:8004/actuator/hystrix.stream
查看方式
- 7色:分别对应Success | Short-Circuited | Bad Request | Timeout | Rejected | Failure | Error %
- 1圈:实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度从绿色>黄色>橙色>红色递减。该实心圆除了颜色变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实例中快速的发现故障实例和高压实例。
- 1线:实例调用的波动
问题:hystrix-dashboard 报错 /actuator/hystrix.stream 404 Not Found
这是因为springboot2.x使用了endpoint
解决办法是在被监控的微服务的yml文件中加入如下配置
management:
endpoints:
web:
exposure:
include: ["hystrix-stream"]
这样就可以了,但是actuator/health
就无法访问了,所以还可以选择全部放开。
management:
endpoints:
web:
exposure:
include: '*'
八、Zuul路由网关
Zuul包含了对请求的路由和过滤两个最主要的功能:
- 其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础
- 过滤功能是负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础
Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。
注意:Zuul服务最终还是会注册进Eureka
提供代理、路由、过滤三大功能
1.路由基本配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
server:
port: 10001
spring:
application:
name: zuul-server
eureka:
client:
service-url:
defaultZone: http://server7001:7001/eureka/,http://server7002:7002/eureka/,http://server7003:7003/eureka/
instance:
hostname: zuulserver # 不能相同,同时配置hosts 例如 127.0.0.1 zuulserver
// 启动类
@EnableZuulProxy // 开启zuul代理
@SpringBootApplication
...
调用微服务方式,有如下信息
1.Zuul的hostname为zuulserver,端口为10001;
2.服务提供者的服务名为provider,接口名为user/list
调用地址为 http://zuulserver:10001/provider/user/list
2.路由访问映射规则
# 路由映射
zuul:
prefix: /service #增加统一的访问前缀
#此处添加ignored-services的意义是忽略通过服务名可以访问微服务
ignored-services: client
#ignored-services: "*" #忽略所有服务名,不可通过服务名访问微服务
routes:
user:
serviceId: client
path: /myusers/**
九、SpringCloud Config分布式配置中心
1.概述
分布式系统面临的配置问题
SpringCloud Config为微服务架构中的微服务微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。
SpringCloud Config分为服务端和客户端两部分
服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口。
客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。
配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。
一个配置中心提供的核心功能
-
提供服务端和客户端支持
-
集中管理各环境的配置文件
-
配置文件修改之后,可以快速的生效
-
可以进行版本管理
-
支持大的并发查询
-
支持各种语言
2.SpringCloud Config服务端配置
准备工作:在GitHub上创建一个空仓库并添加一个yml文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: git@github.com:JasonWangQB/springcloud-config.git # 仓库地址
server:
port: 3344
@EnableConfigServer //开启config服务端
@SpringBootApplication
...
简单实例通过http://localhost:3344/application-dev.yml 来访问profile为dev的yml配置信息
http://localhost:3344/application/dev/master 访问master分支下的dev配置信息,json形式呈现
http://localhost:3344/master/application-dev.yml 访问master分支下的dev配置信息,yml形式呈现
以下是中文官网提供的文档
HTTP服务具有以下格式的资源:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
3.SpringCloud Config客户端配置与测试
客户端添加bootstrap.yml
application.yml是用户级的资源配置项
bootstrap.yml是系统级的,优先级更高
SpringCloud会创建一个“Bootstrap Context”,作为Spring应用的“Application Context”的父上下文。初始化的时候“Bootstrap Context”负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的“Environment”。
“BootStrap”属性有高优先级,默认情况下,它们不会被本地配置覆盖。“Bootstrap Context”和“Application Context”有着不同的约定。
所以新增了一个“bootstrap.yml"文件,保证“Bootstrap Context”和"Application Context"配置的分离。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
# bootstrap.yml
spring:
cloud:
config:
name: eureka-client # 名称要与git上配置文件名一直
profile: 8001
uri: http://localhost:3344 #Config Server地址
# application.yml
# 空
本地git下新增eureka-client.yml文件并push到GitHub
# eureka-client.yml
spring:
profiles:
active: 8001
management:
endpoints:
web:
exposure:
include: '*'
---
server:
port: 8001
spring:
profiles: 8001
application:
name: client
...
eureka:
client:
service-url:
defaultZone: http://server7001:7001/eureka/,http://server7002:7002/eureka,http://server7003:7003/eureka
instance:
instance-id: eurekaClient8001
prefer-ip-address: true
info: #微服务info内容详细信息修改
app.name: eurekaClient8001
project.name: springcloud
---
server:
port: 8002
spring:
profiles: 8002
application:
name: client
...
eureka:
client:
service-url:
defaultZone: http://server7001:7001/eureka/,http://server7002:7002/eureka,http://server7003:7003/eureka
instance:
instance-id: eurekaClient8002
prefer-ip-address: true
info: #微服务info内容详细信息修改
app.name: eurekaClient8002
project.name: springcloud
---
server:
port: 8003
spring:
profiles: 8003
application:
name: client
...
eureka:
client:
service-url:
defaultZone: http://server7001:7001/eureka/,http://server7002:7002/eureka,http://server7003:7003/eureka
instance:
instance-id: eurekaClient8003
prefer-ip-address: true
info: #微服务info内容详细信息修改
app.name: eurekaClient8003
project.name: springcloud
4.SpringCloud Config Server的高可用
转载:https://blog.csdn.net/a561066292/article/details/81357859
4.1考虑的问题
- config-server服务如果崩了,config-client就无法获取新的配置了,需要将config-server配置为集群而实现高可用。
- config-server如果地址改变了,那么config-client中配置的地址也要改变,不太方便,需要解耦合。
所以这里就要提到注册中心Eureka,Eureka是一个自带负载均衡的注册中心,可以将服务注册在这里,调用服务的时候从这里获取服务。
4.2搭建高可用的Config Server集群
-
创建多个SpringCloud Config Server及Config Client注册入Eureka Server(和正常的服务注册基本一致)
-
Config Client从Eureka Server中访问Config Server
# 修改Config Client,bootstrap.yml配置 spring: cloud: config: name: eureka-client label: master profile: dev discovery: enabled: true #开启去注册中心寻找config-server服务 service-id: config-server #注册中心的config-server服务名,会根据这个名称找到地址,从而获取配置 fail-fast: true #config-client在没有获取到配置时会抛出异常并终止项目