赞
踩
微服务的概念源于2014年3月Martin Fowler所写的一篇文章“Microservices”。
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。另外,应尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。
微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。
复杂度可控:在将应用分解的同时,规避了原本复杂度无止境的积累。每一个微服务专注于单一功能,并通过定义良好的接口清晰表述服务边界。由于体积小、复杂度低,每个微服务可由一个小规模开发团队完全掌控,易于保持高可维护性和开发效率。
独立部署:由于微服务具备独立的运行进程,所以每个微服务也可以独立部署。当某个微服务发生变更时无需编译、部署整个应用。由微服务组成的应用相当于具备一系列可并行的发布流程,使得发布更加高效,同时降低对生产环境所造成的风险,最终缩短应用交付周期。
技术选型灵活:微服务架构下,技术选型是去中心化的。每个团队可以根据自身服务的需求和行业发展的现状,自由选择最适合的技术栈。由于每个微服务相对简单,故需要对技术栈进行升级时所面临的风险就较低,甚至完全重构一个微服务也是可行的。
容错:当某一组建发生故障时,在单一进程的传统架构下,故障很有可能在进程内扩散,形成应用全局性的不可用。在微服务架构下,故障会被隔离在单个服务中。若设计良好,其他服务可通过重试、平稳退化等机制实现应用层面的容错。
扩展:单块架构应用也可以实现横向扩展,就是将整个应用完整的复制到不同的节点。当应用的不同组件在扩展需求上存在差异时,微服务架构便体现出其灵活性,因为每个服务可以根据实际需求独立进行扩展。
Microservice 解决方案主流的有两种:
-Dubbo (阿里系)
-spring cloud 系列
Spring Cloud 本身其实只是一套微服务规范,并不是一个拿来即可用的框架,Spring Cloud Netflix 和Spring Cloud alibaba是为开发者提供了这套规范的实现方式。由于Spring Cloud Netflix 2018年12月12日进入维护模式(Maintenance Mode),所以不太适合长期再使用。故选择Spring Cloud alibaba的技术方案。
2019年7月24日Spring官方社区官方博文中宣布了Spring Cloud Alibaba正式从 Spring Cloud Incubator 孵化器毕业,成为了Spring社区的正式项目。同时国内版Github码云也提供了Spring Cloud Alibaba极速下载镜像。
(一)服务的注册与发现(对标Netflix的Eureka译音尤利卡): Nacos
(二)负载均衡Ribbo
(三)声明式HTTP客户端Feign
(四)服务容错Sentinel
(五)消息驱动RocketMQ
(六)API网关GateWay( 可以集成开源的Soul网关或者 spring Cloud Gateway)
(七)配置管理Nacos
(八)调用链监控Sleuth
(九)服务限流降级(即熔断限流,对标Hystrix):Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
(十)分布式配置管理:Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
(十一)分布式事务: Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
(十二)阿里云对象存储(收费): Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务
(十三)分布式任务调度(收费):Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
(十四)阿里云短信服务(收费):Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
(十五)用户认证与授权
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是Spring Boot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,Spring Boot整合了所有的框架(不知道这样比喻是否合适)。
Spring Boot简化了基于Spring的应用开发,通过少量的代码就能创建一个独立的、产品级别的Spring应用。 Spring Boot为Spring平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。Spring Boot的核心思想就是约定大于配置,多数Spring Boot应用只需要很少的Spring配置。采用Spring Boot可以大大的简化你的开发模式,所有你想集成的常用框架,它都有对应的组件支持。
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包
以下为Spring Cloud的核心功能:
我们再来看一张图:
通过这张图,我们来了解一下各组件配置使用运行流程:
上图只是Spring Cloud体系的一部分,Spring Cloud共集成了19个子项目,里面都包含一个或者多个第三方的组件或者框架!
Spring Cloud 工具框架
1、Spring Cloud Config 配置中心,利用git集中管理程序的配置。
2、Spring Cloud Netflix 集成众多Netflix的开源软件
3、Spring Cloud Bus 消息总线,利用分布式消息将服务和服务实例连接在一起,用于在一个集群中传播状态的变化
4、Spring Cloud for Cloud Foundry 利用Pivotal Cloudfoundry集成你的应用程序
5、Spring Cloud Cloud Foundry Service Broker 为建立管理云托管服务的服务代理提供了一个起点。
6、Spring Cloud Cluster 基于Zookeeper, Redis, Hazelcast, Consul实现的领导选举和平民状态模式的抽象和实现。
7、Spring Cloud Consul 基于Hashicorp Consul实现的服务发现和配置管理。
8、Spring Cloud Security 在Zuul代理中为OAuth2 rest客户端和认证头转发提供负载均衡
9、Spring Cloud Sleuth SpringCloud应用的分布式追踪系统,和Zipkin,HTrace,ELK兼容。
10、Spring Cloud Data Flow 一个云本地程序和操作模型,组成数据微服务在一个结构化的平台上。
11、Spring Cloud Stream 基于Redis,Rabbit,Kafka实现的消息微服务,简单声明模型用以在Spring Cloud应用中收发消息。
12、Spring Cloud Stream App Starters 基于Spring Boot为外部系统提供spring的集成
13、Spring Cloud Task 短生命周期的微服务,为SpringBooot应用简单声明添加功能和非功能特性。
14、Spring Cloud Task App Starters
15、Spring Cloud Zookeeper 服务发现和配置管理基于Apache Zookeeper。
16、Spring Cloud for Amazon Web Services 快速和亚马逊网络服务集成。
17、Spring Cloud Connectors 便于PaaS应用在各种平台上连接到后端像数据库和消息经纪服务。
18、Spring Cloud Starters (项目已经终止并且在Angel.SR2后的版本和其他项目合并)
19、Spring Cloud CLI 插件用Groovy快速的创建Spring Cloud组件应用。
当然这个数量还在一直增加…
微服务是一种架构的理念,提出了微服务的设计原则,从理论为具体的技术落地提供了指导思想。Spring Boot是一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务;Spring Cloud是一个基于Spring Boot实现的服务治理工具包;Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架。
当我们将所有的新业务都使用Spring Cloud这套架构之后,就会出现这样一个现象,公司的系统被分成了两部分,一部分是传统架构的项目,一部分是微服务架构的项目,如何让这两套配合起来使用就成为了关键,这时候Spring Cloud里面的一个关键组件解决了我们的问题,就是Zuul。在Spring Cloud架构体系内的所有微服务都通过Zuul来对外提供统一的访问入口,所有需要和微服务架构内部服务进行通讯的请求都走统一网关。如下图:
从上图可以看出我们对服务进行了分类,有四种:基础服务、业务服务、组合服务、前置服务。不同服务迁移的优先级不同
在这四类服务之外,新上线的业务全部使用Sprng Boot/Cloud这套技术栈。就这样,我们从开源项目云收藏开始,上线几个Spring Boot项目,到现在公司绝大部分的项目都是在Spring Cloud这个架构体系中。
服务拆分有以下几个原则和大家分享
横向拆分。按照不同的业务域进行拆分,例如订单、营销、风控、积分资源等。形成独立的业务领域微服务集群。
纵向拆分。把一个业务功能里的不同模块或者组件进行拆分。例如把公共组件拆分成独立的原子服务,下沉到底层,形成相对独立的原子服务层。这样一纵一横,就可以实现业务的服务化拆分。
要做好微服务的分层:梳理和抽取核心应用、公共应用,作为独立的服务下沉到核心和公共能力层,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求
服务拆分是越小越好吗?微服务的大与小是相对的。比如在初期,我们把交易拆分为一个微服务,但是随着业务量的增大,可能一个交易系统已经慢慢变得很大,并且并发流量也不小,为了支撑更多的交易量,我会把交易系统,拆分为订单服务、投标服务、转让服务等。因此微服务的拆分力度需与具体业务相结合,总的原则是服务内部高内聚,服务之间低耦合。
使用微服务有一段时间了,这种开发模式和传统的开发模式对比,有很大的不同。
每个微服务都有自己独立的数据库,那么后台管理的联合查询怎么处理?这应该是大家会普遍遇到的一个问题,有三种处理方案。
1)严格按照微服务的划分来做,微服务相互独立,各微服务数据库也独立,后台需要展示数据时,调用各微服务的接口来获取对应的数据,再进行数据处理后展示出来,这是标准的用法,也是最麻烦的用法。
3)数据库严格按照微服务的要求来切分,以满足业务高并发,实时或者准实时将各微服务数据库数据同步到NoSQL数据库中,在同步的过程中进行数据清洗,用来满足后台业务系统的使用,推荐使用MongoDB、HBase等。
三种方案在不同的公司我都使用过,第一种方案适合业务较为简单的小公司;第二种方案,适合在原有系统之上,慢慢演化为微服务架构的公司;第三种适合大型高并发的互联网公司。
1、建议尽量不要使用Jsp,页面开发推荐使用Thymeleaf。Web项目建议独立部署Tomcat,不要使用内嵌的Tomcat,内嵌Tomcat部署Jsp项目会偶现龟速访问的情况。
2、服务编排是个好东西,主要的作用是减少项目中的相互依赖。比如现在有项目a调用项目b,项目b调用项目c…一直到h,是一个调用链,那么项目上线的时候需要先更新最底层的h再更新g…更新c更新b最后是更新项目a。这只是这一个调用链,在复杂的业务中有非常多的调用,如果要记住每一个调用链对开发运维人员来说就是灾难。
有这样一个好办法可以尽量的减少项目的相互依赖,就是服务编排,一个核心的业务处理项目,负责和各个微服务打交道。比如之前是a调用b,b掉用c,c调用d,现在统一在一个核心项目W中来处理,W服务使用a的时候去调用b,使用b的时候W去调用c,举个例子:在第三方支付业务中,有一个核心支付项目是服务编排,负责处理支付的业务逻辑,W项目使用商户信息的时候就去调用“商户系统”,需要校验设备的时候就去调用“终端系统”,需要风控的时候就调用“风控系统”,各个项目需要的依赖参数都由W来做主控。以后项目部署的时候,只需要最后启动服务编排项目即可。
3、不要为了追求技术而追求技术,确定进行微服务架构改造之前,需要考虑以下几方面的因素:
1)团队的技术人员是否已经具备相关技术基础。
2)公司业务是否适合进行微服务化改造,并不是所有的平台都适合进行微服务化改造,比如:传统行业有很多复杂垂直的业务系统。
3)Spring Cloud生态的技术有很多,并不是每一种技术方案都需要用上,适合自己的才是最好的。
引入使用springcloud 一定对应对应相关版本信息。
例如:Hoxton版本
https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-Hoxton-Release-Notes
LEARN :springcloud 使用三部曲 引依赖 、 写配置 、加注解、
在创建springcloud 环境时候创建springboot module时候可以使用
#spring io 环境
Default :https://start.spring.io
#aliyun 环境
Custom: https://start.aliyun.com/
1.创建一个空项目 2.创建一个module 模块 3.pom文件中引入springcloud的版本约束 ----------------------------------------------------------------------------------------------- <properties> <spring.cloud-version>Hoxton.SR8</spring.cloud-version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring.cloud-version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ----------------------------------------------------------------------------------------------- 用来指定相关springcloud的版本和spring cloud 组件仓库下载声名,这样就不会在默认阿里云的maven仓库中下载出现失败的情况。
注册中心主要涉及到三大角色:
它们之间的关系大致如下:
注册中心的架构图如下:
注册中心应具备以下功能:
Spring Cloud提供了多种注册中心的支持,例如**Eureka(netflix)、Consul(go语言开发)和ZooKeeper(dubbo系列)、nacos(alibaba) **等。
总结:注册中心的用途主要是用来管理服务的注册和发现以及服务状态的检查。
springcloud 使用组件服务三部曲
引依赖 、 写配置 、加注解、
---详细可以参考springcloud的官方网站
https://spring.io/projects/spring-cloud
注意⚠️:企业中主流的是consul +RestTemplate +Ribbon (默认轮训负载均衡)/ OpenFeign (可插拔组件)
(其本身也是springcloud的项目组件的一个微服务)
When a client registers with Eureka, it provides meta-data about itself — such as host, port, health indicator URL, home page, and other details. Eureka receives heartbeat messages from each instance belonging to a service. If the heartbeat fails over a configurable timetable, the instance is normally removed from the registry.
The following example shows a minimal Eureka client application:
注册服务,心跳检测,宕机移除。
Eureka 是一个Netflix开发的服务注册与发现的组件。
-官网:https://github.com/Netflix/eureka/wiki
-是一个基于REST的服务,springcloud 将他集成在其子项目spring-cloud-netfilx 中
Eureka 包含两个组件 Eureka Server 和Eureka Client
---引依赖 1.0 创建spingInit 项目模块(springboot版本要对应springcloud版本) 由于他只是一个注册发现组件因此不需要任何勾选任何功能依赖,只需要勾选spring web 支持。 1.1 配置pom文件 # pom 中引入spingcloud环境配置相关版本和管理 ----------------------------------------------------------------------------------------------- # 引入Eureka的client <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> # 引入Eureka的server <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> -----------------------------------------------------------------------------------------------
---写配置 2.配置文件 application.yml server: port: 8761 # 执行服务的端口号 spring: application: name: eurekaserver # 指定服务名称,唯一标识 eureka: instance: hostname: localhost client: registerWithEureka: false #关闭作为客户端注册在服务中,声名只作为是Eureka的server服务端 fetchRegistry: false # 关闭立即注册 serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #指定服务注册中心地址
# 说明:
-1.指定服务名称唯一标识则以这个名称进行微服务实例注册到注册中心。
-2.指定服务只是一个server服务端,而不作为客户端注册在客户端中。
-3.关闭服务的立即注册,是该eureka服务端server会认为自己是一个微服务,则一启动就会把服务注册向注册中心,然而现在注册中心还没有初始化成功,则这样会出现错误,异常。
-查看Eureka服务注册中的实例对象(server)
http://localhost:8761
---加注解
3.添加注解项目启动类中
- @EnableEurekaServer 表示这是一个eureka 注册中心服务中心
@SpringBootApplication
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
Eureka客户端就是一个微服务,注册到Eureka的服务端server中。
使用Eureka的client 在module中的pom.xml文件中引入的依赖 |
---|
To include the Eureka Client in your project, use the starter with a group ID of org.springframework.cloud and an artifact ID of spring-cloud-starter-netflix-eureka-client |
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
#指定这个服务的应用端口
server.port=8888
#指定唯一的应用名称
spring.application.name=eurekaclient
# 指定eureka的服务器地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
@EnableEurekaClient
//在入口类上加注解
@EnableEurekaClient
@SpringBootAppliction
public class Eurekaclient8888Application{
public static void main (String [] args ){
SpringApplication.run(Eurekaclient8888Applicarion.class,args)
}
}
#官方不建议关闭自我保护模式
#注意
1. Self Preservation Mode
默认90s 内出现无法接收到心跳。则会自动提剔除该微服务在注册中心的注册。(接收心跳的时间)
//
自动保护模式,当网络分区故障发生,微服务和eureka注册中心server 无法正常通信,而微服务本身是正常运行的,这时注册中心不应该把服务剔除,所以引入保护机制。Eureka server回去统计心跳失败比例(失败数/总心跳数),在15分钟之内是否低于85%,如果低于这把这些微服务示例保护起来,让这些微服务不会过期。
这是一个针对网络异常的保护模式。
# https://github.com/netflix/eureka/wiki/server-self-preservation-mode
#The self preservation config is defined here. To disable self preservation in the example, set the property:
#官方不建议关闭自我保护模式 (写在eureka 的server配置文件中)
eureka.enableSelfPreservation=false #关闭自我保护
eureka.server.eviction-interval-timer-in-ms=3000 #表示超时3s 则逐出这个微服务。
#2.在微服务中缩短服务心跳时间 (写在eureka 的server配置文件中)
eureka.instance.lease-expiration-duration-in-seconds=10 #修改eureka server 默认接受形体心跳时间段默认90s
eureka.instance.lease-renewal-interval-in-seconds=5 #指定客户端多久向eureka server 发送一次心跳默认30s
# 其他相关配置文件参考网址
https://docs.spring.io/spring-cloud-netflix/docs/2.2.6.RELEASE/reference/html/appendix.html
说明:
eureka 2.x停止维护了,出现问题后果自负!!!(discontinued) 闭源了,
eureka 1.x项目还是活跃的
服务注册中心只是一张白纸,和微服务之间的调用没有任何关系,因此我们可以使用任何一个注册中心,这样只要使用特定注册中心的方式去配置使用即可。
服务注册中心是每个服务用来注册,记录,查找,宕机剔除的“白纸”。
因此我是使用eureka过度学习,在现在一般主流使用:consul、zookeeper 、nacos(alibaba)
介绍
consul: Service Networking Across Any Cloud 跨任何云的服务网络
consul 本身就是一个软件,本身就是一个服务注册中心,我们安装运行后只需要开发相关consul客户端(微服务)把springcloud 微服务注册到consul中。
consul使用
-下载:
在mac环境: consul_1.8.0_darwin_amd64.zip
windows环境:consul_1.8.0_windows_amd64.zip
-启动:windows 直接打开使用cmd
$ consul.exe #执行
$ consul agent -dev #执行命令启动
mac 在terminal 中使用带参数方式启动使用:
$ consul agent -dev #使用开发环境
$ consul -v #查看当前consul版本信息
#如果要配置环境变量:
//bash.conf 指定consul(执行文件)的所在文件路径地址
export CONSUL_HOME=/Users/fanyuanxiang/D/springcloud/consul
-查看
consul启动后查看实例web页面: https://localhost:8500
简介:这是基于golanguage 开发出来的应用。
在springcloud 环境使用consul
As long as Spring Cloud Consul and the Consul API are on the classpath any Spring Boot application with @EnableDiscoveryClient
will try to contact a Consul agent on localhost:8500
(the default values of**spring.cloud.consul.host
** and spring.cloud.consul.port
respectively)
-引依赖
#To activate Consul Service Discovery use the starter with group org.springframework.cloud and artifact id spring-cloud-starter-consul-discovery. See the Spring Cloud Project page for details on setting up your build system with the current Spring Cloud Release Train.
0.引入springclud版本环境依赖
1.cousul client 依赖环境
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
2.在consul中必须引入健康检查依赖,否则consul注册中心会始终认为该服务是不可的。
<dependency>
<gorupId>org.springframework.boot</gorupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
-写配置
#指定该微服务客户端的端口和应用程序名称
server.port=8889
spring.application.name=consulclient
#指定consul server 位置
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
#consul 健康检查一定要使用 默认就是true
spring.cloud.consul.discovery.register-health-check=true
#修改consulclient 在注册中心的名字
spring.cloud.consul.discovery.service-name=aaa
#consul 相关的其他的consul的配置文件
#consul 健康检查一定要使用 默认就是true
spring.cloud.consul.discovery.register-health-check=true
#修改consulclient 在注册中心的名字
spring.cloud.consul.discovery.service-name= ${spring.application.name}
更多配置文件:
https://docs.spring.io/spring-cloud-consul/docs/2.2.5.BUILD-SNAPSHOT/reference/html/appendix.html
-加注解
-加在启动类上
加入consul client 的注解在启动类上
@EnableDiscoveryClient (可省略)
#consul 使用中,与Eureka 不同,这里写了关于consul server的配置文件后,就可以省略注解了。
服务注册中心本质上是为了解耦服务提供者和服务消费者。对于任何一个微服务,原则上都应存在或者支持多个提供者,这是由微服务的分布式属性决定的。更进一步,为了支持弹性扩缩容特性,一个微服务的提供者的数量和分布往往是动态变化的,也是无法预先确定的。因此,原本在单体应用阶段常用的静态LB机制就不再适用了,需要引入额外的组件来管理微服务提供者的注册与发现,而这个组件就是服务注册中心。
CAP理论是分布式架构中重要理论
分布式系统就是不同中间件、不同服务的集群构成的集合系统,共同协同提供服务的系统。
关于
P的理解,我觉得是在整个系统中某个部分,挂掉了,或者宕机了,并不影响整个系统的运作或者说使用,而可用性是,某个系统的某个节点挂了,但是并不影响系统的接受或者发出请求,CAP 不可能都取,只能取其中2个
原因
如果C是第一需求的话,那么会影响A的性能,因为要数据同步,不然请求结果会有差异,但是数据同步会消耗时间,期间可用性就会降低。
如果A是第一需求,那么只要有一个服务在,就能正常接受请求,但是对与返回结果变不能保证,原因是,在分布式部署的时候,数据一致的过程不可能想切线路那么快。
再如果,同时满足一致性和可用性,那么分区容错就很难保证了,也就是单点,也是分布式的基本核心,好了,明白这些理论,就可以在相应的场景选取服务注册与发现了。
设计或者选型一个服务注册中心,首先要考虑的就是服务注册与发现机制。纵观当下各种主流的服务注册中心解决方案,大致可归为三类:
TIP1:对于第一类注册方式,除了Eureka这种一站式解决方案,还可以基于ZooKeeper或者Etcd自行实现一套服务注册机制,这在大公司比较常见,但对于小公司而言显然性价比太低。
TIP2:由于DNS固有的缓存缺陷,本文不对第三类注册方式作深入探讨。
除了基本的服务注册与发现机制,从开发和运维角度,至少还要考虑如下五个方面:
Nacos | Eureka | Consul | CoreDNS | Zookeeper | |
---|---|---|---|---|---|
一致性协议 | CP+AP | AP | CP | — | CP |
健康检查 | TCP/HTTP/MYSQL/Client Beat | Client Beat | TCP/HTTP/gRPC/Cmd | — | Keep Alive |
负载均衡策略 | 权重/metadata/Selector | Ribbon | Fabio | RoundRobin | — |
雪崩保护 | 有 | 有 | 无 | 无 | 无 |
自动注销实例 | 支持 | 支持 | 不支持 | 不支持 | 支持 |
访问协议 | HTTP/DNS | HTTP | HTTP/DNS | DNS | TCP |
监听支持 | 支持 | 支持 | 支持 | 不支持 | 支持 |
多数据中心 | 支持 | 支持 | 支持 | 不支持 | 不支持 |
跨注册中心同步 | 支持 | 不支持 | 支持 | 不支持 | 不支持 |
SpringCloud集成 | 支持 | 支持 | 支持 | 不支持 | 支持 |
Dubbo集成 | 支持 | 不支持 | 不支持 | 不支持 | 支持 |
K8S集成 | 支持 | 不支持 | 支持 | 支持 | 不支持 |
与 Eureka 有所不同,Apache Zookeeper 在设计时就紧遵CP原则,即任何时候对 Zookeeper 的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性,但是 Zookeeper 不能保证每次服务请求都是可达的。
从 Zookeeper 的实际应用情况来看,在使用 Zookeeper 获取服务列表时,如果此时的 Zookeeper 集群中的 Leader 宕机了,该集群就要进行 Leader 的选举,又或者 Zookeeper 集群中半数以上服务器节点不可用(例如有三个节点,如果节点一检测到节点三挂了 ,节点二也检测到节点三挂了,那这个节点才算是真的挂了),那么将无法处理该请求。所以说,Zookeeper 不能保证服务可用性。
当然,在大多数分布式环境中,尤其是涉及到数据存储的场景,数据一致性应该是首先被保证的,这也是 Zookeeper 设计紧遵CP原则的另一个原因。
但是对于服务发现来说,情况就不太一样了,针对同一个服务,即使注册中心的不同节点保存的服务提供者信息不尽相同,也并不会造成灾难性的后果。
因为对于服务消费者来说,能消费才是最重要的,消费者虽然拿到可能不正确的服务实例信息后尝试消费一下,也要胜过因为无法获取实例信息而不去消费,导致系统异常要好(淘宝的双十一,京东的618就是紧遵AP的最好参照)。
当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120s,而且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。
在云部署环境下, 因为网络问题使得zk集群失去master节点是大概率事件,虽然服务能最终恢复,但是漫长的选举事件导致注册长期不可用是不能容忍的。
Spring Cloud Netflix 在设计 Eureka 时就紧遵AP原则**(尽管现在2.0发布了,但是由于其闭源的原因 ,但是目前 Ereka 1.x 任然是比较活跃的)。**
Eureka Server 也可以运行多个实例来构建集群,解决单点问题,但不同于 ZooKeeper 的选举 leader 的过程,Eureka Server 采用的是Peer to Peer 对等通信。这是一种去中心化的架构,无 master/slave 之分,每一个 Peer 都是对等的。在这种架构风格中,节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向其他节点。每个节点都可被视为其他节点的副本。
在集群环境中如果某台 Eureka Server 宕机,Eureka Client 的请求会自动切换到新的 Eureka Server 节点上,当宕机的服务器重新恢复后,Eureka 会再次将其纳入到服务器集群管理之中。当节点开始接受客户端请求时,所有的操作都会在节点间进行复制(replicate To Peer)操作,将请求复制到该 Eureka Server 当前所知的其它所有节点中。
当一个新的 Eureka Server 节点启动后,会首先尝试从邻近节点获取所有注册列表信息,并完成初始化。Eureka Server 通过 getEurekaServiceUrls() 方法获取所有的节点,并且会通过心跳契约的方式定期更新。
默认情况下,如果 Eureka Server 在一定时间内没有接收到某个服务实例的心跳(默认周期为30秒),Eureka Server 将会注销该实例(默认为90秒, eureka.instance.lease-expiration-duration-in-seconds 进行自定义配置)。
当 Eureka Server 节点在短时间内丢失过多的心跳时,那么这个节点就会进入自我保护模式。
Eureka的集群中,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
1、Eureka不再从注册表中移除因为长时间没有收到心跳而过期的服务
2、Eureka仍然能够接受新服务注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
3、当网络稳定时,当前实例新注册的信息会被同步到其它节点中
因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使得整个注册服务瘫痪。
Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X)基于Raft算法实现数据强一致性。
consul 多个节点中,node1 ,node2 ,node3(leader)使用leader 去承担所有的处理工作,势必加大注册和发现的代价,降低了服务的可用性(available)
Consul 内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其他工具(比如 ZooKeeper 等),使用起来也较为简单。
Consul 遵循CAP原理中的CP原则,保证了强一致性和分区容错性,且使用的是Raft算法,比zookeeper使用的Paxos算法更加简单。虽然保证了强一致性,但是可用性就相应下降了,例如服务注册的时间会稍长一些,因为 Consul 的 raft 协议要求必须过半数的节点都写入成功才认为注册成功 ;在leader挂掉了之后,重新选举出leader之前会导致Consul 服务不可用。
默认依赖于SDK
Consul本质上属于应用外的注册方式,但可以通过SDK简化注册流程。而服务发现恰好相反,默认依赖于SDK,但可以通过Consul Template(下文会提到)去除SDK依赖。
Consul Template
Consul Template
Consul,默认服务调用者需要依赖Consul SDK来发现服务,这就无法保证对应用的零侵入性。
所幸通过Consul Template,可以定时从Consul集群获取最新的服务提供者列表并刷新LB配置(比如nginx的upstream),这样对于服务调用者而言,只需要配置一个统一的服务调用地址即可。
Consul强一致性©带来的是: (zookeeper cp)
**1、**服务注册相比Eureka会稍慢一些。因为Consul的raft协议要求必须过半数的节点都写入成功才认为注册成功
**2、**Leader挂掉时,重新选举期间整个consul不可用。保证了强一致性但牺牲了可用性。
Eureka保证高可用(A)和最终一致性:
**1、**服务注册相对要快,因为不需要等注册信息replicate(复制)到其他节点,也不保证注册信息是否replicate成功
**2、**当数据出现不一致时,虽然A, B上的注册信息不完全相同,但每个Eureka节点依然能够正常对外提供服务,这会出现查询服务信息时如果请求A查不到,但请求B就能查到。如此保证了可用性但牺牲了一致性。
其他方面,eureka就是个servlet程序,跑在servlet容器中; Consul则是go编写而成。
Nacos是阿里开源的,Nacos 支持基于 DNS 和基于 RPC 的服务发现。在Spring Cloud中使用Nacos,只需要先下载 Nacos 并启动 Nacos server,Nacos只需要简单的配置就可以完成服务的注册发现。
Nacos除了服务的注册发现之外,还支持动态配置服务。动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。
-基于Zab协议(原子广播协议),Zookeeper 可用于构建具备强一致性的服务注册与发现中心,而牺牲的服务的可用性、注册需要的时间。(CP)
服务之间调用使用Http Restful 。
前后端之间的调用使用Json数据格式响应。
#说明
-spring 框架提供的RestTemplate 类可以应用在调用rest服务,他简化了http服务的通信方式,统一了Restful的标准,封装了http连接,我们只需要传入url及返回值类型即可,比Apache的httpClient更加优雅
#注意⚠️
1.使用RestTemplate这样调用是通过点对点的直接url方式调用,无法做到负载均衡,且写url路径十分麻烦。
2.没有经过注册中心调用,代码写死,不利于维护,当服务宕机后不能高效剔除。
#在测试集群环境时候可以直接使用Run DashBoard 中的copy configuration
1.修改参数 name
2.添加覆盖VM option :-Dserver.port=9997 #修改端口号参数
这样可以做到用一套代码运行多次的效果,而不用在重复在项目中创建一个module中复制完全一样的一套代码。(修改端口号即可)
使用:
@RestController
@Slf4j
public class UserController {
//1。第一种调用方式, 服务地址 :http://localhost:9998/product/showMsg
RestTemplate restTemplate=new RestTemplate();
@GetMapping("/user/showMsg")
public String showProductMsg(){
String msg =
restTemplate.getForObject("http://localhost:9998/product/showMsg",String.class);
log.info("调用成功信息:{}",msg);
return msg;
}
}
使用参考博客:https://www.cnblogs.com/f-anything/p/10084215.html
最新api地址:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html
这是springcloud 公司开发的是一个基于HTTP/tcp 的客户端负载均衡工具。他是基于Netflix Ribbon 实现。
可以帮我们实现面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。
#简而言之 使用Ribbon通过注册中心拿去服务注册的实例地址,结合RestTemplate 实现一系列的负载均衡的调用服务,因此这样也可以让注册中心发挥作用(可以高效的剔除宕机服务,而不影响整个系统的调用。)
注意⚠️ Ribbon不能单独使用,使用过程是,Ribbon+RestTemplate。
#做负载均衡
目前主流的负载方案分为以下两种:
集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的(比如 F5),也有软件的(比如 Nginx)。
客户端自己做负载均衡,根据自己的请求情况做负载,Ribbon 就属于客户端自己做负载。(请求后会缓存到客户端,不是每次都去注册中心拿实例列表,缓存失效后会进行剔除(心跳响应))
Ribbon 的调用方式
引依赖
说明:Consul集成了Ribbon 的依赖,因此引入了consul client 就包含了Ribbon的依赖。我们就不需要再显示引用。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
2.使用
//使用discoveryclient @Autowired private DiscoveryClient discoveryClient; @GetMapping("/user/showMsgRb") public String showProductMsgRb(){ //获取指定微服务id 下的8微服务实例列表 List<ServiceInstance> products = discoveryClient.getInstances("products"); for (ServiceInstance instance :products){ log.info("服务主机:{}",instance.getHost()); log.info("服务端口:{}",instance.getPort()); log.info("服务地址:{}",instance.getUri()); } //2。使用loadBalanceclient 实现负载均衡之后获取到的一个实例服务对象 ServiceInstance instance = loadBalancerClient.choose("products"); log.info("uri:{}",instance.getUri()); // 在Restful 中会自动将对象转换成json格式数据 //配合RestTemplate请求其他微服务实现服务间的调用 String product = restTemplate.getForObject(instance.getUri() + "/product/showMsg", String.class); System.out.println("获取到product服务:"+product); return products; }
3.使用@LoadBalance。 (底层就是使用LoadBanlacerClient)
@Configuration //指定这是一个配置配,并交给spring工厂
public class RestTemplateConfig {
@Bean //把对象交给spring 工厂管理
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
@RestController
public class UserController2 {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user/Msg")
public String UserController(){
//⚠️直接使用微服id名 获取负载均衡后 的服务实例对象节点实例instance
String forObject = restTemplate.getForObject("http://products/product/showMsg", String.class);
return "这是测试@RestTemplate 实现负载均衡的请求:"+forObject;
}
}
Ribbon 作为一款客户端负载均衡框架,默认的负载策略是轮询,同时也提供了很多其他的策略,能够让用户根据自身的业务需求进行选择。
整体策略代码实现类如图 1 所示。
图 1 Ribbon 自带负载策略
图中说明如下:
选择一个最小的并发请求的 Server,逐个考察 Server,如果 Server 被标记为错误,则跳过,然后再选择 ActiveRequestCount 中最小的 Server。
过滤掉那些一直连接失败的且被标记为 circuit tripped 的后端 Server,并过滤掉那些高并发的后端 Server 或者使用一个 AvailabilityPredicate 来包含过滤 Server 的逻辑。其实就是检查 Status 里记录的各个 Server 的运行状态。
使用 ZoneAvoidancePredicate 和 AvailabilityPredicate 来判断是否选择某个 Server,前一个判断判定一个 Zone 的运行性能是否可用,剔除不可用的 Zone(的所有 Server),AvailabilityPredicate 用于过滤掉连接数过多的 Server。
随机选择一个 Server。
轮询选择,轮询 index,选择 index 对应位置的 Server。
对选定的负载均衡策略机上重试机制,也就是说当选定了某个策略进行请求负载时在一个配置时间段内若选择 Server 不成功,则一直尝试使用 subRule 的方式选择一个可用的 Server。
作用同 WeightedResponseTimeRule,ResponseTime-Weighted Rule 后来改名为 WeightedResponseTimeRule。
根据响应时间分配一个 Weight(权重),响应时间越长,Weight 越小,被选中的可能性越低。
说明:因为Ribbon是一个客户端的实现负载均衡的。所以在该服务上配置文件上修改。
#修改服务随机策略
-服务id.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
example:
products.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
#说明 服务id为我们注册注册中心的id,后面为IRule 接口实现类的全限定名
思考:使用RestTemplate +Ribbon 已经可以完成对客户端的调用,为什么还要使用OpenFeign
String restTemplateForObject =restTemplate.getForObject("http://服务名/url?参数"+name,Strring.class);
- 1
#存在的问题: -1.每次调用服务都需要写这些代码,存在大量的代码冗余 -2.服务地址如果修改,维护成本高, -3.使用时不够灵活
- 1
- 2
- 3
- 4
⚠️OpenFeign 他是RestTemplate +Ribbon 的优化组件。
#1.引依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
#2.写配置
#1.写一个configfeign配置类
//指定包openfeign接口类所在的包名给spring对其的实现类对象管理。(指定接口ProductClient的注入)
@Configuration
@EnableFeignClients(basePackages = "com.chiry.business")
public class FeignConfig {
}
#2.写一个openFeign client的接口类
//调用商品服务的Openfeign 组件
@FeignClient(name = "products") //作用:用来标识当前接口是一个feign的组件, value\name(default):书写的是你要调用服务id
public interface ProductClient { //该接口类直接使用,系统会自动进行实现。
@GetMapping("/product/showMsg")
String showMsg(); //表示我们要调用的其他微服务的接口。
}
#3.加注解 :@EnableFeignClents(加载调用者的启动类中)
@EnableFeignClients //这是使用OpenFeign的声名注解,开启支持OpenFeign
public class Users9999Application {
public static void main(String[] args) {
SpringApplication.run(Users9999Application.class, args);
}
}
#使用
@Slf4j
public class FeignController {
@Autowired
private ProductClient productClient; //直接注入我们写的配置接口类
@GetMapping("/feign/test")
public String test(){
log.info("进入测试 feign 调用的方法");
String msg=productClient.showMsg(); //直接调用配置的方法,即调用指定为服务的接口地址
log.info("调用商品服务返回的信息:{}",msg);
return msg;
}
}
//ProductController.java
@RestController
@Slf4j
public class ProductController {
@GetMapping("/product/findOne")
public Map<String,Object> findOne(String productId) {
Map map =new HashMap<String,Object>();
map.put("status",true);
map.put("msg","根据商品id查询商品信息成功!");
map.put("productId",productId);
return map;
}
}
//ProductClient.java -接口类 -声名调用指定微服务接口地址 //调用商品服务的Openfeign 组件 @FeignClient(name = "products") //作用:用来标识当前接口是一个feign的组件, value\name(default):书写的是你要调用服务id public interface ProductClient { //该接口类直接使用,系统会自动进行实现。 @GetMapping("/product/showMsg") String showMsg(); //表示我们要调用的其他微服务的接口。 @GetMapping("/product/findOne") //注意使用OpenFeign 的GET方式传递参数,参数变量必须使用⚠️@RequestParam注解进行修饰 //该注解参数名必须和对应服务的方法的参数保持一致 public Map<String,Object> findOne(@RequestParam("productId") String productId); } /* Annotation which indicates that a method parameter should be bound to a web request parameter. */ //⚠️说明:如果不使用该注解并加参数则会出现error: feign.FeignException$MethodNotAllowed :[405] 、typw:internal Server Error ,status=500
//FeignController.java 测试类 调用其他微服务
@Slf4j
@RestController
public class FeignController {
@Autowired
private ProductClient productClient;
@GetMapping("/feign/test")
public String test(String id){
log.info("进入测试 feign 调用的方法");
String msg=productClient.findOne(id);
log.info("调用商品服务返回的信息:{}",msg);
return msg;
}
}
在使用服务之间通信调用使用的是http 方式,故在传递对象参数时候 ,在被调用方使用@RequesBody 标记,这样可以将http通信的Json格式数据封装信息到参数对象上。
⚠️注意在FeignClinet接口类中,也要加@RequestBody,方法要和被调用方一样。
#说明
-默认情况下,openFeign在进行服务调用时,要求服务提供方必须在1s内给返回,如果超时openFeign会直接报错,不会等待服务的执行,但往往在复杂业务中,业务逻辑的处理时间往往会超过这个默认时间设定,因此我们要对openFeign进行默认服务调用超时设定。
#模拟超时设定
-服务提供方加入线程等待阻塞
try{Thread.sleep(2000);}catch(InterruptedException e ){e.printStackTrace();}
修改默认等待时间. (在feign接口类 所在的配置文件中)
@Configuration
@EnableFeignClients(basePackages = "com.spark.bitrade.service")
public class FeignConfig {
}
feign.client.config.服务id.connectTimeout=5000 # 设置指定等待连接超时时间5s
feign.client.config.服务id.readTimeout=5000 #设置指定服务等待时间
feign.client.config.default.connectTimeout=5000 #设置所有连接等待超时时间
feign.client.config.default.readTimeout=5000 #设置所有服务等待超时时间
#说明
有时候我们遇到 Bug,比如接口调用失败、参数没收到等问题,或者想看看调用性能,就需要配置 Feign 的日志了,以此让 Feign 把请求信息输出来。默认只会打印DEBUG级别
Log4j建议只使用四个级别,优先级从高到低分别是 ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来,也是说大于等于的级别的日志才输出。
通过源码可以看到日志等级有 4 种,分别是:
NONE:不输出日志。
BASIC:只输出请求方法的 URL 和响应的状态码以及接口执行的时间。
HEADERS:将 BASIC 信息和请求头信息输出。
FULL:输出完整的请求信息。
public enum Level { NONE, BASIC, HEADERS, FULL }
- 1
- 2
- 3
- 4
- 5
- 6
-开启日志
logging.level.net.biancheng.feign_demo.UserRemoteClient=DEBUG #在配置文件中执行 Client 的日志级别才能正常输出日志,格式是“logging.level.client类地址=级别”。
feign.client.config.注册id.loggerLevel=full
feign.client.config.default.loggerLevel=full
==Hystrix 是 Netflix 针对微服务分布式系统采用的熔断保护中间件,相当于电路中的保险丝。==
#说明
Hystrix is a latency and fault tolerance library designed to isolate points of access to remote systems, services and 3rd party libraries, stop cascading failure and enable resilience in complex distributed systems where failure is inevitable.
In a distributed environment, inevitably some of the many service dependencies will fail. Hystrix is a library that helps you control the interactions between these distributed services by adding latency tolerance and fault tolerance logic. Hystrix does this by isolating points of access between the services, stopping cascading failures across them, and providing fallback options, all of which improve your system’s overall resiliency. --[摘自官网]
https://github.com/Netflix/Hystrix/wiki (官网)
说明:
-在分布式环境中,不可避免地会有许多服务依赖项中的某些失败。 Hystrix是一个库,可通过添加延迟公差和容错逻辑来帮助您控制这些分布式服务之间的交互。 Hystrix通过隔离服务之间的访问点,停止服务之间的级联故障并提供后备选项来实现此目的,所有这些都可以提高系统的整体弹性。
通俗定义:Hystrix 能够保证在一个依赖出现问题的情况下,不会导致整体服务失败,避免级连故障(服务雪崩现象),提高分布式系统的弹性。
服务雪崩(
服务像雪崩一样崩塌破坏
)服务雪崩的概念简单的理解为,一条服务链A(用户服务)、B(订单服务)、C(支付服务)三个服务,分别是A调用B,B调用C。
一般而言任务量最大的是底层服务C。
服务C如果挂了(宕机)导致B服务间接也不可用、B服务不可用又间接导致A不可用。这样这条服务链
A->B->C
也就全部挂了,就像雪崩一样,因为一个服务不可用导致全部服务不可用
。因此这种现象就是服务雪崩。服务熔断(
断开上层服务,保护下层服务
)服务熔断:好比
保险丝(生活化打比方)、断路器(专业化打比方)
,是防止服务雪崩出现的一种保护措施。服务熔断的作用:底层服务C不可用、或者处理不过来请求,上层服务B就开启熔断,相当于保险丝断开的作用,让服务B不再调用C,等C服务恢复。服务B会按照周期性的询问C是否可用。当C恢复了。B则会恢复到正常的状态(断路器关闭、服务恢复)
服务降级(
服务按照重要性处理流量
)服务降级:将不重要或不紧急的服务 延迟或暂停这种现象叫做服务降级。好比权重分配
服务雪崩效应是一种因“服务提供者的不可用”(原因)导致“服务调用者不可用”(结果),并将不可用逐渐放大的现象。
最终整个调用链路都不可以使用了。
形成原因:
服务雪崩的过程可以分为三个阶段:
线程资源被耗尽(线程资源占用,但是得不到完成执行的释放)
使用Jmeter 压力测试请求。
启动:bin - Jmeter/ jmeter.bat
服务雪崩解决方案:
对调用链路的保护。
-对整个系统的保护。关闭系统中边缘服务,保证系统核心服务的正常运行,
在双十一时候,淘宝 中采用服务降级, 当天的删除地址、确认收货,删除订单,取消支付等这些边缘服务都被暂时不可使用,来把资源都交给核心服务。(对用户暂时是没有特别影响,并且节省cpu 、内存)
#共同点
#区别点
总结:
熔断必会触发降级,所以熔断也是降级的一种,区别在于熔断是对调用链路的保护,而降级是对系统过载的一种保护处理。
#思路:
-引入Hystrix 依赖,并开启熔断器
-模拟降级方法
-进行调用调试
开启熔断器
1.引依赖
<!--引入hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.写配置
3.加注解
-@EnableCircuitBreaker //用来开启熔断器
@SpringBootApplication
@EnableCircuitBreaker //用来开启熔断器
public class Product9998Application{
psvm(String[] args) { SpringApplication.run(Product9998Application.class,args)}
}
使用@HystrixCommand注解实现断路器的在服务指定方法上开启
@HystrixCommand(fallbackMethod="testBreakFall")
@GetMapping("/product/break")
public String testBreak(){
log.info("接受的商品id为,"+id);
if(id<=0){
throw new RuntimeException("数据不合法!")
}
return "当前接收的商品ID"+id;
}
public String testBreakFall(int id){
return "当前数据不合法!"+id; //当服务发生熔断后直接调用该方法返回数据,不再访问目标服务方法
}
-针对上面的参数对每个方法指定一个熔断调用方法会造成大量的代码冗余
@HystrixCommand(defaultFallback="defaultFallback") //这个默认每个方法调用失败都会使用该调用defaultFallback方法。服务端熔断优先于客户端的服务降级。
总结
#客户端openfeign +hystrix 实现服务降级。 当边缘服务被关闭了,这会儿服务降级,然后核心服务在调用该服务,则直接返回fallback方法。
-引入hystrix 依赖
-配置文件开启feign支持hystrix (默认feign没有开启hystrix 服务降级的支持)
-在feign客户端调用加入fallback 指定降级处理
-开发降级处理方法
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
feign.hystrix.enabled=true #开启openFeign 支持服务降级
@FeignClient(value="products",fallback=ProductFallback.class)
public interface ProductClient{
@GetMapping("/product/hystrix")
String testHystrix(@RequestParam("name") String name);
}
@Component //创建一个该对象的实例交给spring工厂管理
public class ProductFallback implements ProductClient{
@GetMapping("/product/hystrix")
String testHystrix(@RequestParam("name") String name){
return "我是客户端hystrix 服务的实现";
}
}
如果没有把该ProductFallback对象交给spring管理则feign创建ProductClient接口类的实现类无法被创建,产生异常:FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: No fallback instance of type class com.chiry.fallback.ProductClientFallback found for feign client products
#说明
Hystrix DashBoard是一个主要优点是收集每个HystrixCommand 的一组度量,Hystrix仪表盘以高效的方式显示每个断路器的运行状况。
#使用
<properties>
<groupId>org.springframework.cloud</groupId>
<aritfactId>spring-cloud-starter-netflix-hystrix-dashboard</aritfactId>
</properties>
2.写配置
入口类中开启hystrix dashboard,@EnableHystrixDashboard
@SpringBootApplication
@EnableHystrixDashBoard
public class Hystrixdashboard9990Application{
public static void main(String [] args){
SpringApplication.run(Hystrixdashboard9990Application.class,args);
}
}
监听:
访问web页面:http://localhost:9990(dashboard服务端口)/hystrix
老版本直接启动hystrixDashBoard后直接输入 :指定服务的http:// host +port/hystrix.stream
http://localhsot:9998/hystrix.stream
/**无法连接上hystrix 在被监控的启动类中添加 *此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑 *ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream", *只要在自己的项目里配置上下面的servlet就可以了 * */ @Bean public ServletRegistrationBean getServlet() { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); registrationBean.addUrlMappings("/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean; }
只在这个启动类中添加以上代码还不行,在hystrix-service模块色yml文件中添加
#一直loading 无法显示
#Uncaught TypeError: e.indexOf is not a function
# at k.fn.init.k.fn.load (jquery.min.js:2)
# at monitor?stream=http%3A%2F%2Flocalhost%3A9999%2Fhystrix.stream:110
management:
endpoints:
web:
exposure:
include: 'hystrix.stream'
hystrix:
dashboard:
proxy-stream-allow-list: "localhost"
网关的统一了服务的地址端口访问管理;简化了开发方便统一的身份验证(类似于单体应用的拦截器作用)。
-路由转发 服务的统一管理
-过滤器
网关=路由转发+过滤器
Gateway 是 Spring Cloud 新推出的网关框架,该项目基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术,底层使用了高性能的通信框架Netty。
网关的作用:在没有API网关作为统一出口的情况下,需要调用方自己组合各种服务,而且容易让调用方感知后端各种服务的存在,加入网关后,客户端调用服务需要通过网关来进行,并且网关可以处理路由,安全,限流,缓存,日志,监控,重试,熔断等事务,使业务开发更纯净。
一些概念:
Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。
Predicate(断言):这是一个 Java 8 的 Predicate。我们可以使用它来匹配来自 HTTP 请求的任何内容。
Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。
详细使用参照官网
This project provides a library for building an API Gateway on top of Spring WebFlux. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.
官网:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
网关配置有两种方式,一种是快捷方式,一种是完全展开方式
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
#⚠️ 在使用gateway中基于最新的编程模型spring webFlux ,这是在spring mvc 基础上做到新的延伸的编程模型
因此在使用gateway时候不能再引入spring web依赖。
配置文件配置路由转发
server: port: 8989 spring: application: name: gateway cloud: consul: host: localhost port: 8500 gateway: routes: - id: user_route #指定路由唯一标识 uri: http://localhost:9998/ #指定路由服务的地址 predicates: - Path=/user/** #指定路由规则 - id: product_route uri: http://localhost:9998/ predicates: - Path=/product/** #转发的地址。http://localhost:8989/product/**--> http://localhost:9998/product/**
#更多相关路由配置
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#configuring-route-predicate-factories-and-gateway-filter-factories
--参照官网
Java代码配置路由转发
@Configuration
public class GatewayConfig{
@Bean
public RouteLocator coustomRouteLocator(RouteLocatorBuilder builder){
return builder.routes()
.route("order_route",r->r.path("/order/**").uri("http://localhost:9997")).build();
}
//表示该网关路径转发http://localhost:port/order/**--->http://localhost:9997/order/**
}
#1。说明
-gateway 提供路由访问规则的web界面,但是默认是关闭的,如果想要查看服务路由规则可以在配置文件中开启。
#开启
management:
endpoints:
web:
exposure:
include: "*" #开启所有的web暴露
-访问路由管理列表地址
- http://localhost:8989/actuator/gateway/routes
spring: application: name: gateway cloud: consul: host: localhost port: 8500 gateway: routes: - id: user_route uri: lb://users #lb代表转发后服务使用负载均衡策略,users代表服务注册中心上的服务名 predicates: - Path=/user/** - After= #后置断言,表示服务在该点后生效 - Cookie=chocolate, ch.p #⚠️表示一定要包含cookie 信息 chocolate = ch.p filters: #使用filter 过滤器。 - AddRequestHeader=X-Request-Red, Blue-{segment} discovery: locator: enabled: true #开启根据服务名动态获取路由
curl http://localhost:8989/product/showMsg --Cookie "username=chiry"
//使用终端模拟浏览器发送请求 并携带cookie
路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由过滤器适用于特定路由。 Spring Cloud Gateway包括许多内置的GatewayFilter工厂
-参照官网https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fT0LsUI0-1622948491571)(/Users/fanyuanxiang/Library/Application Support/typora-user-images/截屏2021-02-02 19.35.50.png)]
微服务集群架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GR1EAiBO-1622948491575)(/Users/fanyuanxiang/Library/Application Support/typora-user-images/截屏2021-02-02 22.04.52.png)]
参考官网地址:
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters
@Configuration @Slf4j public class CustomGlobalFilter implements GlobalFilter,Ordered { @Override public Mono<Void> filter (ServerWebExchange exchange,GatewayFilterChain chain){ log.info("进入定义的filter"); if(exchange.getRequest().getQueryParams().get(username)!=null){ log.info("用户身份信息合法,放行请继续执行!!"); return chain.filter(exchange); //放行请求 } log.info("非法用户,拒绝访问!!!"); return exchange.getResponse().setComplete(); //拒绝请求,直接返回response } @Override public int get Order(){ //filter 执行顺序。数字越小执行越早(-1在所有filter之前执行) return -1; //执行最先 } }
#说明
-config (配置)又称为 统一配置中心,顾名思义就是将配置统一管理,配置统一管理的好处就是,日后大规模集群部署服务应用时相同的服务配置一致,日后再统一配置只需要统一修改全部同步,不需要一个个服务手动维护。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7HwPq4On-1622948491577)(/Users/fanyuanxiang/Library/Application Support/typora-user-images/截屏2021-02-02 22.21.44.png)]
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
#配置服务基础配置
server.port=7878
spring.application.name=configserver
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.service-name=${spring.application.name}
#配置远端仓库git地址
spring.cloud.config.server.git.uri=https://github.com/fanyuanxiang/config.git
#如果是私有仓库
spring.cloud.config.server.git.username= #访问git仓库的用户名
spring.cloud.config.server.git.password= #访问git仓库的密码
#配置本地拉取仓库地址
spring.cloud.config.server.git.basedir=/localresptory #一定要是一个空目录,在首次会将该目录清空
spring.cloud.config.server.git.default-label=master (dev、main) #指定拉取的分支
@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class, args);
}
}
注意⚠️:拉取时分公共环境、 dev(研发) 、prod(生产)、test(测试),
拉取test-dev.yml 时候会自动合并test.yml 和test-dev.yml 文件。
其他同理。
-1. http://locahost:7878/test-xxxx.properties
http://locahost:7878/test-dev.properties //
-2. http://locahost:7878/test-xxxx.json
-3. http://locahost:7878/test-xxxx.yml
==也可以
http://localhost:7878/test/dev
#会展示test 配置文件和test-dev文件详细信息。
http://localhost:7878/test/prod
spring.cloud.config.server.git.basedir=/localresptory #一定要是一个空目录,在首次会将该目录清空
spring.cloud.config.server.git.default-label=master (dev、mian) #指定拉取的分支
#⚠️拉取其他分支时,也会合并master分支。
项目中config client 即是我们开发微服务(如:user、order…)的配置。
注意客户端拉取的配置是在config server端拉取之下基础上的。
#1.引依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
#2.编写配置文件
spring.application.name=configclient #注意微服务中一定要指定服务名id才可以启动和服务间的调用
spring.cloud.config.discovery.enabled=true #开启配置中心服务
spring.cloud.config.discovery.service-id=configserver #指定统一配置中心的服务标识
spring.cloud.config.label=main #指定从仓库的那个分支拉取配置
spring.cloud.config.name=client #指定拉取配置文件的名称
spring.cloud.config.profile=dev #指定拉取配置文件的环境 如client-dev.properties
#3.远程仓库创建配置文件
-client.properties [用来配置公共配置]
spring.application.name=configclient
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
-client-dev.properties [用来存放研发相关配置](这里以端口为例,以后不同配置分别存放)
server.port=9099
-client-pord.properties [用来存法生产相关的配置]
server.port=9098
#4.配置文件名
bootstrap.properties #预加载文件,会等远程配置拉取完再读取配置,在配置中心中使用。(优先级比application高)
application.properties #这个约定俗称的配置,一旦加载就会自动把他作为最终配置,启动。
#说明
-在生产环境中,微服务可能非常多,每次修改完远端配置之后,每次修改完后不可能对所有微服务进行重启。(提高维护效率)
这时候我们需要被修改配置的微服务后,能够刷新远端修改后的配置 。这里springcloud为我们提供手动刷新配置,和自动刷新配置两种策略。
#1.在config client 端加入刷新暴露端点。(写配置)
management.endpoints.web.exposure.include=*
#2.在需要刷新代码的类中加入刷新配置的注解@RefreshScope
@RestController
@RefreshScope
@Slf4j
public class TestController{
@Value("${name}")
private String name;
@GetMapping("/test/test")
public String test(){
log.info("当前加载配置文件信息为:[{}]",name);
return name;
}
}
手动操作:
当修改完配置后发送一个post给client 请求。
-curl -X POST http://localhost:7879(clientid)/actuator/refresh
自动刷新springcloud 提供了一个bus 组件
#说明
Spring Cloud Bus links nodes of a distributed system with a lightweight message broker. This can then be used to broadcast state changes (e.g. configuration changes) or other management instructions. AMQP and Kafka broker implementations are included with the project. Alternatively, any Spring Cloud Stream binder found on the classpath will work out of the box as a transport.
---【摘自官网】
Spring Cloud Bus将轻量级消息代理程序链接到分布式系统的节点。然后可以将其用于广播状态更改(例如配置更改)或其他管理指令。该项目包括AMQP(Rabbitmq)和Kafka中间件实施。另外,在类路径上找到的任何Spring Cloud Stream绑定程序都可以作为传输工具使用。
-⚠️通俗定义:Bus 称之为springcloud 中消息中线,主要用来在微服务系统中实现远端配置更新时,通过广播形式通知所有客户端刷新配置信息,避免手动重启服务工作。(通过bus消息总线连接rabbitmq 或者 kafka 自动发送广播实现刷新配置数据)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XL9THTdp-1622948491579)(/Users/fanyuanxiang/Library/Application Support/typora-user-images/截屏2021-02-03 22.28.27.png)]
#1.引入依赖(configserver微服务中)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
#说明如果使用kafka artifactId:spring-cloud-starter-bus-kafka
#2.统一配置中心连接到mq
#连接主机
spring.rabbitmq.host=localhost
#连接mq端口
spring.rabbitmq.port=5672
#连接mq用户名密码
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#2.1.远端配置中加入mq配置
#连接主机
spring.rabbitmq.host=localhost
#连接mq端口
spring.rabbitmq.port=5672
#连接mq用户名密码
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#3.加入注解(启动类)
@SpringBootApplication //springboot启动类
@EnableDiscoveryClient //服务注册中心服务发现
@EnableConfigServer //声明启动远端配置中心服务
#4.启动配置中心
#5.启动客户端服务
-配置同配置中心已入bus、配置、 注解加@RefreshScope (可以接受post消息)
-原因springcloud 中 默认连接不到远程 服务器,使用消息总线bus 时必须开启连接远程服务失败报错(新版bus中)
A component required a bean name "configServerRetryInterceptor" that could not be found
Action :
Consider defining a bean named 'configServerRetryInterceptor' in your configuration
#开启连接不到远程服务器立即报错。
spring.cloud.config.fail-fast=true
#6.bus消息刷新
bus使用实现只要向configServer发送一个post请求即可以实现所有客户端都进行配置刷新。
(我们配置重新刷新不需要向configClient 。)
~ curl -X POST http://localhost:7878(configserver端口)/actuator/bus-refresh
通过以上可以实现所有配置client刷新。给configserver发送一个消息,配置中心会给bus发送一个消息,bus会发送消息给连接到配置中心、bus的所有客户端,配置都会进行刷新。
⚠️存在的问题,配置中心通知bus会通知所有微服务, 进行配置刷新,然后有些微服务配置并没有进行修改,因此这些服务进行配置刷新拉取是不必要的!
#7.指定服务刷新
为了解决上面
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。