0%

简介

  • 数据模型

数据模型是什么

  • 在软件工程中,数据模型是定义数据如何输入和与输出的一种模型。其主要作用是为信息系统提供数据的定义和格式。数据模型是数据库的核心和基础,现有的数据库系统都是基于某种模型而建立起来的。
  • 数据模型的要求
    • 比较直观的模拟现实世界
    • 容易为人理解
    • 便于计算机实现
  • 数据模型的三要素
    • 数据结构:储存在数据库中对象类型的集合,作用是描述数据库组成对象以及对象之间的联系
    • 数据操作:指对数据库中各种对象示例允许执行的操作的集合,包括操作及其相关的操作规则
    • 数据完整性约束条件:指在给定的数据模型中,数据及其联系所遵守的一组通用的完整性规则,它能够保证数据的正确性和一致性。

简介

  • workflow,工作流

工作流是什么

工作流(Workflow)是对工作流程及其各操作步骤之间业务规则的抽象、概括描述¹。工作流建模,即将工作流程中的工作如何前后组织在一起的逻辑和规则,在计算机中以恰当的模型表达并对其实施计算¹。工作流要解决的主要问题是:为实现某个业务目标,利用计算机在多个参与者之间按某种预定规则自动传递文档、信息或者任务¹。从代码层面上来说,工作流是对业务逻辑代码的按照指定的流程格式化¹。工作流引擎是实现驱动工作流的一套实现工具¹。工作流引擎负责对不同类别的工作流进行具体的定义和实现¹。工作流设计器,我们常说的工作流引擎,一般都包括设计器的能力,即负责对业务流程的拖拽式工具¹。通用型的工作流引擎,比较知名的有jBPM5与Activiti¹。

¹: 知乎专栏

Source: Conversation with Bing, 12/19/2023
(1) 一文读懂工作流 - 知乎 - 知乎专栏. https://zhuanlan.zhihu.com/p/113387814.
(2) C++输入流和输出流(超级详细) - 知乎 - 知乎专栏. https://zhuanlan.zhihu.com/p/270262116.
(3) 什么是工作流?为什么程序员要用它? - CSDN博客. https://blog.csdn.net/qq_41956016/article/details/107043485.

工作流是什么 (chatgpt)

工作流(Workflow)是指一系列有序的任务、活动或流程,旨在完成特定的工作或目标。它涉及到将不同的任务或活动以特定的顺序结合起来,以实现预期的结果。工作流可以是手动的、自动化的,或者是二者的组合。

工作流可以在各种领域和行业中应用,如企业管理、生产制造、软件开发、客户服务、医疗保健等。它有助于提高效率、降低错误率、标准化流程、优化资源利用,从而提升组织的整体绩效。

通常,一个工作流包括以下几个要素:

  1. 任务或活动: 每个工作流都由一系列任务、活动或步骤组成,这些步骤按照特定顺序进行。

  2. 流程逻辑: 描述了任务之间的依赖关系、执行顺序、条件和规则,确保工作流能够按照预期的方式运行。

  3. 资源分配: 工作流通常涉及分配资源(人力、物力、时间)来执行各项任务。

  4. 控制和监控: 对工作流进行监控、管理和控制,以确保它们按照设计的方式执行,并能够在需要时进行调整和优化。

  5. 自动化: 某些工作流可能利用技术和自动化工具来加速执行过程,减少人为干预,提高效率和准确性。

工作流可以通过各种方法进行建模和管理,包括流程图、流程建模工具、专门的工作流管理系统(Workflow Management System,WMS)等。

总的来说,工作流是组织和管理任务的一种方法,它有助于确保任务按照一定顺序和规则进行,以达到预期的业务目标。

C++ 工作流是什么

在C++中,工作流(Workflow)通常指的是一系列任务、步骤或操作的有序流程,旨在完成特定的工作或目标。C++本身是一种通用的编程语言,提供了丰富的功能和库,可以用于创建各种类型的应用程序,包括涉及工作流程的应用程序。

在C++中,工作流可以用于不同的应用场景,例如:

  1. 应用程序开发: 在软件开发中,工作流可以指代程序执行的特定步骤或模块,例如初始化、数据处理、用户交互、结果呈现等,它们按照特定的顺序组合成完整的应用程序。

  2. 并行计算: 在并行编程领域,工作流可以指代并行任务的流程,将任务分解为多个步骤,并以特定的顺序执行,以实现并行计算或处理。

  3. 数据处理和流程控制: C++可以用于创建处理数据的工作流,例如将数据流经过一系列处理步骤,执行特定的操作或转换,并最终产生期望的输出。

要在C++中实现工作流,通常需要结合C++语言的特性,编写代码来定义和管理任务或操作的执行顺序,以及控制流程的逻辑。有时候,可以利用C++的类、函数和库来创建自定义的工作流管理系统,或者利用现有的工作流框架和库,例如Boost C++库中的一些组件,来简化工作流的实现和管理。

实际的C++工作流实现可能涉及任务调度、状态管理、数据传递、并发处理等方面的编程技巧和设计模式,以确保工作流能够按照预期的方式运行,并达到特定的业务或功能目标。

工作流 详解

工作流(Workflow)是指一系列有序的任务、活动或步骤,按照特定的规则和流程顺序进行,以完成特定的工作或达到某个目标。这个概念在不同领域和行业中被广泛应用,包括企业管理、软件开发、生产制造、服务行业等等。下面详细解释工作流的重要方面:

  1. 任务和步骤: 工作流由一系列任务或步骤组成,这些任务按照预定的顺序和规则执行。每个任务可能需要特定的输入并产生相应的输出,作为下一个任务的输入。

  2. 流程设计和逻辑: 工作流的设计包括定义任务之间的顺序、条件和依赖关系。这些规则和逻辑决定了工作流程的执行顺序,确保任务按照预期的方式完成。

  3. 资源分配和管理: 工作流需要合理分配和管理资源,包括人力、时间、物料和技术等。这确保了任务得以及时完成,并且资源得到最有效地利用。

  4. 控制和监控: 对工作流进行监控、控制和管理是至关重要的。这意味着能够跟踪任务的执行进度,识别潜在的问题并进行调整,确保工作流能够按照预期达到目标。

  5. 自动化和优化: 在一些情况下,工作流可以通过自动化工具、软件或系统来执行任务,提高效率并减少人为错误。不断优化工作流也是提高效率的关键,可以通过改进流程、去除瓶颈、提高自动化程度等方式实现。

  6. 流程建模和管理工具: 为了更好地设计、分析和实施工作流,人们使用流程建模工具和专门的工作流管理系统(Workflow Management System)。这些工具有助于可视化工作流程、管理任务和资源、执行控制和监控等功能。

总的来说,工作流是一种组织和管理任务的方法,旨在提高效率、降低错误率、优化资源利用并确保任务按照预期的方式完成。它是一个动态的概念,可以根据不同的需求和情境进行调整和优化,以满足特定的业务需求或目标。

工作流建模是什么

工作流建模指的是通过图形化或符号化的方式描述和表达工作流程。这种建模方法旨在清晰地展示任务、活动或步骤之间的关系、依赖性、顺序和流程逻辑,以便更好地理解、分析和管理工作流程。

在工作流建模中,常用的方法包括:

  1. 流程图: 流程图是最常见和常用的工作流建模方法之一。它使用各种符号(如圆形代表起始或结束点,矩形代表任务或活动,箭头表示流程的方向等)来描述任务或活动之间的顺序、逻辑和依赖关系。流程图可以使用不同的标准,如BPMN(Business Process Model and Notation)、UML(Unified Modeling Language)等。

  2. 流程建模工具: 有许多专门的流程建模工具和软件可供使用,例如Microsoft Visio、Lucidchart、Bizagi Modeler等。这些工具提供了简单易用的界面和符号库,帮助用户创建、编辑和管理复杂的工作流模型。

  3. 值流映射: 值流映射是一种工作流建模方法,侧重于识别和优化价值流程,以最大化价值并减少浪费。它通常用于精益生产和精益管理方法中。

  4. 数据流程图: 数据流程图描述了数据如何在系统中流动和处理。它强调数据的来源、目的地和处理方式,有助于了解数据在整个流程中的传递和转换。

工作流建模的目的是帮助团队和利益相关者更清晰地理解工作流程,发现潜在的瓶颈、问题或改进空间,并为优化工作流程提供参考依据。这种建模方法可以促进团队协作、沟通和对工作流程的改进,从而提高效率、降低成本,并确保任务按照规定的方式完成。

工作流建模中的数据流程图 详解

数据流程图(Data Flow Diagram,DFD)是一种用于描述信息系统中数据流动和处理的图形化建模工具。它显示了系统内部数据的流向、处理过程和存储位置,以及数据与外部实体之间的交互关系。DFD 通常由一系列图表组成,用于表示不同层次和角度的数据流程。

DFD 包括以下主要元素:

  1. 实体(Entity): 实体代表外部与系统交互的对象,可以是人、组织、其他系统或外部数据源。在 DFD 中,实体通常用方框表示,位于图的边缘,表示系统的边界。

  2. 过程(Process): 过程表示数据流动的处理过程或转换。它们是对数据执行的操作,可以是计算、转换、存储或其他操作。在 DFD 中,过程通常用圆角矩形表示。

  3. 数据流(Data Flow): 数据流代表数据在系统内部或系统与外部实体之间流动的路径。数据流表示数据从一个地方移动到另一个地方,通常用箭头表示。

  4. 数据存储(Data Store): 数据存储表示数据在系统中的持久存储位置,可以是数据库、文件或其他数据存储设备。在 DFD 中,数据存储通常用双条线矩形表示。

DFD 通常分为多个层次,从整体上描述系统的概念,然后逐渐展开细节。DFD 的层次可以通过多个图表来表示,从顶层图开始,再逐步展开到更详细的层次,以便更好地理解系统的数据流动和处理逻辑。

DFD 的优点包括:

  • 易于理解: DFD 使用图形化的表示方式,使得系统中数据的流动和处理逻辑更直观和易于理解。
  • 抽象表达: 它提供了对系统的高层次抽象表示,可以忽略细节,更关注系统的整体结构和功能。
  • 沟通与设计: DFD 有助于不同团队成员之间的沟通和协作,并且为系统设计和优化提供了基础。

