单机、集群、分布式和微服务等相关概念解析以及Redis实现分布式锁

文章目录

    • 1、各项概念
    • 2、分布式锁的解决方案

1、各项概念

单机结构
    单机结构我想大家最最最熟悉的就是单机结构,一个系统业务量很小的时候所有的代码都放在一个项目中就好了,然后这个项目部署在一台服务器上就好了。整个项目所有的服务都由这台服务器提供。这就是单机结构。那么,单机结构的缺点是显而易见的,单机的处理能力毕竟是有限的,当你的业务增长到一定程度的时候,单机的硬件资源将无法满足你的业务需求。此时便出现了集群模式。
集群模式
    单机处理到达瓶颈的时候,你就把单机复制几份,这样就构成了一个“集群”。集群中每台服务器就叫做这个集群的一个“节点”,所有节点构成了一个集群。每个节点都提供相同的服务,那么这样系统的处理能力就相当于提升了好几倍(有几个节点就相当于提升了这么多倍)。但问题是用户的请求究竟由哪个节点来处理呢?最好能够让此时此刻负载较小的节点来处理,这样使得每个节点的压力都比较平均。要实现这个功能,就需要在所有节点之前增加一个“调度者”的角色,用户的所有请求都先交给它,然后它根据当前所有节点的负载情况,决定将这个请求交给哪个节点处理。这个“调度者”有个牛逼了名字——负载均衡服务器。集群结构的好处就是系统扩展非常容易。如果随着你们系统业务的发展,当前的系统又支撑不住了,那么给这个集群再增加节点就行了。但是,当你的业务发展到一定程度的时候,你会发现一个问题——无论怎么增加节点,貌似整个集群性能的提升效果并不明显了。这时候,你就需要使用微服务结构了。
分布式
    分布式结构先来对前面的知识点做个总结。从单机结构到集群结构,你的代码基本无需要作任何修改,你要做的仅仅是多部署几台服务器,每台服务器上运行相同的代码就行了。但是,当你要从集群结构演进到微服务结构的时候,之前的那套代码就需要发生较大的改动了。
    下面开始介绍所谓的分布式结构。分布式结构就是将一个完整的系统,按照业务功能,拆分成一个个独立的子系统,在分布式结构中,每个子系统就被称为“服务”。这些子系统能够独立运行在web容器中,它们之间通过RPC方式通信。举个例子,假设需要开发一个在线商城。按照微服务的思想,我们需要按照功能模块拆分成多个独立的服务,如:用户服务、产品服务、订单服务、后台管理服务、数据分析服务等等。这一个个服务都是一个个独立的项目,可以独立运行。如果服务之间有依赖关系,那么通过RPC方式调用。这样的好处有很多:系统之间的耦合度大大降低,可以独立开发、独立部署、独立测试,系统与系统之间的边界非常明确,排错也变得相当容易,开发效率大大提升。系统之间的耦合度降低,从而系统更易于扩展。我们可以针对性地扩展某些服务。假设这个商城要搞一次大促,下单量可能会大大提升,因此我们可以针对性地提升订单系统、产品系统的节点数量,而对于后台管理系统、数据分析系统而言,节点数量维持原有水平即可。服务的复用性更高。比如,当我们将用户系统作为单独的服务后,该公司所有的产品都可以使用该系统作为用户系统,无需重复开发。简单来说分布式就是将后台工作分布在多个服务器上,多个服务器协同完成工作。
什么是分布式锁?为什么使用分布式锁?
    在java并发编程中,对共享资源进行访问得时候会出现安全问题,比如在售票系统中的一票多卖和卖出负票的问题,那么我们就要对资源进行同步处理即统一时刻仅可以有一个线程进入临界区,如果是单机情况的话用synchronized或者Lock即可解决。但是在分布式系统中这个共享资源可能会在不同的Tomcat上,而synchronized和lock最大的作用域也只能是当前的jvm,所以synchronized和lock不能解决分布式中的共享资源访问问题,这个时候就要引入分布式锁。
集群分布式的不安全问题举例
单机、集群、分布式和微服务等相关概念解析以及Redis实现分布式锁_第1张图片    公司部署了3个服务,这三个服务都可以进行收集购买,而库存只有一个,当有三个人分别在三个服务上进行购买操作的时候很有可能出现超卖的现象。
分布式的不安全问题举例
单机、集群、分布式和微服务等相关概念解析以及Redis实现分布式锁_第2张图片

微服务是一种架构风格
    分布式和微服务很像,把一个系统的业务拆分成多个独立的服务,系统中的各个微服务可被独立部署,各个微服务之间是松耦合的.
    微服务的设计是为了不因为某个模块的升级和BUG影响现有的系统业务。微服务与分布式的细微差别是,微服务的应用不一定是分散在多个服务器上,他也可以是同一个服务器。严格来说分布式也是一种微服务,但是微服务不一定是分布式

微服务优点
1、通过分解巨大单体式应用为多个服务方法解决了复杂性问题,每个微服务相对较小
2、每个单体应用不局限于固定的技术栈,开发者可以自由选择开发技术,提供API服务。
3、每个微服务独立的开发,部署
4、单一职责功能,每个服务都很简单,只关注于一个业务功能
5、易于规模化开发,多个开发团队可以并行开发,每个团队负责一项服务
6、改善故障隔离。一个服务宕机不会影响其他的服务

