答案不规范的话术:
1、答案不符合平台规范,请按规范提问,回答人员会按自己的经验分享给你,给你最好的经验。
如:民大校花是谁?
2、问题范围太广,背景略少,回答人员无法明确经验,建立百度得到更详细的答案。
如:如何独立?
3、重复提问,请在公众号搜索问题。
如:广西民族大学男女平台多少。
4、未明确指明学校,回答人员无法定位问题,无法给出个人经验,感谢对平台的支持。
如:为什么宿舍那么贵,却没有空调。
答案不规范的话术:
1、答案不符合平台规范,请按规范提问,回答人员会按自己的经验分享给你,给你最好的经验。
如:民大校花是谁?
2、问题范围太广,背景略少,回答人员无法明确经验,建立百度得到更详细的答案。
如:如何独立?
3、重复提问,请在公众号搜索问题。
如:广西民族大学男女平台多少。
4、未明确指明学校,回答人员无法定位问题,无法给出个人经验,感谢对平台的支持。
如:为什么宿舍那么贵,却没有空调。
前言
消息最终一致性是用于处理分布式事务解决方案之一,其中主要保证的目的是为了消息的可靠性,最终一致性,而可靠消息并不可靠,由于其它未知原因(如网络、宕机等),依然可能会造成消息的中断,发不出去的情况,就需要人工介入处理或者有一定的处理机制,所以在一定角度上来说,可靠消息在某一程度上,只是最大保障了消息的最终一致性,以为称“消息最终一致性”会更为合理。
以下为分析消息最终一致性,其中按的思路为ACID,即事务的主要特性。
传统事务处理
举例一个场景,以CURD事务举例,一个事务中有添加、删除、修改三个操作,这里我们且称添加为A,删除为B,修改为C,设置A,B,C为同步操作,则一个事务表示为(序号表示执行顺序):
在这一事务中,可以用传统的处理方案处理,保证事务的一致性。
分布式事务处理
由于业务的发展,业务独立在一定程度上更好有效的解决了耦合,达到可扩展,组件化,高性能等,在服务化之后,我们继续使用上面的A、B、C来说明,假设已经分为A服务,B服务,C服务,实际的业务场景则为:
为了保证ACID,在A完成之后,一定要操作B服务,在B完成之后,一定要操作C,但实际中,网络、应用稳定性、异步调用等的不确定性,容易造成事务不统一,如A完成,在调用B时,由于网络或者其它原因,B并没有完成业务操作,同样,C 也没有完成,如下图:
以上的情况,这个事务并不统一,服务是独立的,要实现A回滚的操作,需要做更多的额外工作来处理。所以,为了确定B一定执行,在已完成的事务(如A)需要重复去调用或者有一种验证机制去确定B一定执行。
以上只是一种情况,可能出现多种情况。总之,打破了事务的ACID原则,而我们要做的就是尽最大努力确保这个ACID原则,即事务性。消息最终一致性的方案,为保持这个原则,做了一个保障方案。
消息最终一致性方案
注:可靠消息是一种基于消息中间件的包装,确保消息一致性的服务或者组件。
此方案中引入了可靠消息做为媒介,如图(序号表示执行顺序):
在A完成之后,会一定去调用B服务,B服务一定会去调用C服务,确保一定会完成ACID的操作。
如果说事务失败,即在A失败,这部分可以在A服务内自己控制事务,包括异常处理等,就不会去调用B,此时,整个事务就不会进行。
回到之前的事务讨论
前面提过,添加为A,删除为B,修改为C,其中A,B成功,C操作失败,要不要做A、B回滚的处理?这个之前很多同事都有提问到。事务具有ACID特性,即整个事务来说,对消费方来说是透明的,要么成功,要么失败,A、B属于事务中的一部分,不会有回滚的操作,因为它会确保C一定去执行。
如果需要添加回滚,在程序上处理可实现,考虑到开发成本上,不太建议。如有更好的办法,也希望学习。
可靠消息对业务有一定的入侵性,并不能完全解耦,如在做幂等处理时,需要跟具体业务结合判断。
总结
针对以上可靠消息的特性及使用场景,在划分服务颗粒度上需按实际业务场景划分,以最简单的成本,解决最实际的问题。
Quartz 是个开源的作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。Quartz框架包含了调度器监听、作业和触发器监听。你可以配置作业和触发器监听为全局监听或者是特定于作业和触发器的监听。Quartz 允许开发人员根据时间间隔(或天)来调度作业。它实现了作业和触发器的多对多关系,还能把多个作业与不同的触发器关联。整合了 Quartz 的应用程序可以重用来自不同事件的作业,还可以为一个事件组合多个作业。并且还能和spring配置整合使用。Quartz在功能上远远超越了JDK自带的Timer,很好很强大!好啦,直接上代码:
package
com.zoki.module.timer;
import
ch.qos.logback.classic.Logger;
import
java.util.Date;
import
java.util.Map;
import
java.util.Map.Entry;
import
java.util.concurrent.ConcurrentHashMap;
import
org.quartz.CronScheduleBuilder;
import
org.quartz.CronTrigger;
import
org.quartz.JobBuilder;
import
org.quartz.JobDetail;
import
org.quartz.JobKey;
import
org.quartz.Scheduler;
import
org.quartz.SchedulerException;
import
org.quartz.SchedulerFactory;
import
org.quartz.SimpleScheduleBuilder;
import
org.quartz.Trigger;
import
org.quartz.TriggerBuilder;
import
org.quartz.TriggerKey;
import
org.quartz.impl.StdSchedulerFactory;
import
org.slf4j.LoggerFactory;
/**
* 定时器任务调度管理器
*
* @author zhoukai
*/
public
class
QuartzManager {
private
final
String jobGroupName;
private
final
String triggerGroupName;
private
Scheduler sched;
private
final
static
SchedulerFactory sf =
new
StdSchedulerFactory();
private
final
static
Map<String, QuartzManager> instanceMap =
new
ConcurrentHashMap<>();
private
final
static
Logger logger = (Logger) LoggerFactory.getLogger(QuartzManager.
class
);
/**
* 构造方法,外部不能实例化
*
* @param jobGroupName 工作组名
* @param triggerGroupName 触发器组名
*/
private
QuartzManager(String jobGroupName, String triggerGroupName) {
this
.jobGroupName = jobGroupName;
this
.triggerGroupName = triggerGroupName;
}
/**
* 获取调度器
*
* @return 定时调度器
* @throws SchedulerException 调度器内部执行异常
*/
private
Scheduler getScheduler()
throws
SchedulerException {
if
(sched ==
null
) {
sched = sf.getScheduler();
//sched.getListenerManager().addTriggerListener(new QuartzTriggerListener());
//sched.getListenerManager().addSchedulerListener(new QuartzSchedulerListener(sched));
}
return
sched;
}
/**
* 获取一个定时调度管理器
*
* @param jobGroupName 工作组名
* @param triggerGroupName 触发器组名
* @return 定时调度管理器
*/
public
static
QuartzManager getInstance(String jobGroupName, String triggerGroupName) {
String instanceKey = jobGroupName +
"_"
+ triggerGroupName;
if
(instanceMap.containsKey(instanceKey)) {
return
instanceMap.get(instanceKey);
}
QuartzManager manager =
new
QuartzManager(jobGroupName, triggerGroupName);
instanceMap.put(instanceKey, manager);
return
manager;
}
/**
* 获取一个定时调度管理器
*
* @param groupName 工作组名和触发器组名相同
* @return 定时调度管理器
*/
public
static
QuartzManager getInstance(String groupName) {
return
getInstance(groupName, groupName);
}
/**
* 关闭调度器
*
* @param waitForJobsToComplete 是否等候所有工作被调度完成后再关闭
*/
public
void
shutdown(
boolean
waitForJobsToComplete) {
String instanceKey = jobGroupName +
"_"
+ triggerGroupName;
if
(instanceMap.containsKey(instanceKey)) {
QuartzManager manager = instanceMap.get(instanceKey);
if
(manager !=
null
&& manager.sched !=
null
) {
try
{
manager.sched.shutdown(waitForJobsToComplete);
}
catch
(SchedulerException ex) {
logger.debug(ex.getLocalizedMessage(), ex);
}
}
}
}
/**
* 关闭调度器
*/
public
void
shutdown() {
shutdown(
false
);
}
/**
* 添加一个调度任务
*
* @param jobName 任务名
* @param cls 任务类型
* @param start 任务执行的开始时间,null表示立刻执行
* @param end 任务执行的结束时间,null表示任务在执行repeat次数后自动结束
* @param jobBindData 绑定到job中的数据,可为null
* @param intervalType 任务执行时间间隔类型,1、毫秒,2、秒,3、分,4、小时,默认为2
* @param intervalValue 任务执行时间间隔
* @param repeat 任务执行次数,0表示执行一次,实际执行次数为此值+1
* @throws SchedulerException
*/
public
void
addJob(String jobName, Class cls, Date start, Date end, Map<String, Object> jobBindData,
int
intervalType,
long
intervalValue,
int
repeat)
throws
SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(jobName, jobGroupName).build();
if
(jobBindData !=
null
) {
for
(Entry<String, Object> entry : jobBindData.entrySet()) {
jobDetail.getJobDataMap().put(entry.getKey(), entry.getValue());
}
}
SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
switch
(intervalType) {
case
1
:
//毫秒
simpleScheduleBuilder.withIntervalInMilliseconds(intervalValue);
break
;
case
3
:
//分
simpleScheduleBuilder.withIntervalInMinutes((
int
) intervalValue);
break
;
case
4
:
//时
simpleScheduleBuilder.withIntervalInHours((
int
) intervalValue);
break
;
default
:
//秒
simpleScheduleBuilder.withIntervalInSeconds((
int
) intervalValue);
break
;
}
if
(repeat >=
0
) {
simpleScheduleBuilder.withRepeatCount(repeat);
}
TriggerBuilder<Trigger> builder = TriggerBuilder.newTrigger()
.withIdentity(jobName, triggerGroupName);
if
(start ==
null
) {
builder.startNow();
}
else
{
builder.startAt(start);
}
if
(end !=
null
) {
builder.endAt(end);
}
Trigger trigger = builder.withSchedule(simpleScheduleBuilder).build();
Scheduler scheduler = getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
if
(!scheduler.isShutdown()) {
scheduler.start();
}
}
/**
* 添加一个调度任务,在start时间执行一次
* @param jobName 任务名
* @param cls 任务类型
* @param start 任务执行的开始时间,null表示立刻执行
* @param end 任务执行的结束时间,null表示任务在执行repeat次数后自动结束
* @param jobBindData 绑定到job中的数据,可为null
* @throws SchedulerException
*/
public
void
addJob(String jobName, Class cls, Date start , Date end, Map<String, Object> jobBindData)
throws
SchedulerException {
this
.addJob(jobName, cls, start, end, jobBindData,
2
,
1
,
0
);
}
/**
* 添加一个调度任务,在start时间执行一次
* @param jobName 任务名
* @param cls 任务类型
* @param start 任务执行的开始时间,null表示立刻执行
* @param jobBindData 绑定到job中的数据,可为null
* @throws SchedulerException
*/
public
void
addJob(String jobName, Class cls, Date start, Map<String, Object> jobBindData)
throws
SchedulerException {
this
.addJob(jobName, cls, start,
null
, jobBindData);
}
/**
* 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
*
* @param jobName 任务名
* @param cls
* @param time 时间设置,参考quartz说明文档
* @throws SchedulerException 调度器内部执行异常
*/
public
void
addJob(String jobName, Class cls, String time)
throws
SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(jobName, jobGroupName).build();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName, triggerGroupName)
//触发器名,触发器组
.withSchedule(CronScheduleBuilder.cronSchedule(time))
.build();
Scheduler scheduler = getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
if
(!scheduler.isShutdown()) {
scheduler.start();
}
}
/**
* 移除一个任务
*
* @param triggerKey 触发器键
* @param jobKey 工作任务键
* @throws SchedulerException 调度器内部执行异常
*/
public
void
removeJob(TriggerKey triggerKey, JobKey jobKey)
throws
SchedulerException {
Scheduler scheduler = getScheduler();
scheduler.pauseTrigger(triggerKey);
scheduler.unscheduleJob(triggerKey);
scheduler.deleteJob(jobKey);
}
/**
* 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
*
* @param jobName 工作任务名
* @throws SchedulerException 调度器内部执行异常
*/
public
void
removeJob(String jobName)
throws
SchedulerException {
TriggerKey triggerKey =
new
TriggerKey(jobName, triggerGroupName);
JobKey jobKey =
new
JobKey(jobName, jobGroupName);
removeJob(triggerKey, jobKey);
}
}
以上就是quartz任务调度对于任务的常用操作,封装起来以便在外部调用!这样我们就需要任务的执行了:
Ok,我们来测试一下:
运行一下,看看效果图:
虽然大家都有很多项目经验,但是不知道是否进行了项目经验的整理、甚至是工作经验的整理,相对来讲对开发人员来说收获最大的还是项目后期的经验更值钱一些,当然从项目开始到结案为止能得到整个过程经验,那会更理想一些,将来对你的职业提升铺垫个牢固的基础了。
其实很少有人项目从头参与到尾巴的,一般是前期参与,中期开发过程中辞职,或者是项目中期开发阶段进入项目组,后期项目实施阶段就辞职了,或者是干脆,这个项目怎么开发实施的都不知道,只是做一些项目后期的维护工作,彻底的绕过了整个项目折腾过程。
其实一个完整的软件项目里,你最起码要关注哪些比较好呢?我简单的整理了一下
01.项目是谁拉的?如何拉到了这个项目?公司给了多少业务经费?以为自己做业务员,收入会如何?
02.客户是谁是说了算?谁是这个项目的关键人物?这个项目的关系人都有谁谁,他们在项目里都起什么作用?
03.项目的工期是多少?都需要有哪些功能?客户的理想工期是多少?客户的期望值是什么?
04.项目竞标是如何进行的?我们竞争对手是谁?我们采用了什么战术把对手打败的?
05.竞标都写了哪些文档?为什么我们会中标?我们的优势,对手的弱点都在哪里?
06.项目的开工准备工作是如何进展的?
07.项目的需求分析,演示模型的制作,与客户的沟通等,都是如何进行的?都需要多久的工期及工作量?都写了哪些文档?
08.项目的成员都是如何组织的?人员是如何管理的?项目里分工是如何划分的?
09.项目的预算是多少?预算谁说了算?计划工期是多少?
10.每周的工作报告,项目计划是如何弄的?项目进度控制上都出现了哪些问题?
11.项目的款是如何收的?项目的进度款是如何安排的?项目里程碑是如何确定?
12.那些比较较真的客户也是业务精英,我们是如何搞定的?
13. 项目开发中的错误是如何管理的?
14.项目中遇到的技术难题都是怎么解决的?
15.不能完成的功能,或者不在项目范围内的功能是如何解决的?
16.项目里都用了什么技术架构?
17.项目里的技术分层是如何实现的?
18.项目里与其他系统是如何组织在一起的?
19.后期发生的问题,是怎么处理的?
20.上线是如何进行的?
21.项目里都哪些功能在上线时遇到了哪些问题?后来是怎么解决的?
22.项目的后期如何把项目结束的?都发生了什么事情?都怎么解决的?
23.客户上线后,都有啥问题?希望都改进哪里?客户是否满意?
24.后期有没有再挖掘这个客户,有没有又能挖出个啥项目来?
当然还有很多很多可以通过实际的项目过程学到有价值的知识点。