另外一个进程已经为 dpkg frontend lock 加锁_架构设计 | 高并发流量削峰,共享资源加锁机制...

本文介绍了高并发场景的处理,包括秒杀业务的预抢购、分批抢购和实时秒杀策略,以及流量削峰的Nginx代理、CDN节点、网关控制和并发熔断方法。同时探讨了分布式加锁的悲观锁和乐观锁机制,以及服务保护和数据库保护在高并发业务中的重要性。
摘要由CSDN通过智能技术生成

一、高并发简介

在互联网的业务架构中,高并发是最难处理的业务之一,常见的使用场景:秒杀,抢购,订票系统;高并发的流程中需要处理的复杂问题非常多,主要涉及下面几个方面:

  • 流量管理,逐级承接削峰;
  • 网关控制,路由请求,接口熔断;
  • 并发控制机制,资源加锁;
  • 分布式架构,隔离服务和数据库;

高并发业务核心还是流量控制,控制流量下沉速度,或者控制承接流量的容器大小,多余的直接溢出,这是相对复杂的流程。其次就是多线程并发下访问共享资源,该流程需要加锁机制,避免数据写出现错乱情况。

二、秒杀场景

1、预抢购业务

活动未正式开始,先进行活动预约,先把一部分流量收集和控制起来,在真正秒杀的时间点,很多数据可能都已经预处理好了,可以很大程度上削减系统的压力。有了一定预约流量还可以提前对库存系统做好准备,一举两得。

场景:活动预约,定金预约,高铁抢票预购。

2、分批抢购

分批抢购和抢购的场景实现的机制是一致的,只是在流量上缓解了很多压力,秒杀10W件库存和秒杀100件库存系统的抗压不是一个级别。如果秒杀10W件库存,系统至少承担多于10W几倍的流量冲击,秒杀100件库存,体系可能承担几百或者上千的流量就结束了。下面流量削峰会详解这里的策略机制。

场景:分时段多场次抢购,高铁票分批放出。

3、实时秒杀

最有难度的场景就是准点实时的秒杀活动,假如10点整准时抢1W件商品,在这个时间点前后会涌入高并发的流量,刷新页面,或者请求抢购的接口,这样的场景处理起来是最复杂的。

  • 首先系统要承接住流量的涌入;
  • 页面的不断刷新要实时加载;
  • 高并发请求的流量控制加锁等;
  • 服务隔离和数据库设计的系统保护;

场景:618准点抢购,双11准点秒杀,电商促销秒杀。

三、流量削峰

91209f60b86e2b11144ed7e1b8e66c13.png

1、Nginx代理

Nginx是一个高性能的HTTP和反向代理web服务器,经常用在集群服务中做统一代理层和负载均衡策略,也可以作为一层流量控制层,提供两种限流方式,一是控制速率,二是控制并发连接数。

基于漏桶算法,提供限制请求处理速率能力;限制IP的访问频率,流量突然增大时,超出的请求将被拒绝;还可以限制并发连接数。

高并发的秒杀场景下,经过Nginx层的各种限制策略,可以控制流量在一个相对稳定的状态。

2、CDN节点

CDN静态文件的代理节点,秒杀场景的服务有这样一个操作特点,活动倒计时开始之前,大量的用户会不断的刷新页面,这时候静态页面可以交给CDN层面代理,分担数据服务接口的压力。

CDN层面也可以做一层限流,在页面内置一层策略,假设有10W用户点击抢购,可以只放行1W的流量,其他的直接提示活动结束即可,这也是常用的手段之一。

话外之意:平时参与的抢购活动,可能你的请求根本没有到达数据接口层面,就极速响应商品已抢完,自行意会吧。

3、网关控制

网关层面处理服务接口路由,一些校验之外,最主要的是可以集成一些策略进入网关,比如经过上述层层的流量控制之后,请求已经接近核心的数据接口,这时在网关层面内置一些策略控制:如果活动是想激活老用户,网关层面快速判断用户属性,老用户会放行请求;如果活动的目的是拉新,则放行更多的新用户。

经过这些层面的控制,剩下的流量已经不多了,后续才真正开始执行抢购的数据操作。

话外之意:如果有10W人参加抢购活动,真正下沉到底层的抢购流量可能就1W,甚至更少,在分散到集群服务中处理。

4、并发熔断

在分布式服务的接口中,还有最精细的一层控制,对于一个接口在单位之间内控制请求处理的数量,这个基于接口的响应时间综合考虑,响应越快,单位时间内的并发量就越高,这里逻辑不难理解。

