标签归档:分布式

基于Redis的分布式锁的简易实现

本文讨论的分布式锁适用于集群节点程序的互斥执行,注意此处的“分布式”是指该锁的适用场景是分布式系统,而非锁本身是分布式的,关于锁本身的分布式设计可以参考Redis官方介绍:『Distributed locks with Redis』。

之前在『如何用Spring实现集群环境下的定时任务』一文中提到利用Redis的"GET/SET+TTL"机制实现集群场景下定时任务的互斥执行功能,这实际上就是本文实现的原型。该文描述的方法的问题在于GET和SET方法的组合并非原子操作,在多进程并行执行场景下可能有多个客户端获得锁,从而破坏了锁的安全性。

本文的改进在两个方面:

1. 要解决分布式锁的安全性问题,需要使用Redis提供的锁原语:SETNX(since 1.0.0)或者SET NX EX(since 2.6.12)。这类命令的语义是:如果Key已存在,则返回SET成功,否则返回失败。

2. 使用注解方式加锁和解锁,避免代码重复和耦合。

以下是Java实现(Spring AOP)的原型:

1. 定义注解类,接收expire参数,表示此锁的过期时间。如果在定时任务中使用,一般要大于节点间的时间差,小于定时任务的时间间隔。

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DLock {
    public String value() default "";

    public String expire() default "30";
}

2. 定义切面类,完成锁的申请和释放逻辑。

@Aspect
@Component
public class DLockAdvice {
    private static String DLOCK_PREFIX = "DLOCK_PREFIX_";

    @Autowired
    private RedisService redisService = null;

    @Around("@annotation(lock)")
    public Object lock(ProceedingJoinPoint pjp, DLock lock) throws Throwable {
        Long expire = Long.valueOf(lock.expire());;
        Signature sig = pjp.getSignature();
        String lockKey = DLOCK_PREFIX + sig.getDeclaringTypeName() + sig.getName() + expire;
        if (redisService.setValueIfAbsent(lockKey, true, expire)) {
            return pjp.proceed();
        } 
        return null;
    }
}

考虑到注解的通用性,锁名称的区分度越大越好,此处采用的“前缀+包名+类名+方法名+过期时间”,因此假如有个方法通过参数个数或者类型不同进行重载,则该锁会对这个重载的每个方法都生效,除非把参数个数或者类型信息加到锁名称里。

另外考虑到Redis服务本身或者网络的不稳定性,需要在RedisService的setValueIfAbsent()方法中对异常进行处理,假如:

1. 能够容忍多个客户端同时获得锁。那么当执行Redis命令异常时返回true。
2. 无法容忍多个客户端同时获得锁,宁愿没有客户端可以获得锁。那么当执行Redis命令异常时返回false。

--EOF--

一种分布式系统消息服务器设计

设计一个分布式系统,首先面临的就是如何解决服务间的通信问题,同步还是异步,是采用基于消息总线的事件驱动架构(EDA)还是分布式服务框架,这在很大程度上决定了系统的可扩展性。

消息服务器的可选择性很多,比如早些年的XMPP服务器,传统的JMS服务器,还有目前比较流行的AMQP消息服务器,简单的优缺点对比如下:

类型 优点 缺点
Openfire (XMPP) 1. 成熟,稳定。
2. 适合做聊天服务,
   在IM领域(Gtalk,网易POPO等)应用广泛。
1. 消息可靠性无保障。
2. 路由策略不够灵活。
3. 集群模式不完善。
4. 协议太重。
ActiveMQ (JMS) 1. 成熟,稳定。
2. 与Java应用契合度高。
1. 路由策略不够灵活。
2. 集群模式不稳定。
RabbitMQ (AMQP) 1. 成熟,稳定。
2. 路由策略灵活。
3. 消息可靠传输。
4. 集群方案成熟。
1. 配置项多,学习和运维成本高。

本文分享一种基于RabbitMQ的消息服务器设计方案:

MQ Server

如上图所示,黄色虚线表示的是分布式系统的一个子服务,假设分别为WEB、REPO和CTRL服务。P表示生产者,C表示消费者。生产者把消息发送到一个topic模式的Exchange (X),根据route key和binding key的运算结果,将消息路由到相应队列,再由队列投递给具体的消费者。

