在进入Spring Cloud框架学习前,我们需要先进行一些基础概念的学习。首先,我们需要认识什么是微服务,然后我们在日常生活中怎么进行微服务中服务的远程调用。
简介:
微服务架构是使用一套小服务来开发单个应用的方式或途径,每个服务基于单一业务能力构建,运行在自己的进程中,并使用轻量级机制通信,通常是HTTP API,并能够通过自动化部署机制来独立部署。这些服务可以使用不同的编程语言实现,以及不同数据存储技术,并保持最低限度的集中式管理。
结构图:
注:API Gateway网关是一个服务器,是系统的唯一入口。网关提供RESTful/HTTP的方式访问服务。而服务端通过服务注册中心进行服务注册和管理。
特点:
总结:
通过微服务的特点,我们可知,当终端访问一个微服务项目时,微服务项目通过API网关匹配对应的REST接口,进入对应的服务中,然后实现功能。微服务中的每个服务就单独做好自己的事情,微服务框架就相当于一个集合器将各个服务整合到一个项目中,实现多种多样的功能,并且各个功能相对独立,互不干扰,既符合松耦合的开发标准,也使得项目更易拓展和维护。
在微服务中,服务间通过远程调用,使之联系成一个整体
常见的远程调用方式有以下几种:
RPC,即 Remote Procedure Call(远程过程调用),是一个计算机通信协议。 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。说得通俗一点就是:A计算机提供一个服务,B计算机可以像调用本地服务那样调用A计算机的服务。
RPC远程调用流程图如下:
总结:
由上面两图可知,在RPC远程调用中,客户端和服务端是将需要进行通信的数据进行序列化后通过Socket进行网络通信的,然后接收到结果数据后再通过反序列化得到返回的结果。
HTTP其实是一种网络传输协议,基于TCP,工作在应用层,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用HTTP协议,也可以用来进行远程服务调用。缺点是消息封装臃肿,优势是对服务的提供和调用方没有任何技术限定,自由灵活,更符合微服务理念。现在热门的REST风格,就可以通过HTTP协议来实现。
备注:
简介:
Spring Cloud是Spring旗下的的项目之一,主要提供微服务的功能。
Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。
Spring Cloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。
其主要涉及的组件包括:
Netflflix
Spring Cloud
架构图如下:
版本:
Spring Cloud具有多个版本,每个版本都对应着不同的SpringBoot版本,所以当我们在项目中使用Spring Cloud时,需要注意pom.xml文件中对应的springboot版本,以免因为版本不对应出现不必要的错误。
Spring Cloud的版本名主要对应着英国伦敦地铁站的名字:
Spring Cloud 和Spring Boot版本对应关系 :
Release Train | Boot Version |
---|---|
Hoxton | 2.2.x |
Greenwich | 2.1.x |
Finchley | 2.0.x |
Edgware | 1.5.x |
Dalston | 1.5.x |
Spring Cloud的核心组件主要有五个,分别为注册中心、负载均衡、断路器、服务网关和分布式配置中心,其中服务网关我主要以Spring Cloud 框架中的Gateway为例进行介绍。
注册中心——Netflix Eureka
负载均衡——Netflix Ribbon
断路器——Netflix Hystrix
服务网关——Spring Cloud Gateway(Netflix Zuul)
分布式配置中心——Spring Cloud Config
另外,再补充两个重要组件:
动态代理——Feign
服务总线——Spring Cloud Bus
各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里。Eureka Client中包括服务的提供者和服务的调用者,Eureka负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。
同时,服务提供方与Eureka之间通过 “心跳” 机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。
这就实现了服务的自动注册、发现、状态监控。
Eureka架构中的三个核心角色:
服务注册中心
Eureka的服务端应用,提供服务注册和发现功能,就是刚刚我们建立的eureka-server
服务提供者
提供服务的应用,可以是Spring Boot应用,也可以是其它任意技术实现,只要对外提供的是REST风格服务即可。
服务消费者
消费应用从注册中心获取服务列表,从而得知每个服务方的信息,知道去哪里调用服务方。
简介:
服务同步:
多个Eureka Server之间也会互相注册为服务,当服务提供者注册到Eureka Server集群中的某个节点时,该节点会把服务的信息同步给集群中的每个节点,从而实现数据同步。因此,无论客户端访问到Eureka Server集群中的任意一个节点,都可以获取到完整的服务列表信息。
而作为客户端,需要把信息注册到每个Eureka中
如果有三个Eureka,则每一个EurekaServer都需要注册到其它几个Eureka服务中。
例如:有三个分别为10086、10087、10088,则:
10086要注册到10087和10088上
10087要注册到10086和10088上
10088要注册到10086和10087上
实际运用效果图:
服务提供者要向EurekaServer注册服务,并且完成服务续约等工作。
服务注册
服务提供者在启动时,会检测配置属性中的: eureka.client.register-with-erueka=true 参数是否为true,事实上默认就是true。如果值确实为true,则会向EurekaServer发起一个Rest请求,并携带自己的元数据信息,EurekaServer会把这些信息保存到一个双层Map结构中 。
默认注册时使用的是主机名或者localhost,如果想用ip进行注册,可以在 user-service 中添加配置如下:
eureka:
instance:
ip-address: 127.0.0.1 # ip地址
prefer-ip-address: true # 更倾向于使用ip,而不是host名
服务续约:
在注册服务完成以后,服务提供者会维持一个心跳(定时向EurekaServer发起Rest请求),告诉EurekaServer:“我还活着”。这个我们称为服务的续约(renew);
有两个重要参数可以修改服务续约的行为;可以在 user-service 中添加如下配置项:
eureka:
instance:
# 服务续约(renew)的间隔,默认为30秒
lease-expiration-duration-in-seconds: 90
# 服务失效时间,默认值90秒
lease-renewal-interval-in-seconds: 30
也就是说,默认情况下每个30秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过90秒没有发送心跳,EurekaServer就会认为该服务宕机,会从服务列表中移除,这两个值在生产环境不要修改,默认即可。
获取服务列表
当服务消费者启动时,会检测 eureka.client.fetch-registry=true 参数的值,如果为true,则会从Eureka Server服务的列表拉取只读备份,然后缓存在本地。并且 每隔30秒 会重新拉取并更新数据。可以在 consumer-demo 项目中通过下面的参数来修改:
eureka:
client:
# 重新拉取服务的时间
registry-fetch-interval-seconds: 30
如下的配置都是在Eureka Server服务端进行:
服务下线
当服务进行正常关闭操作时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:“我要下线了”。服务中心接受到请求之后,将该服务置为下线状态
失效剔除
有时我们的服务可能由于内存溢出或网络故障等原因使得服务不能正常的工作,而服务注册中心并未收到“服务下线”的请求。相对于服务提供者的“服务续约”操作,服务注册中心在启动时会创建一个定时任务,默认每隔一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务剔除,这个操作被称为失效剔除。 可以通过eureka.server.eviction-interval-timer-in-ms 参数对其进行修改,单位是毫秒。
自我保护
我们关停一个服务,就会在Eureka面板看到一条红字警告,这是触发了Eureka的自我保护机制。当一个服务未按时进行心跳续约时,Eureka会统计最近15分钟心跳失败的服务实例的比例是否超过了85%,当EurekaServer节点在短时间内丢失过多客户端(可能发生了网络分区故障)。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因 为服务可能没有宕机。Eureka就会把当前实例的注册信息保护起来,不予剔除。生产环境下这很有效,保证了大多数服务依然可用。
在开发时,为了方便进行自我测试,一般会吧自我保护关闭。
简介:
负载均衡策略:
实现方式:
因为Eureka中已经集成了Ribbon,所以我们无需引入新的依赖。直接在RestTemplate的配置方法上添加 @LoadBalanced 注解:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
修改负载均衡策略的方式:
# 默认为轮询负载均衡,修改为随机负载均衡
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Hystix是Netflflix开源的一个延迟和容错库,用于隔离访问远程服务,防止出现级联失败。
发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
基本情况下,服务调用间的关系如下:(微服务中,一个业务请求调用A、H、I和P四个服务)
当所有微服务都正常运行的情况,请求得到响应后正常结束,线程释放。
此时,微服务I出现了问题,导致请求不能得到响应。
微服务I 发生异常,请求阻塞,用户请求就不会得到响应,则tomcat的这个线程不会释放,于是越来越多的用户请求到来,越来越多的线程会阻塞:
服务器支持的线程和并发数有限,请求一直阻塞,会导致服务器资源耗尽,从而导致所有其它服务都不可用,形成雪崩效应。
解决方案:
Hystrix解决雪崩问题的手段,主要包括:
原理图:
分析:
优点:
可以优先保证核心服务
用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回友好的提示信息) 。服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其它服务没有响应。 触发Hystrix服务降级的情况:
请求超时的默认时间为1S,我们可以在配置文件中使用以下代码进行修改
# 默认超时时长为1S,修改为2S
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000
原理:
在服务熔断中,使用的熔断器,也叫断路器,其英文单词为:Circuit Breaker 熔断机制与家里使用的电路熔断原理类似;当如果电路发生短路的时候能立刻熔断电路,避免发生灾难。在分布式系统中应用服务熔断后;服务调用方可以自己进行判断哪些服务反应慢或存在大量超时,可以针对这些服务进行主动熔断,防止整个系统被拖垮。
Hystrix的服务熔断机制,可以实现弹性容错;当服务请求情况好转之后,可以自动重连。通过断路的方式,将后续请求直接拒绝,一段时间(默认5秒)之后允许部分请求通过,如果调用成功则回到断路器关闭状态,否则继续打开,拒绝请求的服务。
原理图:
状态机的3个状态:
设置熔断参数,可以在配置文件进行修改:
hystrix:
command:
default:
circuitBreaker:
errorThresholdPercentage: 50 # 触发熔断错误比例阈值,默认值50%
sleepWindowInMilliseconds: 10000 # 熔断后休眠时长,默认值5秒
requestVolumeThreshold: 10 # 熔断触发最小请求次数,默认值是20
Feign是声明式Web Service客户端,它让微服务之间的调用变得更简单,类似controller调用service
基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求,实现URL地址的动态代理功能。
Feign中已经自动集成了Ribbon负载均衡和Hystrix,所以我们在配置Feign动态代理的时候,可以直接通过修改配置文件来实现
通过引入Feign的依赖,我们即可使用其中的Ribbon和Hystrix
org.springframework.cloud
spring-cloud-starter-openfeign
创建一个客户端client,通过注解@FeignClient(value=“user-service”),然后在controller类中调用该客户端client,在controller类中使用自定义的请求地址,当我们调用该controller类实现user-serivce功能的时候,采用的则是动态代理的功能。
即,无需通过真实的访问地址,即可访问到想要的服务,采用代理地址的方式,实现了动态访问服务的功能。
Feign中默认集成了Ribbon负载均衡的功能,我们可以通过配置文件实现负载均衡的功能
# 当我们要针对某个服务进行负载均衡的时候,仅需要在ribbon的上级目录中添加相应的服务名即可
ribbon:
ConnectTimeout: 1000 # 连接超时时长
ReadTimeout: 2000 # 数据通信超时时长
MaxAutoRetries: 0 # 当前服务器的重试次数
MaxAutoRetriesNextServer: 0 # 重试多少次服务
OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试
Feign中也继承了熔断器Hystrix,当我们需要用到熔断器时,仅需要在配置文件中开启熔断服务即可
feign:
hystrix:
enabled: true # 开启Feign的熔断功能
Spring Cloud Feign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。通过下面的参数即可开启请求与响应的压缩功能:
feign:
compression:
request:
enabled: true # 开启请求压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
min-request-size: 2048 # 设置触发压缩的大小下限
response:
enabled: true # 开启响应压缩
简介:
Gateway作用的简要理解就是:如果前端要调用后端系统,那么所有的前端请求统一从Gateway网关进入,由Gateway网关进行过滤后转发请求给对应的服务。
网关的核心功能是:过滤和路由
原理图:
核心概念:
直接用一段代码来展示一下网关的作用:
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
# 路由id,可以随意写
- id: user-service-route
# 代理的服务地址(lb表示负载均衡)
uri: lb://user-service
# 路由断言,可以配置映射路径
predicates:
- Path=/api/user/**
filters:
# 添加请求路径的前缀
- PrefixPath=/user
# 表示过滤1个路径,2表示两个路径,以此类推
- StripPrefix=1
# 自定义过滤器
- MyParam=name
# 默认过滤器,对所有路由都生效
default-filters:
- AddResponseHeader=X-Response-Foo, Bar
- AddResponseHeader=myname
# 跨域配置
globalcors:
corsConfigurations:
'[/**]':
#allowedOrigins: * # 这种写法或者下面的都可以,*表示全部
allowedOrigins: - "http://docs.spring.io"
allowedMethods: - GET
分析:
Gateway网关过滤器的常用应用场景如下:
简介:
将配置文件放在配置服务的本地或者远程仓库,进行集中管理的分布式配置组件。例如 application.yml 等配置文件即可放在远程的Git仓库上进行管理。
原理图:
config主要配置信息:
spring:
application:
name: config-server
cloud:
config:
server:
# 此处使用的远程仓库为git
git:
uri: # 仓库地址,根据项目的地址来导入对应的项目
# 因为仓库为私有,所以此处需要配置用户名和密码
username: # 仓库用户名
password: # 仓库密码
分布式配置中心也是一个微服务,所以要在注册中心进行注册。
搭建好配置中心后,其他的需要将配置文件放置在远程仓库的微服务配置文件也需要进行修改:
步骤:
bootstrap.yml配置:
# 采用bootstrap.yml配置文件,从git仓库的配置中心中获取对应配置文件,而不在本地进行配置
# bootstrap.yml相较application.yml:bootstrap.yml更常用于系统配置,即基本不进行修改,application.yml更常用于动态调整的配置
spring:
cloud:
config:
# 与git仓库中配置文件的application名一致
name: user
# 与git仓库中配置文件的profile名一致
profile: dev
# 与git仓库中配置文件存放的分支一致
label: master
discovery:
# 使用配置中心
enabled: true
# 配置中心服务名,项目中连接配置中心模块的名称
service-id: config-server
# 同样需要在注册中心进行注册
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
通过以上配置,我们则完成了spring cloud config配置中心的配置了,这种配置方式,能够让我们静态从远程仓库中去的想要的配置参数,而且当每次配置文件发生变化时,我们需要重启本地服务才能重新获取到更改后的数据,所以仍有些许不便。
为了实现远程仓库中配置文件的动态获取,我们需要引入服务总线Bus和
简介:
引入Spring Cloud Bus总线功能后,服务调用原理图如下:
使用方式:
# config配置文件
spring:
# 配置rabbitmq信息;如果是都与默认值一致则不需要配置,rabbitmq访问地址为127.0.0.1:15672,此处使用的port只有后四位5672即可
# 以下配置即为默认配置,可删
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
management:
endpoints:
web:
exposure:
# 暴露触发消息总线的地址
include: bus-refresh
# user-service配置文件
spring:
# 配置rabbitmq信息;如果是都与默认值一致则不需要配置,rabbitmq访问地址为127.0.0.1:15672,此处使用的port只有后四位5672即可
# 以下配置即为默认配置,可删
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
经过以上配置,成功引入Spring Cloud Bus总线服务,使用消息队列功能实现了远程仓库中配置文件的动态获取功能。