微服务框架功能
微服务具有以上的这些特点,那么作为⼀个微服务框架,⽐如Spring Cloud,应该具备⼀些什么功能
呢?微服务框架的功能主要体现在以下⼏个⽅⾯:
- 注册中⼼ :服务提供者和消费者,能够从注册中⼼注册和得到服务信息;
- 配置中⼼:在微服务架构中设计服务较多需要对于配置⽂件统⼀管理;
- 服务链路追踪:对于服务之间的负载调⽤,要能通过链路追踪,得到具体参与者,调⽤链路出现问题能够快速定位;
- 负载均衡 :服务调⽤服务会采⽤⼀定的负载均衡策略,来保证服务的⾼可⽤;
- 服务容错:通过熔断、降级服务容错策略,对系统进⾏有效的保护,降级是在服务或依赖的服务异
常时,返回保底数据,熔断是指依赖服务多次失效,则熔断器打开,不再尝试调⽤,直接返回降级
信息。熔断后,定期探测依赖服务可⽤性,若恢复则恢复调⽤; - 服务⽹关 :⽤户请求过载时进⾏限流、排队、过载保护、⿊⽩名单、异常⽤户过滤拦截等都可以通过服务⽹关实现。;
- 服务发布与回滚:蓝绿部署、灰度、AB Test等发布策略,可快速回滚应⽤;
- 服务动态伸缩、容器化 :根据服务负载情况,可快速⼿动或⾃动进⾏节点增加和减少。
Spring Cloud简介
Spring Cloud是Spring提供的微服务框架。它利⽤Spring Boot的开发特性简化了微服务开发的复
杂性,如服务发现注册、配置中⼼、消息总线、负载均衡、断路器、数据监控等,这些⼯作都可以借助
Spring Boot的开发⻛格做到⼀键启动和部署。
Spring Cloud的⽬标是通过⼀系列组件,帮助开发者迅速构件⼀个分布式系统,Spring Cloud 是通
过包装其它公司产品来实现的,⽐如Spring Cloud整合了开源的Netflix很多产品。Spring Cloud提供了
微服务治理的诸多组件,例如服务注册和发现、配置中⼼、熔断器、智能路由、微代理、控制总线、全
局锁、分布式会话等。
Spring Cloud组件⾮常多,涉及微服务开发的诸多场景,本节主要介绍Spring Cloud以及最为基础
的Spring Cloud 五⼤组件,后续在进⾏详细的学习。Spring Cloud的架构图如图1-5所示。
Spring Cloud实现微服务的治理功能产品很多,下⾯简单介绍下Spring Cloud各个产品的作⽤,以
及采⽤的原则,如图1-6所示
Spring提供了⼀个RestTemplate模板⼯具类,对基于Http的客户端进⾏了封装,并且实现了对象与json
的序列化和反序列化,⾮常⽅便。RestTemplate并没有限定Http的客户端类型,⽽是进⾏了抽象,⽬前
常⽤的3种都有⽀持:
** HttpClient
** OkHttp
** JDK原⽣的URLConnection(默认的)
Eureka简介
和Zookeeper类似,Eureka是⼀个⽤于服务注册和发现的组件,最开始主要应⽤与亚⻢逊公司的云
计算服务平台AWS,Eureka分为Eureka Server和Eureka Client,Eureka Server为Eureka服务注册中
⼼,Eureka Client为Eureka客户端。
举个例⼦:Eureka好⽐滴滴⽹约⻋平台,没有滴滴时,⼈们出⻔叫⻋只能叫出租⻋。⼀些私家⻋想
做出租却没有资格,被称为⿊⻋。⽽很多⼈想要约⻋,但是⽆奈出租⻋太少,不⽅便。私家⻋很多却不
敢拦,⽽且满⼤街的⻋,谁知道哪个才是愿意载⼈的。⼀个想要,⼀个愿意给,就是缺少引⼦,缺乏管
理啊。
此时滴滴这样的⽹约⻋平台出现了,所有想载客的私家⻋全部到滴滴注册,记录你的⻋型(服务类
型),身份信息(联系⽅式)。这样提供服务的私家⻋,在滴滴那⾥都能找到,⼀⽬了然。
此时要叫⻋的⼈,只需要打开APP,输⼊你的⽬的地,选择⻋型(服务类型),滴滴⾃动安排⼀个
符合需求的⻋到你⾯前,为你服务,完美!
Eureka相当于微服务架构中的“滴滴”。负责微服务的注册和发现⼯作,它记录了服务和服务地址的
映射关系。在分布式架构中,服务会注册到Eureka注册中⼼,当服务需要调⽤其它服务时,就从Eureka
找到服务的地址,进⾏调⽤。Eureka在Spring Cloud中的作⽤是⽤来作为服务治理实现服务注册和发
现。Eureka主要涉及到三⼤⻆⾊:服务提供者、服务消费者、注册中⼼。
服务注册是指,各个微服务在启动时,将⾃⼰的⽹络地址等信息注册到Eureka,服务提供者将⾃⼰
的服务信息,如服务名、IP等告知服务注册中⼼。
服务发现是指当⼀个服务消费者需要调⽤另外⼀个服务时,服务消费者从Eureka查询服务提供者的
地址,并通过该地址调⽤服务提供者的接⼝。⼀个服务既可以是服务消费者,也可以是服务发现者。
各个微服务与注册中⼼使⽤⼀定机制(例如⼼跳)通信。如果Eureka与某微服务⻓时间⽆法通信,
Eureka会将该服务实例从服务注册中⼼中剔除,如果剔除掉这个服务实例过了⼀段时间,此服务恢复⼼
跳,那么服务注册中⼼将该实例重新纳⼊到服务列表中,Eureka架构图,如图2-1所示。
Eureka⼊⻔
Eureka的基本使⽤,创建Eureka Server,让后将上⾯⽀付微服务,和订单微服务注册到
Eureka Server中。Eureka基本机构主要包括以下3个⻆⾊。
.Eureka Server:服务注册中⼼,提供服务注册和发现功能。
Provider Service:服务提供者,案例中就是⽀付微服务。
Consumer Service:服务消费者,案例中就是订单微服务。
源码解析
Eureka的⼀些概念
Register — 服务注册
当Eureka Client向Eureka Server注册时,Eureka Client提供⾃身的元数据,⽐如IP地址、端⼝、
运⾏状况指标的URL,主⻚地址等信息。
Renew — 服务续约
Eureka Client在默认情况下会每隔30秒发送⼀次⼼跳来进⾏服务续约,通过服务续约来告知
Eureka Server该Eureka Client依然可⽤,正常情况下,如果Eureka Server在90秒内没有收到Eureka
Client的⼼跳,Eureka Server会将Eureka Client实例从注册列表中删除,注意:官⽹建议不要更改服
务续约的间隔时间。
Fetch Registries — 获取服务注册列表信息
Eureka Client从Eureka Server获取服务注册表信息,并将其缓存到本地。Eureka Client 会使⽤服
务注册列表信息查找其他服务的信息,从⽽进⾏远程调⽤,改注册列表信息定时(每隔30秒)更新⼀
次,每次返回的注册列表信息可能与Eureka Client的缓存信息不同,Erueka Client会重新获取整个注
册表信息。Eureka Server缓存了所有的服务注册表信息,并且进⾏了压缩。Eureka Client和Eureka
Server可以使⽤json和xml的数据格式进⾏通信,默认,Eureka Client使⽤JSON格式⽅式来获取服务器
注册列表信息。
Cancel — 服务下线
Eureka Client在程序关闭时可以向Eureka Server发送下线请求,发送请求后,该客户端的实例信
息将从Eureka Server的服务注册列表信息中删除。改下线请求不会⾃动完成,需要在程序关闭时调⽤
以下代码
DiscoveryManager.getInstance().shutdownComponent();
Eviction — 服务
在默认情况下,Eureka Client连续90秒没有想Eureka Server发送服务续约(⼼跳)时,Eureka
Server会将该服务实例从服务列表中删除。即服务剔除。
Register 服务注册
Eureka Client源码
Eureka Client向Eureka Server提交⾃⼰的服务信息,包括IP、端⼝、ServiceId等信息。如果
Eureka Client没有配置ServiceId,则默认为配置⽂件中的配置的服务名,即
${spring.application.name}的值。
(1)DiscoveryClient初始化⽅法initScheduledTasks⽅法
该⽅法主要开启了获取服务注册列表的信息,如果需要向Eureka Server注册,则开启注册,
同时开启了定时任务向Eureka Server服务续约,代码如下。
/**
* Initializes all scheduled tasks.
*/
private void initScheduledTasks() {
if (clientConfig.shouldFetchRegistry()) {
...//省略了任务调度获取注册列表的代码。
}
if (clientConfig.shouldRegisterWithEureka()) {
...
// Heartbeat timer
heartbeatTask = new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
);
scheduler.schedule(
heartbeatTask,
renewalIntervalInSecs, TimeUnit.SECONDS);
// InstanceInfo replicator
instanceInfoReplicator = new InstanceInfoReplicator(
this,
instanceInfo,
clientConfig.getInstanceInfoReplicationIntervalSeconds(),
2); // burstSize
...
}
}
(2) instanceInfoReplicator类
initScheduledTasks⽅法中,定时任务调⽤instanceInfoReplicator类,instanceInfoReplicator类
继承Runable接⼝,run⽅法代码如下。
public void run() {
try {
discoveryClient.refreshInstanceInfo();
Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
if (dirtyTimestamp != null) {
discoveryClient.register();
instanceInfo.unsetIsDirty(dirtyTimestamp);
}
} catch (Throwable t) {
logger.warn("There was a problem with the instance info
replicator", t);
} finally {
Future next = scheduler.schedule(this,
replicationIntervalSeconds, TimeUnit.SECONDS);
scheduledPeriodicRef.set(next);
}
}
(3)DiscoveryClient的register⽅法
在com.netflix.discovery包下的DiscoveryClient类中有⼀个register()⽅法,该⽅法通过Http请求向
Eureka Server注册,代码如下所示。
boolean register() throws Throwable {
logger.info(PREFIX + "{}: registering service...",
appPathIdentifier);
EurekaHttpResponse<Void> httpResponse;
try {
httpResponse =
eurekaTransport.registrationClient.register(instanceInfo);
} catch (Exception e) {
logger.warn(PREFIX + "{} - registration failed {}",
appPathIdentifier, e.getMessage(), e);
throw e;
}
if (logger.isInfoEnabled()) {
logger.info(PREFIX + "{} - registration status: {}",
appPathIdentifier, httpResponse.getStatusCode());
}
return httpResponse.getStatusCode() ==
Status.NO_CONTENT.getStatusCode();
}