我们将请求从服务调用方的角度分成两类:同步和异步。同步(rpc.call)是指需要应答的请求,比如获取依赖服务的状态。异步(rpc.cast)是指无需应答的请求,比如下发一个命令。

1. 对于同步请求,服务调用方将消息publish到Exchange,根据route key路由到相应队列,投递给服务提供方的消费者处理。服务提供方消费者处理完请求后,将响应内容封装成消息格式,指定route key(TYPETYPE.${HOSTNAME}),将处理结果消息返回。

2. 对于异步请求,服务调用方将消息publish到Exchange,根据route key路由到相应队列,投递给服务提供方的消费者处理。服务提供方消费者处理完请求后无需返回。

无论同步还是异步,服务调用方在发出请求(消息)后会立即返回,不会阻塞。如果是同步请求,那么只需提供回调处理函数,等待响应事件驱动。

每个服务启动后会初始化一条AMQP连接(基于TCP),该连接由3个Channel复用:一个Channel负责生产消息,一个Channel负责从TYPE(REPO/CTRL/WEB等)类型的队列消费消息,一个Channel负责从TYPE.${HOSTNAME}类型的队列消费消息。从队列的角度来看,一个TYPE.${HOSTNAME}类型队列只有一个消费者,一个TYPE类型队列可能有多个消费者。

这样的设计满足以下四类需求:

1. 点对点(P2P)或请求有状态服务:消息的route key设置为TYPE.${HOSTNAME}。比如host1上的WEB服务需要向host2上的REPO服务发送同步请求,只需将消息的route key设置为REPO.host2即可。REPO服务处理完请求后,将响应消息的route key设置为WEB.host1,发送回消息服务器。再比如REPO服务是有状态服务,伸缩性不好做,需要WEB服务做Presharding或者一致性哈希来决定调用哪个REPO服务,也跟上面一样,WEB服务根据计算好的值填充REPO.${HOSTNAME},进行点对点模式的消息通信。

2. 请求无状态服务:如果服务提供方是无状态服务,服务调用方不关心由哪个服务进行响应,那么只需将消息的route key设置为TYPE。比如CTRL是无状态服务,host1上的WEB服务只需将消息的route key设置为CTRL即可。CTRL队列会以Round-robin的调度算法将消息投递给其中的一个消费者。视WEB服务是否无状态而定,CTRL可以选择将响应消息的route key设置为WEB(假设WEB无状态)或者WEB.host1(假设WEB有状态)。

3. 组播:如果服务调用方需要与某类服务的所有节点通信,可以将消息的route key设置为TYPE.*,Exchange会将消息投递到所有TYPE.${HOSTNAME}队列。比如WEB服务需通知所有CTRL服务更新配置,只需将消息的route key设置为CTRL.*

4. 广播:如果服务调用方需要与所有服务的所有节点通信,也就是说对当前系统内所有节点广播消息,可以将消息的route key设置为*.*

本方案优缺点如下:

优点:
1. 路由策略灵活。
2. 支持负载均衡。
3. 支持高可用部署。
4. 支持消息可靠传输(生产者confirm,消费者ack,消息持久化)。
5. 支持prefetch,流控。

缺点:
1. 存在消息重复投递的可能性。
2. 对于多服务协作的场景支持度有限。比如以下场景:WEB服务发送同步请求给CTRL服务,CTRL本身无法提供该服务,需要调用REPO服务,再将REPO服务的响应结果返回给WEB。这个时候就需要CTRL缓存WEB请求,直至REPO响应。
3. 缺少超时管理,错误处理等。

以上列举缺点需要由业务方考虑解决。

顺便再提供本方案的一个简单SDK:Gear

Gear SDK提供的功能包括:
1. 基础组件(Exchange, Binding, Queue, etc)初始化。
2. 连接复用,断线重连。
3. P2P方式,组播,广播消息发送。
4. 异步消息接收。

--EOF--

『大型网站技术架构:核心原理与案例分析』(四)

『大型网站技术架构:核心原理与案例分析』读书笔记系列:
(一):架构演化、模式、要素
(二):高性能架构
(三):高可用架构
(四):可伸缩架构
(五):可扩展架构
(六):安全性架构


