Pipeline模式应用
本文记录Pipeline设计模式在业务流程编排中的应用
前言
Pipeline模式意为管道模式,又称为流水线模式。旨在通过预先设定好的一系列阶段来处理输入的数据,每个阶段的输出即是下一阶段的输入。
本案例通过定义PipelineProduct(管道产品),PipelineJob(管道任务),PipelineNode(管道节点),完成一整条流水线的组装,并将“原材料”加工为“商品”。其中管道产品负责承载各个阶段的产品信息;管道任务负责不同阶段对产品的加工;管道节点约束了管道产品及任务的关系,通过信号量定义了任务的执行方式。
依赖
工具依赖如下
<!-- 工具类大全 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>最新版本</version>
</dependency>
编程示例
1. 管道产品定义
package com.example.demo.pipeline.model;
/**
* 管道产品接口
*
* @param <S> 信号量
* @author
* @date 2023/05/15 11:49
*/
public interface PipelineProduct<S> {
}
2. 管道任务定义
package com.example.demo.pipeline.model;
/**
* 管道任务接口
*
* @param <P> 管道产品
* @author
* @date 2023/05/15 11:52
*/
@FunctionalInterface
public interface PipelineJob<P> {
/**
* 执行任务
*
* @param product 管道产品
* @return {@link P}
*/
P execute(P product);
}
3. 管道节点定义
package com.jd.baoxian.mall.market.service.pipeline.model;
import java.util.function.Predicate;
/**
* 管道节点定义
*
* @param <S> 信号量
* @param <P> 管道产品
* @author
* @date 2023/05/15 11:54
*/
public interface PipelineNode<S, P extends PipelineProduct<S>> {
/**
* 节点组装,按照上个管道任务传递的信号,执行 pipelineJob
*
* @param pipelineJob 管道任务
* @return {@link PipelineNode}<{@link S}, {@link P}>
*/
PipelineNode<S, P> flax(PipelineJob<P> pipelineJob);
/**
* 节点组装,按照传递的信号,判断当前管道的信号是否相等,执行 pipelineJob
*
* @param signal 信号
* @param pipelineJob 管道任务
* @return {@link PipelineNode}<{@link S}, {@link P}>
*/
PipelineNode<S, P> flax(S signal, PipelineJob<P> pipelineJob);
/**
* 节点组装,按照传递的信号,判断当前管道的信号是否相等,执行 pipelineJob
*
* @param predicate 信号
* @param pipelineJob 管道任务
* @return {@link PipelineNode}<{@link S}, {@link P}>
*/
PipelineNode<S, P> flax(Predicate<S> predicate, PipelineJob<P> pipelineJob);
/**
* 管道节点-任务执行
*
* @param product 管道产品
* @return {@link P}
*/
P execute(P product);
}
4. 管道产品、任务,节点的实现
4.1 管道产品
package com.example.demo.pipeline.factory;
import com.example.demo.model.request.DemoReq;
import com.example.demo.model.response.DemoResp;
import com.example.demo.pipeline.model.PipelineProduct;
import lombok.*;
/**
* 样例-管道产品
*
* @author
* @date 2023/05/15 14:04
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DemoPipelineProduct implements PipelineProduct<DemoPipelineProduct.DemoSignalEnum> {
/**
* 信号量
*/
private DemoSignalEnum signal;
/**
* 产品-入参及回参
*/
private DemoProductData productData;
/**
* 异常信息
*/
private Exception exception;
/**
* 流程Id
*/
private String tradeId;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class DemoProductData {
/**
* 待验证入参
*/
private DemoReq userRequestData;
/**
* 待验证回参
*/
private DemoResp userResponseData;
}
/**
* 产品-信号量
*
* @author
* @date 2023/05/15 13:54
*/
@Getter
public enum DemoSignalEnum {
/**
*
*/
NORMAL(0, "正常"),
/**
*
*/
CHECK_NOT_PASS(1, "校验不通过"),
/**
*
*/
BUSINESS_ERROR(2, "业务异常"),
/**
*
*/
LOCK_ERROR(3, "锁处理异常"),
/**
*
*/
DB_ERROR(4, "事务处理异常"),
;
/**
* 枚举码值
*/
private final int code;
/**
* 枚举描述
*/
private final String desc;
/**
* 构造器
*
* @param code
* @param desc
*/
DemoSignalEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
}
}
4.2 管道任务(抽象类)
package com.example.demo.pipeline.factory.job;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.json.JSONUtil;
import com.example.demo.pipeline.factory.DemoPipelineProduct;
import com.example.demo.pipeline.model.PipelineJob;
import lombok.extern.slf4j.Slf4j;
/**
* 管道任务-抽象层
*
* @author
* @date 2023/05/15 19:48
*/
@Slf4j
public abstract class AbstractDemoJob implements PipelineJob<DemoPipelineProduct> {
/**
* 公共执行逻辑
*
* @param product 产品
* @return
*/
@Override
public DemoPipelineProduct execute(DemoPipelineProduct product) {
DemoPipelineProduct.DemoSignalEnum newSignal;
try {
newSignal = execute(product.getTradeId(), product.getProductData());
} catch (Exception e) {
product.setException(e);
newSignal = DemoPipelineProduct.DemoSignalEnum.BUSINESS_ERROR;
}
product.setSignal(newSignal);
defaultLogPrint(product.getTradeId(), product);
return product;
}
/**
* 子类执行逻辑
*
* @param tradeId 流程Id
* @param productData 请求数据
* @return
* @throws Exception 异常
*/
abstract DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception;
/**
* 默认的日志打印
*/
public void defaultLogPrint(String tradeId, DemoPipelineProduct product) {
if (!DemoPipelineProduct.DemoSignalEnum.NORMAL.equals(product.getSignal())) {
log.info("流水线任务处理异常:流程Id=【{}】,信号量=【{}】,任务=【{}】,参数=【{}】", tradeId, product.getSignal(),
ClassUtil.getClassName(this, true), JSONUtil.toJsonStr(product.getProductData()), product.getException());
}
}
}
4.3 管道节点
package com.example.demo.pipeline.factory;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.json.JSONUtil;
import com.example.demo.pipeline.model.PipelineJob;
import com.example.demo.pipeline.model.PipelineNode;
import lombok.extern.slf4j.Slf4j;
import java.util.function.Predicate;
/**
* 审核-管道节点
*
* @author
* @date 2023/05/15 14:32
*/
@Slf4j
public class DemoPipelineNode implements PipelineNode<DemoPipelineProduct.DemoSignalEnum, DemoPipelineProduct> {
/**
* 下一管道节点
*/
private DemoPipelineNode next;
/**
* 当前管道任务
*/
private PipelineJob<DemoPipelineProduct> job;
/**
* 节点组装,按照上个管道任务传递的信号,执行 pipelineJob
*
* @param pipelineJob 管道任务
* @return {@link DemoPipelineNode}
*/
@Override
public DemoPipelineNode flax(PipelineJob<DemoPipelineProduct> pipelineJob) {
return flax(DemoPipelineProduct.DemoSignalEnum.NORMAL, pipelineJob);
}
/**
* 节点组装,按照传递的信号,判断当前管道的信号是否相等,执行 pipelineJob
*
* @param signal 信号
* @param pipelineJob 管道任务
* @return {@link DemoPipelineNode}
*/
@Override
public DemoPipelineNode flax(DemoPipelineProduct.DemoSignalEnum signal, PipelineJob<DemoPipelineProduct> pipelineJob) {
return flax(signal::equals, pipelineJob);
}
/**
* 节点组装,上个管道过来的信号运行 predicate 后是true的话,执行 pipelineJob
*
* @param predicate
* @param pipelineJob
* @return
*/
@Override
public DemoPipelineNode flax(Predicate<DemoPipelineProduct.DemoSignalEnum> predicate,
PipelineJob<DemoPipelineProduct> pipelineJob) {
this.next = new DemoPipelineNode();
this.job = (job) -> {
if (predicate.test(job.getSignal())) {
return pipelineJob.execute(job);
} else {
return job;
}
};
return next;
}
/**
* 管道节点-任务执行
*
* @param product 管道产品
* @return
*/
@Override
public DemoPipelineProduct execute(DemoPipelineProduct product) {
// 执行当前任务
try {
product = job == null ? product : job.execute(product);
return next == null ? product : next.execute(product);
} catch (Exception e) {
log.error("流水线处理异常:流程Id=【{}】,任务=【{}】,参数=【{}】", product.getTradeId(), ClassUtil.getClassName(job, true), JSONUtil.toJsonStr(product.getProductData()), product.getException());
return null;
}
}
}
5. 业务实现
通过之前的定义,我们已经可以通过Pipeline完成流水线的搭建,接下来以“审核信息提交”这一业务场景,完成应用。
5.1 定义Api、入参、回参
package com.example.demo.api;
import com.example.demo.model.request.DemoReq;
import com.example.demo.model.response.DemoResp;
import com.example.demo.pipeline.factory.PipelineForManagerSubmit;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 演示-API
*
* @author
* @date 2023/08/06 16:27
*/
@Service
public class DemoManagerApi {
/**
* 管道-审核提交
*/
@Resource
private PipelineForManagerSubmit pipelineForManagerSubmit;
/**
* 审核提交
*
* @param requestData 请求数据
* @return {@link DemoResp}
*/
public DemoResp managerSubmit(DemoReq requestData) {
return pipelineForManagerSubmit.managerSubmitCheck(requestData);
}
}
package com.example.demo.model.request;
/**
* 演示入参
*
* @author
* @date 2023/08/06 16:33
*/
public class DemoReq {
}
package com.example.demo.model.response;
import lombok.Data;
/**
* 演示回参
*
* @author
* @date 2023/08/06 16:33
*/
@Data
public class DemoResp {
/**
* 成功标识
*/
private Boolean success = false;
/**
* 结果信息
*/
private String resultMsg;
/**
* 构造方法
*
* @param message 消息
* @return {@link DemoResp}
*/
public static DemoResp buildRes(String message) {
DemoResp response = new DemoResp();
response.setResultMsg(message);
return response;
}
}
5.2 定义具体任务
假定审核提交的流程需要包含:参数验证、加锁、解锁、事务提交
package com.example.demo.pipeline.factory.job;
import cn.hutool.json.JSONUtil;
import com.example.demo.model.request.DemoReq;
import com.example.demo.pipeline.factory.DemoPipelineProduct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 加锁-实现层
*
* @author
* @date 2023/05/17 17:00
*/
@Service
@Slf4j
public class CheckRequestLockJob extends AbstractDemoJob {
/**
* 子类执行逻辑
*
* @param tradeId 流程Id
* @param productData 请求数据
* @return
* @throws Exception 异常
*/
@Override
DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception {
DemoReq userRequestData = productData.getUserRequestData();
log.info("任务[{}]加锁,线程号:{}", JSONUtil.toJsonStr(userRequestData), tradeId);
return DemoPipelineProduct.DemoSignalEnum.NORMAL;
}
}
package com.example.demo.pipeline.factory.job;
import cn.hutool.json.JSONUtil;
import com.example.demo.model.request.DemoReq;
import com.example.demo.pipeline.factory.DemoPipelineProduct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 解锁-实现层
*
* @author
* @date 2023/05/17 17:00
*/
@Service
@Slf4j
public class CheckRequestUnLockJob extends AbstractDemoJob {
/**
* 子类执行逻辑
*
* @param tradeId 流程Id
* @param productData 请求数据
* @return
* @throws Exception 异常
*/
@Override
DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception {
DemoReq userRequestData = productData.getUserRequestData();
log.info("任务[{}]解锁,线程号:{}", JSONUtil.toJsonStr(userRequestData), tradeId);
return DemoPipelineProduct.DemoSignalEnum.NORMAL;
}
}
package com.example.demo.pipeline.factory.job;
import cn.hutool.json.JSONUtil;
import com.example.demo.model.request.DemoReq;
import com.example.demo.pipeline.factory.DemoPipelineProduct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 审核-参数验证-实现类
*
* @author
* @date 2023/05/15 19:50
*/
@Slf4j
@Component
public class ManagerCheckParamJob extends AbstractDemoJob {
/**
* 执行基本入参验证
*
* @param tradeId
* @param productData 请求数据
* @return
*/
@Override
DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) {
/*
* 入参验证
*/
DemoReq userRequestData = productData.getUserRequestData();
log.info("任务[{}]入参验证,线程号:{}", JSONUtil.toJsonStr(userRequestData), tradeId);
// 非空验证
// 有效验证
// 校验通过,退出
return DemoPipelineProduct.DemoSignalEnum.NORMAL;
}
}
package com.example.demo.pipeline.factory.job;
import cn.hutool.json.JSONUtil;
import com.example.demo.model.request.DemoReq;
import com.example.demo.model.response.DemoResp;
import com.example.demo.pipeline.factory.DemoPipelineProduct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 审核-信息提交-业务实现
*
* @author
* @date 2023/05/12 14:36
*/
@Service
@Slf4j
public class ManagerSubmitJob extends AbstractDemoJob {
/**
* 子类执行逻辑
*
* @param tradeId 流程Id
* @param productData 请求数据
* @return
* @throws Exception 异常
*/
@Override
DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception {
DemoReq userRequestData = productData.getUserRequestData();
try {
/*
* DB操作
*/
log.info("任务[{}]信息提交,线程号:{}", JSONUtil.toJsonStr(userRequestData), tradeId);
productData.setUserResponseData(DemoResp.buildRes("成功"));
} catch (Exception ex) {
log.error("审核-信息提交-DB操作失败,入参:{}", JSONUtil.toJsonStr(userRequestData), ex);
throw ex;
}
return DemoPipelineProduct.DemoSignalEnum.NORMAL;
}
}
5.3 完成流水线组装
针对入回参转换,管道任务执行顺序及执行信号量的构建
package com.example.demo.pipeline.factory;
import com.example.demo.model.request.DemoReq;
import com.example.demo.model.response.DemoResp;
import com.example.demo.pipeline.factory.job.CheckRequestLockJob;
import com.example.demo.pipeline.factory.job.CheckRequestUnLockJob;
import com.example.demo.pipeline.factory.job.ManagerCheckParamJob;
import com.example.demo.pipeline.factory.job.ManagerSubmitJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Objects;
import java.util.UUID;
/**
* 管道工厂入口-审核流水线
*
* @author
* @date 2023/05/15 19:52
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PipelineForManagerSubmit {
/**
* 审核-管道节点
*/
private final DemoPipelineNode managerSubmitNode = new DemoPipelineNode();
/**
* 审核-管道任务-提交-防刷锁-加锁
*/
private final CheckRequestLockJob checkRequestLockJob;
/**
* 审核-管道任务-提交-防刷锁-解锁
*/
private final CheckRequestUnLockJob checkRequestUnLockJob;
/**
* 审核-管道任务-参数验证
*/
private final ManagerCheckParamJob managerCheckParamJob;
/**
* 审核-管道任务-事务操作
*/
private final ManagerSubmitJob managerSubmitJob;
/**
* 组装审核的处理链
*/
@PostConstruct
private void assembly() {
assemblyManagerSubmit();
}
/**
* 组装处理链
*/
private void assemblyManagerSubmit() {
managerSubmitNode
// 参数验证及填充
.flax(managerCheckParamJob)
// 防刷锁
.flax(checkRequestLockJob)
// 事务操作
.flax(managerSubmitJob)
// 锁释放
.flax((ignore) -> true, checkRequestUnLockJob);
}
/**
* 审核-提交处理
*
* @param requestData 入参
* @return
*/
public DemoResp managerSubmitCheck(DemoReq requestData) {
DemoPipelineProduct initialProduct = managerSubmitCheckInitial(requestData);
DemoPipelineProduct finalProduct = managerSubmitNode.execute(initialProduct);
if (Objects.isNull(finalProduct) || Objects.nonNull(finalProduct.getException())) {
return DemoResp.buildRes("未知异常");
}
return finalProduct.getProductData().getUserResponseData();
}
/**
* 审核-初始化申请的流水线数据
*
* @param requestData 入参
* @return 初始的流水线数据
*/
private DemoPipelineProduct managerSubmitCheckInitial(DemoReq requestData) {
// 初始化
return DemoPipelineProduct.builder()
.signal(DemoPipelineProduct.DemoSignalEnum.NORMAL)
.tradeId(UUID.randomUUID().toString())
.productData(DemoPipelineProduct.DemoProductData.builder().userRequestData(requestData).build())
.build();
}
}
总结
本文重点为管道模式的抽象与应用,上述示例仅为个人理解。实际应用中,此案例长于应对各种规则冗杂的业务场景,便于规则编排。
待改进点:
各个任务其实隐含了执行的先后顺序,此项内容可进一步实现;
针对最后“流水线组装”这一步,可通过配置描述的方式,进一步抽象,从而将变动控制在每个“管道任务”的描述上,针对规则项做到“可插拔”式处理。
作者:京东保险 侯亚东
来源:京东云开发者社区 转载请注明来源
Pipeline模式应用的更多相关文章
- Pipeline模式(netty源码死磕6)
精进篇:netty源码死磕6 巧夺天工--Pipeline模式揭秘 1. 巧夺天工--Pipeline模式揭秘 1.1. Pipeline模式简介 管道的发名者叫,Malcolm Douglas M ...
- 奇思妙想-java实现另类的pipeline模式
磕叨 在公司做项目是见到前辈们写的一端任务链的代码,大概如下 Runnable task = new TaskA(new TaskB(new TaskC(new taskD()))); task.ru ...
- Golang 实现 Redis(6): 实现 pipeline 模式的 redis 客户端
本文是使用 golang 实现 redis 系列的第六篇, 将介绍如何实现一个 Pipeline 模式的 Redis 客户端. 本文的完整代码在Github:Godis/redis/client 通常 ...
- Pipeline模式与Factory+Provider模式的应用
前言 我正在写FastGithub这个小麻雀项目,里面主要涉及了Pipeline模式和Factory+Provider模式,这两种设计模式,让这个项目在"ip扫描"和"i ...
- 腾讯新闻基于 Flink PipeLine 模式的实践
摘要 :随着社会消费模式以及经济形态的发展变化,将催生新的商业模式.腾讯新闻作为一款集游戏.教育.电商等一体的新闻资讯平台.服务亿万用户,业务应用多.数据量大.加之业务增长.场景更加复杂,业务对实时 ...
- saltstack系列(四)——zmq Paraller Pipeline模式
push/pull模式 push/pull模式,这是一个什么模式呢?战争时期,食物紧缺,实行配给制,大家都排好队,有人专门发放食物,前一个人领取了食物,后一个人跟上继续领取食物,这个push端就是发放 ...
- My.Ioc 代码示例——谈一谈如何实现装饰器模式,兼谈如何扩展 My.Ioc
装饰器模式体现了一种“组合优于继承”的思想.当我们要动态为对象增加新功能时,装饰器模式往往是我们的好帮手. 很多后期出现的 Ioc 容器都为装饰器模式提供了支持,比如说 Autofac.在 My.Io ...
- 浅谈管道模型(Pipeline)
本篇和大家谈谈一种通用的设计与处理模型--Pipeline(管道). Pipeline简单介绍 Pipeline模型最早被使用在Unix操作系统中.据称,假设说Unix是计算机文明中最伟大的发明,那么 ...
- [持续交付实践] pipeline使用:Multibranch Pipeline
前言 在探讨multiBranch Pipeline之前,很有必要先探讨下如何制定有效的代码分支管理规范,使用高效的版本控制系统,并对构建产物及其依赖进行管理.我们首先要强调,需要进行版本控制的不仅是 ...
- 使用jenkins pipeline,并发selenium测试 --- 你值得了解
一.契机 相信很多使用selenium进行UI测试,再对接jenkins时,都是简单的在jenkins上将命令输入就完事了. 但是,相信你一定会遇到以下问题: 1.你需要同时跑不同文件或不同类的用例, ...
随机推荐
- Win11+ VS2022编译 FFmpeg6.0 静态库
目录 编译前言 为什么项目编译? 前期准备 环境配置 ffmpeg外部库 额外的编译选项-for渲染 opengl (需要glext) ffnvcodec (需要nv-codec-headers) A ...
- vue 实现 pdf 预览功能
1 技术背景 1.1 Vue.js 简介和特点 Vue.js 是一种用于构建用户界面的渐进式框架.它具有以下特点: 易学易用:Vue.js 的 API 设计简单直观,使得开发者可以快速上手. 响应式数 ...
- 使用kube-bench检测Kubernetes集群安全
目录 一.系统环境 二.前言 三.CIS (Center for Internet Security)简介 四.什么是Kube-Bench? 五.使用kube-bench检测不安全的设置 5.1 手动 ...
- 探索ChatGPT的Fine-tuning和Embeddings
1.概述 今天我们将深入探索ChatGPT的两项核心技术:Fine-tuning(微调)和Embeddings(嵌入).这些技术在现代自然语言处理领域扮演着至关重要的角色,为模型的性能提升和适应特定任 ...
- 《Kali渗透基础》05. 主动信息收集(二)
@ 目录 1:端口扫描 2:UDP 扫描 2.1:Scapy 2.2:nmap 3:半开放扫描 3.1:Scapy 3.2:nmap 3.3:hping3 4:全连接扫描 4.1:Scapy 4.2: ...
- 微服务架构|go-zero 的自适应熔断器
原文链接: go-zero 的自适应熔断器 上篇文章我们介绍了微服务的限流,详细分析了计数器限流和令牌桶限流算法,这篇文章来说说熔断. 熔断和限流还不太一样,限流是控制请求速率,只要还能承受,那么都会 ...
- js监控微信浏览器的自带的返回事件(延迟解决微信返回立即执行popstate事件)
/** * 浏览器回退事件监听 */ var listenerBackHandler = { param: { isRun: false, //防止微信返回立即执行popstate事件 }, list ...
- KRPano插件一键解密大师 支持最新版KRPano XML/JS解密 ,支持分析下载静态/动态网站资源
KRPano插件一键解密大师,可以一键解密KRPano的XML/JS插件,并可以分析下载静态和动态网站的所有资源.软件下载安装即可使用,解密仅需鼠标一键点击即可,无需配置任何开发环境,方便全景开发人员 ...
- 你准备好了吗,9月19日Java21要来了
前言 9月份的TIOBE编程语言榜单已公布,Python依然是第一,Java第四. 而这个月还有一个重要的事情,就是9月19日Java21将会全面发布,一段时间没关注的我一口老血喷在屏幕上. 我记得我 ...
- 1-MySQL数据库的安装和基础语法介绍
1.MySQL是什么? MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品.它是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最 ...