总的来说,数据流程图是一种有用的工具,用于描述信息系统中数据流动和处理的方式,有助于分析、设计和沟通系统的数据处理流程。

工作流引擎是什么

工作流引擎(Workflow Engine)是一种软件工具或系统,用于管理、执行和自动化工作流程中的任务、活动和流程。它是一个核心组件,负责解释、控制和监视工作流程中的各个步骤,以确保任务按照预期的方式顺利执行。

工作流引擎通常具有以下功能和特点:

  1. 流程执行和控制: 它负责执行工作流程中的各个步骤,根据定义的流程规则和逻辑来控制任务的顺序和执行流程。

  2. 任务调度和分配: 工作流引擎能够分配任务给适当的执行者,根据特定条件或规则自动调度任务。

  3. 状态管理: 它跟踪任务或流程的状态,允许监控和管理流程的进展,并在需要时进行状态更新。

  4. 规则引擎: 有些工作流引擎内置规则引擎,能够根据预定义的规则自动决定任务的流转和执行。

  5. 通知和提醒: 引擎能够发送通知、提醒或警报,以便参与者知晓并及时处理任务。

  6. 错误处理和异常流程: 处理工作流程中可能出现的异常情况,可以自动或手动进行错误处理。

  7. 集成和扩展性: 可以集成到不同的应用程序或系统中,并具有扩展性,以满足不同的业务需求和场景。

工作流引擎可以以多种方式实现,例如基于规则、状态机、图形化工作流编辑器等。它们通常支持标准的工作流建模语言,如BPMN(Business Process Model and Notation),这使得用户能够使用可视化工具轻松地定义和管理工作流程。

这些引擎被广泛应用于企业资源规划(ERP)、客户关系管理(CRM)、项目管理、服务台管理、工作流自动化等领域。通过使用工作流引擎,组织能够提高效率、降低成本、减少错误并加强对业务流程的控制和监督。

工作流设计器是什么

工作流设计器(Workflow Designer)是一种软件工具,用于创建、设计和编辑工作流程图。它提供了一个图形化的界面,让用户能够直观地建模和定义工作流程,包括任务、活动、流程步骤、条件和规则等。

主要功能包括:

  1. 图形化建模: 工作流设计器通常提供了丰富的图形化工具和符号,让用户能够以直观的方式创建工作流程图。这些工具包括表示任务、活动、决策、状态、数据流等元素的图形符号。

  2. 流程编辑和连接: 用户可以通过拖放图形符号来创建流程步骤,并通过连接线条来定义这些步骤之间的顺序和逻辑关系。这使得用户能够轻松地构建工作流程的结构。

  3. 属性设置: 工作流设计器允许用户设置每个流程步骤或活动的属性,例如名称、描述、执行条件、执行者等信息。

  4. 条件和规则定义: 用户可以定义触发任务执行的条件或规则。这些条件可以是时间触发、数据状态变化、外部事件等。

  5. 实时预览和验证: 设计器通常提供实时预览功能,让用户在设计过程中可以预览工作流程的执行流程。同时也能够进行验证,确保工作流程的合法性和正确性。

  6. 导出和集成: 完成工作流设计后,设计器通常支持将设计的工作流程导出为可执行的文件或配置,并且可以与其他应用程序或系统集成。

工作流设计器广泛应用于企业资源规划(ERP)、客户关系管理(CRM)、项目管理、工作流自动化等领域。它们有助于团队和业务人员更轻松地创建、管理和优化工作流程,同时提高了可视化建模的效率和准确性。

常用的工作流引擎

在软件开发和业务流程管理中,有许多常用的工作流引擎可供选择,每个引擎都有其独特的特点和适用场景。以下是一些常见的工作流引擎:

  1. Apache Airflow: 一个开源的工作流编排工具,用于调度、监控和管理复杂的工作流程。它支持基于代码的工作流定义,并提供了丰富的插件生态系统和可视化界面。

  2. Activiti: 一个轻量级的开源BPM(Business Process Management)引擎,基于Java语言,用于设计、执行和管理业务流程。Activiti支持BPMN标准,提供了可扩展性和灵活性。

  3. Camunda: 一个基于Activiti的开源工作流和决策引擎,支持BPMN标准,可用于设计和执行复杂的业务流程。Camunda提供了高度可扩展性和灵活性,并提供了REST API以进行集成。

  4. JBoss jBPM: 一个开源的、基于Java的业务流程管理工具,支持BPMN2.0标准,用于设计、执行和监控业务流程。

  5. IBM Business Automation Workflow(BAW): IBM提供的企业级工作流引擎,可用于建模和自动化复杂的业务流程,提供了业务决策管理和高级的分析功能。

  6. Microsoft Power Automate(以前称为Microsoft Flow): Microsoft提供的工作流和自动化平台,可用于连接各种应用程序和服务,并创建自动化的工作流程,不需要编写代码。

  7. Nintex Workflow Platform: 一个可视化的工作流程和自动化平台,用于构建和管理业务流程,集成了许多常用的业务应用程序和系统。

  8. Zeebe: 一个分布式的工作流引擎,专注于高性能和水平扩展,可用于构建具有高吞吐量和可靠性要求的业务流程。

每个工作流引擎都有其独特的特性和适用场景,选择合适的工作流引擎通常取决于项目需求、可扩展性、集成性能以及对标准支持的要求。

C++如何实现工作流引擎

在C++中实现工作流引擎需要一些基本的组件和设计模式,尤其是涉及到任务调度、状态管理和流程控制。下面是一些主要的步骤和概念,可以帮助你理解如何在C++中构建一个简单的工作流引擎:

  1. 任务定义: 定义任务或活动的抽象类或接口,表示工作流中的可执行单元。

    1
    2
    3
    4
    5
    class Task {
    public:
    virtual void execute() = 0;
    // Other necessary methods or properties
    };
  2. 任务实现: 实现具体的任务类,继承自任务定义,并实现具体的任务逻辑。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class ConcreteTaskA : public Task {
    public:
    virtual void execute() override {
    // Implement logic for task A
    }
    };

    class ConcreteTaskB : public Task {
    public:
    virtual void execute() override {
    // Implement logic for task B
    }
    };
  3. 流程管理: 创建流程管理器,负责任务的调度和管理。这可能涉及队列、状态跟踪等。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class WorkflowEngine {
    private:
    std::queue<Task*> tasksQueue;
    // Other necessary data structures

    public:
    void addTask(Task* task) {
    tasksQueue.push(task);
    }

    void run() {
    while (!tasksQueue.empty()) {
    Task* currentTask = tasksQueue.front();
    currentTask->execute();
    tasksQueue.pop();
    // Add logic for managing task flow, transitions, etc.
    }
    }
    };
  4. 创建和执行工作流程: 利用上述构建的任务和流程管理器,创建并执行工作流程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    int main() {
    WorkflowEngine workflowEngine;

    // Define tasks
    Task* taskA = new ConcreteTaskA();
    Task* taskB = new ConcreteTaskB();

    // Add tasks to workflow
    workflowEngine.addTask(taskA);
    workflowEngine.addTask(taskB);

    // Run the workflow
    workflowEngine.run();

    // Clean up tasks
    delete taskA;
    delete taskB;

    return 0;
    }

这是一个简单的示例,用于说明在C++中构建工作流引擎的基本思路。在实际的项目中,工作流引擎通常需要更复杂的功能,如条件分支、错误处理、状态管理、并发处理等。因此,在构建更完整和可扩展的工作流引擎时,可能需要更多的设计和实现工作。同时,考虑到复杂性和维护性,也可以考虑使用现有的开源工作流引擎或库,以便快速构建并具备更丰富功能的工作流系统。

C++ 工作流引擎中常用的组件和设计模式

在C++中构建工作流引擎时,常用的组件和设计模式可以帮助实现更具可维护性和扩展性的系统。以下是一些常用的组件和设计模式:

组件:

  1. 任务(Task): 表示工作流中可执行的单元。每个任务应该是一个抽象类或接口,具体的任务类继承自这个接口。

  2. 流程管理器(Workflow Manager): 负责任务的调度、管理和执行。它可能包括任务队列、状态管理、任务执行逻辑等。

  3. 任务执行上下文(Task Execution Context): 用于在任务之间传递数据和状态。这可以是一个数据结构或对象,用于保存任务执行期间的上下文信息。

  4. 触发器(Trigger): 触发工作流中任务执行的条件或事件。这些可以是时间触发、数据状态变化触发等。

设计模式:

  1. 工厂模式(Factory Pattern): 用于创建任务实例,根据需要实例化具体的任务对象。可以是简单工厂模式、工厂方法模式或抽象工厂模式。

  2. 命令模式(Command Pattern): 将任务封装成对象,以支持任务的参数化和延迟执行。这有助于将任务作为对象进行管理和执行。

  3. 观察者模式(Observer Pattern): 用于在任务执行时监视任务状态的变化。例如,通知其他组件或系统特定任务的执行状态。

  4. 策略模式(Strategy Pattern): 用于在工作流引擎中定义任务的执行策略。不同的任务可以有不同的执行策略。

  5. 状态模式(State Pattern): 用于管理任务的状态转换。任务可能有多种状态,状态模式可以帮助组织和管理状态转换的逻辑。

  6. 责任链模式(Chain of Responsibility Pattern): 用于构建任务执行的责任链。任务按照一定的顺序传递并执行,直到有任务能够处理该请求。

  7. 享元模式(Flyweight Pattern): 用于共享可能被多个任务所使用的共享对象或资源,以减少内存占用和提高性能。

这些组件和设计模式可以在C++工作流引擎中使用,以创建更灵活、可扩展和易于维护的系统。具体的实现方式可能会根据项目需求和规模的不同而有所差异,但这些是常用的方法,有助于构建功能强大的工作流引擎。

C++ 工作流怎么设计

设计一个工作流引擎涉及多个方面,包括任务定义、流程管理、状态管理、任务调度等。以下是一个简单的示例,展示如何在C++中设计一个基本的工作流引擎:

