【原创】modb 功能设计之“多级 modb 之间的同步”

      modb 在最初设计中采用的是两级模型,但很快就遇到了多级同步的需求。解决多级同步问题首先需要引入多级路由 key ,即将原来两级模型中的单个 key 值扩展为 key 的 array ,具体变化如下: 

两级使用的数据格式 
1
2
3
4
5
6
7
8
{
    "src" : "172.16.80.111",
    "keys" : "key_1",
    "app" : "A",
    "state" : "transfer"
    "sql" : "insert into users values(?,?,?)";
    "sql-args" : [1, 2, "abc"]
}
多级使用的数据格式 
1
2
3
4
5
6
7
{
    "src" : "172.16.80.111",
    "keys" : ["key_1","key_2"]
    "app" : "A",
    "sql" : "insert into users values(?,?,?)";
    "sql-args" : [1, 2, "abc"]
}
而在 modb 的业务逻辑处理中也需要增加相应的处理,具体如下: 

      假设处理的是三级数据同步情况,分别为 modb-A 、modb-B 和 modb-C 三级平台域(分别位于三级服务域中),且 A 是 B 的上级,B 是 C 的上级。A 级平台域中某业务对数据进行了变更,然后需要将此变更信息同步到 B 和 C 级平台域。 
【原创】modb 功能设计之“多级 modb 之间的同步” 

通过数据操作入口进行数据变更后,将发送如下消息给 modb-A: 
1
2
3
4
5
6
7
{
    "src" : "172.16.80.111",
    "keys" : ["key_B","key_C"]
    "app" : "A",
    "sql" : "insert into users values(?,?,?)";
    "sql-args" : [1, 2, "abc"]
}
modb-A 接收到此消息后(此时 A 级平台域的数据库已经发生了变更),并根据配置将 
  • 向同在 A 级的其他平台域进行消息转发;
  • 根据 key 值进行对下级的路由转发;
在进行消息转发前,modb-A 还会对消息内容做如下修改: 
1
2
3
4
5
6
7
{
    "src" : "172.16.80.111",
    "keys" : ["key_C"]
    "app" : "A",
    "sql" : "insert into users values(?,?,?)";
    "sql-args" : [1, 2, "abc"]
}
即从 keys 数组中移除 key_B 。 

      modb-B 在接收到上述消息后,将从中提取出 sql 内容进行执行,并对 keys 数组内容进行检查,看是否还有其他 key 的存在,若有,则表明需要继续进行向下级的路由转发。         
      modb-B 重新获取到该消息后(此时 B 级平台域的数据库已经得到了更新),首先检测 keys 数组中是否还有其他 key 存在,若有,则在进行转发前会对消息内容做如下修改: 
      (这里 modb-B 先后两次作为 consumer 收到 rabbitmq 消息是因为分别订阅了远端和本地的 queue 所致,好处是处理起来模型统一    
1
2
3
4
5
6
7
{
    "src" : "172.16.80.111",
    "keys" : []
    "app" : "A",
    "sql" : "insert into users values(?,?,?)";
    "sql-args" : [1, 2, "abc"]
}
即从 keys 数组中移除 key_C 。 

      modb-C 接收到上述消息后,将从中提取 sql 内容进行执行,并对 keys 数组内容进行检查,看是否还有其他 key 的存在,若没有,则表明已不再需要继续进行路由。 
      modb-C 重新获取到该消息后(此时 C 级平台域的数据库已经得到了更新),首先检测 keys 数组中是否还有其他 key 存在,若没有,则只需要对相同服务域中的平台域进行广播,而无需进行下一级转发。 

      上述模型的优点是简单,仅通过配置就可以完成多级关系的数据同步功能;缺点是存在单点,不具有动态变更上下级关系的能力,需要通过其他方式进行后续配置。 

可能遇到的异常情况: 
  • rabbitmq 服务器异常 -- 业务更新本地数据库成功后,将发送通知消息到 rabbitmq 服务器,需要业务端能够判断出“服务器不可用”状态,并触发重传等操作;
  • modb 进程异常 -- 业务更新本地数据库成功后,发送通知消息到 rabbitmq 服务器,若要求在 modb 进程异常的情况下消息不丢失,则需要 rabbitmq 启用对消息的持久化功能,并且最好取消掉 exclusive 和 auto_delete 等相关属性的设置,可能还需要处理当 queue 不存在时消息被 blackholed 的情况;
  • Atlas 异常 -- 若在业务尚未成功更新本地数据库前发生 Atlas 异常,则要求业务能够告之当前情况下“无法建立数据库连接”;若在业务成功更新本地数据库后发生 Atlas 异常,此时向外部 modb 同步当前更新没有问题,但无法同步外部 modb 对本地数据库的更新。(可能的一种解决办法:令 modb 支持缓存功能,当发现 Atlas 异常时则将更新消息放入缓存队列,并在每次接收到新的更新消息时触发一次针对 Atlas 是否恢复的检测。若 Atlas 已经恢复,则将缓存队列中的内容顺序执行

      上述问题并未针对“异常情况下对 数据库进行操作时可能遇到的问题” 进行深入的说明,具体问题一定会更加复杂。一点体会:若想真正的把数据库同步功能做好,最起码需要深入理解数据一致性的相关理论,若有一个成熟的框架可以使用就更加完美了。 
上一篇:陈正冲老师讲c语言之内存的申请malloc() 和释放free()


下一篇:记一次redis连接超时问题