言外之意:流量经过层层控制,数据接口层面分担的压力已经不大,这时候就是面对秒杀业务中的加锁问题了。

四、分布式加锁

1、悲观锁

机制描述

所有请求的线程必须在获取锁之后,才能执行数据库操作,并且基于序列化的模式,没有获取锁的线程处于等待状态,并且设定重试机制,在单位时间后再次尝试获取锁,或者直接返回。

过程图解

784205c942e4f14fc6ef77db6250e9e6.png

Redis基础命令

SETNX:加锁的思路是,如果key不存在,将key设置为value如果key已存在,则 SETNX 不做任何动作。并且可以给key设置过期时间,过期后其他线程可以继续尝试锁获取机制。

借助Redis的该命令模拟锁的获取动作。

代码实现

这里基于Redis实现的锁获取和释放机制。

import org.springframework.stereotype.Component;import redis.clients.jedis.Jedis;import javax.annotation.Resource;@Componentpublic class RedisLock {    @Resource    private Jedis jedis ;    /**     * 获取锁     */    public boolean getLock (String key,String value,long expire){        try {            String result = jedis.set( key, value, "nx", "ex", expire);            return result != null;        } catch (Exception e){            e.printStackTrace();        }finally {            if (jedis != null) jedis.close();        }        return false ;    }    /**     * 释放锁     */    public boolean unLock (String key){        try {            Long result = jedis.del(key);            return result > 0 ;        } catch (Exception e){            e.printStackTrace();        }finally {            if (jedis != null) jedis.close();        }        return false ;    }}

这里基于Jedis的API实现,这里提供一份配置文件。

@Configurationpublic class RedisConfig {    @Bean    public JedisPoolConfig jedisPoolConfig (){        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig() ;        jedisPoolConfig.setMaxIdle(8);        jedisPoolConfig.setMaxTotal(20);        return jedisPoolConfig ;    }    @Bean    public JedisPool jedisPool (@Autowired JedisPoolConfig jedisPoolConfig){        return new JedisPool(jedisPoolConfig,"127.0.0.1",6379) ;    }    @Bean    public Jedis jedis (@Autowired JedisPool jedisPool){        return jedisPool.getResource() ;    }}

问题描述

在实际的系统运行期间可能出现如下情况:线程01获取锁之后,进程被挂起,后续该执行的没有执行,锁失效后,线程02又获取锁,在数据库更新后,线程01恢复,此时在持有锁之后的状态,继续执行后就会容易导致数据错乱问题。

这时候就需要引入锁版本概念的,假设线程01获取锁版本1,如果没有执行,线程02获取锁版本2,执行之后,通过锁版本的比较,线程01的锁版本过低,数据更新就会失败。