任务定义:

  1. 任务基类: 定义一个任务的抽象基类或接口,用于表示工作流中的可执行单元。

    1
    2
    3
    4
    5
    class Task {
    public:
    virtual void execute() = 0;
    // Other necessary methods or properties
    };
  2. 具体任务类: 实现具体的任务,继承自任务基类,定义任务的具体逻辑。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class ConcreteTaskA : public Task {
    public:
    virtual void execute() override {
    // Implement logic for task A
    }
    };

    class ConcreteTaskB : public Task {
    public:
    virtual void execute() override {
    // Implement logic for task B
    }
    };

流程管理器:

  1. 工作流引擎类: 创建一个流程管理器,负责任务的调度、管理和执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class WorkflowEngine {
    private:
    std::vector<Task*> tasks;

    public:
    void addTask(Task* task) {
    tasks.push_back(task);
    }

    void run() {
    for (Task* task : tasks) {
    task->execute();
    }
    }
    };

创建和执行工作流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main() {
WorkflowEngine workflowEngine;

// 定义具体的任务
Task* taskA = new ConcreteTaskA();
Task* taskB = new ConcreteTaskB();

// 添加任务到工作流引擎
workflowEngine.addTask(taskA);
workflowEngine.addTask(taskB);

// 执行工作流程
workflowEngine.run();

// 清理资源
delete taskA;
delete taskB;

return 0;
}

注意事项:

  • 这个示例是一个基本的工作流引擎,用于说明设计的基本思路。在实际应用中,需要考虑更多的复杂性和扩展性,例如任务状态管理、任务执行的条件控制、任务之间的依赖关系、错误处理等等。
  • 为了更好地管理资源和防止内存泄漏,需要确保在不再需要时正确地清理任务对象。
  • 需要考虑异常处理、并发任务的执行、任务执行顺序的控制等更复杂的情况。
  • 这只是一个简单示例,实际的工作流引擎可能需要更多的设计和实现来满足特定的需求和复杂性。

C++ 工作流中的任务定义怎么设计 详解

在C++工作流中,任务定义的设计是工作流引擎的关键部分之一。任务定义指定了工作流中需要执行的具体操作或活动。下面详细解释如何设计C++中的任务定义:

任务基类设计:

  1. 抽象任务基类(Abstract Task Base Class): 定义一个抽象基类或接口,描述任务的基本特征和行为。

    1
    2
    3
    4
    5
    class Task {
    public:
    virtual void execute() = 0; // 纯虚函数,用于执行任务的具体逻辑
    // 可以包含其他虚函数或纯虚函数,用于获取任务信息或处理其他任务相关操作
    };

具体任务类设计:

  1. 具体任务类(Concrete Task Classes): 派生自任务基类的具体任务类,实现具体任务的逻辑。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class ConcreteTaskA : public Task {
    public:
    virtual void execute() override {
    // 实现具体任务A的逻辑
    // 可能包括操作、计算、调用其他函数等
    }
    // 可以包含其他私有或公有成员函数和数据成员
    };

    class ConcreteTaskB : public Task {
    public:
    virtual void execute() override {
    // 实现具体任务B的逻辑
    // 可能包括操作、计算、调用其他函数等
    }
    // 可以包含其他私有或公有成员函数和数据成员
    };

任务定义的要点:

  • 抽象基类的定义: 任务基类应该定义一组任务需要遵循的共同操作或接口。这些操作可能包括执行任务、获取任务信息、更新任务状态等。

  • 虚函数 execute(): 在任务基类中定义一个纯虚的 execute() 函数,它是任务的核心执行逻辑,由具体的任务类来实现。这种设计允许不同的任务类有不同的实现方式。

  • 多态性支持: 通过将具体任务类派生自抽象基类,利用多态性来处理不同类型的任务。这样可以在不修改基础架构的情况下,轻松地添加新的任务类型。

  • 扩展性和灵活性: 设计任务类的目的是使得系统能够轻松地添加新任务或修改现有任务的行为,以适应不同的业务需求。

通过良好设计的任务定义,工作流引擎可以实现对任务的统一管理和执行,同时使得系统更加灵活和可扩展。

C++ 工作流中的流程管理怎么设计 详解

在C++工作流中,流程管理是工作流引擎的关键部分之一,负责管理和执行定义好的任务序列、控制任务的执行顺序以及处理任务之间的依赖关系。下面详细解释如何设计C++中的流程管理:

流程管理器设计:

  1. 工作流引擎类(Workflow Engine Class): 定义一个管理工作流程的类,负责添加、管理和执行任务。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class WorkflowEngine {
    private:
    std::vector<Task*> tasks; // 任务容器,用于存储待执行的任务

    public:
    void addTask(Task* task) {
    tasks.push_back(task);
    }

    void executeWorkflow() {
    for (Task* task : tasks) {
    task->execute(); // 执行任务
    }
    }
    };

流程管理器要点:

  • 任务容器: 使用容器(如 std::vector)存储任务,以保持待执行任务的顺序。你也可以选择其他容器类型,如队列或链表,根据执行顺序的不同选择合适的数据结构。

  • 添加任务方法: 提供 addTask() 方法,用于向流程管理器中添加新的任务。这个方法可以动态地向工作流中添加不同类型的任务。

  • 执行工作流程方法: executeWorkflow() 方法用于遍历任务列表,并依次执行每个任务的 execute() 函数。这里假设任务已经按照执行顺序添加到了任务容器中。

  • 其他方法和属性: 根据实际需求,可以添加其他方法或属性来支持任务状态管理、错误处理、任务执行的条件控制等功能。

注意事项:

  • 这个示例是一个简单的流程管理器的概念示例,用于说明设计的基本思路。实际应用中,可能需要更多的逻辑和条件来管理复杂的工作流程。
  • 考虑到任务之间的依赖关系,你可能需要在流程管理器中添加逻辑来确保任务按照正确的顺序执行,或者根据某些条件跳过特定的任务。
  • 为了更好地模拟真实系统,可能需要添加更多的属性和方法来描述任务的属性和行为。
  • 确保在不再需要时正确地清理任务对象,以避免内存泄漏。

C++ 工作流中的状态管理怎么设计 详解

在C++工作流中,状态管理是对任务执行状态的追踪和处理,以确保任务在执行过程中的正确性和可控性。以下是关于如何设计C++中的状态管理:

状态管理器设计:

  1. 任务状态枚举(Task Status Enum): 定义任务的不同状态,例如:未开始、进行中、已完成、出错等。

    1
    2
    3
    4
    5
    6
    7
    enum class TaskStatus {
    NOT_STARTED,
    IN_PROGRESS,
    COMPLETED,
    ERROR,
    // 其他可能的状态
    };
  2. 任务状态属性: 在任务类中添加状态属性,用于追踪任务的执行状态。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Task {
    private:
    TaskStatus status; // 任务的状态

    public:
    Task() : status(TaskStatus::NOT_STARTED) {} // 默认状态为未开始

    TaskStatus getStatus() const {
    return status;
    }

    void setStatus(TaskStatus newStatus) {
    status = newStatus;
    }
    };
  3. 状态更新逻辑: 在任务的执行过程中,在适当的时机更新任务的状态。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class ConcreteTask : public Task {
    public:
    virtual void execute() override {
    // 在任务执行过程中更新状态
    setStatus(TaskStatus::IN_PROGRESS);

    // 实现具体任务的逻辑

    // 任务完成后更新状态为已完成
    setStatus(TaskStatus::COMPLETED);
    }
    };

状态管理器要点:

  • 状态枚举定义: 定义一个清晰的状态枚举,包含任务可能的所有状态。这有助于对任务状态的准确追踪和管理。

  • 任务类状态属性: 在任务基类或具体任务类中添加状态属性,用于存储任务的当前状态。

  • 状态更新逻辑: 在任务的执行过程中,根据任务的执行阶段更新状态。状态更新应该在适当的时机调用,以反映任务的实际状态。

  • 其他状态相关的功能: 可以根据需要添加其他状态管理的功能,例如状态查询方法、状态转换条件的验证、错误处理逻辑等。

注意事项:

  • 任务状态管理的设计应该是简洁且清晰的,以便准确追踪任务的状态。
  • 考虑在状态管理器中添加错误处理机制,以处理任务执行过程中可能出现的错误状态。
  • 状态管理的设计应该适应实际业务需求,根据任务的复杂性和执行过程的特点来进行设计和扩展。

C++ 工作流中的任务调度怎么设计 详解

任务调度在工作流中是管理任务执行顺序和控制任务执行的过程。下面是关于如何设计C++中的任务调度的解释:

任务调度器设计:

  1. 任务优先级枚举(Task Priority Enum): 定义任务的不同优先级,用于确定任务执行的顺序。

    1
    2
    3
    4
    5
    6
    enum class TaskPriority {
    LOW,
    NORMAL,
    HIGH,
    // 其他可能的优先级
    };
  2. 任务带优先级属性: 在任务类中添加优先级属性,用于指定任务的优先级。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Task {
    private:
    TaskPriority priority; // 任务的优先级

    public:
    Task(TaskPriority prio) : priority(prio) {}

    TaskPriority getPriority() const {
    return priority;
    }
    };
  3. 任务调度逻辑: 在工作流引擎中实现任务的调度逻辑,根据任务的优先级进行排序和执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class WorkflowEngine {
    private:
    std::vector<Task*> tasks; // 任务容器,用于存储待执行的任务

    public:
    void addTask(Task* task) {
    tasks.push_back(task);
    // 在添加任务时,可能需要对任务进行排序以确保按优先级执行
    sortTasksByPriority();
    }

    void sortTasksByPriority() {
    // 根据任务的优先级进行排序,例如使用 std::sort() 等排序算法
    }

    void executeWorkflow() {
    for (Task* task : tasks) {
    task->execute(); // 执行任务
    }
    }
    };

