以下为《平台化多租户架构实践》的无排版文字预览,完整格式请下载
下载前请仔细阅读文字预览以及下方图片预览。图片预览是什么样的,下载的文档就是什么样的。
KK平台化多租户架构实践
——初探
一、模块工程搭建 2
二、单表自动生成dao层增删改查方法 7
三、jenkins添加模块和小程序 8
四、meShow api多租户实践 11
五、kkrpc模块多租户实践 11
六、melot-jedis Redis数据缓存多租户实践 12
七、melot-cache-util本地内存缓存多租户实践 13
八、MQ 消息生产消费多租户实践 14
九、小程序多租户任务实践 16
十、Mybatis sharding-jdbc 多租户数据源实践 16
十一、disconf多租户差异化配置实践 17
十二、日志框架melot-log实践 17
原理及背景介绍 17
melot-log扩展支持的新feature 17
melot-log接入方式 18
melot-log配置文件示例 18
melot-log程序中应用 20
创建新的Logger 20
业务日志打印 20
业务指标数据埋点方法 20
业务指标数据埋点应用实例 21
附录 22
Spring boot配置文件示例: 22
一、模块工程搭建
基于新开发的骨架模板(kk-module-quickstart-archetype) 快速搭建模块api、服务和小程序
骨架maven配置:
com.melot.module
kk-module-quickstart-archetype
2.7.0
地址: http://maven.kktv2.com:8081/nexus/content/repositories/releases
模块api生成后需要手动将parent标签删除,否则jar包上传会有问题
IDEA配置
/
基于骨架模板新建工程:
选中最新版的骨架模板
/
输入组名和模块业务名称:
组名统一命名:com.melot.kk
模块业务名称:以模块本身的业务含义来命名,例如欢乐PK模块就叫nationalPK
/
添加包某某:由GroupId+ArtifactId组成(eclipse里面会自动添加)
/
输入工程名称:kk-ArtifactId-module组成(eclipse里面会自动添加)
/
创建完成后的工程目录结构如下:
api结构如下:
/
小程序结构如下:
/
server结构如下:
/
eclipse配置:
/
/
二、单表自动生成dao层增删改查方法
编辑resources目录下的这2个文件:
/
运行mybatis-generator命令生成dao层对应的增删改查方法
/
server层查询列表方法示例:
@Overridepublic Result getHistDeliveryDOList(Integer deliveryUserId, String detailAddress) { HistDeliveryExample conditions = makeQueryConditions(deliveryUserId, detailAddress); List histDeliveries = histDeliveryMapper.selectByExample(conditions);; List histDeliveryDOs = Lists.newArrayList(); if(histDeliveries != null) { for(HistDelivery histDelivery : histDeliveries) { HistDeliveryDO histDeliveryDO = getHistDeliveryDO(histDelivery); histDeliveryDOs.add(histDeliveryDO); } } return new Result(CommonStateCode.SUCCESS,"调用成功", histDeliveryDOs);}
private HistDeliveryExample makeQueryConditions(Integer deliveryUserId, String detailAddress) { HistDeliveryExample example = new HistDeliveryExample(); HistDeliveryExample.Criteria criteria = example.createCriteria(); if(deliveryUserId != null) { criteria.andDeliveryUserIdEqualTo(deliveryUserId); } if(StringUtils.isNotEmpty(detailAddress)) { criteria.andDetailAddressLike("%" + detailAddress + "%"); } example.setOrderByClause("create_time desc"); return example;}
三、jenkins添加模块和小程序
添加模块:
/
/
添加小程序:
/
/
/
四、meShow api多租户实践
API作为后台对外提供服务的重要窗口,需要根据来自不同租户方用户请求中的租户ID(TenantId)来进一步向后端底层服务发起请求。采用隐式传参的方式来获取租户ID。具体操作流程为:API网关层根据请求的服务器域名来设置对应的租户ID到http request的header中,API服务处理业务http请求前增加一个拦截器读取request header中租户ID参数,不存在时默认租户ID为0,设置租户ID到当前线程租户上下文变量中,后续业务操作中需要区分租户ID时则直接从当前线程租户上下文中获取租户ID即可。
租户上下文信息的设置和获取依赖melot-utils包
com.melot.common
melot-utils
接口操作方法为:
TenantContext.getContext().getTenantId() //获取当前线程上下文中的租户ID
TenantContext.getContext().setTenantId(tenantId) //设置租户ID到当前线程租户上下文
五、kkrpc模块多租户实践
为了减少代码维护成本,kkrpc模块跨进程调用过程中,同样采用隐式传递租户上下文的方式。新版kkrpc模块框架中支持了rpc调用过程中租户上下文的隐式传递。实现原理为:客户端发起RPC调用时自动获取当前线程的租户上下文信息添加到rpc请求包的扩展字段中,同rpc请求一起发送到服务端,服务端收到rpc请求后,如果发现请求扩展字段中存在租户上下文信息则解析读取并设置到服务端当前业务处理线程中。同样后续模块业务操作中需要区分租户ID时则直接从当前线程租户上下文中获取租户ID即可
服务的生产者配置基于注解(@RpcService)生成:
@Service@RpcService(interfaceName = "com.melot.kk.test.api.service.DemoService", version = "1.0.0")public class DemoServiceImpl implements DemoService {
服务的调用方也基于注解(@RpcConsumer)来调用:
@Componentpublic class SpringSimpleJob implements SimpleJob {
private static Logger logger = Logger.getLogger(SpringSimpleJob.class); @RpcConsumer private DemoService demoService; @RpcConsumer(version = "1.0.8") private ActorService actorService; int i = 0; @Override public void execute() { RoomInfo roomInfo = actorService.getRoomInfoById(***); i++; logger.info(i + "tenantId:" + TenantContext.getContext().getTenantId() + " roomInfo: " + new Gson().toJson(roomInfo)); }}
六、melot-jedis Redis数据缓存多租户实践
引入依赖:
com.melot
melot-jedis-spring-boot-starter
在application.propeties中配置jedis相关属性,指定tenantId为对应租户的对应sourceName对应的配置。如下例配置了两个sourcename为relation的single source, 租户id为默认值0的对应redis1.kktv2.com等的配置,租户id为10086的对应redis2.kktv2.com
/
使用方式和starter的旧版本相同,会根据是否使用了多租户而注册原始的JedisWrapper。
@Autowired
Qualifier("relation")
private JedisWrapper userCache;
七、melot-cache-util本地内存缓存多租户实践
由melot-cache-util支持多租户的本地缓存。实现原理为:melot-cache-util根据不同的租户id生成不同的实例化对象,内部根据缓存操作时当前线程的租户上下文切换到对应的Cache实例上进行操作。
开发中需要引用的melot-cache-util工具包:
com.melot.module melot-cache-util 2.0.1
使用方法如下示例:
JsonObject jsonObject = EhCache.getFromCache(key, JsonObject.class);
EhCache.putInCacheByLive(key, result, cacheTime);
八、MQ 消息生产消费多租户实践
后续新的功能中状态变更都需要发出相应的 MQ 消息
生产者配置:
# mq配置 生产者 租户0melot.mq.history[0].mqInstance=kk-test-historymelot.mq.history[0].mqNsAddr=mq1.kktv2.com:9876;mq2.kktv2.com:9876melot.mq.history[0].producerName=kk-test-producermelot.mq.history[0].clientIp=melot.mq.history[0].sendTimeout=2000melot.mq.history[0].tenantId=0# mq配置 生产者 租户1melot.mq.history[1].mqInstance=kk-test-historymelot.mq.history[1].mqNsAddr=10.0.13.23:9876melot.mq.history[1].producerName=kk-test-producermelot.mq.history[1].clientIp=melot.mq.history[1].sendTimeout=2000melot.mq.history[1].tenantId=1
生产者代码:
@Componentpublic class SpringOneTimeJob implements OneTimeJob {
@Autowired private KKHistory kkhistory; @Override public void process() { int tenantId = TenantContext.getContext().getTenantId(); // 发送MQ消息 ActionHistory catchDollAction = kkhistory.newActionHistory("catchDollSuccessTopic"); catchDollAction.setMsgKey(String.valueOf(tenantId)); catchDollAction.addActionData("catchDollRecordId", 10086); catchDollAction.addActionData("userId", ***); catchDollAction.addActionData("nickname", "你好"); catchDollAction.complete(); }}
消费端配置:
/
@Componentpublic class SpringOneTimeConsumerJob { private static Logger logger = Logger.getLogger(SpringOneTimeConsumerJob.class); @RpcConsumer DemoService demoService; @Bean public HistoryProcessor getHistoryProcessor() { return new ActionHistoryProcessor() { @Override public boolean process(MessageExt messageExt, ActionHistory actionHistory) { int tenantId = TenantContext.getContext().getTenantId(); CatchDollRecordDO catchDollRecordDO = demoService.getCatchDollRecordDO(129277).getData(); String nickname = catchDollRecordDO.getNickName(); int userId = catchDollRecordDO.getThirdRecordId(); logger.info("tenantId:" + tenantId + " 昵称:" + nickname + " userId:" + userId + " -->start SpringOneTimeConsumerJob job..."); logger.info("consumer actionHistory: " + new Gson().toJson(actionHistory)); return true; } }; }}
九、小程序多租户任务实践
多租户需要提供一套相同业务代码的不同租户id执行的小程序。为了方便开发和改造,在之前的melot-ejob-spring-boot-starter基础上提供了基于配置,即可实现多个相同任务,不同租户的调度和监控。
在原有的ejob配置基础上添加tenantIds属性,设置租户id以逗号分隔符分割,即可启动对应租户数量的线程,每个线程含有对应的租户id变量。具体小程序框架starter的配置如下:
# elasticJob配置melot.elasticJob.regCenter.serverList=zk1.kktv2.com:2181,zk2.kktv2.com:2181,zk3.kktv2.com:2181melot.elasticJob.regCenter.namespace=api-melot-jobmelot.elasticJob.simpleJob[0].cron=0/10 * * * * ?melot.elasticJob.simpleJob[0].jobName=springSimpleJobmelot.elasticJob.simpleJob[0].refJobName=springSimpleJobmelot.elasticJob.simpleJob[0].email=jian.sheng@melot.cnmelot.elasticJob.simpleJob[0].host=10.0.*.*melot.elasticJob.simpleJob[0].tenantIds=0melot.elasticJob.oneTimeJob[0].jobName=springOneTimeJobmelot.elasticJob.oneTimeJob[0].refJobName=springOneTimeJobmelot.elasticJob.oneTimeJob[0].email=jian.sheng@melot.cnmelot.elasticJob.oneTimeJob[0].ho 内容过长,仅展示头部和尾部部分文字预览,全文请查看图片预览。 is.single[1].sourceName=userdata
melot.redis.single[1].tenantId=1
melot.redis.single[1].connectionHost=10.0.1.219
melot.redis.single[1].connectionPort=6380
melot.redis.single[1].connectionTimeOut=5000
melot.redis.single[1].connectionDatabase=1
melot.redis.single[1].maxActive=10
melot.redis.single[1].maxIdle=5
melot.redis.single[1].minIdle=1
melot.redis.single[1].testOnBorrow=true
melot.redis.single[1].testOnReturn=true
melot.redis.single[1].testWhileIdle=true
melot.redis.single[1].maxWait=3000
[文章尾部最后500字内容到此结束,中间部分内容请查看底下的图片预览]请点击下方选择您需要的文档下载。
以上为《平台化多租户架构实践》的无排版文字预览,完整格式请下载
下载前请仔细阅读上面文字预览以及下方图片预览。图片预览是什么样的,下载的文档就是什么样的。