『大型网站技术架构』(四):可伸缩架构

“大型“定义:

  • Facebook: 大量用户及大量访问,10亿用户。
  • 腾讯: 功能复杂,产品众多,1600+种产品。
  • Google:大量服务器,100w台服务器。

一、网站架构的伸缩性设计

  • 不同功能进行物理分离实现伸缩

    单一服务器处理所有服务 -> 数据库从应用服务器分离 -> 缓存从应用服务器分离 -> 静态资源从应用服务器分离

    横向分离(分层后分离)、纵向分离(业务分割后分离)

  • 单一功能通过集群规模实现伸缩

    当一头牛拉不动车的时候,不要去寻找一头更强壮的牛,而是用两头牛来拉车。

    集群伸缩性:应用服务器集群伸缩性、数据服务器集群伸缩性(缓存数据服务器集群和存储数据服务器集群)

二、应用服务器集群的伸缩性设计

负载均衡:实现网站伸缩性,改善网站可用性。

负载均衡类型

1. HTTP重定向负载均衡

通过一台HTTP重定向服务器,返回302实现负载均衡。实践中很少见。

优点:简单
缺点:性能差(2次请求)、伸缩性有限(重定向服务器容易成为瓶颈)、被搜素引擎判为SEO作弊(302请求),

2. DNS域名解析负载均衡

在DNS服务器中配置多个A纪录。大型网站用于进行第一级负载均衡。

优点:支持基于地理位置域名解析,加快用户访问速度。
缺点:DNS多级解析,生效和失效时间久。

3. 反向代理负载均衡

利用反向代理服务器(缓存资源、安全等)进行负载均衡。应用层负载均衡。

优点:反向代理功能和负载均衡功能集成,部署简单。
缺点:反向代理服务器容易成为瓶颈。

4. IP负载均衡

在网络层通过修改请求目标地址进行负载均衡。

  • 负载均衡服务器修改目的IP的同时修改源地址,将数据包源地址设为自身IP。(SNAT)
  • 负载均衡服务器同时作为Real Server的网关服务器。(LVS/NAT模式)

优点:相比应用层负载均衡(反向代理)有更好的性能。
缺点:进出流量走负载均衡服务器,依然存在瓶颈。

5. 数据链路层负载均衡

在通信协议的数据链路层修改mac地址进行负载均衡(LVS/DR)。数据三角传输模式,流量从用户->负载均衡服务器->Real Server->用户。

目前使用最广泛。

负载均衡算法

  • 轮询(Round Robin, RR)
  • 加权轮询(Weighted Round Robin, WRR)
  • 随机(Random)
  • 最少连接(Least Connections)
  • 源地址散列(Source Hashing)

三、分布式缓存集群的伸缩性设计

目标:必须让新上线/下线的缓存服务器对整个分布式缓存集群影响最小,也就是说经过调整使整个缓存服务器集群中已经缓存的数据尽可能还被访问到。

一致性Hash:解决集群扩减容时过多节点缓存失效问题。
使用虚拟节点的一致性Hash环:避免集群扩减容造成的节点负载不均问题,通过增加一层虚拟节点与物理节点的映射来使节点增删带来的影响平均到所有节点。

四、数据存储服务器集群的伸缩性设计

数据存储服务器必须保证数据可靠存储、可用性、正确性,伸缩性设计原则与缓存不同。

1. 关系数据库集群的伸缩性设计

  • 主从读写分离
  • 业务分隔、数据分库。跨库不能Join操作、避免事务
  • 数据分片:通过分布式关系数据库访问代理,比如Amoeba、Cobar(Ali)。伸缩性:一致性Hash + 数据迁移

2. NoSQL数据库的伸缩性设计

HBase伸缩性: 依赖可分裂的HRegion及可伸缩的分布式文件系统HDFS实现。

--EOF--

『大型网站技术架构:核心原理与案例分析』(三)

『大型网站技术架构:核心原理与案例分析』读书笔记系列:
(一):架构演化、模式、要素
(二):高性能架构
(三):高可用架构
(四):可伸缩架构
(五):可扩展架构
(六):安全性架构


『大型网站技术架构』(三):高可用架构

一、可用性度量与考核

度量

衡量方式:多少个9。