任务调度器要点:

  • 优先级枚举定义: 定义一个清晰的优先级枚举,包含任务可能的所有优先级。这有助于对任务按照优先级进行排序。

  • 任务类优先级属性: 在任务类中添加优先级属性,以便在任务调度时确定任务的执行顺序。

  • 任务调度逻辑: 在工作流引擎中实现任务的调度逻辑,可以在任务添加时进行排序,也可以在执行时动态根据优先级选择下一个要执行的任务。

  • 排序方法的实现: 实现一个合适的排序方法,根据任务的优先级对任务容器中的任务进行排序。

注意事项:

  • 任务调度器的设计应该适应实际业务需求,根据任务的复杂性和优先级要求来进行设计和扩展。
  • 考虑添加其他调度策略,如循环调度、条件调度等,以满足不同场景下的任务调度需求。
  • 确保在任务添加时或执行时对任务进行合适的排序,以保证按照优先级执行任务。

C++ 动态抓取工作流设计 示例

动态抓取工作流是指根据实际情况动态地抓取或创建工作流程。这种设计使得工作流程可以根据环境变化或特定条件进行动态调整。以下是一个简单的示例,展示如何设计一个简单的动态抓取工作流的思路:

工作流定义:

  1. 任务定义: 定义任务或活动的抽象类或接口。

    1
    2
    3
    4
    5
    class Task {
    public:
    virtual void execute() = 0;
    // Other necessary methods or properties
    };
  2. 具体任务类: 实现具体的任务,继承自任务基类,定义任务的具体逻辑。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class ConcreteTaskA : public Task {
    public:
    virtual void execute() override {
    // Implement logic for task A
    }
    };

    class ConcreteTaskB : public Task {
    public:
    virtual void execute() override {
    // Implement logic for task B
    }
    };

动态工作流管理:

  1. 工作流管理器类: 负责动态抓取和管理工作流程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class WorkflowManager {
    private:
    std::vector<Task*> tasks;

    public:
    void addTask(Task* task) {
    tasks.push_back(task);
    }

    void executeWorkflow() {
    for (Task* task : tasks) {
    task->execute();
    }
    }
    };

创建和执行动态工作流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main() {
WorkflowManager workflowManager;

// 动态抓取任务或根据条件添加任务到工作流程
if (/*某些条件满足*/) {
workflowManager.addTask(new ConcreteTaskA());
} else {
workflowManager.addTask(new ConcreteTaskB());
}

// 执行动态工作流程
workflowManager.executeWorkflow();

return 0;
}

注意事项:

  • 这个示例是一个简单的动态抓取工作流的概念示例。实际应用中,根据具体需求和条件,可能需要更多的逻辑和条件来动态构建工作流程。
  • 可能需要更复杂的逻辑来根据实时数据或外部条件动态添加、修改或删除工作流程中的任务。
  • 在设计动态抓取工作流时,需考虑到任务之间的依赖关系、错误处理机制、任务状态管理等方面的复杂性。
  • 确保在不再需要时正确地清理任务对象,以避免内存泄漏。

简介

1
2
3
4
5
6
7
8
+--------------------------------------------------+
| Oray PeanutHull Linux 5.2.0 |
+--------------------------------------------------+
| SN: oray6c9e9e0c15a5 Default password: admin |
+--------------------------------------------------+
| Remote Management Address http://b.oray.com |
+--------------------------------------------------+

  • 官网:https://console.hsk.oray.com/forward

  • ssh 登录公司内网

    • 用户名: 公司内网的主机用户名
    • IP地址:花生壳外网ip地址
    • 端口:花生壳创建端口映射产生的随机端口
  • 示例:

    • ssh -p 25397 user@g321z68399.goho.co

简介

  • CPU在整个计算机硬件中,技术体系中都算是最重要的东西了。
  • 以CPU型号是 Intel(R) Core(TM) i5-7200U CPU @2.50GHz 2.71GHz 为例

Intel的CPU型号规则

  • 无论是哪家CPU厂商,为了更好地管理自己生产的众多型号的产品,也为了能更好地让消费者对自己家的产品快速地了解。都会定义一套产品规则。

  • Intel把CPU的编号分成了 品牌标志,品牌修饰符,代际编号,SKU,产品线后缀 五个部分

  • 第一部分是品牌标志。最常见的x86 CPU品牌包括Intel和AMD两家公司。而Intel又面向不同的市场需求,包括凌动,赛扬,奔腾,酷睿,志强等多个子品牌。

    • 酷睿(CORE),赛扬(CELERON),奔腾(PENTIUM)都是应用在家用PC电脑上的品牌。其中奔腾和赛扬这两个品牌已经算作是历史了。奔腾在1992年推出,之后在很长一段时间里是PC家用电脑的主流。赛扬是1998年时为了和AMD竞争低价市场推出的产品,可以理解为缩水版的奔腾。酷睿时2006年推出的,直到现在仍然是PC电脑里的主流品牌。
    • 志强(Xeno)是企业级的CPU,多用于服务器和工作站。大家在工作中使用的线上服务器,基本都是这个子品牌旗下的产品。
    • 凌动(Atom)主打省电和低功耗,主要用在手机平板等设备上。Intel也是做过手机CPU的,但因为功耗问题在竞争中被arm架构各家处理器厂商打败,在2016年后逐渐推出市场
  • 第二部分是品牌修饰符,这个部分是为了区分处理器的市场定位的,一般来说同一年代生产的CPU中,i3 i5 i7 i9 的性能是依次递增的。分别代表着低端,中端和高端的定位

  • 第三部分是CPU的代际编号,这一部分有可能是一位数字也有可能是两位的数字在我们的举例中,代际编号是 7 。这个代际编号对应的是Intel于2016年推出的内部代号为Kaby Lake的CPU架构。一般来说,CPU代际编号越大,则代表架构推出的时间点越新。单核的性能也会更好

  • 第四部分是SKU编号。这是CPU厂商为了方便对所有的产品进行库存管理而定制的编号。这类似于超市里商品的条形码,图书的ISBN号,方便查询和管理产品的库存量。一般来说,这个值月大性能越好但不绝对。

  • 第五部分是产品线后缀。在笔记本电脑中,H代表的是高性能,U系列代表的是较低功耗,Y系列代表的是极低的功耗。在台式机中,X代表的是最高性能,K代表的是高性能,T代表的是功耗优化

  • CPU型号是 Intel(R) Core(TM) i5-7200U CPU @2.50GHz 2.71GHz 。这里面包含的含义是:

    • 这是一个Intel的酷睿子品牌的CPU,i7代表这中高端,代际编号是7,这个代际的处理器架构是2016年发布的采用的Kaby Lake架构生产的,SKU编号是200。产品线后缀的U代表的是低功耗,比较省电,同时也就代表着性能并不是最强的。

简介

  • 算法
  • 算法研究的目的是为了更有效的处理数据,提高数据运算效率。数据的运算是定义在数据的逻辑结构上,但运算的具体实现要在存储结构上进行。

常用算法

  • 检索:检索就是在数据结构里查找满足一定条件的节点。一般是给定一个某字段的值,找具有该字段值的节点。
  • 插入:往数据结构中增加新的节点。
  • 删除:把指定的结点从数据结构中去掉。
  • 更新:改变指定节点的一个或多个字段的值。
  • 排序:把节点按某种指定的顺序重新排列。例如递增或递减。

简介

  • 后端开发,基本设计规则

前言

  • 后端开发工程师,主要的工作就是:如何把一个接口设计好

接口参数校验

  • 入参出参校验是每个程序员必备的基本素养。你设计的接口,必须先校验参数。
  • 比如入参是否允许为空,入参长度是否符合你的预期长度。

修改老接口时,注意接口的兼容性

  • 如果需求是在原来的接口上修改,尤其是这个接口是对外提供服务的话,一定要考虑接口的兼容性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    你加了一个参数C,就可以考虑这样处理:

    //老接口
    void oldService(A,B){
    //兼容新接口,传个null代替C
    newService(A,B,null);
    }

    //新接口,暂时不能删掉老接口,需要做兼容。
    void newService(A,B,C){
    ...
    }

设计接口时,充分考虑接口的可扩展性

  • 要根据实际业务场景设计接口,充分考虑接口的可扩展性。

接口考虑是否需要防重处理

  • 如果前端重复请求,你的逻辑如何处理?是不是考虑接口去重处理

  • 当然,如果是查询类的请求,其实不用防重。

  • 如果是更新修改类的话,尤其是金融转账的,就要过滤重复请求了。

  • 推荐使用数据库的防重表,以唯一流水号作为主键或者唯一索引

重点接口,考虑线程池隔离

  • 一些登录,转账交易,下单等重要接口,考虑线程池隔离。

  • 如果所有业务都公用一个线程池,有些业务出bug导致线程阻塞打满的话,所有业务都被影响了。

  • 因此进行线程池隔离,重要业务多分配一点核心线程,就更好的保护业务。

调用第三方接口要考虑异常和超时处理

  • 异常处理

    • 比如,调用了别人的接口,如果异常了,怎么处理,是重试还是当作失败还是告警处理
  • 接口超时

    • 没办法预估对方接口一般多久返回,一般设置一个超时断开时间,以保护你的接口。
    • 一个生产问题:就是http调用不设置超时时间,最后响应进程假死,请求一直占着线程不释放,拖垮线程池
  • 重试次数

接口实现考虑熔断和降级

  • 当前互联网系统一般是分布式部署的。而分布式系统中经常会出现某个基础服务不可用,最终导致整个服务不可用的情况,这种现象被称为服务雪崩效应

  • 为了应对服务雪崩,常见的作法就是熔断和降级。

    • 最简单的是加开关控制,当下游系统出问题时,开关降级,不在调用下游系统,还可以选用开源组件 Hystrix

日志打印好,接口的关键代码一定要有日志

  • 入参之前打印一下参数
  • 接口调用之后,打印一下异常

接口的功能定义要具备单一性

  • 单一性是指接口做的事情比较单一,专一

    • 比如一个登陆接口,它做的事情就只是校验账户名密码,然后返回登陆成功以及userId即可。但是如果你为了减少接口交互,把一些注册、一些配置查询等全放到登陆接口,就不太妥
  • 其实这也是微服务一些思想,接口的功能单一

