1金蝶云苍穹5.0·集成服务云服务编排开发指南金蝶云苍穹·集成平台服务部张天越2022.11.032提纲1.基本概念2.流程元素3.流程监控4.流程设计5.脚本引擎3苍穹集成服务云的核心功能:取数、转换、加载数据集成概念模型数据转换源目标4异构系统的类似功能不可能全是一一对应的,必要时可能需要组合多个功能调用才能将一份业务数据完整的同步到目标系统中。苍穹集成服务云的可视化配置工具--服务流程,可以帮助客户快速完成涉及多个系统的业务功能调用配置和执行顺序控制配置;并提供相应的运行时监控基础设施,方便客户了解的业务执行情况、分析业务异常原因和执行错误处理。什么是服务编排?51.提供业务执行过程的图形化设计、运行时展示、日志记录与实例下载功能2.提供错误时自动重试与异常处理3.提供错误时通知与人工重试4.提供事务补偿机制,撤销流程时自动执行相应的补偿操作服务编排的价值?降低复杂业务集成的开发/运维成本6微服务实现了业务的拆分,但复杂的业务可能涉及多个微服务甚至多个云,微服务拆得越细,组合的需求就越强烈;微服务架构又不能提供全局事务管理来保证多个微服务的事务完整性,通过消息的编排来实现最终一致性也特别麻烦;使用服务流程提供的流程设计、事务补偿机制,可以为微服务的组合、API的开发提供另一种实现思路。服务编排还能做什么?微服务编排、API开发7提纲1.基本概念2.流程元素3.流程监控4.流程设计5.脚本引擎8服务流程的业务执行通过节点来表示,例如:数据集成用来调用“数据集成方案”或“启动方案”;也可组合使用数据获取、字段映射、数据加载节点来实现“数据集成方案”的功能,以享受更好的灵活性;脚本可以灵活访问各类资源、完成复杂的数据处理、调用第三方系统提供的API、或调用平台的微服务(尤其适合调用简单值及其组成的List/Map作为参数的微服务);时间等待节点可用来实现与第三方系统业务执行的协调;还可通过通知发送节点知会业务相关人员,API节点可以调用外部API和苍穹的微服务,节点块可以将节点组装便于流程展示,循环块可以将节点组装后循环处理,子流程可以将另一个流程引入异步执行,RPA流程可以调用苍穹的RPA服务,事件等待可以等待苍穹、EAS、集成云的HUB事件和集成云自定义事件,流程终止可以撤销当前流程实例。流程元素–节点9服务流程的执行控制通过转移来表示,表示前置节点完成后该执行的后继节点。(注意:转移方向只代表先后顺序,没有并行执行的含义)普通转移–表示正常流向;错误转移–表示前置节点发生错误时该执行的后续处理;转移上可以添加条件实现更灵活的控制。补偿转移–表示前置节点完成后,流程撤销时执行后继节点对已完成业务进行补偿,以实现分布式事务的最终一致性。流程元素–转移补偿转移错误转移10流程变量服务流程在调用业务时需要准备参数,得到的结果也可能需要暂存起来作为流程控制和后续业务调用的参数,因此流程运行时需要保留一个临时空间存储相关数据,为方便管理,这片临时空间以流程变量指示,并通过变量名进行存取。服务流程用流程变量接收外部传入的参数、在节点间传递数据、向调用者传出执行结果、以及作为流程执行控制的条件。流程元素-变量11流程变量类型流程变量分为两大类,其中:简单值,主要作为流程的输入/输出参数和流程执行的控制变量;集成对象,主要用于数据获取、字段映射、数据加载节点,用于数据集成。从类比的角度看,流程变量与Java类的实例字段(Field)类似。流程元素-变量类型12流程资源流程资源是服务流程与外部交互的中介,唯有通过资源服务流程才能调用业务功能,例如:第三方系统的API、数据库系统的连接、值转换规则、数据集成方案/启动方案等。在服务流程的脚本节点和转移条件上,通过资源的别名访问资源;资源在运行期是不可改变的;大部分类型的资源在服务流程的脚本中可当成函数以资源的别名调用。当前苍穹的微服务可以通过系统函数直接调用;其他系统的API、SQL的执行都必须有相应的资源支持。流程元素-资源13提纲1.基本概念2.流程元素3.流程监控4.流程设计5.脚本引擎14人工启动–主要用于数据初始同步,点击“立即执行”按钮启动,必要时可以需要录入输入参数。定时启动–主要用于数据增量同步,点击“立即执行”按钮启动,该类流程不需要录入输入参数。事件触发–主要用于数据补偿同步,点击“立即执行”按钮启动,输入查询条件对遗漏同步的数据执行流程。消息触发–主要用于准实时同步,暂不支持“立即执行”按钮启动。除通过服务流程列表启动服务流程的执行外,还可以使用集成服务云提供的微服务启动流程、在自定义API中使用脚本调用启动流程、将服务流程发布为API至开放平台供外部系统调用启动等。运行监控–流程启动15流程图–查看流程运行时流程图,可以了解那些节点的业务执行了,哪些失败了,还可查看各节点的执行耗时和配置信息。日志–服务流程失败时,可以查看日志了解错误细节以确定解决办法(重试/撤销流程、人工修复数据、修改流程设置等)。重试–通过分析服务流程失败日志,某些环境相关的异常可以通过重试让流程从失败点继续执行下去。撤销–某些失败的流程或配置错误导致死循环的流程,可以通过撤销操作中止掉。查看追溯–如果环境启用了“业务追溯”,可以通过追溯信息了解流程在整个调用链条中的上下游业务,方便查看关联业务的日志信息。修改变量–当流程变量由于异常原因出错且无法通过单据修复时,可通过修改流程变量值,将流程实例更新。忽略–通常配合修改变量使用,当流程重试无法继续流程时,可将变量修改后忽略出错节点,继续执行。下载变量–将流程实例全部变量下载成txt压缩包或者excel表格,方便定位出错变量值。运行监控–流程图16服务流程提交启动请求后,有可能一直停在“新建”状态(即:不执行),可按下述步骤排查原因:1.首先,查找服务流程实例关联的集成云任务。将“编码”复制下来,打开“监控统计–集成云任务监控”,使用复制下来的编码搜索任务,如:右上图。2.然后,检查任务状态。如果任务状态是“失败”,根据任务详细信息中的错误原因酌情处理,排除故障原因后,再“重试”;如果任务状态是“就绪”,说明平台的MQ服务器可能发生了异常,可以“立即执行”以解燃眉之急,但仍需要联系运维人员解决MQ服务器的问题。运行监控–流程不执行?17如果原子业务是可补偿的,可以为这些业务定制一个逆向操作作为补偿;当流程执行失败时,如果需要撤销流程,那么已完成业务的逆向操作将被调用以消除影响,确保整个复杂业务的一致性。运行监控–业务补偿18提纲1.基本概念2.流程元素3.流程监控4.流程设计5.脚本引擎19子流程将独立的功能模块建立一个单独的服务流程,通过资源引入当前流程,同步子流程的当时异步调用,将子流程的执行结果返回,可以达到流程之间的组装嵌套调用。节点块将多个独立节点组装配置在节点块中,使得流程配置简洁,看起来轻便易懂,节点块内节点与当前流程同步调用。应用技巧–子流程、节点块20循环块将被循环的变量的每一个值对象执行块内配置的流程节点,减少使用中通过流程变量配置多次循环的复杂逻辑,只需配置好循环变量,输入,输出,关注每一个值的配置输出即可。应用技巧–循环块21状态保存服务流程默认每5秒保存一次状态数据,如果在执行过程中突然中断可能丢失最近5秒内执行的业务结果;因此当业务调用不是幂等的或执行时间很长时,那么执行后需要立即保存流程状态(勾选“完成时保存流程状态”),以免流程失败重试时重复执行。自动重试分布式系统可能因为各种原因导致业务调用失败,某些原因导致的失败在稍后重试常常能够继续执行(比如:常见的网络异常、数据库死锁),对于易于发生这类异常的节点,可以配置失败时重试调度计划和失败时重试前提条件,服务流程引擎则业务失败且条件满足时自动延时重试,即使环境条件恶劣时,业务也可在无需人工干预的情况下执行成功。应用技巧–状态保存与自动重试22应用技巧–API开发微服务编排微服务可以实现业务的拆分,复杂业务拆分后通常需要调用多个微服务,但微服务架构天生不支持全局分布式事务,用传统的代码方式调用,业务的完整性非常难以保证,出错时很难分析原因和修复数据。集成服务云提供的服务流程可以用于编排多个微服务或第三方系统API的调用,从而满足复杂业务的需要,通过节点提供一种自然的复杂业务分解和协调机制,极大的降低了复杂业务的开发难度。23应用技巧–复杂业务拆分复杂业务是指其细节作为一个整体考虑时超过了我们的理解能力上限的业务,这些业务通常可以拆分为多个易于理解的微服务、API、任务等,在使用服务流程进行编排时,以下事项需要注意拆分的粒度。•粒度太小则节点太多,流程图过于复杂同样难以理解;•粒度太大则单个节点完成事情太多,事务的完整性难以保证;建议:•以涉及系统的事务管理的能力范围为依据对业务进行拆分,确保流程图中的每个节点完成的业务在一个事务内完成或无需事务。•流程图中节点太多时,尽量按聚簇的原则对节点进行分组,每组节点只有一个入口/一个出口,各节点组之间有明显间隔区分;•保证流程图中的连线整洁,尽量避免交叉重叠与长连线。24提纲1.基本概念2.流程元素3.流程监控4.流程设计5.脚本引擎251.转换为日期/时间vardate=T(s);//T("1999-12-3112:30:00")2.转换为长整数varv=L(s);//L("19999999")3.转换为整数varv=I(s);//I("19999999")4.转换为布尔值varb=X(s);//X("true")5.转换为定点小数varn=N(s);//N("1.001")6.转换为浮点小数vard=D(s);//D("1.001")7.转换为字符串vars=String(v);//String(1.0)8.查看值类型vart=typeof(v);//typeof(1.0);脚本引擎–简单值类型转换261.if(value){…}判断为true要求value是:null,true,"",0,0.0,[],{}2.if(valueisNull){…}判断为true要求value是:null3.if(valueisEmpty){…}判断为true要求value是:null,"",[],{}4.if(valueisXXX){…}用来判断value是指定类型的对象,XXX取值可以是:Map,List,String,Integer,Long,Number,Date,Boolean等。脚本引擎–条件判断271.s0startsWiths1判断字符串s0是否以字符串s1开头,例如:namestartsWith''2.s0endsWiths1判断字符串s0是否以字符串s1结尾,例如:nameendsWith''3.s0containss1判断字符串s0是否包含子字符串s1,例如:namecontains''4.s0matchess1判断字符串s0是否符合字符串s1表示的字符串模式(正则表达式),例如:nmatches'\\d+'脚本引擎–字符串比较28acontainsb等价于bina具体用法有:•s0containss1字符串s0是否包含子串s1,例如:'abc'contains'b'•listcontainse列表list中是否包含元素e,例如:[1,3,5]contains3•setcontainse集合set中是否包含元素e,例如:Data.set(1,2,4)contains2•mapcontainskey哈希表map中是否包含键key,例如:{id:1,name:'Star'}contains'name'•rangecontainse范围range内是否包含成员e,例如:[1..100]contains98脚本引擎–contains&in291.SQL查询函数varids=[1,2];varsql="SELECTfnumberFROMT_SEC_USER@SYSWHEREfidIN("+ids.concat("?",",")+")";vartypes=(ids=>BIGINT);varnumbers=query_column(cn,sql,ids,types);//cn是系统连接的变量名提示:其他SQL查询函数还包括query_value,query_row,query_list,使用方法参考《脚本开发指南》。2.嵌入式SQL查询varids=[1,2];SELECT@@numbers[]=fnumberFROMcn.T_SEC_USER@SYSWHEREfidIN(@@ids);//cn是系统连接的变量名returnnumbers;3.SQL更新函数varids=[-1,-2];varsql="DELETEFROMT_SEC_USER@SYSWHEREfidIN(?,?)";vartypes=[BIGINT,BIGINT];varnumbers=execute_update(cn,sql,ids,types);//cn是系统连接的变量名脚本引擎–数据库访问301.列表分组,根据指定字段值将列表分成子列表varlist=[{name:'Sky',subject:'math',score:95.0},{name:'Sky',subject:'art',score:98.0},{name:'Star',subject:'art',score:99.0},{name:'Star',subject:'music',score:85.0}];//根据姓名对列表分组varlist2=list.group(name);2.列表转Map,以指定字段值为Key构造Mapvarlist=[{subject:'math',score:95.0},{subject:'art',score:98.0}];//以subject为key,score为value构造新Mapvarmap=list.mapping(subject,score);3.列表过滤,从列表中取满足条件的成员构成新列表varlist=[{subject:'math',score:95.0},{subject:'art',score:98.0}];//score>97;varsublist=list[score>97];//或list.filter(score>97);4.列表遍历,返回新列表varlist=[{subject:'math',score:95.0},{subject:'art',score:98.0}];varnewlist=list=>{subject:subject,grade:(score>95?'A':'B')};或varnewlist=list.each(subject:subject,grade:(score>95?'A':'B'));脚本引擎–数据结构变换311.列表连接成一个字符串varids=[1,2,3,4,5];vars=ids.concat("?",",");//结果是“?,?,?,?,?”2.复杂的例子(一)varlist=[{name:'Sky',subject:'math',score:95.0},{name:'Sky',subject:'art',score:98.0},{name:'Star',subject:'art',score:99.0},{name:'Star',subject:'music',score:85.0},{name:'Rose',subject:'music',score:85.0}];//生成成绩大于90的学生的科目列表varresult=list[score>90].group(name).entries(name:key,subjects:value.concat(subject,','));3.复杂的例子(二)varlist=[{name:'Sky',subject:'math',score:95.0},{name:'Sky',subject:'art',score:98.0},{name:'Star',subject:'art',score:99.0},{name:'Star',subject:'music',score:85.0},{name:'Rose',subject:'music',score:85.0}];//生成学生的成绩大于90的科目列表varresult=list.group(name).entries(name:key,subjects:value[score>90].concat(subject,','));脚本引擎–字符串连接321.列表连接成一个字符串varids=[1,2,3,4,5];vars=ids.concat("?",",");//结果是“?,?,?,?,?”2.复杂的例子(一)varlist=[{name:'Sky',subject:'math',score:95.0},{name:'Sky',subject:'art',score:98.0},{name:'Star',subject:'art',score:99.0},{name:'Star',subject:'music',score:85.0},{name:'Rose',subject:'music',score:85.0}];//生成成绩大于90的学生的科目列表varresult=list[score>90].group(name).entries(name:key,subjects:value.concat(subject,','));3.复杂的例子(二)varlist=[{name:'Sky',subject:'math',score:95.0},{name:'Sky',subject:'art',score:98.0},{name:'Star',subject:'art',score:99.0},{name:'Star',subject:'music',score:85.0},{name:'Rose',subject:'music',score:85.0}];//生成学生的成绩大于90的科目列表varresult=list.group(name).entries(name:key,subjects:value[score>90].concat(subject,','));脚本引擎–字符串连接331.HttpGetvarreturns=HttpGet(url,params,charset,cookies,headers);//params是Map或字符串,cookies,headers可省略2.HttpPostvarreturns=HttpPost(url,params,charset,cookies,headers);//params是Map或字符串,cookies,headers可省略3.HttpInvokevarreturns=HttpInvoke(url,params,cookies,headers);//params是Map或字符串,cookies,headers可省略4.HttpAccessvarreturns=HttpAccess(url,method,params,charset,cookies,headers);//params是Map或字符串,cookies,headers可省略//method取值为GET、POST、PUT、HEAD、OPTIONS、DELETE、TRACE之一5.CallWebServicevarreturns=CallWebService(url,method,data,cookies,headers,charset);//cookies,headers,charset可省略//method是Web服务的方法名,对于本方法不支持的Web服务请自行构造XML,使用HttpPost函数调用注意:returns为Map对象,包含以下部分•result–远程服务器返回的响应结果,其中:HttpInvoke函数返回的Json对象,其他函数返回的字符串(某些场景可能是空值);•cookies–远程服务器返回的Cookie值,key为Cookie的名称,value为Cookie的设置值;•headers–远程服务器返回的Header信息,key为Header的名称,value为Header值。脚本引擎–Web访问34打开集成管理-其他-脚本调试器,以#debug为标识,任意脚本执行到这一行都会进入调试器,此时可以开始调试。脚本引擎–脚本调试35集成服务云5.0帮助文档入门必读必学https://vip.kingdee.com/article/106799555437294080服务流程操作指南https://club.kdcloud.com/article/98890752746688768脚本学习汇总https://vip.kingdee.com/article/136617550254708480API集成https://club.kdcloud.com/knowledge/specialDetail/136524013400082176消息集成https://club.kdcloud.com/knowledge/specialDetail/13660722634486553636Thanksterimakasih感謝谢谢ありがとうขอบคุณ【金蝶云.苍穹】-集成云交流群