今年做了一个dubbo的本地存根优化,觉得卓有成效所以在这记录分享下。
问题:
1. Account服务如何将日均调用从千万优化到百万?看到这个问题,你是否也在纳闷,如果业务量没有下来,调用量怎么会下来呢?
答:其实真可以有,Account的getAccount之前dubbo调用量在1000w+,优化到现在基本在100w+左右。如果Account服务的业务方可以充分使用Account的公共资源如缓存等,那么就可以减少dubbo的RPC调用,但是又有个问题,如何做到让业务方不care缓存调用的相关逻辑呢?这个我们用了一个小小的技巧,dubbo的stub。
2. dubbo的stub是啥?
答:请参照dubbo官网链接
技术实现:
1. 流程图如下
2.系统架构图如下:
3. 缺点
a) 缓存地址变化,所以使用本地存根的消费者都需要修改配置
b) 如果缓存从nkv切换到ncr则需要升级jar包中的缓存,影响所有的使用本地存根的消费者。
c) 无法监控哪些消费者使用者本地存根。
核心代码:
// 核心配置支持配置文件和zk的配置方式 public class AccountConfig { /** NKV 配置 */ public String accountNkvConfig; public String masterUrl; public String slaveUrl; public Short namespace; public Long timeOut; /** 开关控制是否走缓存优化 */ public boolean useCacheOptimize = true; /** Disconf配置 */ private ConfigService configService; /** 0 未初始化, 1 disconf 2 properties */ private int initType = 0; public void init() { if(null != configService) { accountNkvConfig = configService.getString("account_nkv_config"); initConfig(); if(checkConfig()) { initType = 1; } } else { if(checkConfig()) { initType = 2; } } } /** 初始化配置 */ public void initConfig() { try { if(StringUtils.isNotBlank(accountNkvConfig)) { JSONObject nkvConfigJSON = JSONObject.parseObject(accountNkvConfig); masterUrl = nkvConfigJSON.getString("master"); slaveUrl = nkvConfigJSON.getString("slave"); namespace = nkvConfigJSON.getShort("namespace"); timeOut = nkvConfigJSON.getLong("timeout"); } } catch (Exception e) { } } public boolean checkConfig() { if( StringUtils.isBlank(masterUrl) || StringUtils.isBlank(slaveUrl) || null == namespace || null == timeOut ) { return false; } return true; } public boolean getUseCacheOptimize() { if(null != configService) { return configService.getBoolean("use_cache_optimize", true); } return useCacheOptimize; } }
// 初始化cache public class CacheClient extends MemcacheManagerImpl{ @Autowired public AccountConfig accountConfig; public void init() { try { MemcacheTransactionManagerImpl spaceMemcacheTransactionManager = new MemcacheTransactionManagerImpl(); DefaultNkvClient nkvClient = new DefaultNkvClient(); nkvClient.setMaster(accountConfig.masterUrl); nkvClient.setSlave(accountConfig.slaveUrl); nkvClient.setGroup("group_1"); nkvClient.setCompressEnabled(true); nkvClient.init(); setMemcacheTransactionManager(spaceMemcacheTransactionManager); setClient(nkvClient); setNamespace(accountConfig.namespace); setTimeoutMilliseconds(accountConfig.timeOut); } catch (Exception e) { LogUtil.accountOpt.info("Account init Cache Exception ", e); } } }
// 判断cache是否初始化成功 @Component public class ApplicationContextHolder implements ApplicationContextAware { public static ApplicationContext applicationContext; /** 是否初始化成功 */ public static boolean isInitSuccess = false; public static AccountConfig accountConfig = null; public static Object getBean(String beanName) { return applicationContext.getBean(beanName); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ApplicationContextHolder.applicationContext = applicationContext; try { Object accountConfigObj = applicationContext.getBean("accountConfig"); LogUtil.accountOpt.info("Account Cache Config : " + (null == accountConfigObj ? null : accountConfigObj.toString())); if(null != accountConfigObj) { accountConfig = (AccountConfig) accountConfigObj; if(accountConfig.getInitType() > 0) { Object memcacheManagerObj = applicationContext.getBean("cacheClient"); if(null != memcacheManagerObj) { isInitSuccess = true; } } } } catch (Exception e) { LogUtil.accountOpt.error("Account Cache Init exception " , e); } LogUtil.accountOpt.info("Account Cache Init Result : " + isInitSuccess); } }
// 使用本地存根 public class AccountServiceStub extends CacheManager<Account> implements AccountService { // dubbo会自动注入该对象,使用注解无效 private AccountService accountService; public AccountServiceStub(AccountService accountService) { this.accountService = accountService; } @Override public Account queryByUserId(Long userId) { return accountService.queryByUserId(userId); } }
<!-- dubbo消费方配置 --> <dubbo:reference id="accountService" interface="com.xxx.account.service.AccountService" stub="com.xxx.account.service.AccountServiceStub" />