接口有些场景,使用异步更合适

  • 做异步的方式,简单的就是用线程池。还可以使用消息队列,就是用户注册成功后,生产者产生一个注册成功的消息,消费者拉到注册成功的消息,就发送通知

  • 不是所有的接口都适合设计为同步接口。比如你要做一个转账的功能,如果你是单笔的转账,你是可以把接口设计同步。用户发起转账时,客户端在静静等待转账结果就好。如果你是批量转账,一个批次一千笔,甚至一万笔的,你则可以把接口设计为异步。就是用户发起批量转账时,持久化成功就先返回受理成功。然后用户隔十分钟或者十五分钟等再来查转账结果就好。又或者,批量转账成功后,再回调上游系统

优化接口耗时,远程串行考虑改为并行调用

接口合并或者说 考虑批量处理思想

  • 数据库操作或者是 远程过程调用时,能够批量操作就不要for循环调用

  • kafka使用批量消息提升服务端处理能力

接口实现过程中,恰当使用缓存

  • 哪些场景适合使用缓存?

    • 读多,写少且数据时效要求越低的场景
  • 缓存用的好,可以承载更多的请求,提升查询效率,减少数据库的压力

考虑接口热点数据隔离性

  • 瞬时间的高并发,可能会打垮你的系统。可以做一些热点数据隔离。比如业务隔离,系统隔离,用户隔离,数据隔离等等。

可变参数配置化,比如红包皮肤切换等

接口考虑幂等性

  • 接口是需要考虑幂等性的。尤其抢红包、转账这些重要接口。最直观的业务场景,就是用户连着点击两次,你的接口有没有hold住。或者消息队列出现重复消费的情况,你的业务逻辑怎么控制?

  • 什么是幂等?

    • 计算机科学中,幂等表示一次和多次请求某一个资源应该具有同样的副作用,或者说,多次请求所产生的影响与一次请求执行的影响效果相同。
    • 大家别搞混哈,防重和幂等设计其实是有区别的。防重主要为了避免产生重复数据,把重复请求拦截下来即可。而幂等设计除了拦截已经处理的请求,还要求每次相同的请求都返回一样的效果。不过呢,很多时候,它们的处理流程、方案是类似的哈。
  • 接口幂等实现方案主要有8种:

    • select+insert+主键/唯一索引冲突
    • 直接insert + 主键/唯一索引冲突
    • 状态机幂等
    • 抽取防重表
    • token令牌
    • 悲观锁
    • 乐观锁
    • 分布式锁

读写分离,优先考虑读从库,注意主从延迟问题

  • 我们的数据库都是集群部署的,有主库也有从库,当前一般都是读写分离的。比如你写入数据,肯定是写入主库,但是对于读取实时性要求不高的数据,则优先考虑读从库,因为可以分担主库的压力。

  • 如果读取从库的话,需要考虑主从延迟的问题

接口注意返回的数据量,如果数据大需要分页

  • 一个接口返回报文,不应该包含过多的数据量。过多的数据量不仅处理复杂,并且数据量传输的压力也非常大。因此数量实在是比较大,可以分页返回,如果是功能不相关的报文,那应该考虑接口拆分

好的接口实现,离不开SQL优化

  • SQLL优化从这几个维度思考:
    • explain 分析SQL查询计划(重点关注type、extra、filtered字段)
    • show profile分析,了解SQL执行的线程的状态以及消耗的时间
    • 索引优化 (覆盖索引、最左前缀原则、隐式转换、order by以及group by的优化、join优化)
    • 大分页问题优化(延迟关联、记录上一页最大ID)
    • 数据量太大(分库分表、同步到es,用es查询)

代码锁的粒度控制好

  • 什么是加锁粒度呢?
    • 其实就是你要锁住的范围是多大

接口状态和错误需要统一明确

  • 提供必要的接口调用状态信息。比如你的一个转账接口调用是成功、失败、处理中还是受理成功等,需要明确告诉客户端。如果接口失败,那么具体失败的原因是什么。这些必要的信息都必须要告诉给客户端,因此需要定义明确的错误码和对应的描述。同时,尽量对报错信息封装一下,不要把后端的异常信息完全抛出到客户端。

接口要考虑异常处理

  • 实现一个好的接口,离不开优雅的异常处理。对于异常处理,提十个小建议吧:
    • 尽量不要使用e.printStackTrace(),而是使用log打印。因为e.printStackTrace()语句可能会导致内存占满。
    • catch住异常时,建议打印出具体的exception,利于更好定位问题
    • 不要用一个Exception捕捉所有可能的异常
    • 记得使用finally关闭流资源或者直接使用try-with-resource
    • 捕获异常与抛出异常必须是完全匹配,或者捕获异常是抛异常的父类
    • 捕获到的异常,不能忽略它,至少打点日志吧
    • 注意异常对你的代码层次结构的侵染
    • 自定义封装异常,不要丢弃原始异常的信息Throwable cause
    • 运行时异常RuntimeException ,不应该通过catch的方式来处理,而是先预检查,比如:NullPointerException处理
    • 注意异常匹配的顺序,优先捕获具体的异常

优化程序逻辑

  • 优化程序逻辑这块还是挺重要的,也就是说,你实现的业务代码,如果是比较复杂的话,建议把注释写清楚。还有,代码逻辑尽量清晰,代码尽量高效。

  • 你要使用用户信息的属性,你根据session已经获取到userId了,然后就把用户信息从数据库查询出来,使用完后,后面可能又要用到用户信息的属性,有些小伙伴没想太多,反手就把userId再传进去,再查一次数据库。。。我在项目中,见过这种代码。。。直接把用户对象传下来不好嘛。。

接口实现过程中,注意大文件,大事务,大对象

  • 读取大文件时,不要Files.readAllBytes直接读取到内存,这样会OOM的,建议使用BufferedReader一行一行来。
  • 大事务可能导致死锁、回滚时间长、主从延迟等问题,开发中尽量避免大事务。
  • 注意一些大对象的使用,因为大对象是直接进入老年代的,可能会触发fullGC

你的接口,需要考虑限流

  • 如果你的系统每秒扛住的请求是1000,如果一秒钟来了十万请求呢?换个角度就是说,高并发的时候,流量洪峰来了,超过系统的承载能力,怎么办呢?

  • 如果不采取措施,所有的请求打过来,系统CPU、内存、Load负载飚的很高,最后请求处理不过来,所有的请求无法正常响应。

  • 针对这种场景,我们可以采用限流方案。就是为了保护系统,多余的请求,直接丢弃。

  • 限流定义:

    • 在计算机网络中,限流就是控制网络接口发送或接收请求的速率,它可防止DoS攻击和限制Web爬虫。限流,也称流量控制。是+ 指系统在面临高并发,或者大流量请求的情况下,限制新的请求对系统的访问,从而保证系统的稳定性。
    • 可以使用Guava的RateLimiter单机版限流,也可以使用Redis分布式限流,还可以使用阿里开源组件sentinel限流

代码实现时,注意运行时异常

  • 日常开发中,我们需要采取措施规避数组边界溢出,被零整除,空指针等运行时错误。类似代码比较常见:

保证接口安全性

  • 如果你的API接口是对外提供的,需要保证接口的安全性。保证接口的安全性有token机制和接口签名

  • 机制身份验证方案还比较简单的,就是

    • 客户端发起请求,申请获取token。
    • 服务端生成全局唯一的token,保存到redis中(一般会设置一个过期时间),然后返回给客户端。
    • 客户端带着token,发起请求。
    • 服务端去redis确认token是否存在,一般用 redis.del(token)的方式,如果存在会删除成功,即处理业务逻辑,如果删除失败不处理业务逻辑,直接返回结果。
  • 接口签名的方式,就是把接口请求相关信息(请求报文,包括请求时间戳、版本号、appid等),客户端私钥加签,然后服务端用公钥验签,验证通过才认为是合法的、没有被篡改过的请求。

  • 除了加签验签和token机制,接口报文一般是要加密的。当然,用https协议是会对报文加密的。如果是我们服务层的话,如何加解密呢?

  • 可以参考HTTPS的原理,就是服务端把公钥给客户端,然后客户端生成对称密钥,接着客户端用服务端的公钥加密对称密钥,再发到服务端,服务端用自己的私钥解密,得到客户端的对称密钥。这时候就可以愉快传输报文啦,客户端用对称密钥加密请求报文,服务端用对应的对称密钥解密报文。

  • 有时候,接口的安全性,还包括手机号、身份证等信息的脱敏。就是说,用户的隐私数据,不能随便暴露

分布式事务,如何保证

  • 分布式事务:就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单来说,分布式事务指的就是分布式系统中的事务,它的存在就是为了保证不同数据库节点的数据一致性。

  • 分布式事务的几种解决方案:

    • 2PC(二阶段提交)方案、3PC
    • TCC(Try、Confirm、Cancel)
    • 本地消息表
    • 最大努力通知
    • seata

事务失效的一些经典场景

  • 我们的接口开发过程中,经常需要使用到事务。所以需要避开事务失效的一些经典场景。
    • 方法的访问权限必须是public,其他private等权限,事务失效
    • 方法被定义成了final的,这样会导致事务失效。
    • 在同一个类中的方法直接内部调用,会导致事务失效。
    • 一个方法如果没交给spring管理,就不会生成spring事务。
    • 多线程调用,两个方法不在同一个线程中,获取到的数据库连接不一样的。
    • 表的存储引擎不支持事务
    • 如果自己try…catch误吞了异常,事务失效。
    • 错误的传播特性

掌握常用的设计模式

  • 把代码写好,还是需要熟练常用的设计模式,比如策略模式、工厂模式、模板方法模式、观察者模式等等。设计模式,是代码设计经验的总结。使用设计模式可以可重用代码、让代码更容易被他人理解、保证代码可靠性。

写代码时,考虑线性安全问题

  • 在高并发情况下,HashMap可能会出现死循环。因为它是非线性安全的,可以考虑使用ConcurrentHashMap。所以这个也尽量养成习惯,不要上来反手就是一个new HashMap();

接口定义清晰易懂,命名规范

  • 我们写代码,不仅仅是为了实现当前的功能,也要有利于后面的维护。说到维护,代码不仅仅是写给自己看的,也是给别人看的。所以接口定义要清晰易懂,命名规范。

