首页 > 其他分享 >做好设计:流程设计

做好设计:流程设计

时间:2024-01-14 10:33:06浏览次数:28  
标签:异步 封装 并发 检测 流程 复用 做好 设计

没有什么是一成不变的。


在“做好设计:存储设计基础”一文中,探讨了软件设计中的关键环节:存储设计。存储设计完成之后,要进行流程设计。

要做好流程设计,首先要熟悉各种基本流程模式。可阅:““驯服”业务流程:盘点业务开发中的常见流程模式 ”。

熟悉常用流程模式之后,就需要灵活加以组合,来构建复杂的业务流程。

业务流程需要满足的若干特性

  • 完整性:构建完整的业务流程,从数据流入到数据存储或数据流出,畅通无阻。
  • 清晰性:流程表达要足够清晰,容易理解。
  • 健壮性:对于流程中的各种错误,要确保合理地处理,避免流程中断,或者僵死在某个状态。
  • 性能:流程在可接受的时间内完成。
  • 稳定性: 流程在大数据量或大流量的压力下,不会阻塞崩溃。
  • 可扩展性:如果要在流程中添加新的子流程处理,能否很好地添加而不影响已有。

构建复杂流程的方法

如何构建复杂的业务流程呢? 运用“分解-抽象-封装-复用-组合-关联-重构-函数式编程”的思想和方法来搞定。

分解

分解,即是将具有独立语义的子流程和流程逻辑单元分离出来。

  • 将整体流程分解为若干个子流程;
  • 将每个子流程进一步分解为更小粒度的流程逻辑单元;
  • 一直分解,直至每个流程逻辑单元简单而清晰(只做一件事)。

构建流程逻辑单元的模式可以对应到““驯服”业务流程:盘点业务开发中的常见流程模式 ”一文中的基本流程模式。

分解这一步非常重要:

  • 将整体流程中的子流程分离出来再组合,可使其依赖和组合关系分割得更清晰,使凌乱的整体流程逻辑整通顺,变得整洁有序;
  • 分解整体流程的过程中可以增进对整体流程的理解;
  • 为抽象、封装和复用打下基础。

分解得足够细,就更容易抽象和封装,更容易复用。

抽象

在分解的基础上,将关键信息提炼出来,将流程共性提炼出来,封装成可复用的标准对象和方法。

  • 关键信息提炼;
  • 共性流程提炼。

标准对象提炼非常重要,体现了对流程中关键信息的认知。流程通常是围绕关键信息而构建起来的。

流程共性抽象亦非常重要,将子流程的共性抽离出来,更容易复用,减少重复代码及伴随的BUG。

比如恶意进程检测流程的关键信息是什么? MD5, SHA256, filePath。准确地构造这 3 个信息,基本就把握了恶意进程检测流程的关键。

比如各种检测流程有什么共性流程?获取规则信息、构建告警详情、存储告警详情、白名单检测。

抽象的结果通常是若干个可复用的独立语义的概念。而封装则需要将概念转换为具体的实现形式。

封装

抽象好后,就需要设计良好的可复用的封装形式。

  • 关键信息封装成标准对象(可对接不同业务)。
  • 子流程共性封装成流程组件;
  • 流程组件封装成高层次的流程组块。

良好的封装可以减少重复代码的出现,让流程更清晰而容易理解,减少认知负担。

封装单元可大可小。封装单元可以是一个对象、方法、组件类,也可以是多个类组成的库或框架,或一个相对完整的流程。

比如知道恶意进程检测的关键信息 MD5, SHA256, filePath,就可以把这三个信息封装成一个标准对象 VirusCheckTaskInfo。

比如通用入侵检测流程里的获取规则信息、检测结果填充、告警详情构建、告警存储、发送大数据,都可以封装成流程组件,可以在不同的检测流程里复用。

复用

  • 提炼流程组件的通用部分进行复用;
  • 提炼流程组块的通用部分进行复用。

有了封装的基础,复用就比较容易了。只需考虑在具体的业务场景如何去引用,或者对现有封装做一点小的改造。

流程组块是理解和构建复杂流程的基础。越是能够在高层次流程组块思考和构建流程,就能驾驭越复杂的流程。

有了可复用的封装的基础,接下来就只需要考虑封装单元的组合和关联。

组合

  • 在可复用的流程组件和流程组块的级别上进行组合。

比如上述多个流程组件可组合成通用入侵检测流程的核心部分,而这个通用入侵检测流程的核心部分又在更复杂的检测流程里复用。比如过各种已有引擎检测库,就几个字,却代表着一个包含多个引擎库的复杂的恶意进程检测流程。

组合通常是串行、并发的各种流程组合模式。这就运用到““驯服”业务流程:盘点业务开发中的常见流程模式 ”一文总结的各种业务流程模式及组合模式。

