服务下线——我的命运我做主!

我又想起第一次去网吧的时候,那时候还没有网吧管理系统,走的时候一定要大声对着网吧老板来一句:“老板,下机!”-这就是上世纪末的服务下线模型

我的命运我做主

前面讲了一堆续约,剔除和自保,都是由注册中心在控制,在Eureka的价值观中,难道服务节点的生死就如同浮萍一样,只能被动接受注册中心安排的命运吗?不!服务节点还可以选择一条自己的路,做一回命运的主人,只不过这条路是一条“不归路”-服务下线
服务下线——我的命运我做主!_第1张图片

老板,下机!

服务下线,通常由服务器关闭,或主动调用shutdown方法来触发,是由服务节点主动向注册中心发起的资源释放命令。

下线代表着服务的生命周期走到了尾声,服务节点在工作岗位站到了最后一刻。我们遵从服务节点的要求,-切从简,今天追悼会我们只有一个环节-追忆服务节点下线前的点点滴滴:
服务下线——我的命运我做主!_第2张图片

  1. **标记状态:**下线的第一步,就是先要给自己立一个flag,
    告诉别人“我不行了”,这一步在 EurekaServiceRegistry.deregister里完成,这个方法也只做这么一件事情。
  2. 获取系统锁服务下线是一件严肃的事情,总不能反反复复
    被执行吧,所以这一步需要借助一个特殊的锁,来完成线程安全的下线操作。至于这个锁是什么,我想大部分的研发人员是没有接触过的,这也是我面试时经常问别人的问题,等咱开完追悼会,我在番外篇跟大家再絮叨。
  3. 释放资源在服务注册伊始,服务节点创建了很多监听器和
    后台任务,比如状态监听器会在服务状态发生变化的时候同步给注册中心,心跳任务会主动发送心跳包,还有很多生前的亲密战友们,都会被一同关闭,或者释放。
  4. 发送下线指令最后,发送一个Delete指令到注册中心,完成整个服务下线请求。

一个服务节点就这样走完了它平凡又忙碌的一生。

番外篇-锁?

前面提到了一个很特殊的同步锁,这里我们展开讲一下。

Java中的线程安全方法有很多,比如大家熟知的synchronized,或者借助concurrent包下的各种类。可是当面试官问你,抛开这些,你还有什么线程安全的方式?有的同学可能会说Thread中的waitnotify。很好,那还有其他的方式吗?

刚才我们说的都是Java层面提供的封装好的机制,然而还有一个利用底层操作系统实现的乐观同步锁,他叫做CAS,即Compare and Swap。从字面意思上理解,就是先比较,再替换的过程,比如我们拿服务下线里的操作看,它调用了AtomicBooleanCAS操作

public final boolean compareAndSet(boolean expect, boolean update){
	int e = expect ? 1: 0;
	int u = update ? 1 : 0;
	return unsafe.compareAndSwapInt(this, valueOffset, e);
}

这里的expect和update入参是不同的两个值,expect= false, update=true,而最后一行unsafe.compareAndSwapInt操作是看不到源码的,因为CAS操作是借助了底层操作系统的接口,因此这实际是一个被native关键字修饰的由操作系统实现的方法。操作系统的cas操作会将内存值与expect值进行比较,如果相等就会将update参数更新到内存,并返回成功,如果不等则返回失败,在操作系统层面,这个比对替换的操作是原子性的,所以也就可以保证线程安全。这和我们平时业务代码中的乐观锁实现比较类似。

当然, CAS也有一个著名的ABA问题,也就是当内存值从A变到B然后再变回A的情况下,假如我的期望值是A,尽管中途发生了A->B的变化,可是因为最终又变回了A,因此CAS操作依然认为内存值是没有发生变化的。我想这个问题难不倒大家,解决方法很简单,用简单的版本号控制的方式规避掉就可以了(在比对的时候同时验证版本号,每次修改后版本号+1)。

本文已收录至我的个人网站:程序员波特,主要记录Java相关技术系列教程,共享电子书、Java学习路线、视频教程、简历模板和面试题等学习资源,让想要学习的你,不再迷茫。

你可能感兴趣的:(微服务系列,分布式,系统架构,微服务)