• 建模杂谈系列153 程序的集成与自动化


    说明

    想了想,这块概念应该还是归为集成与自动化。软件工程的发展应该是借鉴硬件(半导体)的思路:在极小的体积和能耗下,实现高密度的集成,从而具有强大的功能。

    目前我的思考是:如何极大简化工作中的重复、机械以及不必要的劳动时间。

    内容

    1 回顾

    流程(函数)式编程

    我最早接触的应该就是函数式的编程,好处是可以探索非常深和细致的逻辑处理,缺点也很明显,大量的功能散乱,重复编写了很多次。

    后来开始尝试使用对象来整合相关的属性和功能,一开始是好的,但是后来事慢慢变多了之后,有些类一旦不常用,最后又是废弃。

    后来开始转向微服务。不得不说,这是一个很庞大的工程。基于Docker,我发现有很多事情变得可行。例如python的运行开发环境可以很容易保持一致,可以给自己搭建数据库,可以部署各种API服务。将很多功能抽象为微服务后,同时会编写接口文档,这样很多基础内容就真的固化下来。有错误维护起来也比较容易。

    但是微服务又带来了使用成本升高的问题。每个操作都要发起请求,写起来比较繁琐。而且有些接口名称和参数不太好记,又要查文档。

    所以,我又开始使用对象来集成这些操作。有些工具类的对象不必关心逻辑是怎么实现的,只是调用对应的微服务处理,这些纯粹就是为了简化操作;有些对象则开始尝试处理复杂的逻辑集成。

    对象开发好了,可以再发布为微服务;对象也可以随着应用,去调用整合好的微服务。

    感觉像是一个个圈圈,我稍微思考了一下,这应该是正常的,而且是可行的一种方式。

    2 一些对象

    Computation Network, 简称CN , 我给第一个命名为 CN001

    从数据存取、处理与应用的角度,列出几个对象(有些正在开发中)

    序号名称功能
    1SyncFile完成文件的压缩打包、上传和下载;依赖简单文件存取微服务。
    2WMongo完成mongo数据库的基本操作,增强功能(自动编号)以及自动分表和并发查询。 依赖的微服务有MongoAgent和GeneralTaskManager
    3WRedis目前只考虑用来在CN001内传递内存数据
    4X某数据处理过程
    5X某任务嗅探过程
    6BatchRun管理批次运行程序的对象

    数据的存取比我想的更琐碎和费时一些,当然也是基于CN001的结构决定的,分布式提供了更多的资源,但是同样的也带来的管理上的混乱。

    所以要进行一些规划,设定规范,以及做一些便于操作的工具(我觉得Handler 句柄很适合这个概念)。

    2.1 CN001主要以局域网的方式工作

    虽然通过透传,CN001实际上可以视为广域网,但事实上考虑大量数据的的传输和计算时,显然集中在几台高性能台式机上。目前我用千兆路由器把这几台机器连在一起(m1,m4,m5,m7)。

    数据和程序可能会部署在其中任何一台机器上,所以对象应该以local, lan_m1, lan_m4, lan_m5, lan_m7作为默认的命名/连接配置,当程序需要访问不同主机上的资源时只要切换一个参数就可以。

    2.2 主数据库Mongo(集群)

    之所以选择Mongo主要因为:

    • 1 吞吐比较快。Mongo本身会利用内存,可以视为半个内存数据库。当然即使限制了内存,也还是可以的。
    • 2 灵活。Mongo的字段比较灵活,可以和可Json的数据无缝衔接。这样无论是微服务还是算法,都可以很好的利用数据库持久化。

    至于集群,是一把双刃剑,但是又是分布式系统不可或缺的组成部分。

    一种是元数据集群。因为元数据很重要,而且不大,所以从数据灾备的角度,或者是稳定使用的角度,都应该有。

    另外就是用于存业务数据的集群。最好还是建副本集,读写分离之后,可以把负载有效的均摊到整个网络上。

    分片的话我觉得是可以手工做的。既有数据太大的原因,也有访问效率的原因。如果数据太大,可以在元数据集群中定义不同分片的位置,最简单的方式就是通过哈希模。总是去检索一个1亿的表肯定是会比并发查询10个1千万的表要慢很多,所以天然地就会有很多分表。

    WMongo对象则会将这些操作尽可能简化:

    • 1 实例缓存。首先会有一些规划,确定了一些应用之后,那么就会实例化一个对象,之后只要找到这个实例就可以直接对数据操作,而不用再去管数据库连接的问题。
    • 2 简化方法。常用的操作,直接封装为对象的方法,大幅简化操作。一则避免了时不时去查文档(这个还是有点浪费时间),另外则是一些组合操作。例如Query_with_ACK, 在取数后直接更新「任务状态字段」。
    • 3 使一些复杂操作可行。自动分表是比较麻烦的,而且之后的查询也要按照一些规则进行并行操作。

    既然确定了数据都使用Mongo来进行持久,就要尽可能的优化,使得这种方法足以满足瞬时大量的吞吐,又能满足长期的维护需求。 首先必须要承认数据库的吞吐一定比直接访问某个内存对象或者硬盘文件要慢(一个量级),但是现在通过M.2以及大内存的支持,完全基于数据库的数据处理效率上也是可行的。满足了这个短板,数据库的优势就会体现出来。

    2.3 辅助库 Redis

    Redis有一些功能是蛮不错的,正好和Mongo可以互补。

    • 1 缓存。这个真的很方便,可以大幅提高系统的承受能力。例如某个查询,将query做hash,然后将结果压成字符串。在某个ttl之内,同样的查询就直接返回。(Redis可以在设置变量值的时候同时设置过期时间,这样既能控制权限,也能释放内存空间。缓存至少在两方面有用:「缓存接口的查询」以及「用户的身份权限认证」
    • 2 队列。肯定不如kafka,但是用于简单的缓冲,是足够了。胜在简单。

    在每台机器上启动了Redis,也就意味着数据可以在CN001的不同主机间自由传播。

    Redis的每个字符变量最大是512M,对于大部分的数据是够了。如果还有更大的,做一个哈希模,例如分为10份,那么5G的一份数据已经很大了。所以可以认为Redis可以共享不同主机间的内存数据。这样的好处是当程序在一台主机产生了数据,不需要在经过保存文件、再上传和载入,直接从对应主机中获取即可。

    WRedis就预设好几台主机的设置,使用的时候就是无缝的。(实验下来的确很方便)

    2.4 关系库 Neo4j

    这个库迟早会有大用

    之前其实搞过几波,在亿级别的数据处理是完全没有问题的,所以数据吞吐和查询效率是ok的。其实工作中,比较复杂的其实是关系。

    • 1 定位问题。一个数据经过了几步的流转,每个步骤的状态是什么?当成千上万的数据在处理中流转时,我们去发现问题、锁定问题和处理问题是比较困难的。如果关系流梳理好了,那么通过图库的查询就可以很快的定位问题。

    • 2 复杂描述。通常业务数据不会只有一种处理方法,可能会分成具备若干特征的子类,特征标记的问题是比较散的去做的。当时要挑出合适的待处理数据时就不那么容易了。图库的描述和选择是可以做到异常灵活和丰富的,完全可以做这类的任务。

    • 3 依赖。可以辅助运行的检查,服务的启动等。

    WNeo4j 将联合Mongo(构成图的基础数据存储在Mongo中)来完成复杂关系管理。Neo4j暂时就不考虑做集群了,本身图就是可以进行子图分割的。

    和Mongo类似,Neo4j也会分为元数据库和数据库两类。元数据库将会负责一些全局的,通用的关系存储,例如对象里的流。而数据库将会进行上述的业务数据多重打标和查询。

    在开始重新实现Neo4j之前,我需要将关系进行抽象,还要把以前的功能重新聚合起来。创建类似Neo4jAgent这样的微服务。

    我觉得最核心是建立起一套切实可行的,基于图的工程方法论。工具已经Ready了。

    2.5 嗅探 Sniff

    这个才是自动化的关键

    嗅探就是去找到那些要做的任务,然后在任务表里进行创建,也可以理解是触发器。通常来说,去查探并决定让计算机做哪些任务是人工在操作的。问题是当有太多任务要做时,又需要时不时的周期性检查,这是人无法胜任的。

    简单的嗅探是依据规则去执行的探查

    虽然很简单,但是已经可以解决80%的问题了。例如:

    • 1 数据同步。我有若干表需要和生产保持同步,通过对id的嗅探,创建对应的区块任务。
    • 2 数据处理。检查用户的查询缓存,创建对这些记录的数据更新、校正任务。
    • 3 任务管理。检查任务的处理状态,重置超时和错误的任务。

    嗅探的频率是分钟级的,这样就可以保证服务总是在实时的检查、处理和修正。如果处理程序设计合理的话(操作幂等、逻辑稳健),那么处理服务一旦开始,后续几乎就不用去管了。在运行中如果出问题还是很烦的。

    2.6 处理 Process

    逻辑处理核心

    这块无疑是最费脑的。一方面是本身的处理会很复杂,另一方面要考虑到复用和优化算法。

    很明确的一点是,我们通常都是寻找可行解。首先是可行性问题,判断这条路对不对本身就是一个比较麻烦的处理。
    通常遇到的问题是:如何可长期维护?逻辑处理的链条很深,那么很快的,即时是创造者也会对这些逻辑失去控制。而要长期的一直维护这些逻辑也是成本极高的,所以是需要机制来处理的。

    • 1 函数链。对象里实现了运行过程的 函数名称-参数-变量三元组的执行链记录。
    • 2 缓存空间。在一次处理中,所有相关的变量都放在缓存字典里。
    • 3 入参校验。对于输入要执行对应的集合检查。
    • 4 结果映射。每个函数处理的结果都可以重命名。

    这里也形成了一个函数的小规范, 就是参数全部使用para_dict这关键字参数,以字典传入参数(或者为空)。

    3 一些核心的功能

    3.1 异步并发

    很显然,现在大部分的操作都是通过微服务连接的,所以异步并发是一个基础的核心功能

    3.2 处理追踪

    追踪某个处理运行的全过程,可能需要将处理也进行抽象,以便于统一的管理。

    3.3 数据分表

    也是为了更快更有效的利用数据库,另外就是准备处理更大的数据。

    3.4 任务机制

    定义一版新的任务机制,在前版的基础上进行简化。以支持更灵活的单次处理以及更复杂的多次处理。

    4 如何提高应用效率

    4.1 Handler 句柄

    以基础服务(例如数据库连接),或者某个处理对象的实例作为切入点, 这些切入点可以称为句柄。它们应该被缓存,并加以简单的名称和描述,一旦有需要,可以通过简单的操作(3个操作之内)就立即调出,恢复到开发时的状态。

    4.2 规划与分类

    这块其实更难一些。因为到目前为止,我一直在高速的、大刀阔斧的改进程序的设计方法和运行机制,除非达到一个实际可行的高效版本,不然很难规范处理流程,那么就始终在变动。

    一方面,我会尽快的锁定一个完整的可行域,然后锁定各组件和规范,开始发挥作用。(不知道还要多久,感觉接近了,可能还要几个月?)

    另一方面,能确定的先确定。把一些比较明确的类别确定下来,例如数据库啥的,暂时不会再做改进了。因为实践下来,效率是ok的,暂时没有必要进一步提升了。

  • 相关阅读:
    Swin Transformer目标检测实验——环境配置的步骤和避坑
    解决Ubuntu18.04安装好搜狗输入法后无法打出中文的问题
    C. Card Game(dp&组合数)
    基于元数据的无代码平台存储设计
    计算机毕业设计之java+springboot基于vue的校园交友网站
    jvs-rules(规则引擎)和jvs智能bi(自助式数据分析)9.22更新内容
    通过kubernetes可视化界面(rancher)安装kibana
    Eastern Exhibition【中位数 距离和的最小值】
    React之事件处理之受控组件和非受控组件以及函数柯里化
    springboot异常(一):springboot自定义全局异常处理
  • 原文地址:https://blog.csdn.net/yukai08008/article/details/126539595