从零开始学架构二 架构设计流程
识别复杂度
架构的复杂度主要来源于“高性能”“高可用”“可扩展”等几个方面
- “高性能”主要从软件系统未来的TPS、响应时间、服务器资源利用率等客观指标,也可以从用户的主观感受方面去考虑。
- “可用性”主要从服务不中断等质量属性,符合行业政策、国家法规等方面去考虑。
- “扩展性”则主要从功能需求的未来变更幅度等方面去考虑。
常见的性能指标:nginx负载均衡性能是3万左右,mc的读取性能5万左右,kafka号称百万级,zookeeper写入读取2万以上,http请求访问大概在2万左右。
消息队列的复杂性主要体现在这几个方面:高性能消息读取、高可用消息写入、高可用消息存储、高可用消息读取。
设计备选方案
(1)备选方案不要过于详细。备选=阶段解决的是技术选型问题,而不是技术细节。
(2)备选方案的数量以 3~5个为最佳。 (3)备选方案的技术差异要明显。 (4)备选方案不要只局限于已经熟悉的技术。评估和选择备选方案
RocketMQ 和 Kafka 有什么区别?
(1) 适用场景
Kafka适合日志处理;RocketMQ适合业务处理。 公号-Java大后端 2018-05-24 极客时间 hongfenghuoju/7832[2018/8/6 9:13:44] (2) 性能 Kafka单机写入TPS号称在百万条/秒;RocketMQ大约在10万条/秒。Kafka单机性能更高。 (3) 可靠性 RocketMQ支持异步/同步刷盘;异步/同步Replication;Kafka使用异步刷盘方式,异步Replication。RocketMQ所支持的同步方式提升了数据的可靠性。 (4) 实时性 均支持pull长轮询,RocketMQ消息实时性更好 (5) 支持的队列数 Kafka单机超过64个队列/分区,消息发送性能降低严重;RocketMQ单机支持最高5万个队列,性能稳定(这也是适合业务处理的原因之一)详细方案设计
Nginx的负载均衡策略,五个
备选有轮询、权重分配、ip_hash、fair、url_hash
轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,后端服务器分配的请求数基本一致,如果后端服务器“down掉”,能自动剔除。
加权轮询 根据权重来进行轮询,权重高的服务器分配的请求更多,主要适应于后端服务器性能不均的情况,如新老服务器混用。ip_hash 每个请求按访问IP的hash结果分配,这样每个访客固定访问一个后端服务器,主要用于解决session的问题,如购物车类的应用。fair 按后端服务器的响应时间来分配请求,响应时间短的优先分配,能够最大化地平衡各后端服务器的压力,可以适用于后端服务器性能不均衡的情况,也可以防止某台后端服务器性能 不足的情况下还继续接收同样多的请求从而造成雪崩效应。url_hash 按访问URL的hash结果来分配请求,每个URL定向到同一个后端服务器,适用于后端服务器能够将URL的响应结果缓存的情况。1.数据库表如何设计?
数据库设计两类表,一类是日志表,用于消息写入时快速存储到MySQL中;另一类是消息表,每个消息队列一张表。
业务系统发布消息时,首先写入到日志表,日志表写入成功就代表消息写入成功;后台线程再从日志表中读取消息写入记录,将消息内容写入到消息表中。 业务系统读取消息时,从消息表中读取。 日志表表名为MQ_LOG,包含的字段:日志ID、发布者信息、发布时间、队列名称、消息内容。 消息表表名就是队列名称,包含的字段:消息ID(递增生成)、消息内容、消息发布时间、消息发布者。 日志表需要及时清除已经写入消息表的日志数据,消息表最多保存30天的消息数据。2.数据如何复制?
直接采用MySQL主从复制即可,只复制消息存储表,不复制日志表。
3.主备服务器如何倒换?
采用ZooKeeper来做主备决策,主备服务器都连接到ZooKeeper建立自己的节点,主服务器的路径规则为“/MQ/server/分区编号/master”,备机为“/MQ/server/分区编
号/slave”,节点类型为EPHEMERAL。 备机监听主机的节点消息,当发现主服务器节点断连后,备服务器修改自己的状态,对外提供消息读取服务。4.业务服务器如何写入消息?
消息队列系统设计两个角色:生产者和消费者,每个角色都有唯一的名称。
消息队列系统提供SDK供各业务系统调用,SDK从配置中读取所有消息队列系统的服务器信息,SDK采取轮询算法发起消息写入请求给主服务器。如果某个主服务器无响应或者 返回错误,SDK将发起请求发送到下一台服务器。5.业务服务器如何读取消息?
消息队列系统提供SDK供各业务系统调用,SDK从配置中读取所有消息队列系统的服务器信息,轮流向所有服务器发起消息读取请求。
消息队列服务器需要记录每个消费者的消费状态,即当前消费者已经读取到了哪条消息,当收到消息读取请求时,返回下一条未被读取的消息给消费者。6.业务服务器和消息队列服务器之间的通信协议如何设计?
考虑到消息队列系统后续可能会对接多种不同编程语言编写的系统,为了提升兼容性,传输协议用TCP,数据格式为ProtocolBufer
补充:
1、发送端和消费端如何寻址
利用zookeeper做注册中心,把broker的地址注册到zk上,发送端和消费端只要配置注册中心的地址即可获取集群所以broker地址,当有broker下线时,发送端和消费端能及时更新broker地 址。 2、发送端消息重试 当发送消息发生网络异常时(不包括超时异常),可以重新选择下一台broker来重试发送,重试策略可以自定义。 3、消息消费采用pull还是push? 考虑push模式会更复杂,故放弃,采用pull模式,消费端主动去拉,为了达到与push模式相同的低延迟效果,可以采用长轮询的方式,消费端轮询拉取消息费,当有消费可消费时,返回消息, 如果没有可消费的消息,挂起当前线程,直到超时或者有可消费的消息为止。 4、消息重复问题 消息中间件不解决消息重复的问题,有业务系统自己根据业务的唯一id去重。 5、顺序消息 发送端在发生顺序消息时,只发送到相同broker的相同队列,消费端消费时,顺序消息只能由同一个消费端消息。 6、定时消息 发送端指定消息延时多长时间消费,broker端定时扫描定时消息,达到延时时间的消息加入到消费队列。 7、事务消息 发送端分两步,先预发送消息,broker端只记录消息为预发送状态,再执行本地事务,然后再根据本地事务的成功或者失败发送确认消息(回滚还是提交),这步如果发生异常,broker启动定 时任务,把未确认的消息发送给发送端回查事务状态(需要发送端提供回查接口)。Memcache是纯内存缓存,支持基于一致性hash的集群;而Redis同时支持持久化、
支持数据字典、支持主备、支持集群,看起来比Memcache好很多啊