摘要:加载配置文件失败加载配置文件失败添加定时调度任务定时调度任务添加定时调度任务定时调度任务执行的张表入数据库添加启动类简单微服务,采用注解配置分布式集群。
SpringCloud(第 054 篇)简单 Quartz-Cluster 微服务,采用注解配置 Quartz 分布式集群
-
一、大致介绍1、因网友提到有没有采用注解式配置的Quartz例子,因此本人就贴上了这样一个样例; 2、至于如何修改定时任务的 cronExpression 表达式值的话,大家可以参照之前的(第 010 篇)样子看看如何修改; 3、注意:配置文件中的 mysql 数据库链接配置大家就各自配置自己的哈;二、实现步骤 2.1 添加 maven 引用包
2.2 添加应用配置文件(springms-simple-quartz-config-clustersrcmainresourcesapplication.yml)4.0.0 springms-simple-quartz-config-cluster 1.0-SNAPSHOT jar com.springms.cloud springms-spring-cloud 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-web mysql mysql-connector-java org.springframework.boot spring-boot-starter-jdbc org.quartz-scheduler quartz 2.3.0 org.quartz-scheduler quartz-jobs 2.3.0 org.springframework spring-context-support com.alibaba druid 1.1.3
server: port: 8405 spring: application: name: springms-simple-quartz-config-cluster #全部小写 ##################################################################################################### # mysql 属性配置 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://ip:3306/hmilyylimh?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true username: username password: password jpa: hibernate: #ddl-auto: create #ddl-auto:设为create表示每次都重新建表 ddl-auto: update #ddl-auto:设为update表示每次都不会重新建表 show-sql: true ##################################################################################################### ##################################################################################################### # 打印日志 logging: level: root: INFO org.hibernate: INFO org.hibernate.type.descriptor.sql.BasicBinder: TRACE org.hibernate.type.descriptor.sql.BasicExtractor: TRACE com.springms: DEBUG #####################################################################################################2.3 添加 quartz 配置文件(springms-simple-quartz-config-cluster/src/main/resources/quartz.properties)
org.quartz.scheduler.instanceName = quartzScheduler org.quartz.scheduler.instanceId = AUTO org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.isClustered = true org.quartz.jobStore.useProperties = false org.quartz.jobStore.clusterCheckinInterval = 20000 org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 10 org.quartz.threadPool.threadPriority = 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true2.4 添加数据源配置类(springms-simple-quartz-config-clustersrcmainjavacomspringmscloudconfigDataSourceConfig.java)
package com.springms.cloud.config; import org.apache.tomcat.jdbc.pool.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import javax.annotation.Resource; /** * 数据源配置。 * * @author hmilyylimh * * @version 0.0.1 * * @date 2017/12/17 * */ @Configuration public class DataSourceConfig { @Resource private Environment env; @Bean public DataSource dataSource(){ DataSource configDataSource = new DataSource(); configDataSource.setUrl("jdbc:mysql://ip:3306/hmilyylimh?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true"); configDataSource.setDriverClassName("com.mysql.jdbc.Driver"); configDataSource.setUsername("username"); configDataSource.setPassword("password"); configDataSource.setInitialSize(5); configDataSource.setDefaultAutoCommit(true); if(configDataSource.getUrl().startsWith("jdbc:mysql://ip")){ throw new RuntimeException("请配置数据源地址"); } if(configDataSource.getUsername().startsWith("username")){ throw new RuntimeException("请配置数据源用户名"); } if(configDataSource.getPassword().startsWith("password")){ throw new RuntimeException("请配置数据源密码"); } // 这些配置看个人需要,需要的话可以全部配置起来,我这里写Demo的话就没有全部配置起来了 //configDataSource.setInitialSize(); //configDataSource.setMinEvictableIdleTimeMillis(); //configDataSource.setNumTestsPerEvictionRun(); //configDataSource.setTestWhileIdle(); //configDataSource.setMaxActive(); //configDataSource.setMaxIdle(); //configDataSource.setMinIdle(); //configDataSource.setMaxWait(); //configDataSource.setRemoveAbandoned(); //configDataSource.setRemoveAbandonedTimeout(); //configDataSource.setValidationQuery(); //configDataSource.setValidationQueryTimeout(); //configDataSource.setValidationInterval(); //configDataSource.setTimeBetweenEvictionRunsMillis(); return configDataSource; } }2.5 添加Quartz调度配置类(springms-simple-quartz-config-clustersrcmainjavacomspringmscloudconfigQuartzSchedulerConfig.java)
package com.springms.cloud.config; import com.springms.cloud.job.Job1; import com.springms.cloud.job.Job2; import org.apache.tomcat.jdbc.pool.DataSource; import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.spi.JobFactory; import org.quartz.spi.TriggerFiredBundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.JobDetailFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.scheduling.quartz.SpringBeanJobFactory; import java.io.IOException; import java.util.Properties; /** * Quartz调度配置类。 * * @author hmilyylimh * * @version 0.0.1 * * @date 2017/12/17 * */ @Configuration public class QuartzSchedulerConfig { @Autowired private DataSource dataSource; private static final Logger Logger = LoggerFactory.getLogger(QuartzSchedulerConfig.class); private static final String QUARTZ_PROPERTIES_NAME = "/quartz.properties"; @Bean public JobFactory jobFactory(ApplicationContext applicationContext) { AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); jobFactory.setApplicationContext(applicationContext); return jobFactory; } @Bean public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, CronTrigger[] cronTrigger, JobDetail[] jobDetails) { SchedulerFactoryBean factoryBean = new SchedulerFactoryBean(); try { factoryBean.setQuartzProperties(quartzProperties()); factoryBean.setDataSource(dataSource); factoryBean.setJobFactory(jobFactory); factoryBean.setTriggers(cronTrigger); factoryBean.setJobDetails(jobDetails); factoryBean.setOverwriteExistingJobs(true); } catch (Exception e) { Logger.error("加载 {} 配置文件失败.", QUARTZ_PROPERTIES_NAME, e); throw new RuntimeException("加载配置文件失败", e); } return factoryBean; } @Bean(name = "job1Trigger") public CronTriggerFactoryBean job1Trigger(@Qualifier("job1Detail") JobDetail jobDetail) { CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean(); cronTriggerFactoryBean.setJobDetail(jobDetail); cronTriggerFactoryBean.setCronExpression("0/15 * * * * ?"); return cronTriggerFactoryBean; } @Bean(name = "job1Detail") public JobDetailFactoryBean job1Detail() { JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean(); jobDetailFactoryBean.setJobClass(Job1.class); jobDetailFactoryBean.setDurability(true); return jobDetailFactoryBean; } @Bean(name = "job2Trigger") public CronTriggerFactoryBean job2Trigger(@Qualifier("job2Detail") JobDetail jobDetail) { CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean(); cronTriggerFactoryBean.setJobDetail(jobDetail); cronTriggerFactoryBean.setCronExpression("0/5 * * * * ?"); return cronTriggerFactoryBean; } @Bean(name = "job2Detail") public JobDetailFactoryBean job2Detail() { JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean(); jobDetailFactoryBean.setJobClass(Job2.class); jobDetailFactoryBean.setDurability(true); return jobDetailFactoryBean; } @Bean public Properties quartzProperties() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); propertiesFactoryBean.setLocation(new ClassPathResource(QUARTZ_PROPERTIES_NAME)); propertiesFactoryBean.afterPropertiesSet(); return propertiesFactoryBean.getObject(); } class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { private transient AutowireCapableBeanFactory beanFactory; @Override public void setApplicationContext(final ApplicationContext context) { beanFactory = context.getAutowireCapableBeanFactory(); } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { final Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); return job; } } }2.6 添加定时调度任务Job1(springms-simple-quartz-config-clustersrcmainjavacomspringmscloudjobJob1.java)
package com.springms.cloud.job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.util.Date; /** * 定时调度任务Job1. * * @author hmilyylimh * * @version 0.0.1 * * @date 2017/12/17 * */ @Component public class Job1 extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); System.out.println("@@@@@@@@@@@@ "+(new SimpleDateFormat("yyyy-mm-dd HH:mm:ss.SSSSSS")).format(new Date())+" @@@@@@@@@@@"); System.out.println("@@@@@@@@@@@@@@@@@@@@@@@ Job1 @@@@@@@@@@@@@@@@@@@@@@"); System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); System.out.println(); } }2.7 添加定时调度任务Job2(springms-simple-quartz-config-clustersrcmainjavacomspringmscloudjobJob2.java)
package com.springms.cloud.job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.quartz.QuartzJobBean; import java.text.SimpleDateFormat; import java.util.Date; /** * 定时调度任务Job2. * * @author hmilyylimh * * @version 0.0.1 * * @date 2017/12/17 * */ public class Job2 extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println("=================================================================="); System.out.println("=========== "+(new SimpleDateFormat("yyyy-mm-dd HH:mm:ss.SSSSSS")).format(new Date())+" ============"); System.out.println("======================= Job2 ======================"); System.out.println("=================================================================="); System.out.println(); } }2.8 执行 Quartz 的 11 张表入数据库(springms-simple-quartz-config-cluster/quartz-tables.log)
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, JOB_CLASS_NAME VARCHAR(250) NOT NULL, IS_DURABLE VARCHAR(1) NOT NULL, IS_NONCONCURRENT VARCHAR(1) NOT NULL, IS_UPDATE_DATA VARCHAR(1) NOT NULL, REQUESTS_RECOVERY VARCHAR(1) NOT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, NEXT_FIRE_TIME BIGINT(13) NULL, PREV_FIRE_TIME BIGINT(13) NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR SMALLINT(2) NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, REPEAT_COUNT BIGINT(7) NOT NULL, REPEAT_INTERVAL BIGINT(12) NOT NULL, TIMES_TRIGGERED BIGINT(10) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, CRON_EXPRESSION VARCHAR(200) NOT NULL, TIME_ZONE_ID VARCHAR(80), PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INT NULL, INT_PROP_2 INT NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 VARCHAR(1) NULL, BOOL_PROP_2 VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, BLOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR(200) NOT NULL, CALENDAR BLOB NOT NULL, PRIMARY KEY (SCHED_NAME,CALENDAR_NAME) ); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR(95) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, FIRED_TIME BIGINT(13) NOT NULL, SCHED_TIME BIGINT(13) NOT NULL, PRIORITY INTEGER NOT NULL, STATE VARCHAR(16) NOT NULL, JOB_NAME VARCHAR(200) NULL, JOB_GROUP VARCHAR(200) NULL, IS_NONCONCURRENT VARCHAR(1) NULL, REQUESTS_RECOVERY VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,ENTRY_ID) ); CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, LAST_CHECKIN_TIME BIGINT(13) NOT NULL, CHECKIN_INTERVAL BIGINT(13) NOT NULL, PRIMARY KEY (SCHED_NAME,INSTANCE_NAME) ); CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME) ); commit;2.9 添加 Quartz-Cluster 启动类(springms-simple-quartz-config-clustersrcmainjavacomspringmscloudSimpleQuartzConfigClusterApplication.java)
package com.springms.cloud; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; /** * 简单 Quartz-Cluster 微服务,采用注解配置 Quartz 分布式集群。 * * @author hmilyylimh * * @version 0.0.1 * * @date 2017/12/17 * */ @SpringBootApplication public class SimpleQuartzConfigClusterApplication { private static final Logger Logger = LoggerFactory.getLogger(SimpleQuartzConfigClusterApplication.class); public static void main(String[] args) { SpringApplication.run(SimpleQuartzConfigClusterApplication.class, args); System.out.println("【【【【【【 简单Quartz-Config-Cluster微服务 】】】】】】已启动."); } }三、测试
/**************************************************************************************** 一、简单 Quartz-Cluster 微服务,采用注解配置 Quartz 分布式集群: 1、添加 Quartz 相关配置文件; 2、启动 springms-simple-quartz-config-cluster 模块服务,启动3个端口(8405、8406、8407); 3、然后看到 3 台服务器只有 1 台服务器调用了 Job1 类中的方法,因此 Quartz 的集群分布式也算是部署成功了; 4、然后关闭 1 台活跃 Quartz 服务器; 5、再等一会儿就看到 2 台服务器中的 1 台服务器每隔一定的时间调用 Job1 类中的方法; ****************************************************************************************/四、下载地址
https://gitee.com/ylimhhmily/SpringCloudTutorial.git
SpringCloudTutorial交流QQ群: 235322432
SpringCloudTutorial交流微信群: 微信沟通群二维码图片链接
欢迎关注,您的肯定是对我最大的支持!!!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70837.html
摘要:当前时间打印当前时间定时任务触发,操作多个添加数据,事务中任一异常,都可以正常导致数据回滚。当前时间当前时间添加微服务启动类注解式配置定时任务,不支持任务调度。 SpringCloud(第 046 篇)注解式Schedule配置定时任务,不支持任务调度 - 一、大致介绍 1、很多时候我们需要隔一定的时间去执行某个任务,为了实现这样的需求通常最普通的方式就是利用多线程来实现; 2、但是有...
摘要:通过再添加其他的元素可在中放置多个触发器。停止触发器重启触发器停止触发器移除触发器添加调度的任务类调度的任务。执行的张表入数据库添加启动类简单微服务,支持集群分布式,并支持动态修改任务的执行时间。 SpringCloud(第 010 篇)简单 Quartz-Cluster 微服务,支持集群分布式,并支持动态修改 Quartz 任务的 cronExpression 执行时间 - 一、大致...
摘要:提供给文件上传微服务用的。注意注解能注册到服务上,是因为该注解包含了客户端的注解,该是一个复合注解。地址可以查看该微服务网关代理了多少微服务的。 SpringCloud(第 024 篇)简单文件上传微服务,并加入 zuul 微服务后用 zuul 微服务地址采取curl或者页面点击实现文件上传 - 一、大致介绍 1、本章节主要将文件上传微服务加入到 zuul 服务中去,然后利用 zuul...
摘要:注意注解能注册到服务上,是因为该注解包含了客户端的注解,该是一个复合注解。包含了客户端注解,同时也包含了断路器模块注解,还包含了网关模块。 SpringCloud(第 027 篇)集成异构微服务系统到 SpringCloud 生态圈中(比如集成 nodejs 微服务) - 一、大致介绍 1、在一些稍微复杂点系统中,往往都不是单一代码写的服务,而恰恰相反集成了各种语言写的系统,并且我们还...
阅读 3409·2021-11-25 09:43
阅读 3467·2021-11-19 09:40
阅读 2465·2021-10-14 09:48
阅读 1283·2021-09-09 11:39
阅读 1922·2019-08-30 15:54
阅读 2824·2019-08-30 15:44
阅读 1995·2019-08-29 13:12
阅读 1545·2019-08-29 12:59