date

comments

categories

tags

permalink

title

2020/3/19

true

5.12

软件架构流程之四 架构详细设计

软件架构

详细设计简介

所谓详细设计就是将方案设计的关键技术细节给确定下来。

  • 假如我们确定使用 Elasticsearch 来做全文搜索,那么就需要确定 Elasticsearch 的索引是按照业务划分,还是一个大索引就可以了;副本数量是 2 个、3 个还是 4 个,集群节点数量是 3 个还是 6 个等。
  • 假如我们确定使用 MySQL 分库分表,那么就需要确定哪些表要分库分表,按照什么维度来分库分表,分库分表后联合查询怎么处理等。
  • 假如我们确定引入 Nginx 来做负载均衡,那么 Nginx 的主备怎么做,Nginx 的负载均衡策略用哪个(权重分配?轮询?ip_hash?)等。

可以看到,详细设计方案里面其实也有一些技术点和备选方案类似。例如,Nginx 的负载均衡策略,备选有轮询、权重分配、ip_hash、fair、url_hash 五个,具体选哪个呢?看起来和备选方案阶段面临的问题类似,但实际上这里的技术方案选择是很轻量级的,我们无须像备选方案阶段那样操作,而只需要简单根据这些技术的适用场景选择就可以了。

例如,Nginx 的负载均衡策略,简单按照下面的规则选择就可以了。

  • 轮询(默认)每个请求按时间顺序逐一分配到不同的后端服务器,后端服务器分配的请求数基本一致,如果后端服务器“down 掉”,能自动剔除。
  • 加权轮询 根据权重来进行轮询,权重高的服务器分配的请求更多,主要适应于后端服务器性能不均的情况,如新老服务器混用。
  • ip_hash每个请求按访问 IP 的 hash 结果分配,这样每个访客固定访问一个后端服务器,主要用于解决 session 的问题,如购物车类的应用。
  • fair按后端服务器的响应时间来分配请求,响应时间短的优先分配,能够最大化地平衡各后端服务器的压力,可以适用于后端服务器性能不均衡的情况,也可以防止某台后端服务器性能不足的情况下还继续接收同样多的请求从而造成雪崩效应。
  • url_hash按访问 URL 的 hash 结果来分配请求,每个 URL 定向到同一个后端服务器,适用于后端服务器能够将 URL 的响应结果缓存的情况。

这几个策略的使用场景区别还是比较明显的,根据实际的业务需求,挑选一个即可。例如,一个电商平台的架构,由于和session强相关,因此使用ip_hash策略是比较合适的。

详细设计方案阶段有可能遇到一种极端情况,就是在详细设计阶段发现备选方案不可行,一般主要原因是在设计备选方案的时候,漏掉了关键技术点或者关键的技术属性。比如曾经有一个项目,在备选阶段确定是可行的,但在详细设计阶段,发现由于细节点太多,方案非常庞大,整个项目可能要开发长达1年的时间,最后只能飞废弃原来的备选方案,重新调整项目目标,计划和方案。这就是典型的在备选方案评估的时候忽略了开发周期这个质量属性。

如何避免上述情况的出现呢?有以下几种方式

  • 架构师不但要进行备选方案设计和选型,还需要对备选方案的关键细节有比较深入的理解。比如,架构师选择了Elasticsearch作为全文检索的解决方案,前提是架构师自己必须对Elasticsearch有非常深入的理解。不能是道听途说Elasticsearch很牛,所以就选择了它。
  • 通过分步骤、分阶段、分系统等方式,尽量降低方案复杂度。方案本身的复杂度越高,某个细节被推翻的可能性就越高,降低复杂性,可以减少这种风险。
  • 如果方案本身就很复杂,那就采用设计团队的方式来进行设计,博采众长,汇集大家的智慧和经验,防止只有1-2个架构师可能出现的思维忙点和经验盲区。

详细设计方案实战

之前在“前浪微博”消息队列的架构设计挑选了备选方案 2 作为最终方案,但备选方案设计阶段的方案粒度还比较粗,无法真正指导开发人员进行后续的设计和开发,因此需要在备选方案的基础上进一步细化。

下面列出了一些备选方案2典型的需要细化的点供参考。

细化设计点1:数据库表如何设计

  • 数据库设计两类表,一类是日志表,用于消息写入时快速存储到 MySQL 中;另一类是消息表,每个消息队列一张表。
  • 业务系统发布消息时,首先写入到日志表,日志表写入成功就代表消息写入成功;后台线程再从日志表中读取消息写入记录,将消息内容写入到消息表中。
  • 业务系统读取消息时,从消息表中读取。
  • 日志表表名为 MQ_LOG,包含的字段:日志 ID、发布者信息、发布时间、队列名称、消息内容。
  • 消息表表名就是队列名称,包含的字段:消息 ID(递增生成)、消息内容、消息发布时间、消息发布者。
  • 日志表需要及时清除已经写入消息表的日志数据,消息表最多保存 30 天的消息数据。

细化设计点2:数据如何复制

直接使用mysql的主从复制即可,只需要复制存储表,不复制日志表。

细化设计点3: 主备服务器如何倒换

采用 ZooKeeper 来做主备决策,主备服务器都连接到 ZooKeeper 建立自己的节点,主服务器的路径规则为“/MQ/server/ 分区编号 /master”,备机为“/MQ/server/ 分区编号 /slave”,节点类型为 EPHEMERAL。

备机监听主机的节点消息,当发现主服务器节点断连后,备服务器修改自己的状态,对外提供消息读取服务。

细化设计点4:业务服务器如何写入消息

  • 消息队列系统有两个角色:生产者和消费者,每个角色都有唯一的名称。
  • 消息队列系统提供 SDK 供各业务系统调用,SDK 从配置中读取所有消息队列系统的服务器信息,SDK 采取轮询算法发起消息写入请求给主服务器。如果某个主服务器无响应或者返回错误,SDK 将发起请求发送到下一台服务器。

细化设计点6:业务服务器和消息队列服务器之间的通信协议如何设计

考虑到消息队列系统后序可能会对接多种编程语言的系统,为了提升兼容性,传输使用TCP协议,数据格式为ProtoolBuffer.

在实际的项目中,还有其他的一些细节需要处理,因此这还不算是一个完整的设计方案,但这已经可以说明细化方案该如何去做了。