一、分布式系统
大型网站整个系统中有的负责请求处理,有的负责存储,有的负责计算,最终通过相互的协同把用户的请求变成最后的结果返回给浏览器。
分布式的原因:
- 升级单机处理能力的性价比越来越低。
- 单机处理能力存在瓶颈。
- 出于稳定性和可用性的考虑。
分布式系统是有多机组成的系统,可以jin近似看做把单机多进程变为了多机的多进程。
负载均衡:
- 硬件负载均衡设备,所有的请求都要经过这个负载均衡设备来完成请求转发的控制。
软件负载均衡,主要特点是代价低,而且可控性较强,可以相对自由的按照自己的需求去增加负载均衡的策略。不足:第一是会增加网络的开销。第二个不足是这个透明代理处于请求的必经路径上,如果代理出现问题,那么所有的请求都会受到影响。
采用名称服务的直连方式的请求调用。
分布式系统的难点:
- 缺乏全局时钟,在分布式系统中,每个节点都有自己的时钟,在通过相互发送消息进行协调时,如果仍然依赖时序,就会相对难处理。
- 面对故障独立性,在分布式系统中,这个系统的一部分有问题而其他部分正常是经常出现的情况,称为为故障独立性。
处理单点故障,在整个分布式系统中,如果某个角色或功能只有某台单机在支撑,那么这个节点称为单点,其发生故障为单点故障。
处理方案:
1. 给这个单点做好备份,能够在出现问题时进行恢复,并且尽量做到自动恢复,降低恢复需要的时间。
2. 降低单点故障的影响范围。事务的挑战,分布式事务问题相对复杂。
二、大型网站架构演进
大型网站要支撑海量的数据和非常高并发的访问量。
- 单机的网站
- 数据库与应用分离
- 应用服务器集群
- 通过应用服务器集群前增加负载均衡设备解决。
- Session共享问题。解决方案:
- Session Sticky,同一个会话的请求都在同一个Web服务器上处理。缺点:服务器宕机或重启,会话数据丢失,开销较大。
- Session Replication,会话数据同步。,缺点:同步Session数据造成网络带宽的开销。Session数据占用机器内存太大。
- Session数据集中存储,不同Web服务器从同一个地方获取Session。可以用数据库也可以用分布式存储系统。缺点:网络时延和不稳定性。存储Session的机器有问题,会影响应用。
- Cookie Based,通过Cookie来传递Session数据。缺点:Cookie长度限制,安全性,性能影响。
- 总结:一般使用Session Sticky和Session 数据集中存储方案。
数据读写分离
- 数据库作读库,通过读库分担主库上读的压力。写操作要走主库,事务中的读也要走主库。
- 搜索引擎其实是一个读库。
- 加速数据读取-缓存。
- 缓存中存放“热”数据,应用访问缓存,如果数据不存在,则从数据库读取数据后放入缓存。
- 页面缓存,Web服务前端有Apache/Nginx服务器。
考虑的问题是需要有机制去避免局部的热点,并且缓存服务器扩容或者缩容要尽量平滑(一致性Hash是不错的选择)。
- 引入分布式存储系统,分布式文件系统解决小文件和大文件的存储问题;分布式Key-Value系统提供高性能的半结构化的支持;分布式数据库提供一个支持大数据、高并发地数据库系统。
数据库的数据拆分
- 专库专用,数据垂直拆分,把数据库中不同的业务数据拆分到不同的数据库中。需要考虑事务问题。
- 数据水平拆分,把同一个表的数据拆分到两个数据库中。不同的数据库不能直接使用一些数据库的限制来保证主键不重复。同一个业务的数据被拆分到不同的数据库中,因此一些查询需要从两个数据库中取数据。
新挑战
- 拆分应用
- 根据业务的特性把应用拆开。
- 根据功能拆分应用。
- 走服务化的路,应用分为三层,Web系统,用于完成不同的业务功能;服务中心,提供不同的业务服务;数据库。
- 消息中间件,分布式系统中完成消息的发送和接收的基础软件。好处:异步和解耦。
- 拆分应用
三、Java中间件
中间件为软件应用提供了操作系统所提供的服务之外的服务,起的作用是桥梁作用,是应用与应用之间的桥梁,也是应用与服务之间的桥梁。
- 远程过程调用和对象访问中间件,主要解决分布式环境下应用的相互访问问题。是支持应用服务化的基础。
- 消息中间件,解决应用之间的消息传递、解耦、异步的问题。
- 数据访问中间件,主要解决应用访问数据库的共性问题的组件。
四、服务框架
服务化方案使得系统看起来更立体了,应用之间有了直接的访问。
1、服务框架的设计与实现
服务框架应该是既包含调用端逻辑又包含服务端逻辑的一个实现,也就是说虽然我们在每次的请求中是分了调用端和服务端角色,但是从应用来看都是可以提供和调用服务的。构造请求数据包,把对象变为二进制数据,也就是序列化。
基础属性:
- interfaceName,接口名称,进行远程通信时ConsumerBean必须知道被调用的接口是哪一个,然后才能生成这个接口的代理,以供本地调用,这个一个必备属性。
- version,版本号,接口是存在变化的可能性,通过版本号进行区分隔离。
- group,分组,对同一个接口的远程服务有很多机器,可以把这些远程服务的机器归组,这样可以将不同调用者对于同一服务的调用进行隔离。
服务框架的部署方式:
- 一种方案,把服务框架作为应用的一个依赖包并与应用一起打包。
- 另一种方案,把服务框架作为容器的一部分。
使用服务框架是为了把本地对象之间的方法调用变为远程的过程调用(RPC)。
把地址缓存在调用者本地,当有变化时从服务注册查找中心发起通知,告诉调用者可用的服务提供者列表的变化。
负载均衡的实现方式比较常见的有:随机、轮询、权重。
从客户端的角度,控制同一个集群中不同服务的路由并进行请求的隔离是一种可行方案。
多种异步服务调用方式
- Oneway是一种只管发送请求而不关心结果的方式。不保证可靠送达的通知。
- Callback,请求发送后会继续执行自己的操作,等对方有响应时进行一个回调。
- Futrue,先把Future放入队列,然后把数据放入队列,接着就在线程中进行处理,等到请求线程的其他工作处理结束后,就通过Future来获取通信结果并直接控制超时。
- 可靠异步,要保证异步请求能够在远程被执行,一般是通过消息中间件来完成这个保证的。
服务端的通信部分要采用NIO的方式来实现,接收到请求后,通过协议解析及反序列化。 服务提供端的工作现场是一个线程池,路由到本地的服务请求会被放入这个线程池执行。 这个服务框架作为一个产品,可以让集中在单机内部的调用变为远程的服务化。
2、优化
- 服务的拆分,要拆分的服务是需要为多方提供公共功能的。
- 服务的粒度
- 优雅和实用的平衡
- 分布式环境的请求合并
可以将服务治理分为管理服务和查看服务。服务框架为应用提供了从集中式系统转向分布式系统的基础支持。
五、数据访问层
数据库拆分,加压的思路:
- 优化应用,看是否有不必要的压力给了数据库(应用优化)
- 看有没有其他办法可以降低对数据库的压力,例如缓存等。
- 把数据库的数据和访问分到多台数据库上,分开支持。
垂直拆分带来的影响:
- 数据到了多机后,原来的单机通过事务来进行的处理逻辑会受到很大的影响。
- 一些Join操作会变得比较困难,因为数据可能已经在两个数据库中了。
- 靠外键进行约束的场景受到影响。
水平拆分带来的影响:
- ACID可能被打破。
- 有可能有Join操作被影响。
- 靠外键去进行约束的场景会有影响。
- 依赖单库的自增序列生成唯一ID会受影响。
- 针对单个逻辑意义上的表的查询要跨库了。
分布式事务
在X/Open DTP模型中定义了三个组件:
- Application Program(AP),即应用程序,定义了事务边界,并定义了构成该事务的应用程序的特定操作。
- Resource Manager(RM),资源管理器,应用程序通过资源管理器对资源进行控制,资源必须实现XA定义的接口。资源管理器提供了存储共享资源的支持。
- Transaction Manager(TM),事务管理器,负责协调和管理事务,提供给AP应用程序编程接口并管理资源管理器。
DTP概念:
- 事务:一个事务是一个完整的工作单元,由多个独立的计算任务组成,多个任务在逻辑上是原子的。
- 全局事务:一次性操作多个资源管理器的事务就是全局事务。
- 分支事务:在全局事务中,每一个资源管理器有自己独立的任务,这些任务的集合是这个资源管理器的分支任务。
- 控制线程,用来表示一个工作线程,主要是关联AP、TM和RM三者的线程,也就是事务上下文环境。用来标识全局事务和分支事务关系的线程。
在分布式系统中,在提交事务之前增加了准备的阶段,称为两阶段提交。
CAP:
- Consistency:即所有的节点在同一时间读到同样的数据,数据上的一致性。
- Availability:保证无论是成功还是失败,每个请求都能够收到一个反馈。数据的可用性,系统一定要有响应。
- Partition-Tolerance:即便系统有部分问题或者有消息的丢失,但是系统仍能够继续运行,称为分区容忍性。
分布式系统不能同时满足上面三项。
BASE涵义:
- Basically Available:基本可用,允许分区失败。
- Soft state:软状态,接受一段时间的状态不同步。
- Eventually consistent:最终一致,保证最终数据的状态是一致的。
如果一定要引入分布式1事务,可以考虑最终一致的方法,通过补偿机制不断重试,让之前因为异常而没有进行到底的操作继续进行,而不是回滚。
多机的数据查询
- 跨库Join
- 在应用层把原来数据库的Join操作分成多次的数据库操作。
- 数据冗余,也就是对一些常用信息进行冗余。
- 借助外部系统(例如搜索引擎)解决一些跨库的问题。
- 外键约束,比较难解决。
读写分离
- 数据结构相同,多从库对应一主库,通过MySQL的Replication可以解决复制的问题。应用通过数据层访问数据库,通过消息系统就数据库的更新送出消息通知,数据同步服务器获得消息通知后进行数据的复制工作。
- 主/备库分库方式不同的数据复制,非对称复制是指源数据和目标数据不是镜像关系,也指数据库和目标数据库是不同的实现。
六、消息中间件
消息中间件带来了异步的特性,对系统进行了解耦,对大型分布式系统来说非常重要。
消息发送一致性
- 消息发送一致性,产生消息的业务动作与消息发送的一致。
JMS重要要素
- Destination,消息所走通道的目标定义,也就是用来定义消息从发送端发出后要走的通道,而不是最终接收方。
- ConnectionFactory,创建连接的对象。
- Connection,连接接口,所负责的重要工作是创建Session。
- Session,会话接口,消息的发送者、接收者以及消息对象本身。
- MessageConsumer,消息的消费者,也就是订阅消息并处理消息的对象。
- MessageProducer,消息的生产者,就是用来发送消息的对象。
- XXXMessage,是指各种类型的消息对象,包括BytesMessage、MapMessage、ObjectMessage、StreamMessage和TextMessage 5种。
JMS消息模型中,有Queue和Topic之分。事务的控制是在Session层面上的,而Session是通过Connection创建的,Connection是通过ConnectionFactory创建的。
消息一致性
业务处理结果有失败、成功、等待三种,消息中间件根据这个等待结果,更新消息状态。
定时重复这个反向流程,重复查询。
发送消息的正向流程和检查业务操作结果的反向流程合起来,就是解决业务操作与发送消息一致性的方案。
除了发送一致性的消息之外,也应该提供一个传统发送消息的接口,也就是不支持发送一致性的发送接口。
消息模型
如果Queue里面的消息被一个应用处理了,那么连接到JMS Queue上的另一个应用是收不到这个消息的。消息从发送端发送出来是不能确定会被哪个应用消费的,但是可以明确是只有一个应用会去消费这条消息。
JMS Topic,接收消息的应用可以独立收到所有到达Topic消息的。也被称为发布/订阅方式。
非持久订阅,消息接收者和消息中间件之间的消息订阅的关系的存续,与消息接收者自身是否处于运行状态有直接关系。持久订阅,消息订阅关系一旦建立,除非应用显示的取消订阅关系,否则这个订阅关系将一直存在。
消息存储
1. 基于文件的消息存储
2. 采用数据库作为消息存储,考虑采用宽表、冗余数据的方式实现。
3. 基于双机内存的消息存储,采用双机的内存来保证数据的可靠。
消息系统的扩容
1. 通过软负载中心完成。
2. 消息存储的扩容
消息重复
应对消息重复的办法是,使消息接收端的处理是一个幂等操作。
JMS的消息确认方式:
- AUTO_ACKNOWLEDGE,自动确认。
- CLIENT_ACKNOWLEDGE,把控制权完全交给接收消息的客户端应用。客户端确认消息处理成功,进行接收确认。
- DUPS_ OK_ACKNOWLEDGE,消息接收方的消息处理函数执行结束后进行确认。
其他属性
- 消息优先级
- 消息顺序和分级订阅
- 自定义属性
- 局部顺序
七、软负载中心与集中配置管理
服务注册查找中心是用于定位提供服务的机器地址,这个服务注册查找中心就可以用软负载中心来实现。
软负载中心基础职责:
- 聚合地址信息。
- 生命周期感知,对服务上下线自动感知,更新服务地址数据。
软负载数据分组,通过数据标识(dataId)和分组(group)来唯一确定数据。
八、构建大型网站的其他要素
- 加速静态内容访问速度。CDN的作用是把用户需要的内容分发到离用户近的地方,能够就近获取所需内容。
- 存储支持
- 分布式文件系统,存储图片、大文件。
- NoSQL
- 缓存
- 搜索系统
- 倒排索引,把原来作为值得内容拆分为索引的Key,而原来用作索引的Key则变成了值。对索引内容进行分词。
- 查询预处理
- 数据计算支撑
- 离线计算是业务产生的数据离开生产环境后进行的计算。把业务数据从在线存储移动到离线存储中,然后进行数据处理。会有较大延迟。
- 在线计算,比较常见的是流式计算。