关联

  • 将有关联的信息通过唯一标识关联起来;
  • 通过实体表的唯一标识字段的外键关联来建立。

比如脱壳进程的文件和原进程的文件信息,就可以通过在脱壳进程文件记录里关联原进程文件记录的 MD5 或 SHA256 或 fileId 来实现。

重构

  • 反复运用上述方法,对已有流程进行改小步重构,是一种很好的理解和梳理复杂流程的方式;
  • 小步重构技巧:每次抽离一部分流程,然后写一个短函数进行调用。

函数式编程

函数式编程是一种强大的编程思想和编程技艺,可以很好地将共性和差异抽离,让整个流程更加顺畅。

可阅:“函数式+泛型编程:编写简洁可复用的代码

质量属性

性能

常用的提升流程性能的手段如下:

  • 快速过滤或去重。
  • 正确的索引(必要索引,联合索引,减少不必要的索引及索引字段)。
  • 内存数据结构和算法降重优化。
  • 加缓存。
  • 并发(串行变并发,线程池)。
  • 异步(同步变异步,将次要部分从主体流程中分离)。
  • 批量处理(单个变批量)。
  • 确保表设计合理(大小维度的表分开)。
  • 合并重复流程(避免重复文件上传或下载、重复创建同一个对象)。
  • 合并重复调用(重复查询同一条记录)。
  • 加检测结果库(黑白名单),避免重复检测。
  • 精简流程
  • 使用 arthas trace 命令测量方法 RT,找到性能热点区域,专项优化。

稳定性

  • 对接口添加限流。
  • 使用消息中间件削峰填谷。

可扩展性

  • 使用 Dispatcher-Handler 模式。多线程并发模式中,可以在 Dispatcher 中分发任务给不同的 Handler ,Dispatcher 在主线程中执行,不同的 Handler 在不同的线程中执行。 Channel 并发模式中,可以通过 channel 将任务分给不同的 channel ,不同的 Handler 消费不同的 channel 里的任务独立执行。

业务流程构建的四个注意事项

  • 闭环:如果流程有任务状态,则必须让任务状态最终落在完成或失败,避免滞留在中间状态。
  • 并发:多个线程访问共享资源,要保证数据正确性,必要的地方要加同步。
  • 异步:如果流程比较耗时,则需要拆分成异步,保证响应的体验。
  • 多节点: 考虑多节点情形的处理,必要时候需要分布式锁。

闭环问题

当流程中的某个操作失败后,就会导致整体流程无法完成,停滞在某个点上,无法闭环。

闭环的解决思路:

  • 直接失败。对于不能重试(可能导致资损)的情形,直接失败是最简单有效的办法。
  • 重试、设置最大失败次数。对于可重试场景,可间隔一段时间重试一次,达到最大失败次数则设置为终态失败。
  • 保存点。设置保存点,失败之后可以从保存点重续执行,最终达到终态成功。
  • 回滚。作为事务,失败时回滚到最初一致状态。
  • 超时失败定时任务机制。如果超过指定时间,则更新任务状态为失败,原因是超时。超时需要与更新时间做比较。

并发

  • 如果有大量数据要获取或者处理,通常需要采用并发来提升性能。
  • 并发线程获取要设置超时时间,避免网络不佳时长时间阻塞。
  • 如果有多个线程或者多台机器同时访问同一条记录,则需要加锁(行锁或分布式锁)。

异步

  • 不同端的通信,通常采用异步的方式。比如前端是通过 ajax 来异步拉取服务端数据。服务端要发送指令给客户端,也是先下发指令,等客户端完成后,返回给服务端回调消息进行后置处理。
  • 耗时操作。耗时操作如果采用同步,则很容易超时导致失败。一般采用异步来分离。
  • 高并发操作。高并发情况下,为保证核心流程的TPS,可以将非核心流程读写用异步分离出去。
  • 次要操作。一个长流程中,有一些次要操作,不影响最终结果,可以通过异步分离出去单独处理。
  • 异步要注意多操作间的时间差问题。两个操作是异步的,在一个操作完成后在页面查看另一个操作的结果时可能会不一致。比如上报一条告警同时异步上传一个文件。当告警入库展示在页面时,文件上传可能未完成,这时候文件是无法下载的。还有一种情形是两个线程同时更新同一条记录的状态,一个线程可能会覆写另一个线程写入的状态。这个写入顺序的不确定会导致最终记录状态的不确定。

多节点

  • 避免读写本地缓存。因为缓存只在单个 JVM 里,其它机器上读不到该缓存,就会导致问题。
  • 只读本地缓存。本地缓存通常从公共数据库中获取和建立。
  • 采用外部存储来同步。比如使用 redis 来存储会话、做分布式锁。
  • 采用中间件来通信和解耦。

