当前位置: 首页 > 科技观察

Eureka中读写锁的奇思妙想,太顶了

时间:2023-03-19 23:13:04 科技观察

Eureka读写锁的奇思妙想太棒了"title="Eureka读写锁的奇思妙想,太棒了"style="width:872px;能见度:可见;height:381px;"data-type="inline">如果有一个线程加了写锁,那么其他线程不能加写锁,同时只允许一个线程加写锁。因为加了一个写锁的意思是有人要写一个共享数据,同时不能让其他人写这个数据。同时,如果一个线程加了写锁,其他线程就不能加读锁,因为既然有人在写数据,当然其他人是读不到数据的!如果一个线程加了读锁,其他线程是可以随意同时加读锁的,因为只有读数据的线程,而其他线程此时也可以读取数据了!同理,如果一个线程加了读锁,其他线程此时就不能加写锁了,因为既然有人在读数据,那你就不能随意写数据了!好吧!这是对读写锁使用的初步介绍,相信很多同学应该都知道,bec因为这在Java开发中非常重要。一个基础知识。微服务注册中心读写锁的优化现在进入正题。问到读写锁的时候,这个时候自然就可以带出来了。大家之前了解过SpringCloud微服务技术架构,同时对微服务注册中心注册中心读写锁的优化有一些感悟和看法。这样的话,比起单纯跟面试官讲读写锁的基本概念和用法,会好很多!首先,你需要了解一点微服务的整体架构。可以参考之前的一篇文章请不要在面试的时候问我SpringCloud的底层原理!同时,你还需要了解SpringCloudEureka(即微服务注册中心)的核心原理。可以参考我之前写的一篇文章【双十一背后】微服务注册中心是如何承载大型系统的千万级访问量的。好了,了解了这些前置知识之后,我们就正式开始了。让我们看看下面的图片。既然知道了微服务注册中心(可以是Eureka也可以是Consul也可以是你自己写的微服务注册中心),他肯定在记忆中会有一个服务注册中心的概念。这个服务注册中心存储了每个微服务在注册时发送的地址信息。它存储了每个服务有多少个服务实例,每个服务实例部署在哪台机器上,监听哪个端口号,主要是这样一些信息。OK,问题来了。服务注册中心的数据实际上是被别人读写的。比如有些服务在启动的时候会进行注册,这时候服务注册中心的数据就会被修改。这就是写作的过程。然后,其他服务也会读取服务注册表中的数据,因为每个服务都需要知道其他服务部署在哪些机器上。所以这块内存中的服务注册中心数据自然存在读写并发问题!可能有多个线程写,也可能有多个线程读!如果不对同一内存中的注册表数据添加任何保护措施,那么可能会出现多线程并发修改共享数据的问题,导致数据混乱,对吧?上面的过程可以看下图来理解。此时,如果在服务注册和读取服务注册的方法中都加上一个synchronized关键字,是不是就可以了呢?可能你会想,加上synchronized,直接让所有线程对服务注册中心进行读写操作,全部序列化。那样不就保证了内存中服务注册数据的安全吗?下面是一段伪代码,大家感受一下:上面代码中,写入(服务注册)和读取(读取服务注册中心)这两个方法直接加上了synchronized关键字,确实保证了数据在服务注册表中并不凌乱,但这绝对是不合适的。因为这样做的话,相当于所有线程读写服务注册中心的数据,都是序列化的。想一想,我们要的是什么效果?其实就是说,当有人向服务注册中心写入数据时,其他人不可以写入,同时也不允许读取!那么,当有人在读取服务注册中心的数据时,其他人可以同时读取,但是此时,其他人是不允许向服务注册中心写入数据的!对了,这不就是我们想要的吗,其实就是这个效果?经过思考,我们不应该暴力加一个synchronized来序列化所有的读写线程,这样会导致并发度很低。看下图,我们想要的第一个效果:一旦有人在写服务注册中心的数据,我们加一个写锁,这个时候别人不能写也不能读。那么如果有人在读取数据怎么办?这时候其他人可以读,但是不可以写。大家看下图。重点来了,这样做有什么好处呢?其实大部分时候都是读操作,所以使用读锁可以让大量线程同时读取数据,不会阻塞,不会排队,保证高并发读性能比较高。然后在少数时候,有服务上线的时候需要注册数据。写数据的场景比较少。这时候写数据的时候,只能一个一个加写锁,然后再写数据。读取数据。所以读写锁很适合这种读多写少的场景。另外,我们能不能尽量保证在写数据的同时,还能继续读数据呢?当大量加读锁时,会阻塞人写数据,长时间加写锁。这种情况可以避免吗?对,采用了多级缓存机制,本文分析了SpringCloudEureka微服务注册中心中的多级缓存机制。最后我们来看一下上面的伪代码,如果我们使用读写锁来优化它会是什么样子呢?