网站不可用时间(故障时间) = 故障修复时间点 - 故障发现(报告)时间点

网站年度可用性指标 = (1-网站不可用时间/年度总时间) * 100%

  • 2个9:基本可用,年度不可用时间小于88小时
  • 3个9:较高可用,年度不可用时间小于9小时
  • 4个9:具有自动恢复能力的高可用,年度不可用时间小于53分钟
  • 5个9:极高可用,年度不可用时间小于5分钟

考核

故障分:对网站故障进行分类加权计算故障责任。故障分 = 故障时间(分钟) * 故障权重。

  • 事故级故障(100): 严重故障,网站整体不可用
  • A类故障(20): 网站访问不顺畅或核心功能不可用
  • B类故障(5): 非核心功能不可用,或核心功能少数用户不可用
  • C类故障(1): 以上故障以外的其他故障

二、高可用网站架构

高可用架构设计不仅要考虑软硬件故障,还要考虑网站升级发布引起的不可用。

主要手段: 数据和服务的冗余备份和失效转移。

典型分层模型:

  • 应用层: 负责具体业务逻辑处理。思路:负载均衡设备
  • 服务层: 负责提供可复用的服务。思路:分布式服务调用框架,客户端软件负载均衡
  • 数据层: 负责数据的存储与访问。思路:数据冗余

三、高可用应用

主要特点:无状态

无状态应用是指应用服务器不保存业务的上下文信息,仅根据每次请求提交的数据进行相应业务逻辑处理,多个服务实例完全对等。

1. 通过负载均衡进行无状态服务的失效转移

  • 应用访问量小也使用负载均衡技术构建一个小型集群保证高可用
  • 平滑升级

2. 应用服务器集群的Session管理

  • Session复制:Session在集群中同步,大集群不适用。
  • Session绑定:Session Sticky,会话黏滞。Hash(Source IP)、Hash(Cookie),无法实现高可用。
  • 利用Cookie记录Session:服务器端不记录Session,每次从Cookie中解,服务器可线性伸缩。缺点:大小受限、增大传输数据量、用户关闭Cookie时不可用。
  • Session服务器:独立部署Session服务器集群,通过 分布式缓存+数据库 实现。可用性高、伸缩性好、性能不错。

四、高可用服务

主要特点: 无状态

  • 分级管理:核心业务隔离部署、用更好更稳定的硬件。
  • 超时设置
  • 异步调用
  • 服务降级:拒绝服务(拒绝低优先级任务、随机拒绝)、关闭功能。
  • 幂等性设计:允许重复调用。

五、高可用数据

主要手段:数据备份和失效转移机制

含义:

  • 数据持久性
  • 数据可访问性
  • 数据一致性:

    • 1) 数据强一致:最强,各副本数据在物理存储中一致;
    • 2) 数据用户一致: 较强,在物理存储中可能不一致,但是通过纠错和校验机制,可以返回一个一致且正确地的数据给用户;
    • 3) 数据最终一致,较弱,用户得到的数据可能不一致,但是最终会达到一致。

CAP理论: 数据一致性(Consistency)、数据可用性(Availibility)、分区容忍性(Partition Tolerance)。大型网站中,通常会选择强化分布式存储系统的可用性(A)和伸缩性(P),在某种程度上放弃一致性(C)。

缓存服务讨论,两种观点:

  • 缓存需要高可用:缓存承担了业务中绝大多数数据读取访问,缓存服务失效会影响整个网站可用性。
  • 缓存不需要高可用:缓存服务不是数据存储服务,缓存失效引起服务器负载太大的问题应用通过其他手段解决。比如扩大缓存集群规模,单台缓存服务器失效带来影响较小。

数据备份

  • 冷备份:定期将数据备份到某种存储介质(磁带、光盘……)并物理存档保管。简单、廉价、技术难度低,无法保证数据最终一致,可能丢数据,无法保证数据可用性。
  • 热备份:

    • 异步热备:Master-Slave架构。写Master,返回操作成功响应,再由Master同步到Slave,这个过程可能失败。例子:MySQL半同步复制、读写分离等。
    • 同步热备:存储服务器互相间对等。数据多副本写入同步完成。