流程不是一成不变的

随着需求变更,流程会反复更改。因此不要局限于流程的现有实现。当遇到不合理之处时,大胆质疑和改造优化,是为上策。

小结

流程千变万化。掌握基本的流程模式和复杂流程的构建方法,熟悉一些注意事项,辅以并发、异步、同步、消息中间件通信,没有什么流程是搞不定的。

标签:异步,封装,并发,检测,流程,复用,做好,设计
From: https://www.cnblogs.com/lovesqcc/p/17963415

相关文章

  • Java多线程编程实战指南(设计模式篇)PDF
    随着CPU多核时代的到来,多线程编程在充分利用计算资源、提高软件服务质量方面扮演了越来越重要的角色。而解决多线程编程中频繁出现的普遍问题可以借鉴设计模式所提供的现成解决方案。然而,多线程编程相关的设计模式书籍多采用C++作为描述语言,且书中所举的例子多与应用开发人员的......
  • 想做好项目,网工必看!
    上午好,我是老杨。做项目,贯穿一个网络工程师职业生涯的始终。不管是大项目还是小项目,做久了项目,都会形成一种自己的方法论。项目要划分,无非就是新网组建,旧网优化,以及网络排障三大类。不管新老网工,每个网工人都在做项目,大到全球范围,小到一间办公室,甚至外包出来的小派单,只要你在干活,你......
  • FIFO设计
    firstinfirstout,先进先出fifo是基于RAM进行设计的双端口RAM设计(16*8)如果大的RAM可以调用IPRAM的关键参数:深度和宽度moduledual_ram#(parameterADDR_WIDTH=4,parameterRAM_WIDTH=8,parameterDLY=1)(inputwire......
  • spring与设计模式之三代理模式
    部分内容引用:https://blog.csdn.net/shulianghan/article/details/119798155一、定义1.1定义对于现实生活中的代理,大家非常好理解。我们需要代理,主要因为几个原因:太忙-例如房产中介、代购目前对象不是自身可以直接接触的-例如托人办事、例如掏钱购买某种服务都可以理解为代......
  • CS5569芯片,CS556,设计资料,CS5569规格书,CS5569原理图,typec转HDMI 8K带PD方案
    集睿致远/ASL的CS5269是一款低成本、低功耗的半导体器件,通过USBType-C连接器将DisplayPort信号转换为HDMI2.1。这款创新的基于USBType-C的DisplayPort接收器具有高性能DSC解码器,集成的HDMI2.1发射器专门针对USBType-C到HDMI2.1转换器而设计,一个NG设备。通过CS5269的先进的解码/......
  • 设计模式之中介者模式
    1.定义多个对象之间通过一个中介者对象进行通信和协作,而不是直接相互交互2.口语化表述中介,这在生活中很常见,比如租房中介通常,有住房出租的房东有很多,需要租房的租客也很多,但是租客难以直接联系房东,这个时候租房中介这个职业就出现了房东将房屋登记到中介这里,租客来中介这里......
  • 从Bitcask存储模型谈超轻量级KV系统设计与实现
    Bitcask介绍Bitcask是一种“基于日志结构的哈希表”(ALog-StructuredHashTableforFastKey/ValueData)Bitcask最初作为分布式数据库Riak的后端出现,Riak中的每个节点都运行一个Bitcask实例,各自存储其负责的数据。抛开论文,我们先通过一篇博客#Bitcask—alog-struc......
  • 4- if 流程语句和案例
    '''流程控制特点:从上往下依次执行判断语句:通过判断决定做什么事情'''语法1if条件表达式:条件表达式的结果为True,则执行语句1,为False,则不执行语句1执行语句1if1==1:#如果1等于1print("1==1")#打印"1=1"语法2:if条件表达式:条件表达式的结果为True,则执行语句......
  • 金融疆界:支付系统渠道网关的创新设计(一)
    这是《百图解码支付系统设计与实现》专栏系列文章中的第(11.1)篇。点击上方关注,深入了解支付系统的方方面面。整个渠道网关的内容预计会分成5篇来讲:1)定位、术语、概要设计。2)领域模型、状态机设计。3)报文网关。4)文件网关。5)常见差异处理。本篇是其中的第1篇。主要讲清楚什么渠道网关......
  • k8s_Kubernetes 创建 pod 流程
    创建pod流程1.编写Pod配置文件:先创建一个YAML或JSON格式的Pod配置文件,文件中包含了Pod的元数据和规格。元数据包括Pod的名称、命名空间、标签等信息,规格包括容器的镜像、端口、环境变量等配置。2.使用kubectl命令创建Pod:在控制台或命令行中运行kubectlcreate......