CREATE TABLE `dl_data_lock` (`id` INT (11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',`inventory` INT (11) DEFAULT '0' COMMENT '库存量',`lock_value` INT (11) NOT NULL DEFAULT '0' COMMENT '锁版本',PRIMARY KEY (`id`)) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT = '锁机制表';

说明:lock_value就是记录锁版本,作为控制数据更新的条件。

    UPDATE dl_data_lock SET inventory=inventory-1,lock_value=#{lockVersion}    WHERE id=#{id} AND lock_value 

说明:这里的更新操作,不但要求线程获取锁,还会判断线程锁的版本不能低于当前更新记录中的最新锁版本。

2、乐观锁

机制描述

乐观锁大多是基于数据记录来控制,在更新数据库的时候,基于前置的查询条件判断,如果查询出来的数据没有被修改,则更新操作成功,如果前置的查询结果作为更新的条件不成立,则数据写失败。

过程图解

c3b835d11489c6b185108fe83facccb9.png

代码实现

业务流程,先查询要更新的记录,然后把读取的列,作为更新条件。

@Overridepublic Boolean updateByInventory(Integer id) {    DataLockEntity dataLockEntity = dataLockMapper.getById(id);    if (dataLockEntity != null){        return dataLockMapper.updateByInventory(id,dataLockEntity.getInventory())>0 ;    }    return false ;}

例如如果要把库存更新,就把读取的库存数据作为更新条件,如果读取库存是100,在更新的时候库存变了,则更新条件自然不能成立。

    UPDATE dl_data_lock SET inventory=inventory-1 WHERE id=#{id} AND inventory=#{inventory}

五、分布式服务

1、服务保护

在处理高并发的秒杀场景时,经常出现服务挂掉场景,常见某些APP的营销页面,出现活动火爆页面丢失的提示情况,但是不影响整体应用的运行,这就是服务的隔离和保护机制。

基于分布式的服务结构可以把高并发的业务服务独立出来,不会因为秒杀服务挂掉影响整体的服务,导致服务雪崩的场景。

2、数据库保护

数据库保护和服务保护是相辅相成的,分布式服务架构下,服务和数据库是对应的,理论上秒杀服务对应的就是秒杀数据库,不会因为秒杀库挂掉,导致整个数据库宕机。


推荐阅读:架构设计系列 源码 -> GitHub || GitEE

架构设计 | 缓存管理模式,监控和内存回收策略

架构设计 | 异步处理流程,多种实现模式详解

架构设计 | 接口幂等性原则,防重复提交Token管理

架构设计 | 分布式系统调度,Zookeeper集群化管理

架构设计 | 分布式业务系统中,全局ID生成策略

架构设计基础:单服务.集群.分布式,基本区别和联系

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这个错误提示意味着另一个进程正在使用 dpkg 前端并已经锁定了它。这通常是由于另一个软件包管理器或更新程序正在运行而导致的。要解决此问题,您可以尝试等待一段时间,直到另一个进程完成,或者手动解锁 dpkg 前端。要手动解锁,请运行以下命令: sudo rm /var/lib/dpkg/lock 然后再次运行您的 dpkg 命令即可。 ### 回答2: 出现这个错误提示是因为当前电脑上已经一个 dpkg 进程在运行,正在使用 dpkg 前端进行软件包的安装、更新或卸载等操作。由于 dpkg 是单线程应用程序,在一个 dpkg 进程运行期间,不允许同时启动另一个 dpkg 进程。 如果你确定当前没有在进行 dpkg 操作,但是出现了这个错误提示,可能是前一次 dpkg 操作中断或出现异常(比如系统崩溃、升级中断等原因),导致 dpkg 进程锁未被正确释放。 此时可以尝试使用以下命令解决该问题: 1. 查找当前系统中所有的 dpkg 进程: ``` ps aux | grep dpkg ``` 2. 查找到正在运行的 dpkg 进程进程号(PID),并使用命令杀死该进程: ``` sudo kill <PID> ``` 3. 确认该 dpkg 进程被杀死: ``` ps aux | grep dpkg ``` 4. 清理 dpkg 前端锁: ``` sudo rm /var/lib/dpkg/lock ``` 以上命令执行完毕后,再尝试运行 dpkg 操作即可。需要注意的是,若在执行以上命令时未正确杀死正在运行的 dpkg 进程,可能会导致软件包管理系统出现异常而引起其他问题,因此要谨慎操作。 除此之外,也可能是因为可选软件源的问题导致了 dpkg 进程冲突。此时可以通过修改可选软件源,并更新软件包信息来解决该问题。 总之,dpkg:错误:另外一个进程已经dpkg frontend 加锁这个错误提示出现时,需要先确定是哪个进程导致了 dpkg 进程锁未被正确释放,并采取相应措施解决该问题。 ### 回答3: dpkg是在Linux系统中用于管理安装和卸载软件包的命令行工具。当在终端中尝试安装或卸载软件包时,如果系统上已经有其他进程正在运行dpkg的前端,则可能会出现“dpkg:错误:另外一个进程已经dpkg frontend 加锁”的错误。 这个错误通常意味着另一个终端会话或软件包管理工具正在占用dpkg的前端。为了解决这个问题,可以采取以下几个步骤: 1. 确认错误信息 首先,需要在终端中确认错误信息,确保它是由dpkg前端锁定引起的。 2. 找出占用dpkg前端的进程 运行以下命令,找出当前正在运行的dpkg进程: ps aux | grep dpkg 该命令将显示所有与dpkg相关的进程信息。找到正在运行dpkg进程ID(PID)并记下它们。 3. 解锁dpkg前端 运行以下命令,使用先前记录的进程ID解锁dpkg前端: sudo kill -9 <PID> 其中<PID>是先前记录的dpkg进程ID。这将终止该进程并释放dpkg前端。 4. 重新运行操作 成功解锁dpkg前端之后,重新运行dpkg命令,应该就可以执行需要的软件包操作了。 总之,“dpkg:错误:另外一个进程已经dpkg frontend 加锁”通常表示另一个进程占用了dpkg前端。通过找出并解锁占用dpkg前端的进程,可以解决这个问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值