失效转移

  • 失效确认:1. 心跳检查。2. 应用程序访问失败报告。
  • 访问转移:数据读写重新路由
  • 数据恢复:恢复副本数量

六、高可用网站软件质量保证

  • 网站发布:平滑升级,从LB下线->更新程序->挂回LB
  • 自动化测试
  • 预发布验证:预发布服务器与线上机器的唯一不同就是没有配置在负载均衡上,外部用户无法访问。避免验证过程污染生产环境数据。
  • 代码控制:1. 主干开发、分支发布。2. 分支开发、主干发布。
  • 自动化发布:周四发布,火车发布模型(基于规则驱动的流程,一级级评审)
  • 灰度发布:分批次升级,等一批稳定运行后再上下一批。

七、网站运行监控

准则:“不允许没有监控的系统上线”

监控数据采集

  • 用户行为日志收集:服务器端日志、客户端日志
  • 服务器性能监控
  • 运行数据报告:业务场景相关

监控管理

  • 系统报警
  • 失效转移
  • 自动优雅降级

--EOF--

『大型网站技术架构:核心原理与案例分析』(一)

『大型网站技术架构:核心原理与案例分析』读书笔记系列:
(一):架构演化、模式、要素
(二):高性能架构
(三):高可用架构
(四):可伸缩架构
(五):可扩展架构
(六):安全性架构


『大型网站技术架构』(一):架构演化、模式、要素

一、大型网站架构演化

1. 架构演化

  • 应用程序、数据库、文件服务器部署在同一台机器
  • 应用程序、数据库、文件服务器独立部署
  • 使用缓存,加速数据读取
  • 应用程序集群化,负载均衡
  • 数据库读写分离
  • CDN加速
  • 分布式文件系统、分布式数据库
  • NoSQL + 搜索引擎
  • 业务拆分,数据库共享
  • 服务化,业务分库

2. 演化价值观

  • 逐步发展、灵活应对
  • 业务驱动技术发展:业务成就技术、事业成就人

3. 误区

  • 追随大公司解决方案:"taobao/facebook就是这么搞的"
  • 为了技术而技术:一味追求新技术,误入崎岖道路
  • 企图用技术解决所有问题:考虑通过业务手段解决业务问题(12306排队机制、调整放票时段,不搞秒杀)

二、大型网站架构模式

面临高并发访问、海量数据处理、高可靠运行等挑战,总结出许多解决方案和可重复的模式,以实现高性能、高可用、易伸缩、可扩展、安全等各种技术架构目标。

1. 分层

定义:将系统在横向维度上切分成几个部分,每个部分负责一部分相对比较单一的职责,然后通过上层对下层的依赖和调用组成一个完整系统。

举例: OSI 7层协议,计算机架构(硬件、OS、应用软件),网站系统(应用层、服务层、数据层), MVC。

注意事项:

  • 合理规划层次边界和接口
  • 避免跨层调用
  • 避免逆向调用

2. 分割

定义: 在纵向对软件进行切分。

举例:网站业务拆分(购物、论坛、搜索、广告等),购物业务拆分(酒店业务、3C业务、小商品业务等)

3. 分布式

目标: 分层和分割后的模块拥有更小的粒度,目的是分布式部署,便于使用更多资源(CPU、内存、存储等),提供更多服务。

注意事项:

  • 服务间调用通过网络,造成性能影响。
  • 服务器越多,宕机概率越大,服务可用性降低。
  • 数据一致性问题。
  • 依赖复杂。
  • 开发管理苦难。
  • 切莫为了分布式而分布式。

分布式方案:

  • 分布式应用和服务:分层和分割后的应用和服务模块分布式部署。
  • 分布式静态资源:静态资源(JS、CSS、图片等)分布式部署,动静分离。
  • 分布式数据和存储:海量数据存储,分布式NoSQL。
  • 分布式计算:实时计算,搜索引擎、MapReduce分布式计算框架。
  • 分布式配置:服务器配置实时更新。
  • 分布式锁:分布式环境下的并发和协同。
  • 分布式文件系统:云存储。

4. 集群

定义: 多台服务器部署相同应用或模块,通过负载均衡对外提供服务。提高并发性和可用性。

5. 缓存

定义:将数据存放在距离计算最近的位置以加快处理速度。