接口的版本控制

  • 接口要做好版本控制。就是说,请求基础报文,应该包含version接口版本号字段,方便未来做接口兼容。其实这个点也算接口扩展性的一个体现点吧。
  • 比如客户端APP某个功能优化了,新老版本会共存,这时候我们的version版本号就派上用场了,对version做升级,做好版本控制。

注意代码规范问题

  • 注意一些常见的代码坏味道:
    • 大量重复代码(抽共用方法,设计模式)
    • 方法参数过多(可封装成一个DTO对象)
    • 方法过长(抽小函数)
    • 判断条件太多(优化if…else)
    • 不处理没用的代码
    • 不注重代码格式
    • 避免过度设计

保证接口正确性,其实就是保证更少的bug

学会沟通,跟前端沟通,跟产品沟通

  • 我把这一点放到最后,学会沟通是非常非常重要的。比如你开发定义接口时,一定不能上来就自己埋头把接口定义完了,需要跟客户端先对齐接口。遇到一些难点时,跟技术leader对齐方案。实现需求的过程中,有什么问题,及时跟产品沟通。
  • 总之就是,开发接口过程中,一定要沟通好~

简介

  • 不管是移动应用,桌面程序还是后台服务,经常需要从配置文件中读取配置信息,进行程序初始化和改变运行时的状态。以什么样的格式来存储配置信息,这是开发人员需要面临的一个文件。
  • 常用的配置文件格式主要用:
    • 键值对
    • JSON
    • XML
    • YAML
    • TOML

键值对

  • 键值对是一个非常简单易用的配置文件格式。每一个键值对表示一项配置,键值对的分隔符一般使用等号或者冒号。解析时,可以将#号开始的行视为注释行,以达到注释的功能
  • 以键值对为表现形式的配置文件格式常见的用Windows .ini文件和Java中的.properties文件
  • 例如下面是一个使用键值对表示的后台服务配置。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # This is a comment
    name=UserProfileServer
    maxconns=1000
    queuecap=10000
    queuetimeout=300
    loglevel=ERROR
    logsize=10M
    lognum=10
    logpath=/usr/local/app/log
  • 在解析上面的配置时,可以按行读取,然后放到 map 中。下面以 Go 为例,完成对上面配置文件的解析。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    package main

    import(
    "bufio"
    "io"
    "os"
    "fmt"
    "errors"
    "strings"
    )

    func ParseConf(confPath string) (map[string]string, error) {
    if confPath == ""{
    return nil, errors.New("param is ill")
    }

    f, err := os.Open(confPath)
    if err != nil {
    return nil, err
    }
    defer f.Close()

    //store config info
    m := make(map[string]string)

    bfRd := bufio.NewReader(f)
    //read by line, the line terminator is '\n'
    for{
    line, err := bfRd.ReadString('\n')
    if err == nil || err == io.EOF{
    //ignore blank and comment line
    if strings.TrimSpace(line) != "" && strings.TrimSpace(line)[0] != '#'{
    vKV := strings.Split(line, "=")
    if len(vKV) == 2 {
    m[vKV[0]] = vKV[1]
    }
    }
    if err == io.EOF{
    return m, nil
    }
    } else {
    return nil, err
    }
    }
    return m, nil
    }

    func main(){
    mConf, _ := ParseConf("server.kv")
    fmt.Println(mConf)
    }
  • 运行结果:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    map[loglevel:ERROR
    lognum:10
    logpath:/usr/local/app/log
    logsize:10M
    maxconns:1000
    name:UserProfileServer
    queuecap:10000
    queuetimeout:300
    ]

JSON

  • JSON(JavaScript Object Notion),是轻量级的文本数据交换格式,独立于语言,具有自我描述性。JSON类似于XML,但是比XML更小,更快,更易解析

  • JSON语法是JavaScript对象表示法语法的子集:

    • 数据在名称/值对中
    • 数据由逗号分隔
    • 花括号保存对象
    • 方括号保存数组
  • 名称/值包括字段名称(在双引号中),后面写一个冒号,然后是值

    1
    "firstName": "John"
  • 一个合法的JSON可以是:

    • 数字(整数或者浮点数)
    • 字符串(在双引号中)
    • 布尔(true或者 false)
    • 数组(在方括号中)
    • 对象(在花括号中)
    • null
  • 下面以 JSON 表示一个简单的后台服务配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    "-name": "UserProfileServer",
    "maxconns": "1000",
    "queuecap": "10000",
    "queuetimeout": "300",
    "loginfo": {
    "loglevel": "ERROR",
    "logsize": "10M",
    "lognum": "10",
    "logpath": "/usr/local/app/log"
    }
    }
  • 其中 -name 表示服务的名称,前面一个横杠表示该值可以转换为 XML 的标签属性。其它的键值对表示服务的各个配置项

XML

  • XML(Extensible Markup Language)是可扩展标记语言,用来传输和存储数据。因为其允许用户自定义标记名称,具有自我描述性,可灵活地用于存储服务配置信息

  • XML文档结构是一种树结构,它从根部开始,然后扩展到枝叶。XML文档必须有一个唯一的根节点,根节点包含所有其他节点。所有节点均可拥有文本内容和属性(名称/值的对)。XML节点也叫做XML元素