微服务缺点:
1.开发者需要应对创建分布式系统所产生的额外的复杂因素
l 目前的IDE主要面对的是单体工程程序,无法显示支持分布式应用的开发
l 测试工作更加困难
l 需要采用服务间的通讯机制
l 很难在不采用分布式事务的情况下跨服务实现功能
l 跨服务实现要求功能要求团队之间的紧密协作
2.部署复杂
3.内存占用量更高

参考文献:
作者:大闲人柴毛毛(知乎)
链接:https://www.zhihu.com/question/20004877/answer/282033178

2、分布式锁的解决方案

参考自B站,侵权删文,B站视频如下
添加链接描述
    基于数据库实现分布式锁;
优点:简单、方便
缺点:开销大且影响数据库性能。
    基于缓存(Redis等)实现分布式锁;
    基于Zookeeper实现分布式锁;
    这里只讨论基于Redis的分布式锁实现。
Redis实现分布式锁
使用setnx、getset、expire、del这4个redis命令实现:
     setnx 是『SET if Not eXists』(如果不存在,则 SET)的简写。 命令格式:SETNX key value;使用:只在键 key 不存在的情况下,将键 key 的值设置为 value 。若键 key 已经存在, 则 SETNX 命令不做任何动作。返回值:命令在设置成功时返回 1 ,设置失败时返回 0 。
     getset 命令格式:GETSET key value,将键 key 的值设为 value ,并返回键 key 在被设置之前的旧的value。返回值:如果键 key 没有旧值, 也即是说, 键 key 在被设置之前并不存在, 那么命令返回 nil 。当键 key 存在但不是字符串类型时,命令返回一个错误。
     expire 命令格式:EXPIRE key seconds,使用:为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。返回值:设置成功返回 1 。 当 key 不存在或者不能为 key 设置生存时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的生存时间),返回 0 。
     del 命令格式:DEL key [key …],使用:删除给定的一个或多个 key ,不存在的 key 会被忽略。返回值:被删除 key 的数量。

单机、集群、分布式和微服务等相关概念解析以及Redis实现分布式锁_第3张图片1 Redis实现分布式锁的低端版本
单机、集群、分布式和微服务等相关概念解析以及Redis实现分布式锁_第4张图片
过程分析:
     1 A尝试去获取锁lockkey,通过setnx(lockkey,currenttime+timeout)命令,对lockkey进行setnx,将value值设置为当前时间+锁超时时间;
    2 如果返回值为1,说明redis服务器中还没有lockkey,也就是没有其他用户拥有这个锁,A就能获取锁成功;
    3 在进行相关业务执行之前,先执行expire(lockkey),对lockkey设置有效期,防止死锁。因为如果不设置有效期的话,lockkey将一直存在于redis中,其他用户尝试获取锁时,执行到setnx(lockkey,currenttime+timeout)时,将不能成功获取到该锁;
    4 执行相关业务;
    5 释放锁,A完成相关业务之后,要释放拥有的锁,也就是删除redis中该锁的内容,del(lockkey),接下来的用户才能进行重新设置锁新值。
缺陷:
    如果A在setnx成功后,A成功获取锁了,还没来得及设置过期时间就出了问题,那么依然会出现死锁。
2 Redis实现分布式锁的进阶版本
单机、集群、分布式和微服务等相关概念解析以及Redis实现分布式锁_第5张图片过程分析:
     当A通过setnx(lockkey,currenttime+timeout)命令能成功设置lockkey时,即返回值为1,过程与原理1一致;
    当A通过setnx(lockkey,currenttime+timeout)命令不能成功设置lockkey时,这是不能直接断定获取锁失败;因为我们在设置锁时,设置了锁的超时时间timeout,当当前时间大于redis中存储键值为lockkey的value值时,可以认为上一任的拥有者对锁的使用权已经失效了,A就可以强行拥有该锁;具体判定过程如下;
    A通过get(lockkey),获取redis中的存储键值为lockkey的value值,即获取锁的相对时间lockvalueA
lockvalueA!=null && currenttime>lockvalue,A通过当前的时间与锁设置的时间做比较,如果当前时间已经大于锁设置的时间临界,即可以进一步判断是否可以获取锁,否则说明该锁还在被占用,A就还不能获取该锁,结束,获取锁失败;
    步骤4返回结果为true后,通过getSet设置新的超时时间,并返回旧值lockvalueB,以作判断,因为在分布式环境,在进入这里时可能另外的进程获取到锁并对值进行了修改,只有旧值与返回的值一致才能说明中间未被其他进程获取到这个锁。(这一步很重要,其实就是一个CAS操作,而且这个CAS出现ABA问题的概率极低)
    lockvalueB == null || lockvalueA==lockvalueB,判断:若果lockvalueB为null,说明该锁已经被释放了,此时该进程可以获取锁;旧值与返回的lockvalueB一致说明中间未被其他进程获取该锁,可以获取锁;否则不能获取锁,结束,获取锁失败。

你可能感兴趣的:(web,其他)