举例:

  • CDN: 缓存静态资源。
  • 反向代理:缓存静态资源。
  • 本地缓存: 数据缓存在本机内存,减少访问数据库。
  • 分布式缓存: 可以缓存更多地数据。

缓存使用前提:

  • 数据访问热点不均衡。
  • 数据有一段有效期,不会很快过期,否则很容易脏读。

6. 异步

目标:降低系统间耦合性。

类型:

  • 单一服务:多线程共享内存队列。
  • 分布式系统:分布式消息队列。

异步消息队列特性:

  • 提高系统可用性:消费者故障不影响生产者继续处理业务请求。
  • 加快网站响应速度: 生产者处理业务请求后将数据写入队列,无需等待消费者处理完毕,减少响应延迟。
  • 消除并发访问高峰:突发事件、促销活动、热点事件。

7. 冗余

目标:数据冗余备份,当某台服务器宕机,可以将其上的服务和数据访问转移到其他机器,提高服务可用性。

类型:

  • 冷备份: 定期备份,存档保存。
  • 热备份: 主从分离,主从同步。
  • 灾备数据中心:实时同步网站程序和数据到多个数据中心,抵抗地震、海啸等不可抗力因素。

8. 自动化

目标:一切自动化。

类型:

  • 自动化代码管理:针对开发,代码版本控制、代码分支创建合并自动化。
  • 自动化测试:自动部署到测试环境,运行测试用例,发送测试报告,反馈测试结果。
  • 自动化安全检查:代码静态安全扫描,安全攻击测试。
  • 自动化部署:自动化部署到生产环境。
  • 自动化监控
  • 自动化报警
  • 自动化失效转移:将失效服务器从集群中隔离。
  • 自动化失效恢复:故障消除后重启服务,同步数据。
  • 自动化降级:遇到访问高峰时拒绝部分请求及关闭部分不重要服务,将系统负载降至安全水平。
  • 自动化分配资源:将空闲资源分配给重要服务,扩大其部署规模。

9. 安全

安全手段:

  • 用密码和手机校验码进行身份认证。
  • 加密通信。
  • 验证码机制防止机器人。
  • 进行编码转换等防止XSS、SQL注入。
  • 垃圾信息、敏感信息过滤。
  • 交易信息进行风险控制。

三、大型网站架构要素

架构:最高层次的规划,难以改变的决定。

1. 性能

手段

  • 浏览器端: 浏览器缓存、页面压缩、页面布局、减少Cookie传输、CDN等;
  • 应用服务器端:本地缓存、分布式缓存、消息队列、集群,多线程、改善内存管理;
  • 数据库服务器端:索引、缓存、SQL优化,NoSQL(优化数据模型、存储结构、伸缩性);

指标

  • 响应时间
  • TPS
  • 系统性能计数器

2. 可用性

手段:冗余

  • 应用服务器: 负载均衡 + 流量切换;
  • 存储服务器: 实时备份 + 访问转移 + 数据恢复

3. 伸缩性

定义: 通过不断向集群中加入服务器的手段来缓解不断上升的用户并发访问压力和不断增长的数据存储需求。

  • 应用服务器: 无状态;
  • 缓存服务器: 改进缓存路由算法。注意严重依赖缓存场景下,缓存失效导致整个网站崩溃;
  • 关系数据库: 需在数据库外实现伸缩性,通过路由分区等手段将多个数据库组成集群;
  • NoSQL:先天为海量数据而生,线性伸缩。

4. 扩展性

标准:

  • 网站快速发展,功能不断扩展,系统架构能够使其快速响应需求变化;
  • 新增业务,对现有产品透明无影响;
  • 产品间很少耦合。

手段:

  • 事件驱动架构:通过消息队列实现,用户请求和其他业务事件是生产者,消息处理者是消费者,可以透明增加新的消息生产者任务或者消费者任务;
  • 分布式服务:将业务和可复用服务分离开来,通过分布式服务框架调用。新增产品可以通过调用可复用的服务实现自身业务逻辑。可复用服务升级变更时,通过多版本方式对应用实现透明升级。

5. 安全性

标准: 针对现存和潜在的攻击和窃密手段,是否有可靠的应对策略。

--EOF--