当前位置: 首页 > news >正文

汕头市道路建设网站php创建站点

汕头市道路建设网站,php创建站点,广州制作外贸网站,网站关键词的使用一、背景 微服务设计中,跨服务的调用,由于网络或程序故障等各种原因,经常会出现调用失败而需要重试。另外,在异步操作中,我们提供接口让外部服务回调。回调过程中,也可能出现故障。 这就要求我们主动向外…

一、背景

微服务设计中,跨服务的调用,由于网络或程序故障等各种原因,经常会出现调用失败而需要重试。另外,在异步操作中,我们提供接口让外部服务回调。回调过程中,也可能出现故障。

这就要求我们主动向外部服务发起查询,以获取外部服务的操作结果。

见下图:
在这里插入图片描述
这种异步操作流程,比较常见于支付系统,其思路值得借鉴。

本文主要是想介绍,在没有订单号、支付流水号等业务唯一性的场景下,使用一张任务表,实现上面的异步操作,达到最终一致性。

二、定时任务

在这里插入图片描述

1、定时查询

查询下一次重试时间早于当前时间的记录,每次拉取N条。定时机制,可以依赖xxl-job这样的分布式定时任务。

notifyTasksRepository.findTop50ByNextTimeBeforeOrderByNextTimeAsc(DateUtil.date());

2、指数级重试

   public void retry() {this.retryTimes += 1;this.nextTime = calcNextRetryTime();this.lastTime = new Date();}private Date calcNextRetryTime() {final ZoneId zone = ZoneId.systemDefault();final LocalDateTime lastTime = LocalDateTime.ofInstant(Instant.now(), zone);final LocalDateTime nextTime = lastTime.plus(exp(this.retryTimes), ChronoUnit.MILLIS);final Instant instant = nextTime.atZone(zone).toInstant();return Date.from(instant);}private long exp(int retryCount) {long waitTime = ((long) Math.pow(2, retryCount) * 1000L);return waitTime;}

3、请求流水号

既然是微服务之间的异步操作流程,就需要设计一个类似于订单号,能够贯穿整个流程。它就是请求流水号,要求是不能重复。发起请求、回调通知和主动查询,都是需要传递该字段。

4、notifyParams参数

这是一个json格式的string字符串,当外部服务是post请求的时候,请求报文就保存在该字段。
而如果仅仅是保存http接口的请求入参的话,在实际不同的业务还是不够的。
所以,需要你把业务中需要透传的字段,也都保存在该字段里。

比如:http接口,需要requestNo和classroomId两个字段就可以了。而你的业务系统里,还需要字段:课程编号和讲次编号,那么请一并把它们都保存进去。
待外部服务回调的时候,根据requestNo请求流水号查询任务,需要反解析该字段,取出需要的课程编号和讲次编号。
如此一来,很好地体现了任务表的抽象性,又满足了不同业务的具体性。
当然了,前提是任务的查询,不会把它们作查询条件。

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ClassroomCopyRequest {/*** 原课堂ID(http请求必填参数)*/private String classroomId;/*** 请求流水号(http请求必填参数)*/private String requestNo;/*** 讲次编号(透传参数)*/private String lectureNo;/*** 课程编号(透传参数)*/private String courseNo;/*** 任务编号(透传参数)*/private String taskCode;
}

三、代码设计

在这里插入图片描述
从上面的流程图,也可以看出,业务逻辑处理,是有两个方向:一是业务方的回调;二是定时任务的主动查询。

二者最终都需要发送一个异步事件,让异步线程池去处理。

所以,无论从哪个链路获取到的返回结果,都应该是一样的(实际上也如此)。
示例:
复制课堂,返回一个新的课堂ID。由于我们在不同的业务流程中,都会有复制课堂的需求,所以需要用任务编号加以区分。而外部服务是不会关注你的任务,更遑论任务编号了。

在异步设计中,尽量抽象系统之间的交互术语,而不能具化。

进一步说,复制课堂,只需要知道原课堂ID即可,不要去让它去关心是什么业务下的复制。
这就是请求流水号requestNo的巧妙设计之处。