YAML

  • YAML(YAML Ain’t a Markup Language)是专门用来写配置文件的语言,简洁强大,相比于JSON和XML,更加便于开发人员读写。

  • YAML配置文件后缀为.yml或者.yaml

  • YAML 的基本语法规则如下:

    • 数据结构采用键值对的形式 key: value。
    • 键冒号后面要加空格(一般为 1 个空格)。
    • 字母大小写敏感。
    • 使用缩进表示层级关系。
    • 缩进只允许使用空格,不允许使用 Tab 键。
    • 缩进空格数可以任意,只要相同层级的元素左侧对齐即可。
    • 字符串值一般不使用引号,必要时可使用。使用双引号表示字符串时,会转义字符串中的特殊字符(例如\n)。使用单引号时不会转义字符串中的特殊字符。
    • 数组中的每个元素单独一行,并以 - 开头。或使用方括号,元素用逗号隔开。注意短横杆和逗号后面都要有空格。
    • 对象中的每个成员单独一行,使用键值对形式。或者使用大括号并用逗号分开。
    • 文档以三个连字符—表示开始,以三个点号…表示结束,二者都是可选的。
    • 文档前面可能会有指令,在这种情况下,需要使用—来表示指令的结束。指令是一个%后跟一个标识符和一些形参。
    • 目前只有两个指令:%YAML指定文档的 YAML 版本,%TAG用于 tag 简写。二者都很少使用。
    • #表示注释,从这个字符一直到行尾,都会被解析器忽略。
  • YAML支持的数据结构有三种:

    • 对象:键值对的集合,又称为映射(mapping),散列(hashes),字典(dictionary)
    • 数组:一组按次序排列的值,又称为序列(sequence),列表(list)
    • 标量:单个不可再分的值
  • 对象

    • 对象的一组键值对,使用冒号结构表示
      1
      name: Steve
    • YAML也允许另一种写法,将所有键值对写成一个行内对象
      1
      who: {name: Steve, age: 18}
    • 当然,如果对象元素太多一行放不下,那么可以换行
      1
      2
      3
      who:
      name: Steve
      age: 18
  • 数组

    • 一组以连字符开头的行,构成一个数组。注意,连字符后需要添加空格
      1
      2
      3
      4
      animals:
      - Cat
      - Dog
      - Goldfish
    • 连字符前可以没有缩进,也就是说下面的这种写法也是OK的,但是还是建议缩进,因为更加易读
      1
      2
      3
      4
      animals:
      - Cat
      - Dog
      - Goldfish
    • 数组也可以采用行内表示法
      1
      animal: [Cat, Dog, Goldfish]
    • 如果数组元素是一个数组,则可以在连字符下面再缩进输入一个数组
      1
      2
      3
      4
      5
      6
      7
      animals:
      -
      - Cat
      - Dog
      -
      - Fish
      - Goldfish
    • 如果是行内表示,则为:
      1
      animals: [[Cat, Dog], [Fish, Goldfish]]
    • 如果数组元素是一个对象,可以写作:
      1
      2
      3
      4
      5
      6
      animals:
      - species: dog
      name: foo
      - species: cat
      name: bar

    • 对应的 JSON 为:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      {
      "animals": [
      {
      "species": "dog",
      "name": "foo"
      },
      {
      "species": "cat",
      "name": "bar"
      }
      ]
      }
    • 对象和数组可以结合使用,形成复合结构。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      languages:
      - Ruby
      - Perl
      - Python
      websites:
      YAML: yaml.org
      Ruby: ruby-lang.org
      Python: python.org
      Perl: use.perl.org
    • 对应的 JSON 表示如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      {
      "languages": [
      "Ruby",
      "Perl",
      "Python"
      ],
      "websites": {
      "YAML": "yaml.org",
      "Ruby": "ruby-lang.org",
      "Python": "python.org",
      "Perl": "use.perl.org"
      }
      }
  • 标量是最基本、不可再分的值。有以下 7 种:

    • 字符串
    • 布尔值
    • 整数
    • 浮点数
    • Null
    • 时间
    • 日期
  • 使用一个例子来快速了解标量的基本使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    boolean: 
    - TRUE # true、True 都可以
    - FALSE # false、False 都可以
    float:
    - 3.14 # 数值直接以字面量的形式表示
    - 6.8523015e+5 # 可以使用科学计数法
    int:
    - 123 # 数值直接以字面量的形式表示
    - 0b1010_0111_0100_1010_1110 # 二进制表示
    null:
    nodeName: 'node'
    parent: ~ # 使用~表示 null
    string:
    - hello # 字符串默认不使用引号
    - "Hello world" # 使用双引号或单引号包裹含有空格或特殊字符(如冒号)的字符串
    - newline
    newline1 # 字符串可以拆成多行,每一换行符会被转化成一个空格
    date:
    - 2018-02-17 # 日期必须使用 ISO 8601 格式,即 yyyy-MM-dd
    datetime:
    - 2018-02-17T15:02:31+08:00 # 时间使用 ISO 8601 格式,时间和日期之间使用 T 连接,+08:00 表示时区
  • YAML 字符串有三种表示方式:

    • 无引号
    • 单引号
    • 双引号
  • 字符串默认不需要引号,但是如果字符串包含空格或特殊字符(如冒号),需要加引号。

  • 双引号字符串允许在字符串中使用转义序列来表示特殊字符,例如 \n 表示换行,\t 表示制表符,以及 " 表示双引号。

  • 单引号字符串被视为纯粹的字面字符串,不支持转义序列。

  • 如果字符串含有单引号,可以使用双引号包裹,反之亦然

  • 锚点 & 和别名 *,可以用来完成引用

    1
    2
    3
    4
    5
    6
    7
    defaults: &defaults
    adapter: postgres
    host: localhost

    development:
    database: myapp_development
    <<: *defaults
  • 等同于下面的配置。

    1
    2
    3
    4
    5
    6
    7
    8
    defaults:
    adapter: postgres
    host: localhost

    development:
    database: myapp_development
    adapter: postgres
    host: localhost
  • & 用来建立锚点(defaults),<< 表示合并到当前数据,* 用来引用锚点。

  • 如果想引入多行的文本块,可以使用 |,|+,|-,>,>+,>-

  • 当内容换行时,保留换行符

  • 如果最后一行有多个换行符,只保留一个换行符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    linefeed1: |
    some
    text
    linefeed2: |
    some
    text

    linefeed3: |
    some

    text
  • 对应的 JSON 为:

    1
    2
    3
    4
    5
    {
    "linefeed1": "some\ntext\n",
    "linefeed2": "some\ntext\n",
    "linefeed3": "some\n\ntext\n"
    }
  • 当内容换行时,保留换行符。

  • 与 | 的区别是,如果最后一行有多个换行符,则保留实际数目。

    1
    2
    3
    4
    5
    {
    "linefeed1": "some\ntext\n",
    "linefeed2": "some\ntext\n\n",
    "linefeed3": "some\n\ntext\n"
    }
  • 当内容换行时,保留换行符,但最后的换行符不保留

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    linefeed1: |-
    some
    text
    linefeed2: |-
    some
    text

    linefeed3: |-
    some

    text
  • 对应的 JSON 为:

    1
    2
    3
    4
    5
    {
    "linefeed1": "some\ntext",
    "linefeed2": "some\ntext",
    "linefeed3": "some\n\ntext"
    }
  • 当内容换行时,替换为空格,但保留最后一行的换行符

  • 如果最后一行有多个换行符,只保留一个换行符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    linefeed1: >
    some
    text
    linefeed2: >
    some
    text

    linefeed3: >
    some

    text
  • 对应的 JSON 为:

    1
    2
    3
    4
    5
    {
    "linefeed1": "some text\n",
    "linefeed2": "some text\n",
    "linefeed3": "some\ntext\n"
    }
  • 当内容换行时,替换为空格,但保留最后一行的换行符。

  • 与 > 的区别是,如果最后一行有多个换行符,则保留实际数目。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    linefeed1: >+
    some
    text
    linefeed2: >+
    some
    text

    linefeed3: >+
    some

    text
  • 对应的 JSON 为

    1
    2
    3
    4
    5
    {
    "linefeed1": "some text\n",
    "linefeed2": "some text\n\n",
    "linefeed3": "some\ntext\n"
    }
  • -(缺省行为)

  • 当内容换行时,替换为空格,不保留最后一行的换行符。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    linefeed1: >-
    some
    text
    linefeed2: >-
    some
    text

    linefeed3: >-
    some

    text
  • 对应的 JSON 为:

    1
    2
    3
    4
    5
    {
    "linefeed1": "some text",
    "linefeed2": "some text",
    "linefeed3": "some\ntext"
    }
  • 注意:以上 6 个特殊字符,|- 和 >- 用得最多

  • 有时我们需要显示指定某些值的类型,可以使用 !(感叹号)显式指定类型。! 单叹号通常是自定义类型,!! 双叹号是内置类型

    1
    2
    3
    4
    # !!str 指定为字符串
    string.value: !!str HelloWorld!
    # !!timestamp 指定为日期时间类型
    datetime.value: !!timestamp 2021-04-13T02:31:00+08:00
  • 内置的类型如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    !!int:整数类型
    !!float:浮点类型
    !!bool:布尔类型
    !!str:字符串类型
    !!binary:二进制类型
    !!timestamp:日期时间类型
    !!null:空值
    !!set:集合类型
    !!omap,!!pairs:键值列表或对象列表
    !!seq:序列
    !!map:散列表类型
  • 一个 yaml 文件可以包含多个 yaml 文档,使用三个连字符—分隔

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    a: 10
    b:
    - 1
    - 2
    - 3
    ---
    a: 20
    b:
    - 4
    - 5
  • 这种情况在 K8S 和 SpringBoot 中非常常见。

  • 比如 SpringBoot 在一个 application.yml 文件中,通过 — 分隔多个不同配置,根据 spring.profiles.active 的值来决定启用哪个配置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # 公共配置
    spring:
    profiles:
    active: prod #使用名为 prod 的配置,这里可以切换成 dev。
    datasource:
    url: jdbc:mysql://localhost:3306/test_db?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    password: 123456
    username: root
    ---
    # 开发环境配置
    spring:
    profiles: dev #profiles属性代表配置的名称。

    server:
    port: 8080
    ---
    # 生产环境配置
    spring:
    profiles: prod

    server:
    port: 80
  • 下面以 YAML 表示一个简单的后台服务配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    name: UserProfileServer
    maxconns: 1000
    queuecap: 10000
    queuetimeout: 300
    loginfo:
    loglevel: ERROR
    logsize: 10M
    lognum: 10
    logpath: /usr/local/app/log

TOML

  • GitHub 联合创始人 Tom Preston-Werner 觉得 YAML 不够简洁优雅,如缩进要严格对齐,因此和其他几位开发者一起捣鼓了一个 TOML(Tom’s Obvious Minimal Language)。TOML 旨在成为一个语义显著且易于阅读的极简配置文件格式,能够无歧义地转化为哈希表,且能够简单地解析成形形色色语言中的数据结构,用于取代 YAML 和 JSON

  • TOML 的基本语法规则如下:

    • TOML 是大小写敏感的
    • TOML 文件必须是合法的 UTF-8 编码的 Unicode 文档
    • 空白的意思是 Tab(0x09)或空格(0x20)
    • 换行的意思是 LF(0x0A)或 CRLF(0x0D0A)
    • 井号将此行剩下的部分标记为注释

配置文件格式的选择

  • 面对常见配置文件格式,使用时该如何选择呢?这里给几个选择的原则:

    • 支持嵌套结构。仅仅支持 KV 结构的键值对表达能力有点弱;
    • 支持注释。不支持注释的 JSON 是给机器读的,不是给人读的;
    • 支持不同的数据类型,而不仅仅是 string。这一点,键值对和 XML 表现的非常逊色;
    • 最好支持 include 其他配置文件,方便配置模块化。复杂的配置也是无奈之举,但如果支持 include 语法,可以方便的把配置文件模块化。
  • 通过以上几个对配置文件的要求,发现键值对不支持层级关系,JSON 不支持注释,可读性较差,虽然 XML 支持注释和层级结构,且可读性较好,但是因为起始标签一定要有个与之对应的结束标签,文件内容较大,解析时占用较多内存,传输时占用较多带宽。所以这里推荐使用 YAML 和 TOML,很多语言都有其 library 实现,跨语言不成问题

简介

  • VTK(Visualization Toolkit) 常用的编程技巧

vtkDebugLeaksManager 类 链接哪一个动态库

在 VTK 7.1 版本中,vtkDebugLeaksManager 类属于 vtkCommonCore 模块,因此你需要链接到 vtkCommonCore 动态库。以下是可能的库名称,可以在链接命令中使用:

1
-lvtkCommonCore-7.1

请注意,库名称可能因操作系统和安装配置而有所不同。在使用 CMake 构建项目时,CMake 通常会自动处理库的链接。如果你手动编写链接命令,确保链接到 vtkCommonCore 动态库以使用 vtkDebugLeaksManager 类。

如果你使用的是 CMake,可以在你的 CMakeLists.txt 文件中添加类似以下内容来链接 VTK 动态库:

1
2
find_package(VTK 7.1 REQUIRED)
target_link_libraries(YourTargetName PRIVATE ${VTK_LIBRARIES})

其中 YourTargetName 是你要构建的目标名称。上述代码将会自动链接所需的 VTK 动态库,包括 vtkCommonCore

请确保你的 VTK 安装是正确的,包含了 vtkCommonCore 动态库,以及你想要使用的其他模块和类。

pcl::VoxelGrid 链接哪一个动态库

在 Point Cloud Library (PCL) 中,pcl::VoxelGrid 类属于点云处理的模块,因此你需要链接到与点云处理相关的动态库。通常情况下,你需要链接到以下两个主要的动态库:

  1. pcl_common: 这个库包含了 PCL 中一些常用的数据结构、算法和函数,包括点云的表示和操作等。

  2. pcl_filters: 这个库包含了各种点云滤波器,包括 pcl::VoxelGrid 网格滤波器。

如果你使用的是 CMake 来构建项目,你可以在你的 CMakeLists.txt 文件中添加类似以下的内容来链接 PCL 相关的动态库:

1
2
find_package(PCL REQUIRED COMPONENTS common filters)
target_link_libraries(YourTargetName PRIVATE ${PCL_COMMON_LIBRARIES} ${PCL_FILTERS_LIBRARIES})

其中 YourTargetName 是你要构建的目标名称。上述代码会自动链接所需的 PCL 动态库,包括 pcl_commonpcl_filters

请注意,PCL 的库名称可能因为操作系统和 PCL 版本而有所不同。上述示例中的库名称是基于通用的命名约定,具体情况可能会有所变化。你可以查阅你所使用的 PCL 版本的文档以获取更准确的库名称和链接信息。

简介

  • faiss工具的理论基础

faiss 基础

