缓存私有云的github地址 https://github.com/sohutv/cachecloud.git ,此处分析的源码是tag1.0的源码,2.0的实现完全不一样。
spring-load.xml中的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
" default-autowire="byName">
<!--初始化加载已有实例,测试时禁用-->
<bean class="com.sohu.cache.init.RedisInitLoad" init-method="init" />
<bean class="com.sohu.cache.init.MachineInitLoad" init-method="init" />
</beans>
在spring-local.xml中是没有引入这个配置的,测试时禁用了,在这个配置中增加这个引用即和线上一样
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<import resource="classpath:spring/spring-config.xml"/>
<import resource="classpath:spring/spring-quartz.xml"/>
<!-- 增加 spring-load.xml的配置-->
<import resource="classpath:spring/spring-load.xml"/>
<import resource="classpath:spring/spring-mybatis.xml"/>
<import resource="classpath:spring/spring-service.xml"/>
<import resource="classpath:spring/spring-data.xml"/>
<import resource="classpath:spring/spring-manage.xml"/>
<import resource="classpath:spring/spring-mvc.xml"/>
<import resource="classpath:spring/spring-jmx.xml"/>
<import resource="classpath:spring/spring-alert.xml"/>
<import resource="classpath:spring/spring-client-report.xml"/>
<import resource="classpath:spring/spring-inspector.xml"/>
<import resource="classpath:spring/spring-gcache.xml"/>
<import resource="classpath:spring/spring-monitor.xml"/>
<import resource="classpath:spring/spring-gmq.xml"/>
</beans>
启动后会调用 RedisInitLoad类的init方法,debug判断要去掉,否则本地可能会无法进入部署慢日志收集任务方法
public void init() {
if (ConstUtils.IS_DEBUG) {
logger.warn("isDebug=true return");
return;
}
……省略……
RedisInitLoad类的init方法中调用initByType方法创建慢日志收集的触发器。
private void initByType(int type) {
List<InstanceInfo> instanceInfoList = instanceDao.getInstListByType(type);
for (InstanceInfo instanceInfo : instanceInfoList) {
instanceInfo.getPort());
if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {
continue;
}
String host = instanceInfo.getIp();
int port = instanceInfo.getPort();
Long appId = instanceInfo.getAppId();
redisCenter.deployRedisCollection(appId, host, port);
//这里给每个实例增加慢日志收集的job
redisCenter.deployRedisSlowLogCollection(appId, host, port);
}
logger.info("init redis type={} deploy instance done.", type);
}
RedisCenterImpl的deployRedisSlowLogCollection方法部署定时任务
@Override
public boolean deployRedisSlowLogCollection(long appId, String host, int port) {
Assert.isTrue(appId > 0);
Assert.hasText(host);
Assert.isTrue(port > 0);
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put(ConstUtils.HOST_KEY, host);
dataMap.put(ConstUtils.PORT_KEY, port);
dataMap.put(ConstUtils.APP_KEY, appId);
JobKey jobKey = JobKey.jobKey(ConstUtils.REDIS_SLOWLOG_JOB_NAME, ConstUtils.REDIS_SLOWLOG_JOB_GROUP);
TriggerKey triggerKey = TriggerKey.triggerKey(ObjectConvert.linkIpAndPort(host, port), ConstUtils.REDIS_SLOWLOG_TRIGGER_GROUP + appId);
boolean result = schedulerCenter.deployJobByCron(jobKey, triggerKey, dataMap, ScheduleUtil.getRandomHourCron(appId), false);
return result;
}
SchedulerCenter的实现类SchedulerCenterImpl中,注入了 clusterScheduler,这个bean是在spring-quartz.xml中定义的
<!-- 分布式QuartzScheduler -->
<bean id="clusterScheduler" name="clusterScheduler" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="cacheCloudDB"></property>
<property name="taskExecutor" ref="quartzThreadPool"/>
<!--
<property name="transactionManager" ref="transactionManager"/>
-->
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.instanceName">CacheCloudScheduler</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop>
<!-- 表名前缀 -->
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
<prop key="org.quartz.jobStore.isClustered">${isClustered}</prop>
<prop key="org.quartz.jobStore.clusterCheckinInterval">15000</prop>
<prop key="org.quartz.jobStore.maxMisfiresToHandleAtATime">120</prop>
<prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
<!-- 打开JMX 配置 -->
<prop key="org.quartz.scheduler.jmx.export">true</prop>
<prop key="org.quartz.plugin.shutdownHook.class">org.quartz.plugins.management.ShutdownHookPlugin</prop>
<prop key="org.quartz.plugin.shutdownHook.cleanShutdown">true</prop>
<prop key="org.terracotta.quartz.skipUpdateCheck">true</prop>
<!--<prop key="org.quartz.plugin.triggHistory.class">org.quartz.plugins.history.LoggingJobHistoryPlugin</prop>-->
</props>
</property>
<property name="schedulerName" value="CacheCloudScheduler"/>
<property name="applicationContextSchedulerContextKey" value="applicationContext"/>
<property name="overwriteExistingJobs" value="true"/>
<property name="waitForJobsToCompleteOnShutdown" value="false"/>
<property name="startupDelay" value="10"/>
<property name="autoStartup" value="true"/>
<property name="triggers">
<array>
<ref bean="cleanUpStatisticsTrigger"/>
<ref bean="hostInspectorTrigger"/>
<ref bean="appInspectorTrigger"/>
<ref bean="appDailyTrigger"/>
</array>
</property>
<property name="jobDetails">
<array>
<ref bean="redisJobDetail"/>
<ref bean="machineJobDetail"/>
<ref bean="serverJobDetail"/>
<ref bean="machineMonitorJobDetail"/>
<ref bean="redisSlowLogJobDetail"/>
<ref bean="cleanUpStatisticsJobDetail"/>
<ref bean="inspectorJobDetail"/>
<ref bean="appDailyJobDetail"/>
</array>
</property>
</bean>
部署定时任务
@Override
public boolean deployJobByCron(JobKey jobKey, TriggerKey triggerKey, Map<String, Object> dataMap, String cron, boolean replace) {
Assert.isTrue(jobKey != null);
Assert.isTrue(triggerKey != null);
Assert.isTrue(CronExpression.isValidExpression(cron), "invalid cron = " + cron);
try {
//获取相应的任务执行器
JobDetail jobDetail = clusterScheduler.getJobDetail(jobKey);
if (jobDetail == null) {
logger.error("JobKey {}:{} is not exist", jobKey.getName(), jobKey.getGroup());
return false;
}
//在这里创建定时触发任务
fireCronTrigger(triggerKey, jobDetail, cron, replace, dataMap);
} catch (Exception e) {
logger.error(e.getMessage(), e);
return false;
}
return true;
}
激活触发器
private boolean fireCronTrigger(TriggerKey triggerKey, JobDetail jobDetail, String cron, boolean replace, Map<String, Object> dataMap) {
try {
boolean isExists = clusterScheduler.checkExists(triggerKey);
if (isExists) {
if (replace) {
logger.warn("replace trigger={}:{} ", triggerKey.getName(), triggerKey.getGroup());
clusterScheduler.unscheduleJob(triggerKey);
} else {
logger.info("exist trigger={}:{} ", triggerKey.getName(), triggerKey.getGroup());
return false;
}
}
Date startDate = DateUtils.addSeconds(new Date(), 20);
//组建定时任务触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.forJob(jobDetail)
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.startAt(startDate)
.build();
if (dataMap != null && dataMap.size() > 0) {
trigger.getJobDataMap().putAll(dataMap);
}
clusterScheduler.scheduleJob(trigger);
} catch (SchedulerException e) {
logger.error(e.getMessage(), e);
return false;
}
return true;
}
这里看的触发器的时间是每个小时的随机某分钟某秒,直接从实例服务器获取最新100条慢日志
![](https://i-blog.csdnimg.cn/blog_migrate/40ac13e3cde1aa2c546d3e92711c31a2.png)
// 从redis中获取慢查询日志,直接从服务器获取最新100条慢日志
List<RedisSlowLog> redisLowLogList = getRedisSlowLogs(host, port, 100);
if (CollectionUtils.isEmpty(redisLowLogList)) {
return Collections.emptyList();
}