@ApiModel
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ClassroomCopyResultNotify {@ApiModelProperty(notes = "源课堂ID")private String sourceClassroomId;@ApiModelProperty(notes = "新课堂ID", required = true)@NotEmpty(message = "新课堂ID必填")private String newClassroomId;@ApiModelProperty(notes = "请求流水号", required = true)@NotEmpty(message = "请求流水号必填")private String requestNo;

1、回调通知接口

有了上面的回调报文,下面的接口设计就非常简单了。

    @ApiOperation(value = "异步通知课堂的复制结果")@PostMapping(value = {"/api/v1/classroom/copy/callback"})public ResponseEntity<?> copyResultNotify(@Validated @RequestBody ClassroomCopyResultNotify notify) {return ResponseEntity.ok(classroomAppService.copyResultNotify(notify));}

2、服务层ClassroomAppService.java

final NotifyTasks notifyTasks = notifyTasksRepository.findByRequestNoAndTaskCode(request.getRequestNo(),Constants.TaskCode.CLASSROOM_COPY_RESULT);if (null == notifyTasks) {if (log.isWarnEnabled()) {log.warn("请求流水号{}不存在", request.getRequestNo());}
}
//发送异步事件

3、事件订阅者

    @Subscribe@AllowConcurrentEvents@Transactional(rollbackFor = Throwable.class)public void onNotifySuccessEvent(NotifySuccessEvent event) {if (null == event || null == event.getTaskCode()) {return;}if (log.isInfoEnabled()) {log.info("订阅异步通知结果事件,详情={}", JsonUtils.toJsonString(event));}switch (event.getTaskCode()) {case Constants.TaskCode.CLASSROOM_COPY_RESULT:// 解析任务的入参final ClassroomCopyRequest copyRequest = JSON.parseObject(event.getRequestJson(), ClassroomCopyRequest.class);// 解析回调报文final ClassroomCopyResultNotify copyResponse = JSON.parseObject(event.getResponseJson(), ClassroomCopyResultNotify.class);// 调用业务逻辑处理break;default:break;}}

4、业务逻辑处理

经过上一步对参数和回调的解析,接下来就是不同业务的处理了。

处理完成后,删除任务即可。

// 业务处理
...
...
...
// 处理成功,删除定时任务
notifyTasksRepository.deleteByRequestNoAndTaskCode(requestNo, Constants.TaskCode.CLASSROOM_COPY_RESULT);

5、发起对外部服务的请求

ClassroomCopyRequest request = ClassroomCopyRequest.builder().requestNo(requestNo).classroomId(sourceClassroomId).lectureNo(lectureNo).courseNo(courseNo).taskCode(taskCode).build();// notify方法,发起Http请求,如果失败,则会像下面的语句一样,保存至定时任务表
notifyTasksService.notify(taskCode, requestNo, url, gson.toJson(request));// 保存至定时任务表
notifyTasksService.schedule(Constants.TaskCode.CLASSROOM_COPY_RESULT, requestNo,queryUrl, gson.toJson(request));
  • schedule()方法的定义见下:
public void schedule(String taskCode, String requestNo, String notifyUrl, String notifyParams)
  • 这里的设计思路是,发起外部服务的请求,同时保存主动查询的任务到库里。
  • 外部服务处理完成,回调我们的时候,需要把主动查询的任务届时删除。(防止不必要的查询)
  • 主动查询复制结果的任务,必须在发起请求的时候,同时就保存到库表里。这是因为回调的报文中只有请求流水号requestNo,在处理回调的时候,需反查出本任务记录。

四、任务的增删查改

这一小节,我们试着总结下对任务的操作。

1、新增

向外部服务发起请求的时候,保存主动查询的任务(任务内容见notifyParams)

2、删除

业务逻辑处理完成后,删除主动查询的任务。否则定时任务会一直轮询该任务,浪费性能。

3、查询

分布式定时任务,cron表达式规则下,每次查询N条待处理的任务列表。

4、修改

主动查询结果的任务,如果处理出现失败的时候,则需要更新下一次重试时间,支持指数级的退避重试。

五、总结

本文试着通过一个任务表,向你表述如何实现重试补偿,以实现跨服务间的最终一致性。

http://www.yayakq.cn/news/173621/

相关文章:

  • 专业网站设计怎么做企业做网站需要什么资料
  • 网站平台方案设计html 网站
  • 广州新业建设管理有限公司网站设计师 必备的网站
  • 家政网站建设方案分析优化网站的步骤案列
  • 重庆律师网站建设北京三快在线科技有限公司
  • 企业网站开发模型图深圳seo网站优化
  • 免费自助创建网站做网站asp用什么软件
  • 网站建设尾款结算申请wordpress网站翻译插件
  • 长沙市云网站建设免费虚拟主机控制面板
  • 西安高端品牌网站建设网站建设与维护新的体会
  • wordpress网站文件管理oa系统有哪些
  • 怎么生成网站源代码企业招聘广告模板
  • 广州市建设工程检测中心网站广州海珠网站设计
  • 安徽建设工程造价信息网站网站建设和推广需要多少费用
  • 电商网站开发面临的技术问题演出备案在哪里查询
  • 江门做网站软件搞笑资讯网站源码
  • 网站建设 599wordpress ninety
  • 网站建设方案主要有wordpress显示用户无效
  • 网站建设 专家服务器搭建网站环境
  • 医院网站建设需要注意什么西安产品设计公司有哪些
  • 呼和浩特网站建设网络公司品牌官网方案
  • 做一钓鱼网站专门做孕婴用品的网站
  • 乐清手机网站优化推广网站建设的商品分类编码
  • 免费asp企业网站源码企业营销型网站建设厂家
  • dedecms视频网站模板测速网站怎么做
  • 做网站需要收付款功能吗网站怎么做商家定位
  • 电子商务网站建设主管的策划案网站地址格式
  • 南阳做那个网站好福州专业网站制作设计
  • 提供网站技术学校学网页设计需要自带电脑吗
  • 网站建设完成以后应该怎样备份下载app至手机