system: [SYSTEM]

user: 3. 技术综述 3.1. SpringBoot框架 SpringBoot使创建独立的、生产级的、基于Spring的应用程序变得容易。大多数Spring引导应用程序只需要很少的Spring配置。SpringBoot框架可以创建独立的Spring应用程序、直接嵌入Tomcat、Jetty或Underow (无需部署WAR文件)、提供固定的“starter”依赖项以简化构建配置、尽可能自动配置Spring和第三方库、提供生产就绪的特性，如度量、运行状况检查和外部化配置，不需要XML配置 [ 1 ]。 3.2. Apache Shiro权限控制 Apache Shiro是一个强大且易用的Java安全框架，执行身份验证、授权、密码和会话管理 [ 2 ]。包括三个核心组件：Subject，SecurityManager和Realms。Subject，即当前操作用户，Subject代表了当前用户的安全操作。SecurityManager是Shiro框架的核心，管理所有用户的安全操作，典型的Facade模式，Shiro通过SecurityManager来管理内部组件实例，并通过它来提供安全管理的各种服务，引用多个内部嵌套安全组件形成对象图。Realm实质上是一个安全相关的DAO：它封装了数据源的连接细节，并在需要时将相关数据提供给Shiro，充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。当配置Shiro时，必须至少指定一个Realm，用于认证和(或)授权 [ 3 ]。Shiro内部架构如图1所示。 图1. Shiro内部架构 应用代码通过Subject来进行认证和授权，而Subject又委托给SecurityManager，给Shiro的SecurityManager注入Realm后，SecurityManager才能对合法的用户及其权限进行判断 [ 4 ]。认证和授权过程如图2所示。 图2. Shiro认证和授权过程 3.3. Activiti工作流引擎 对Activiti5是由Alfresco软件发布的业务流程管理(BPM)框架，它是覆盖了业务流程管理、工作流、服务协作等领域的一个开源的、灵活的、易扩展的可执行流程语言框架，是基于Apache许可的开源BPM平台。Activiti5基于jBPM4，与Alfresco的集成增加了其流程可视化与管理能力，同时通过创新的Activiti Cycle协作组件支持流程相关人员之间的协调，加强了集成能力 [ 5 ]。 Activiti引擎提供了七大Service接口，均通过ProcessEngine获取，并且支持链式API编程风格。EngineService接口中定义了获取各种服务类实例对象的方法，提供的服务类包括 [ 6 ] ： 1) RepositoryService：提供一系列管理流程部署和流程定义的API。 2) RuntimeService：在流程运行时对流程实例进行管理与控制。 3) TaskService：对流程任务进行管理，例如任务提醒、任务完成和创建任务等。 4) IdentityService：提供对流程角色数据进行管理的API，这些角色数据包括用户组、用户及它们之间的关系。 5) ManagementService：提供对流程引擎进行管理和维护的服务。 6) HistoryService：对流程的历史数据进行操作，包括查询、删除这些历史数据。 7) FormService：表单服务。 Activiti原生支持Spring，可以很轻松的进行Spring集成，非常方便管理事务和解析表达式。Activiti继承自jBPM4，可以快速的读取运行时数据，仅当需要查询历史数据时再从专门的历史数据表中读取。这种设计方式可以大幅度提高运行时存取效率，尤其是当数据日积月累时依然能够快速反应 [ 7 ]。 在使用Activiti的时候，通常情况下需要根据业务需求绘制流程文档，然后将流程文档进行部署，从而进行后续的一系列操作。部署资源无疑至关重要，如何合理使用引擎提供的部署资源功能，也是前期设计的重中之重，该过程可以检查定义的流程资源是否合理，通用以及校验流程资源格式是否正确等，从而对流程资源进行把关，规避一些不可预知的风险点 [ 8 ]。流程文档部署生命周期分为四大步骤 [ 9 ] ： 1) 定义流程文档：客户端可以根据自己的业务需求定义流程文档。使用流程引擎的目的是为了使用流程驱动业务的流转。要用流程驱动业务就必须为业务启动一个流程实例，启动一个流程实例必须定义一系列活动，这一系列的活动就组成了一个流程定义。 2) 启动流程引擎：流程引擎启动后会自动根据构造ProcessEngine实例对象，这样客户端就可以通过该室离对象获取各种各样的服务类实例对象。 3) 部署流程文档：调用流程文档部署命令进行部署，该过程只需要客户端调用部署流程文档的命令即可。流程引擎收到命令之后，将流程文档中定义的元素解析为Activiti的内部表示BaseElement实例，然后对BaseElement实例对象再次解析，进而将其转化为流程虚拟机中的ActivitiImpl实例对象或者TransitionImpl实例对象，该过程也是将BaseElement实例对象注入流程虚拟机的过程。 4) 添加缓存：以上所有步骤完成之后，缓存流程定义信息，这样后续节点运转的时候，只需从缓存中取值即可，无需再次执行以上的步骤，从而大幅提升性能，流程引擎默认开启了缓存功能 [ 10 ]。 3.3.1. 部署流程资源 部署流程资源的方法包括：classpath、InputStream、文本方式、zip格式压缩包 [ 11 ]。 Classpath方式：使用addClasspathResource方式部署流程文档，会读取项目工程中classpath路径下的流程文档。使用该方式部署流程文档会使流程文档与项目产生高耦合，不建议在正式环境中使用。 InputStream方式：使用InputStream方式部署流程资源需要传入一个输入流及资源的名称，直接通过类加载器获取bpmn文件的数据流。 文本方式：利用字符串方式可以直接传入纯文本作为资源的来源，字符串方式的实现原理是把一组字符串的内容转换为字节流后再部署。 zip/bar格式压缩包方式：Activiti提供了打包部署机制，可以把多个流程文档以及流程文档对应的图片或者表单等统一打包为.zip的压缩文件或者.bar压缩文件。 本文采用inputStream方式部署文件。 3.3.2. 流程文档解析 关于XML文档解析技术，目前经常使用的有DOM、SAX、DOM4J等，这些解析方式从模型上可以划分为如下两种：DOM (文档对象)模型和流模型，Activiti选择流模型方式进行文档解析。流模型常用的技术有SAX和STAX两种。SAX使用的是推模型，是一种事件驱动机制，文档解析的时候每当发现一个元素节点就回出发相应的事件，因此客户端需要编写监听触发事件的处理程序，使用该方式解析文档无疑增加了客户端操作的复杂度，使用起来不灵活；STAX使用的是拉模型，文档解析时客户端可以定制自己感兴趣的节点主动从读取器中进行拉取，选择性的处理节点事件，灵活性大大提高，因此Activiti最终选用STAX方式解析流程文档。 Activiti元素解析功能架构分为三层： 1) 元素定义层：完全交给客户端，开发人员可以结合自己的业务场景组装一系列元素完成流程文档定义工作，流程文档定义完毕之后就可以直接调用元素解析层实现流程文档的解析工作。 2) 元素解析层：负责定位流程文档、初始化元素解析器(包括子元素)、加载自定义元素解析器、查找元素解析器，最终将需要解析的元素以及属性进行解析，并封装映射为引擎中的一个个实体对象。 3) 基础支撑层：例如数据库连接管理事务管理等，Activiti将这些公用组件抽取出来作为基础模块使用。 元素解析功能架构设计如图3所示。 图3. 元素解析功能架构 3.3.3. 工作流任务分配算法 工作流调度算法通常分为三类：1) 基于列表；2) 基于集群；3) 基于复制。 基于列表：基于列表的启发式方法根据特定的优先级机制将工作流的所有任务排序到列表中，然后按照列表中的顺序安排任务。 基于集群：基于集群的启发式方法的主要思想是通过将频繁通信的任务分为一个聚类来减少通信延迟。一般来说，基于聚类的启发式方法有两个阶段：聚类和合并。在集群阶段，原始的工作流应用程序被划分成集群，合并阶段合并集群，使集群的剩余数量等于资源的数量。 基于复制：基于复制的启发式方法通过在目标处理器上复制任务，帮助任务将数据传输到后续任务的资源。这种重复安排可以降低从任务到后续任务的通信成本。 在这三类调度中，基于列表的调度具有简单、适用性广的优点。例如，它可以有效地扩展到调度混合并行工作流或并发工作流，而其他两类启发式方法不能直接应用。基于集群的启发式算法不能处理混合的并行工作流，基于重复的启发式算法不适合并行工作流调度，因为任务重复会消耗额外的资源，从而降低了并发工作流的执行性能。因此，本文重点应用基于列表的工作流调度。 基于列表的工作流调度包括两个主要步骤：任务排序和任务分配。 在任务排序步骤中，调度算法根据特定的机制为每个任务分配一个等级值，然后根据其等级值将任务排序到一个列表中。 在任务分配步骤中，调度算法按照列表中的顺序连续地将每个任务分配到根据特定处理器选择机制执行的适当处理器。常用的任务分配方法包括： 最短工作列表(SWL)算法SWL算法的主要思想是将任务分配给工作列表中工作项最少的候选执行者。查找工作列表最短的执行者后返回执行者的工作列表长度。SWL算法不需要知道执行者的任务处理时间，适用于能力表未知的情况。SWL算法的目的是生成一个工作流日志。 最短处理时间(SPT)算法SPT算法主要思想是把任务分配给最优秀的执行者。它试图缩短案例处理时间，寻找任务处理时间最短的执行者。为了避免其他执行者挨饿，SPT算法首先考虑自由执行者(没有工作项)，并且SPT算法对执行者的个体能力很敏感，很难平衡执行者的工作量。 最短完成时间(SCT)算法SCT算法将任务分配给所有候选执行者，然后计算执行者的工作列表中所有工作项的估计完成时间，找出哪个执行者首先完成任务。SCT算法试图减少案例完成时间。 平均工作量(AVGWL)算法AVGWL算法的主要思想类似于SPT算法，同时缩短案例处理时间。与SPT算法不同，AVGWL算法更注重平衡执行者的工作负载。 3.4. Restful API REST (Representational State Transfer)是一种组织Web服务的架构，其目标是创建具有良好扩展性的分布式系统。如果一个系统满足了如下五条约束，那么该系统就被称为是RESTful的。 1) 使用客户/服务器模型。客户和服务器之间通过一个统一的接口来互相通讯。 2) 层次化的系统。在一个REST系统中，客户端并不会固定地与一个服务器打交道。 3) 无状态。在一个REST系统中，服务端并不会保存有关客户的任何状态，即客户端自身负责用户状态的维持，并在每次发送请求时都需要提供足够的信息。 4) 可缓存。REST系统需要能够恰当地缓存请求，以尽量减少服务端和客户端之间的信息传输，以提高性能。 5) 统一的接口。一个REST系统需要使用一个统一的接口来完成子系统之间以及服务与用户之间的交互。这使得REST系统中的各个子系统可以独自完成演化。 由于系统以嵌入式集成了Activiti引擎，除了在内部调用引擎接口外还需要对外提供接口服务，此使REST API就派上了用场，只需要简单配置即可对外提供标准的REST服务接口，节省开发成本。