Faiss 是一个用于高效相似向量搜索和聚类的库,由Facebook AI Research开发。它提供了一系列用于向量索引、相似度搜索和聚类的算法和工具。Faiss 在处理大规模向量数据时非常高效,适用于各种应用场景,例如图像搜索、文本检索、推荐系统等。

以下是 Faiss 的一些主要特性和功能:

  1. 向量索引:Faiss 提供了多种向量索引结构,包括平均哈希(IndexFlat),倒排文件(IndexIVF)等,可以在索引中快速存储和检索向量数据。

  2. 相似度搜索:Faiss 支持通过向量之间的相似度进行搜索,可以使用欧氏距离、内积等度量来计算相似度,并返回最相似的向量。

  3. 高效性能:Faiss 针对大规模向量数据进行了优化,使用了高度并行的算法和数据结构,以实现快速的搜索和检索操作。

  4. GPU 支持:Faiss 提供了对 GPU 的支持,可以利用 GPU 的并行计算能力加速向量检索的速度。

  5. 多种距离度量:Faiss 支持多种距离度量方式,包括欧氏距离、内积、余弦相似度等,可以根据具体需求选择适当的度量方式。

  6. 扩展性:Faiss 具有良好的可扩展性,可以处理数百万或数十亿维度的向量数据,适用于大规模应用场景。

要使用 Faiss 进行向量检索,通常需要执行以下步骤:

  1. 准备数据:将需要进行检索的向量数据准备好,可以是图像特征、文本向量或其他类型的向量。

  2. 创建索引:选择适当的索引结构,根据数据集合的大小和特性创建 Faiss 索引。可以选择不同的索引类型和配置参数。

  3. 添加向量:将向量数据添加到 Faiss 索引中,构建索引结构。

  4. 执行搜索:使用索引结构进行相似向量搜索,提供查询向量,Faiss 将返回最相似的向量结果。

Faiss 提供了多种编程语言的接口,包括 C++、Python 和其他语言的封装。你可以根据自己的需求选择适合的接口和编程语言进行使用。

请注意,Faiss 是一个功能强大的库,需要一定的学习和使用成本。如果你有具体的问题或需要更详细的指导,可以提供更多具体的信息,我将尽力提供帮助。


faiss 详解

Faiss(Facebook AI Similarity Search)是一个由Facebook AI Research开发的库,用于高效相似向量搜索和聚类。它主要用于处理大规模向量数据集,具有快速、可扩展和高效的特性。

以下是对 Faiss 的一些详细解释:

  1. 特点和功能:

    • 高效性能:Faiss 针对大规模向量数据进行了优化,使用了高度并行的算法和数据结构,以实现快速的搜索和检索操作。
    • 多种索引结构:Faiss 提供了多种索引结构,包括平均哈希(IndexFlat)、倒排文件(IndexIVF)等。每种结构适用于不同的数据集和搜索需求。
    • 相似度搜索:Faiss 支持通过向量之间的相似度进行搜索,可以使用欧氏距离、内积等度量来计算相似度,并返回最相似的向量结果。
    • GPU 支持:Faiss 可以利用 GPU 的并行计算能力加速向量检索的速度,提供了与 GPU 相关的索引和搜索功能。
  2. 应用领域:

    • 图像搜索:Faiss 可以应用于图像特征向量的索引和搜索,用于快速的图像相似性匹配和检索。
    • 文本检索:Faiss 也可用于文本向量的索引和搜索,用于语义相似性搜索和相关性匹配。
    • 推荐系统:Faiss 在推荐系统中可以用于用户向量和商品向量的相似性匹配,以提供个性化推荐。
  3. 编程语言和接口:

    • Faiss 提供了 C++ 和 Python 的接口,可以在这两种编程语言中使用 Faiss 进行向量索引和搜索。
    • 对于 Python 用户,Faiss 提供了 numpy 数组和 PyTorch 张量之间的无缝集成,方便数据的加载和处理。
  4. 文档和资源:

    • Faiss 的官方文档提供了详细的使用指南、示例代码和API参考,可以帮助开发者快速上手并了解各种功能和配置选项。
    • Faiss 的 GitHub 存储库包含了源代码、问题跟踪和社区讨论,开发者可以在其中获取更多的资源和支持。

总体而言,Faiss 是一个功能强大的向量检索库,被广泛应用于图像搜索、文本检索和推荐系统等领域。它的高性能、多样的索引结构和灵活的编程接口使得处理大规模向量数据变得高效且可扩展。


C++ 常用接口

在 Faiss C++ 库中,以下是一些常用的接口:

  1. Index:Index 是 Faiss 中索引的基类,定义了向量索引和搜索的通用接口。各种具体的索引结构都派生自 Index 类。常用的派生类包括 IndexFlat、IndexIVF、IndexIVFPQ 等。

  2. IndexFlat:IndexFlat 是 Faiss 中平坦索引的类,适用于小规模数据集。它提供了基于距离度量的搜索功能,例如欧氏距离或内积。

  3. IndexIVF:IndexIVF 是 Faiss 中倒排文件索引的类,适用于大规模数据集。它使用倒排文件结构组织向量数据,并提供了高效的相似向量搜索。

  4. IndexIVFPQ:IndexIVFPQ 是 Faiss 中倒排文件索引的量化版本。它通过对向量进行量化和编码来减小存储空间,并提供了高效的相似向量搜索。

  5. IndexLSH:IndexLSH 是 Faiss 中局部敏感哈希索引的类,用于高维向量的近似搜索。它使用局部敏感哈希函数将向量映射到多个桶中,以实现高效的近似最近邻搜索。

  6. IndexHNSW:IndexHNSW 是 Faiss 中基于 HNSW(Hierarchical Navigable Small World)算法的索引类。它构建了一个层次结构的近邻图,以提供快速的相似向量搜索。

这些是 Faiss C++ 库中的一些常用接口和类示例。每个索引类都提供了特定的功能和选项,可以根据应用需求选择适合的接口和类。在使用 Faiss C++ 库时,建议参考官方文档和示例代码,以了解更多详细的接口和类说明。

简介

  • CSS 相关笔记

  • CSS (Cascading Style Sheets,层叠样式表),是一种用来为结构化文档(如 HTML 文档或 XML 应用)添加样式(字体、间距和颜色等)的计算机语言,CSS 文件扩展名为 .css

CSS 简介

  • 什么是CSS?
    • CSS 指层叠样式表 (Cascading Style Sheets)
    • 样式定义如何显示 HTML 元素
    • 样式通常存储在样式表中
    • 把样式添加到 HTML 4.0 中,是为了解决内容与表现分离的问题
    • 外部样式表可以极大提高工作效率
    • 外部样式表通常存储在 CSS 文件中
    • 多个样式定义可层叠为一个

CSS 语法

  • CSS 实例

    • CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明: h1 {color:blue; font-size:12px;}
    • 选择器通常是您需要改变样式的 HTML 元素。
    • 每条声明由一个属性和一个值组成。
    • 属性(property)是您希望设置的样式属性(style attribute)。每个属性有一个值。属性和值被冒号分开。
    • CSS声明总是以分号 ; 结束,声明总以大括号 {} 括起来:
      1
      p {color:red;text-align:center;}
    • 为了让CSS可读性更强,你可以每行只描述一个属性:
      1
      2
      3
      4
      5
      p
      {
      color:red;
      text-align:center;
      }
  • CSS 注释

    • 注释是用来解释你的代码,并且可以随意编辑它,浏览器会忽略它。
    • CSS注释以 /* 开始, 以 */ 结束, 实例如下:
      1
      2
      3
      4
      5
      6
      7
      8
      /*这是个注释*/
      p
      {
      text-align:center;
      /*这是另一个注释*/
      color:black;
      font-family:arial;
      }

CSS id 和 class

  • id 和 class 选择器

    • 如果你要在HTML元素中设置CSS样式,你需要在元素中设置”id” 和 “class”选择器。
  • id 选择器

    • id 选择器可以为标有特定 id 的 HTML 元素指定特定的样式。
    • HTML元素以id属性来设置id选择器,CSS 中 id 选择器以 “#” 来定义。
    • 以下的样式规则应用于元素属性 id=”para1”:
      1
      2
      3
      4
      5
      #para1
      {
      text-align:center;
      color:red;
      }
    • ID属性不要以数字开头,数字开头的ID在 Mozilla/Firefox 浏览器中不起作用。
  • class 选择器

    • class 选择器用于描述一组元素的样式,class 选择器有别于id选择器,class可以在多个元素中使用。
    • class 选择器在 HTML 中以 class 属性表示, 在 CSS 中,类选择器以一个点 . 号显示:
    • 在以下的例子中,所有拥有 center 类的 HTML 元素均为居中。
      1
      .center {text-align:center;}
    • 在以下实例中, 所有的 p 元素使用 class=”center” 让该元素的文本居中:
      1
      p.center {text-align:center;}
    • 多个 class 选择器可以使用空格分开:
      1
      2
      .center { text-align:center; }
      .color { color:#ff0000; }

CSS 创建

  • 当读到一个样式表时,浏览器会根据它来格式化 HTML 文档。

  • 如何插入样式表

    • 插入样式表的方法有三种:
      • 外部样式表(External style sheet)
      • 内部样式表(Internal style sheet)
      • 内联样式(Inline style)
  • 外部样式表

    • 当样式需要应用于很多页面时,外部样式表将是理想的选择。在使用外部样式表的情况下,你可以通过改变一个文件来改变整个站点的外观。每个页面使用 标签链接到样式表。 标签在(文档的)头部:
      1
      2
      3
      <head>
      <link rel="stylesheet" type="text/css" href="mystyle.css">
      </head>
    • 浏览器会从文件 mystyle.css 中读到样式声明,并根据它来格式文档。
    • 外部样式表可以在任何文本编辑器中进行编辑。文件不能包含任何的 html 标签。样式表应该以 .css 扩展名进行保存。下面是一个样式表文件的例子:
      1
      2
      3
      hr {color:sienna;}
      p {margin-left:20px;}
      body {background-image:url("/images/back40.gif");}
  • 内部样式表

    • 当单个文档需要特殊的样式时,就应该使用内部样式表。你可以使用