最近完成了群组模块的简单的CRUD的编写,在编写过程中使用了Mybatis Plus,好好地偷了一把懒。不得不说MP是真的香,不仅节省了很多写mapper数据库配置、mapper接口的时间,甚至还提供了很多键的自增的策略,键的自填充策略等。最有料的还是代码的逆向生成了,虽然对达梦数据库的小写模式不友好,需要自己写入实体类的字段,但是其他方面都还好。
哦,当然了,如果想的话,可以直接在controller中调用BaseService中的方法进行数据库的操作,但是为了让控制层看着简洁一点,这里还是把业务逻辑放在了Service的实现中。By the way,如果想的话,还可以直接开启Active Record模式,直接通过实例对象进行数据库的操作。
MP当然还提供了逻辑删除(deleted),乐观锁(version),多租户(tenant_id),这些的实现类似,都是在sql中多加入了一个条件,虽然原理简单,但将这些逻辑交给MP,实属方便。
当然上述的种种,说破天就是一个CRUD,我们的真的业务逻辑和架构需求如下:
1. 缓存的构建
如果只是针对单套CRUD的操作而言,或者直接说,针对单个Mapper进行缓存,那么直接采用Mybatis的二级缓存,何乐而不为呢?二级缓存可以针对select方法进行缓存,针对update、delete方法进行缓存删除,但是目前的项目中,显然不是这样的场景。
几个Mapper之间存在一定的交互,即存在多表查询的情况,那么又有下面两种情况:
- 两个Mapper在同一个服务器上,那么可以通过cache-ref将缓存空间指向另一个Mapper,可以实现多表的二级缓存,但是这样的话,只要有一个表更新或者删除都会更新缓存,在无形中增加了开销,且使用不当,会存在脏数据
- 两个Mapper不在一个服务器上......,不用多说了吧。
所以建议,还是一般情况下,仅在单表情况下使用二级缓存(或者遵守二级缓存的注意事项)
所以还是自己实现缓存逻辑比较合适。
那么这里就涉及到了另一点:如何感知其他微服务对数据源的操作,进而更新缓存呢?
一个简单的应用场景:
Ⅰ服务用到了A(x),B(x,y)两张表,其中均有x字段,并通过该字段进行关联,Ⅱ服务中用到了C(y)表,其中存在y字段,当Ⅰ服务通过相关的SQL语句,获取了x,y字段,并以一种数据结构存入了redis,那么当Ⅱ服务通过C表修改了y字段,此时,用户在该阶段去读取redis种的缓存时,缓存中的y字段,即为脏数据。
那么问题就来了,如何做到redis中的字段和数据库中的字段同步?或者,换一个角度,如何做到,在其他服务操作了与redis中相关字段对应的数据库字段后,redis如何同步更新?
根据初步调研,阿里推出的一个开源的数据同步中间件canal,可能可以解决这个问题,但是!! 项目中使用的是DM数据库,中间件并不支持。
2. 数据更新的问题
上面的问题,是针对缓存的,直白点儿,就是针对redis和数据库的同步问题的,而下面要讲的问题,是针对两个数据库之间的。
说到这里,相信很多盆友,都想到了分布式事务的处理方案,如阿里三件套中的seata,但是以目前个人的了解,似乎,并不能解决类似的问题
我们在采用微服务架构之后,微服务的数据库之间存在上述的一种关联,当我们在单库或者说单schema中可以交由数据库,通过简单而暴力的级联删除处理,但是在分布式架构下,我们表不会在一个库中,甚至不会在一台服务器上,这时,就需要思考如何在这种情况下进行数据的"级联"更新删除。
目前想到的几种不成熟的方案:
- 通过消息队列,让"外键"服务(这里本人指的就是内含外键的服务,且外键是待删除的条目主键),订阅|监听“主键”服务,当“主键”服务调用删除的时候,通知“外键”服务,删除其中相关的条目。
- “外键”微服务暴露根据“外键”删除条目的接口,在“主键”服务的删除操作中,调用“外键”服务的相关接口,进行相关数据的删除【耦合度较高】
- 与第二种类似,只不过,将这种统一的删除提取为一个服务,该服务中,托管了根据特定键,删除相关表中字段的服务。【耦合度较低】
3. 位置共享服务
又谈到了老问题上,如何在位置共享的时候,如何将最新的位置推送给相关的群组中呢?
这里用到实时位置,同时还要进行保存,还要供前端进行显示(就不让前端去数据库里找了),甚至还可能对接其他的服务。
目前的初步想法:
提取一个位置获取的微服务,用于获取节点,或者说用户的实时位置,获取之后有三种处理情况:
- 存入数据库
- 存入redis
- 发送给前端显示
那么回到最初的问题,群组如何响应式地获取用户的位置?即在用户位置更新时,才进行相应用户的更新,如果用户关闭共享,及时在群组中剔除该用户?
当用户对某个群组开启共享时,用户开始订阅当前群组中的所有在线用户的位置,这里,订阅的数据源就是来自位置获取微服务,所以,这个服务,要能够得知,哪些用户在哪些群组?这些用户的实时位置?并维护好实时的用户群组表!当得到群组服务的订阅请求时,要能够将呆订阅群组中的用户位置信息进行返回。