搜索​​​​

清除过滤器
文章
Qiao Peng · 一月 14, 2021

InterSystems IRIS 开放授权框架 (OAuth 2.0) 实现 – 第 1 部分

本文以及后面两篇该系列文章,是为需要在其基于 InterSystems 产品的应用程序中使用 OAuth 2.0 框架(下文简称为 OAUTH)的开发人员或系统管理员提供的指南。 作者:InterSystems 高级销售工程师 Daniel Kutac # 发布后校正和更改历史记录 * 2016 年 8 月 3 日 - 修正了 Google 客户端配置屏幕截图,更新了 Google API 屏幕截图以反映新版本的页面 * 2016 年 8 月 28 日 - 更改了 JSON 相关代码,反映了对 Cache 2016.2 JSON 支持的更改 * 2017 年 5 月 3 日 - 更新了文本和屏幕,以反映 Cache 2017.1 的新 UI 和功能 * 2018 年 2 月 19 日 - 将 Caché 更改为 InterSystems IRIS 以反映最新的发展。 但是请记住,尽管产品名称发生更改,但**文章涵盖所有的 InterSystems 产品**——InterSystems IRIS 数据平台、Ensemble 和 Caché。 * 2020 年 8 月 17 日 - 大面积更改,软件方面更改更大。 要获取 Google 的更新版 Oauth2 的网址,请咨询 Micholai Mitchko。 _第 1 部分 客户端_ # **简介** 有关开放式授权框架 InterSystems 实现的相关内容,我们分 3 部分讲述,这是第 1 部分。 在第 1 部分中,我们对该主题进行了简短介绍,并提供了一个 InterSystems IRIS 应用程序担当授权服务器客户端并请求一些受保护资源的简单方案。 第 2 部分将讲述一个复杂一些的方案,在该方案中 InterSystems IRIS 本身通过 OpenID Connect 担当授权服务器和身份验证服务器。 本系列的最后一部分将描述 OAUTH 框架类的各个部分,它们由 InterSystems IRIS 实现。 ## **什么是开放授权框架 [1] ** 许多人已经听说过有关开放授权框架及其用途的信息。 因此这里只做简单介绍,以备未听说过的人参考。 开放授权框架 (OAUTH) 当前为 2.0 版,其是一种协议,允许基于 Web 的主应用程序通过在客户端(应用程序请求数据)和资源所有者(应用程序保存请求的数据)之间建立间接信任来以安全的方式交换信息。 信任本身由客户端和资源服务器都认可并信任的主体提供。 该主体称为授权服务器。 简单举例如下: 假设 Jenny(使用 OAUTH 术语,就是资源所有者)在开展 JennyCorp 公司的一个工作项目。 她为一个潜在的大型业务创建了项目计划,并邀请 JohnInc 公司的业务伙伴 John(客户端用户)审阅此文档。 不过,她并不愿意让 John 访问自己公司的 VPN,因此她将文档放在 Google 云端硬盘(资源服务器)或其他类似的云存储中。 她这样做,已经在她和 Google(授权服务器)之间建立了信任。 她标记了要与 John 共享的文档(John 已经使用 Google 云端硬盘服务,Jenny 知道他的电子邮件)。 当 John 想要阅读该文档时,他进行了 Google 帐户身份验证,然后通过移动设备(平板电脑、笔记本电脑等)启动文档编辑器(客户端服务器)并加载 Jenny 的项目文件。 这听起来很简单,但是两个人与 Google 之间有很多通信。 所有交流均遵循 OAuth 2.0 规范,因此 John 的客户端(阅读器应用程序)必须首先向 Google 进行身份验证(OAUTH 不涵盖此步骤),然后 John 申请获取 Google 对提供表格的同意,经过授权后,Google 就会发出一个访问令牌,授权阅读器应用程序访问文档。 阅读器应用程序使用该访问令牌向 Google 云端硬盘服务发出请求,以检索 Jenny 的文件。 下图说明了各方之间的通信 ![](/sites/default/files/inline/images/1_3.png) 请注意:虽然所有的 OAUTH 2.0 通信都使用 HTTP 请求,但服务器不必非得是 Web 应用程序。 让我们通过 InterSystems IRIS 来说明这一简单方案。 # **简单 Google 云端硬盘演示** 在本演示中,我们将创建一个基于 CSP 的小型应用程序,该应用程序将使用我们自己的帐户(以及作为奖励的日历列表)来请求存储在 Google 云端硬盘服务中的资源(文件列表)。 ## **基本要求** 开始应用程序编码之前,我们需要准备环境。 这包括启用 SSL 的 Web 服务器和 Google 配置文件。 ### **Web 服务器配置** 如上所述,我们需要使用 SSL 与授权服务器进行通信,因为默认情况下 OAuth 2.0 要求如此。 我们需要确保数据安全,对吧? 解释如何配置 Web 服务器来支持 SSL 的内容超出了本文讨论的范围,因此,请以您喜欢的方式参阅相应 Web 服务器的用户手册。 为了您的好奇心(我们稍后可能会显示一些屏幕截图),在此特定示例中,我们将使用 Microsoft IIS 服务器。 ### **Google 配置** 为了向 Google 注册,我们需要使用 Google API Manager-[ https://console.developers.google.com/apis/library?project=globalsummit2016demo ](https://console.developers.google.com/apis/library?project=globalsummit2016demo) 为了进行演示,我们创建了一个帐户 GlobalSummit2016Demo。 确保我们已启用 Drive API ![](/sites/default/files/inline/images/o2.png) 现在,该定义凭据了 ![](/sites/default/files/inline/images/o3.png) 请注意以下事项: _Authorized JavaScript – _我们仅允许本地生成的脚本(相对于调用页面) _Authorized redirect URIs – 从理论上讲,我们可以将客户端应用程序重定向到任何站点,但是当使用 InterSystems IRIS OAUTH 实现时,我们必须重定向到** https://localhost/csp/sys/oauth2/OAuth2.Response.cls**。您可以定义多个授权的重定向 URI,如屏幕截图所示,但是对于本演示,我们只需要两者中的第二个条目。 最后,我们需要将 InterSystems IRIS 配置为 Google 授权服务器的客户端 ### **Caché /IRIS配置** InterSystems IRIS OAUTH2 客户端配置需要两步。 首先,我们需要创建服务器配置。 在 SMP 中,导航至**系统管理 > 安全性 > OAuth 2.0 > 客户端配置**。 点击**创建服务器配置**按钮,填写表格并保存。 ![](/sites/default/files/inline/images/oauth2_1_google_server.png) 输入到表格的所有信息可以在 Google 开发者控制台网站上找到。 请注意,InterSystems IRIS 支持自动 Open ID 发现。 但是,由于我们没有使用它,因此我们手动输入所有信息 现在,点击新创建的 Issuer Endpoint 旁边的“客户端配置”链接。并点击**创建客户端配置**按钮。 ![](/sites/default/files/inline/images/2_6.png) 将“客户端信息”和“JWT 设置”选项卡保留为空(默认值),并填写客户端凭据。 ![](/sites/default/files/inline/images/3_5.png) 请注意:我们正在创建机密客户端(这比公共客户端更安全,这意味着客户端秘密永远不会离开客户端服务器应用程序(永远不会传输到浏览器) 此外,请确保选中**“使用 SSL/TLS**”,并提供主机名(本地主机,因为我们将本地重定向到客户端应用程序),最后提供端口和前缀(当同一台机器上有多个 InterSystems IRIS 实例时,这非常有用)。 根据输入的信息,会计算客户端重定向 URL 并显示在上一行中。 在上面的屏幕截图中,我们提供了一个名为 GOOGLE 的 SSL 配置。 该名称本身实际上仅用于帮助您确定此特定通信通道使用的可能是众多 SSL 配置中的哪个。 Caché 使用 SSL/TLS 配置存储所有必要的信息,以建立与服务器(在本例中,为 Google OAuth 2.0 URI)的安全流量。 有关详细信息,请参阅[文档](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS_ssltls#GCAS_ssltls_aboutconfigs) 。 Supply Client ID 和 Client Secret 值从 Google 凭据定义表中获得(使用手动配置时)。 现在,我们完成了所有的配置步骤,可以开始编写 CSP 应用程序代码。 ## **客户端应用程序** 客户端应用程序是基于 Web 的简单 CSP 应用程序。 因此,它包含由 Web 服务器定义和执行的服务器端源代码,以及由 Web 浏览器向用户公开的用户界面。 下文提供的示例代码期望客户端应用程序在 GOOGLE 名称空间中运行。 请将路径 /csp/google/ 修改为您的命名空间。 ## **客户端服务器** 客户端服务器是一个简单的两页应用程序。 在该应用程序内,我们将: ·        将 URL 重定向到 Google 授权服务器 ·        执行向 Google Drive API 和 Google Calendar API 的请求并显示结果 ### **第 1 页** 这是应用程序的一页,我们决定在此处调用 Google 的资源。 以下是此页面上简单但功能齐全的代码。 Class Web.OAUTH2.Google1N Extends %CSP.Page { Parameter OAUTH2CLIENTREDIRECTURI = "https://localhost/csp/google/Web.OAUTH2.Google2N.cls"; Parameter OAUTH2APPNAME = "Google"; ClassMethod OnPage() As %Status { &html // we need to supply openid scope to authenticate to Google set scope="openid https://www.googleapis.com/auth/userinfo.email "_ "https://www.googleapis.com/auth/userinfo.profile "_ "https://www.googleapis.com/auth/drive.metadata.readonly "_ "https://www.googleapis.com/auth/calendar.readonly" set properties("approval_prompt")="force" set properties("include_granted_scopes")="true" set url=##class(%SYS.OAuth2.Authorization).GetAuthorizationCodeEndpoint(..#OAUTH2APPNAME,scope, ..#OAUTH2CLIENTREDIRECTURI,.properties,.isAuthorized,.sc) w !,"" &html Quit $$$OK } ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ] { #dim %response as %CSP.Response set scope="openid https://www.googleapis.com/auth/userinfo.email "_ "https://www.googleapis.com/auth/userinfo.profile "_ "https://www.googleapis.com/auth/drive.metadata.readonly "_ "https://www.googleapis.com/auth/calendar.readonly" if ##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,scope,.accessToken,.idtoken,.responseProperties,.error) { set %response.ServerSideRedirect="Web.OAUTH2.Google2N.cls" } quit 1 } } 代码的简要说明如下: 1.      OnPreHTTP 方法 - 首先,我们有机会时检查一下,我们是否由于 Google 授权而获得了有效的访问令牌——这种情况可能会发生,例如当我们只是刷新页面时。 如果没有,我们需要授权。 如果我们有令牌,我们只需将页面重定向到显示结果的页面 2.       OnPage 方法 - 只有在我们没有可用的有效访问令牌时,我们才到这里,因此我们需要开始通信——向 Google 进行身份验证和授权,以便它向我们授予访问令牌。 3.       我们定义了作用域字符串和属性数组,用于修改 Google 身份验证对话框的行为(我们需要先向 Google 进行身份验证,然后它才能根据我们的身份对我们进行授权)。 4.       最后,我们收到 Google 登录页面的 URL,然后将其提供给用户,接着提供同意页面。 还有一点注意事项: 我们在 OAUTH2CLIENTREDIRECTURI 参数的 中指定真正的重定向页面。 但是,我们在 Google 凭据定义中使用了 InterSystems IRIS OAUTH 框架的系统页面! 重定向由我们的 OAUTH 处理程序类在内部处理。 ### **第 2 页** 此页面显示 Google 授权的结果,如果成功,我们将调用 Google API 调用以检索数据。 同样,此代码简单,但功能齐全。 相比读者的想象,我们以更结构化的方式来显示输入数据。 Include %occInclude Class Web.OAUTH2.Google2N Extends %CSP.Page { Parameter OAUTH2APPNAME = "Google"; Parameter OAUTH2ROOT = "https://www.googleapis.com"; ClassMethod OnPage() As %Status { &html // Check if we have an access token set scope="openid https://www.googleapis.com/auth/userinfo.email "_ "https://www.googleapis.com/auth/userinfo.profile "_ "https://www.googleapis.com/auth/drive.metadata.readonly "_ "https://www.googleapis.com/auth/calendar.readonly" set isAuthorized=##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,scope,.accessToken,.idtoken,.responseProperties,.error) if isAuthorized { // Google has no introspection endpoint - nothing to call - the introspection endpoint and display result -- see RFC 7662. w "Data from GetUserInfo API" // userinfo has special API, but could be also retrieved by just calling Get() method with appropriate url try { set tHttpRequest=##class(%Net.HttpRequest).%New() $$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).AddAccessToken(tHttpRequest,"query","GOOGLE",..#OAUTH2APPNAME)) $$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).GetUserinfo(..#OAUTH2APPNAME,accessToken,,.jsonObject)) w jsonObject.%ToJSON() } catch (e) { w "ERROR: ",$zcvt(e.DisplayString(),"O","HTML")_"" } /****************************************** * * * Retrieve info from other APIs * * * ******************************************/ w "" do ..RetrieveAPIInfo("/drive/v3/files") do ..RetrieveAPIInfo("/calendar/v3/users/me/calendarList") } else { w "Not authorized!" } &html Quit $$$OK } ClassMethod RetrieveAPIInfo(api As %String) { w "Data from "_api_"" try { set tHttpRequest=##class(%Net.HttpRequest).%New() $$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).AddAccessToken(tHttpRequest,"query","GOOGLE",..#OAUTH2APPNAME)) $$$THROWONERROR(sc,tHttpRequest.Get(..#OAUTH2ROOT_api)) set tHttpResponse=tHttpRequest.HttpResponse s tJSONString=tHttpResponse.Data.Read() if $e(tJSONString)'="{" { // not a JSON d tHttpResponse.OutputToDevice() } else { w tJSONString w "" /* // new JSON API &html s tJSONObject={}.%FromJSON(tJSONString) set iterator=tJSONObject.%GetIterator() while iterator.%GetNext(.key,.value) { if $isobject(value) { set iterator1=value.%GetIterator() w "",key,"" while iterator1.%GetNext(.key1,.value1) { if $isobject(value1) { set iterator2=value1.%GetIterator() w "",key1,"" while iterator2.%GetNext(.key2,.value2) { write !, "",key2, "",value2,"" } // this way we can go on and on into the embedded objects/arrays w "" } else { write !, "",key1, "",value1,"" } } w "" } else { write !, "",key, "",value,"" } } &html */ } } catch (e) { w "ERROR: ",$zcvt(e.DisplayString(),"O","HTML")_"" } } }     让我们快速看一下代码: 1.       首先,我们需要检查一下我们是否具有有效的访问令牌(即我们是否被授权) 2.       如果是,我们可以向 Google 提供且由已发布访问令牌覆盖的 API 发出请求 3.       为此,我们使用标准的 %Net.HttpRequest 类,但根据 API 规范,我们将访问令牌添加到 GET 或 POST 方法中 4.       如您所见,为了方便您,OAUTH 框架已实现 GetUserInfo()方法,但是您可以使用 Google API 规范直接检索用户信息,就像我们在 RetrieveAPIInfo()助手方法中所做的一样。 5.       由于在 OAUTH 世界中以 JSON 格式交换数据司空见惯,因此我们只读取传入的数据,然后简单地将其转储到浏览器。 应用程序开发人员可以解析和格式化接收到的数据,以便用户可以看明白。 但这超出了本演示的范围。 (尽管一些代码有注释,显示了如何完成解析。) 下图是一个输出屏幕截图,显示了原始 JSON 数据。 ![](/sites/default/files/inline/images/6_0.png) 继续阅读[第 2 部分](https://community.intersystems.com/post/cach%C3%A9-open-authorization-framework-oauth-20-implementation-part-2),该部分讲述 InterSystems IRIS 担当授权服务器和 OpenID Connect 提供程序相关的内容。   [1] https://tools.ietf.org/html/rfc6749, https://tools.ietf.org/html/rfc6750
文章
Jingwei Wang · 十月 28, 2021

IRIS 2021 技术文档 First Look 31:InterSystems IRIS 和 UIMA

html {overflow-x: initial !important;}:root { --bg-color: #ffffff; --text-color: #333333; --select-text-bg-color: #B5D6FC; --select-text-font-color: auto; --monospace: "Lucida Console",Consolas,"Courier",monospace; --title-bar-height: 20px; } .mac-os-11 { --title-bar-height: 28px; } html { font-size: 14px; background-color: var(--bg-color); color: var(--text-color); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; } body { margin: 0px; padding: 0px; height: auto; inset: 0px; font-size: 1rem; line-height: 1.42857143; overflow-x: hidden; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; tab-size: 4; background-position: inherit; background-repeat: inherit; } iframe { margin: auto; } a.url { word-break: break-all; } a:active, a:hover { outline: 0px; } .in-text-selection, ::selection { text-shadow: none; background: var(--select-text-bg-color); color: var(--select-text-font-color); } #write { margin: 0px auto; height: auto; width: inherit; word-break: normal; word-wrap: break-word; position: relative; white-space: normal; overflow-x: visible; padding-top: 36px; } #write.first-line-indent p { text-indent: 2em; } #write.first-line-indent li p, #write.first-line-indent p * { text-indent: 0px; } #write.first-line-indent li { margin-left: 2em; } .for-image #write { padding-left: 8px; padding-right: 8px; } body.typora-export { padding-left: 30px; padding-right: 30px; } .typora-export .footnote-line, .typora-export li, .typora-export p { white-space: pre-wrap; } .typora-export .task-list-item input { pointer-events: none; } @media screen and (max-width: 500px) { body.typora-export { padding-left: 0px; padding-right: 0px; } #write { padding-left: 20px; padding-right: 20px; } .CodeMirror-sizer { margin-left: 0px !important; } .CodeMirror-gutters { display: none !important; } } #write li > figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max-width: 100%; vertical-align: middle; image-orientation: from-image; } button, input, select, textarea { color: inherit; font-family: inherit; font-size: inherit; font-style: inherit; font-variant-caps: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; } input[type="checkbox"], input[type="radio"] { line-height: normal; padding: 0px; } *, ::after, ::before { box-sizing: border-box; } #write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p, #write pre { width: inherit; } #write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p { position: relative; } p { line-height: inherit; } h1, h2, h3, h4, h5, h6 { break-after: avoid-page; break-inside: avoid; orphans: 4; } p { orphans: 4; } h1 { font-size: 2rem; } h2 { font-size: 1.8rem; } h3 { font-size: 1.6rem; } h4 { font-size: 1.4rem; } h5 { font-size: 1.2rem; } h6 { font-size: 1rem; } .md-math-block, .md-rawblock, h1, h2, h3, h4, h5, h6, p { margin-top: 1rem; margin-bottom: 1rem; } .hidden { display: none; } .md-blockmeta { color: rgb(204, 204, 204); font-weight: 700; font-style: italic; } a { cursor: pointer; } sup.md-footnote { padding: 2px 4px; background-color: rgba(238, 238, 238, 0.7); color: rgb(85, 85, 85); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; cursor: pointer; } sup.md-footnote a, sup.md-footnote a:hover { color: inherit; text-transform: inherit; text-decoration: inherit; } #write input[type="checkbox"] { cursor: pointer; width: inherit; height: inherit; } figure { overflow-x: auto; margin: 1.2em 0px; max-width: calc(100% + 16px); padding: 0px; } figure > table { margin: 0px; } tr { break-inside: avoid; break-after: auto; } thead { display: table-header-group; } table { border-collapse: collapse; border-spacing: 0px; width: 100%; overflow: auto; break-inside: auto; text-align: left; } table.md-table td { min-width: 32px; } .CodeMirror-gutters { border-right-width: 0px; background-color: inherit; } .CodeMirror-linenumber { } .CodeMirror { text-align: left; } .CodeMirror-placeholder { opacity: 0.3; } .CodeMirror pre { padding: 0px 4px; } .CodeMirror-lines { padding: 0px; } div.hr:focus { cursor: none; } #write pre { white-space: pre-wrap; } #write.fences-no-line-wrapping pre { white-space: pre; } #write pre.ty-contain-cm { white-space: normal; } .CodeMirror-gutters { margin-right: 4px; } .md-fences { font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; overflow: visible; white-space: pre; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; position: relative !important; background-position: inherit; background-repeat: inherit; } .md-fences-adv-panel { width: 100%; margin-top: 10px; text-align: center; padding-top: 0px; padding-bottom: 8px; overflow-x: auto; } #write .md-fences.mock-cm { white-space: pre-wrap; } .md-fences.md-fences-with-lineno { padding-left: 0px; } #write.fences-no-line-wrapping .md-fences.mock-cm { white-space: pre; overflow-x: auto; } .md-fences.mock-cm.md-fences-with-lineno { padding-left: 8px; } .CodeMirror-line, twitterwidget { break-inside: avoid; } .footnotes { opacity: 0.8; font-size: 0.9rem; margin-top: 1em; margin-bottom: 1em; } .footnotes + .footnotes { margin-top: 0px; } .md-reset { margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: top; text-decoration: none; text-shadow: none; float: none; position: static; width: auto; height: auto; white-space: nowrap; cursor: inherit; line-height: normal; font-weight: 400; text-align: left; box-sizing: content-box; direction: ltr; background-position: 0px 0px; } li div { padding-top: 0px; } blockquote { margin: 1rem 0px; } li .mathjax-block, li p { margin: 0.5rem 0px; } li blockquote { margin: 1rem 0px; } li { margin: 0px; position: relative; } blockquote > :last-child { margin-bottom: 0px; } blockquote > :first-child, li > :first-child { margin-top: 0px; } .footnotes-area { color: rgb(136, 136, 136); margin-top: 0.714rem; padding-bottom: 0.143rem; white-space: normal; } #write .footnote-line { white-space: pre-wrap; } @media print { body, html { border: 1px solid transparent; height: 99%; break-after: avoid; break-before: avoid; font-variant-ligatures: no-common-ligatures; } #write { margin-top: 0px; padding-top: 0px; border-color: transparent !important; } .typora-export * { -webkit-print-color-adjust: exact; } .typora-export #write { break-after: avoid; } .typora-export #write::after { height: 0px; } .is-mac table { break-inside: avoid; } .typora-export-show-outline .typora-export-sidebar { display: none; } } .footnote-line { margin-top: 0.714em; font-size: 0.7em; } a img, img a { cursor: pointer; } pre.md-meta-block { font-size: 0.8rem; min-height: 0.8rem; white-space: pre-wrap; background-color: rgb(204, 204, 204); display: block; overflow-x: hidden; } p > .md-image:only-child:not(.md-img-error) img, p > img:only-child { display: block; margin: auto; } #write.first-line-indent p > .md-image:only-child:not(.md-img-error) img { left: -2em; position: relative; } p > .md-image:only-child { display: inline-block; width: 100%; } #write .MathJax_Display { margin: 0.8em 0px 0px; } .md-math-block { width: 100%; } .md-math-block:not(:empty)::after { display: none; } .MathJax_ref { fill: currentcolor; } [contenteditable="true"]:active, [contenteditable="true"]:focus, [contenteditable="false"]:active, [contenteditable="false"]:focus { outline: 0px; box-shadow: none; } .md-task-list-item { position: relative; list-style-type: none; } .task-list-item.md-task-list-item { padding-left: 0px; } .md-task-list-item > input { position: absolute; top: 0px; left: 0px; margin-left: -1.2em; margin-top: calc(1em - 10px); border: none; } .math { font-size: 1rem; } .md-toc { min-height: 3.58rem; position: relative; font-size: 0.9rem; border-top-left-radius: 10px; border-top-right-radius: 10px; border-bottom-right-radius: 10px; border-bottom-left-radius: 10px; } .md-toc-content { position: relative; margin-left: 0px; } .md-toc-content::after, .md-toc::after { display: none; } .md-toc-item { display: block; color: rgb(65, 131, 196); } .md-toc-item a { text-decoration: none; } .md-toc-inner:hover { text-decoration: underline; } .md-toc-inner { display: inline-block; cursor: pointer; } .md-toc-h1 .md-toc-inner { margin-left: 0px; font-weight: 700; } .md-toc-h2 .md-toc-inner { margin-left: 2em; } .md-toc-h3 .md-toc-inner { margin-left: 4em; } .md-toc-h4 .md-toc-inner { margin-left: 6em; } .md-toc-h5 .md-toc-inner { margin-left: 8em; } .md-toc-h6 .md-toc-inner { margin-left: 10em; } @media screen and (max-width: 48em) { .md-toc-h3 .md-toc-inner { margin-left: 3.5em; } .md-toc-h4 .md-toc-inner { margin-left: 5em; } .md-toc-h5 .md-toc-inner { margin-left: 6.5em; } .md-toc-h6 .md-toc-inner { margin-left: 8em; } } a.md-toc-inner { font-size: inherit; font-style: inherit; font-weight: inherit; line-height: inherit; } .footnote-line a:not(.reversefootnote) { color: inherit; } .md-attr { display: none; } .md-fn-count::after { content: "."; } code, pre, samp, tt { font-family: var(--monospace); } kbd { margin: 0px 0.1em; padding: 0.1em 0.6em; font-size: 0.8em; color: rgb(36, 39, 41); background-color: rgb(255, 255, 255); border: 1px solid rgb(173, 179, 185); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; box-shadow: rgba(12, 13, 14, 0.2) 0px 1px 0px, rgb(255, 255, 255) 0px 0px 0px 2px inset; white-space: nowrap; vertical-align: middle; } .md-comment { color: rgb(162, 127, 3); opacity: 0.8; font-family: var(--monospace); } code { text-align: left; } a.md-print-anchor { white-space: pre !important; border: none !important; display: inline-block !important; position: absolute !important; width: 1px !important; right: 0px !important; outline: 0px !important; text-shadow: initial !important; background-position: 0px 0px !important; } .md-inline-math .MathJax_SVG .noError { display: none !important; } .html-for-mac .inline-math-svg .MathJax_SVG { vertical-align: 0.2px; } .md-fences-math .MathJax_SVG_Display, .md-math-block .MathJax_SVG_Display { text-align: center; margin: 0px; position: relative; text-indent: 0px; max-width: none; max-height: none; min-height: 0px; min-width: 100%; width: auto; overflow-y: visible; display: block !important; } .MathJax_SVG_Display, .md-inline-math .MathJax_SVG_Display { width: auto; margin: inherit; display: inline-block !important; } .MathJax_SVG .MJX-monospace { font-family: var(--monospace); } .MathJax_SVG .MJX-sans-serif { font-family: sans-serif; } .MathJax_SVG { display: inline; font-style: normal; font-weight: 400; line-height: normal; text-indent: 0px; text-align: left; text-transform: none; letter-spacing: normal; word-spacing: normal; word-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border: 0px; padding: 0px; margin: 0px; zoom: 90%; } #math-inline-preview-content { zoom: 1.1; } .MathJax_SVG * { transition: none; } .MathJax_SVG_Display svg { vertical-align: middle !important; margin-bottom: 0px !important; margin-top: 0px !important; } .os-windows.monocolor-emoji .md-emoji { font-family: "Segoe UI Symbol", sans-serif; } .md-diagram-panel > svg { max-width: 100%; } [lang="flow"] svg, [lang="mermaid"] svg { max-width: 100%; height: auto; } [lang="mermaid"] .node text { font-size: 1rem; } table tr th { border-bottom-width: 0px; } video { max-width: 100%; display: block; margin: 0px auto; } iframe { max-width: 100%; width: 100%; border: none; } .highlight td, .highlight tr { border: 0px; } mark { background-color: rgb(255, 255, 0); color: rgb(0, 0, 0); } .md-html-inline .md-plain, .md-html-inline strong, mark .md-inline-math, mark strong { color: inherit; } .md-expand mark .md-meta { opacity: 0.3 !important; } mark .md-meta { color: rgb(0, 0, 0); } @media print { .typora-export h1, .typora-export h2, .typora-export h3, .typora-export h4, .typora-export h5, .typora-export h6 { break-inside: avoid; } } .md-diagram-panel .messageText { stroke: none !important; } .md-diagram-panel .start-state { fill: var(--node-fill); } .md-diagram-panel .edgeLabel rect { opacity: 1 !important; } .md-require-zoom-fix foreignObject { font-size: var(--mermaid-font-zoom); } .md-fences.md-fences-math { font-size: 1em; } .md-fences-math .MathJax_SVG_Display { margin-top: 8px; cursor: default; } .md-fences-advanced:not(.md-focus) { padding: 0px; white-space: nowrap; border: 0px; } .md-fences-advanced:not(.md-focus) { background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; background-position: inherit; background-repeat: inherit; } .typora-export-show-outline .typora-export-content { max-width: 1440px; margin: auto; display: flex; flex-direction: row; } .typora-export-sidebar { width: 300px; font-size: 0.8rem; margin-top: 80px; margin-right: 18px; } .typora-export-show-outline #write { --webkit-flex: 2; flex: 2 1 0%; } .typora-export-sidebar .outline-content { position: fixed; top: 0px; max-height: 100%; overflow: hidden auto; padding-bottom: 30px; padding-top: 60px; width: 300px; } @media screen and (max-width: 1024px) { .typora-export-sidebar, .typora-export-sidebar .outline-content { width: 240px; } } @media screen and (max-width: 800px) { .typora-export-sidebar { display: none; } } .outline-content li, .outline-content ul { margin-left: 0px; margin-right: 0px; padding-left: 0px; padding-right: 0px; list-style: none; } .outline-content ul { margin-top: 0px; margin-bottom: 0px; } .outline-content strong { font-weight: 400; } .outline-expander { width: 1rem; height: 1.428571429rem; position: relative; display: table-cell; vertical-align: middle; cursor: pointer; padding-left: 4px; } .outline-expander::before { content: ''; position: relative; font-family: Ionicons; display: inline-block; font-size: 8px; vertical-align: middle; } .outline-item { padding-top: 3px; padding-bottom: 3px; cursor: pointer; } .outline-expander:hover::before { content: ''; } .outline-h1 > .outline-item { padding-left: 0px; } .outline-h2 > .outline-item { padding-left: 1em; } .outline-h3 > .outline-item { padding-left: 2em; } .outline-h4 > .outline-item { padding-left: 3em; } .outline-h5 > .outline-item { padding-left: 4em; } .outline-h6 > .outline-item { padding-left: 5em; } .outline-label { cursor: pointer; display: table-cell; vertical-align: middle; text-decoration: none; color: inherit; } .outline-label:hover { text-decoration: underline; } .outline-item:hover { border-color: rgb(245, 245, 245); background-color: var(--item-hover-bg-color); } .outline-item:hover { margin-left: -28px; margin-right: -28px; border-left-width: 28px; border-left-style: solid; border-left-color: transparent; border-right-width: 28px; border-right-style: solid; border-right-color: transparent; } .outline-item-single .outline-expander::before, .outline-item-single .outline-expander:hover::before { display: none; } .outline-item-open > .outline-item > .outline-expander::before { content: ''; } .outline-children { display: none; } .info-panel-tab-wrapper { display: none; } .outline-item-open > .outline-children { display: block; } .typora-export .outline-item { padding-top: 1px; padding-bottom: 1px; } .typora-export .outline-item:hover { margin-right: -8px; border-right-width: 8px; border-right-style: solid; border-right-color: transparent; } .typora-export .outline-expander::before { content: "+"; font-family: inherit; top: -1px; } .typora-export .outline-expander:hover::before, .typora-export .outline-item-open > .outline-item > .outline-expander::before { content: '−'; } .typora-export-collapse-outline .outline-children { display: none; } .typora-export-collapse-outline .outline-item-open > .outline-children, .typora-export-no-collapse-outline .outline-children { display: block; } .typora-export-no-collapse-outline .outline-expander::before { content: "" !important; } .typora-export-show-outline .outline-item-active > .outline-item .outline-label { font-weight: 700; } .CodeMirror { height: auto; } .CodeMirror.cm-s-inner { background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; background-position: inherit; background-repeat: inherit; } .CodeMirror-scroll { overflow: auto hidden; z-index: 3; } .CodeMirror-gutter-filler, .CodeMirror-scrollbar-filler { background-color: rgb(255, 255, 255); } .CodeMirror-gutters { border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; white-space: nowrap; background-position: inherit; background-repeat: inherit; } .CodeMirror-linenumber { padding: 0px 3px 0px 5px; text-align: right; color: rgb(153, 153, 153); } .cm-s-inner .cm-keyword { color: rgb(119, 0, 136); } .cm-s-inner .cm-atom, .cm-s-inner.cm-atom { color: rgb(34, 17, 153); } .cm-s-inner .cm-number { color: rgb(17, 102, 68); } .cm-s-inner .cm-def { color: rgb(0, 0, 255); } .cm-s-inner .cm-variable { color: rgb(0, 0, 0); } .cm-s-inner .cm-variable-2 { color: rgb(0, 85, 170); } .cm-s-inner .cm-variable-3 { color: rgb(0, 136, 85); } .cm-s-inner .cm-string { color: rgb(170, 17, 17); } .cm-s-inner .cm-property { color: rgb(0, 0, 0); } .cm-s-inner .cm-operator { color: rgb(152, 26, 26); } .cm-s-inner .cm-comment, .cm-s-inner.cm-comment { color: rgb(170, 85, 0); } .cm-s-inner .cm-string-2 { color: rgb(255, 85, 0); } .cm-s-inner .cm-meta { color: rgb(85, 85, 85); } .cm-s-inner .cm-qualifier { color: rgb(85, 85, 85); } .cm-s-inner .cm-builtin { color: rgb(51, 0, 170); } .cm-s-inner .cm-bracket { color: rgb(153, 153, 119); } .cm-s-inner .cm-tag { color: rgb(17, 119, 0); } .cm-s-inner .cm-attribute { color: rgb(0, 0, 204); } .cm-s-inner .cm-header, .cm-s-inner.cm-header { color: rgb(0, 0, 255); } .cm-s-inner .cm-quote, .cm-s-inner.cm-quote { color: rgb(0, 153, 0); } .cm-s-inner .cm-hr, .cm-s-inner.cm-hr { color: rgb(153, 153, 153); } .cm-s-inner .cm-link, .cm-s-inner.cm-link { color: rgb(0, 0, 204); } .cm-negative { color: rgb(221, 68, 68); } .cm-positive { color: rgb(34, 153, 34); } .cm-header, .cm-strong { font-weight: 700; } .cm-del { text-decoration: line-through; } .cm-em { font-style: italic; } .cm-link { text-decoration: underline; } .cm-error { color: red; } .cm-invalidchar { color: red; } .cm-constant { color: rgb(38, 139, 210); } .cm-defined { color: rgb(181, 137, 0); } div.CodeMirror span.CodeMirror-matchingbracket { color: rgb(0, 255, 0); } div.CodeMirror span.CodeMirror-nonmatchingbracket { color: rgb(255, 34, 34); } .cm-s-inner .CodeMirror-activeline-background { background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; background-position: inherit; background-repeat: inherit; } .CodeMirror { position: relative; overflow: hidden; } .CodeMirror-scroll { height: 100%; outline: 0px; position: relative; box-sizing: content-box; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; background-position: inherit; background-repeat: inherit; } .CodeMirror-sizer { position: relative; } .CodeMirror-gutter-filler, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-vscrollbar { position: absolute; z-index: 6; display: none; } .CodeMirror-vscrollbar { right: 0px; top: 0px; overflow: hidden; } .CodeMirror-hscrollbar { bottom: 0px; left: 0px; overflow: hidden; } .CodeMirror-scrollbar-filler { right: 0px; bottom: 0px; } .CodeMirror-gutter-filler { left: 0px; bottom: 0px; } .CodeMirror-gutters { position: absolute; left: 0px; top: 0px; padding-bottom: 30px; z-index: 3; } .CodeMirror-gutter { white-space: normal; height: 100%; box-sizing: content-box; padding-bottom: 30px; margin-bottom: -32px; display: inline-block; } .CodeMirror-gutter-wrapper { position: absolute; z-index: 4; border: none !important; background-position: 0px 0px !important; } .CodeMirror-gutter-background { position: absolute; top: 0px; bottom: 0px; z-index: 4; } .CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4; } .CodeMirror-lines { cursor: text; } .CodeMirror pre { border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; border-width: 0px; font-family: inherit; font-size: inherit; margin: 0px; white-space: pre; word-wrap: normal; color: inherit; z-index: 2; position: relative; overflow: visible; background-position: 0px 0px; } .CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; word-break: normal; } .CodeMirror-code pre { border-right-width: 30px; border-right-style: solid; border-right-color: transparent; width: fit-content; } .CodeMirror-wrap .CodeMirror-code pre { border-right-style: none; width: auto; } .CodeMirror-linebackground { position: absolute; inset: 0px; z-index: 0; } .CodeMirror-linewidget { position: relative; z-index: 2; overflow: auto; } .CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden; } .CodeMirror-measure { position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden; } .CodeMirror-measure pre { position: static; } .CodeMirror div.CodeMirror-cursor { position: absolute; visibility: hidden; border-right-style: none; width: 0px; } .CodeMirror div.CodeMirror-cursor { visibility: hidden; } .CodeMirror-focused div.CodeMirror-cursor { visibility: inherit; } .cm-searching { background-color: rgba(255, 255, 0, 0.4); } span.cm-underlined { text-decoration: underline; } span.cm-strikethrough { text-decoration: line-through; } .cm-tw-syntaxerror { color: rgb(255, 255, 255); background-color: rgb(153, 0, 0); } .cm-tw-deleted { text-decoration: line-through; } .cm-tw-header5 { font-weight: 700; } .cm-tw-listitem:first-child { padding-left: 10px; } .cm-tw-box { border-style: solid; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-color: inherit; border-top-width: 0px !important; } .cm-tw-underline { text-decoration: underline; } @media print { .CodeMirror div.CodeMirror-cursor { visibility: hidden; } } :root { --side-bar-bg-color: #fafafa; --control-text-color: #777; } @include-when-export url(https://fonts.loli.net/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext); /* open-sans-regular - latin-ext_latin */ /* open-sans-italic - latin-ext_latin */ /* open-sans-700 - latin-ext_latin */ /* open-sans-700italic - latin-ext_latin */ html { font-size: 16px; } body { font-family: "Open Sans","Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; color: rgb(51, 51, 51); line-height: 1.6; } #write { max-width: 860px; margin: 0 auto; padding: 30px; padding-bottom: 100px; } @media only screen and (min-width: 1400px) { #write { max-width: 1024px; } } @media only screen and (min-width: 1800px) { #write { max-width: 1200px; } } #write > ul:first-child, #write > ol:first-child{ margin-top: 30px; } a { color: #4183C4; } h1, h2, h3, h4, h5, h6 { position: relative; margin-top: 1rem; margin-bottom: 1rem; font-weight: bold; line-height: 1.4; cursor: text; } h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { text-decoration: none; } h1 tt, h1 code { font-size: inherit; } h2 tt, h2 code { font-size: inherit; } h3 tt, h3 code { font-size: inherit; } h4 tt, h4 code { font-size: inherit; } h5 tt, h5 code { font-size: inherit; } h6 tt, h6 code { font-size: inherit; } h1 { font-size: 2.25em; line-height: 1.2; border-bottom: 1px solid #eee; } h2 { font-size: 1.75em; line-height: 1.225; border-bottom: 1px solid #eee; } /*@media print { .typora-export h1, .typora-export h2 { border-bottom: none; padding-bottom: initial; } .typora-export h1::after, .typora-export h2::after { content: ""; display: block; height: 100px; margin-top: -96px; border-top: 1px solid #eee; } }*/ h3 { font-size: 1.5em; line-height: 1.43; } h4 { font-size: 1.25em; } h5 { font-size: 1em; } h6 { font-size: 1em; color: #777; } p, blockquote, ul, ol, dl, table{ margin: 0.8em 0; } li>ol, li>ul { margin: 0 0; } hr { height: 2px; padding: 0; margin: 16px 0; background-color: #e7e7e7; border: 0 none; overflow: hidden; box-sizing: content-box; } li p.first { display: inline-block; } ul, ol { padding-left: 30px; } ul:first-child, ol:first-child { margin-top: 0; } ul:last-child, ol:last-child { margin-bottom: 0; } blockquote { border-left: 4px solid #dfe2e5; padding: 0 15px; color: #777777; } blockquote blockquote { padding-right: 0; } table { padding: 0; word-break: initial; } table tr { border: 1px solid #dfe2e5; margin: 0; padding: 0; } table tr:nth-child(2n), thead { background-color: #f8f8f8; } table th { font-weight: bold; border: 1px solid #dfe2e5; border-bottom: 0; margin: 0; padding: 6px 13px; } table td { border: 1px solid #dfe2e5; margin: 0; padding: 6px 13px; } table th:first-child, table td:first-child { margin-top: 0; } table th:last-child, table td:last-child { margin-bottom: 0; } .CodeMirror-lines { padding-left: 4px; } .code-tooltip { box-shadow: 0 1px 1px 0 rgba(0,28,36,.3); border-top: 1px solid #eef2f2; } .md-fences, code, tt { border: 1px solid #e7eaed; background-color: #f8f8f8; border-radius: 3px; padding: 0; padding: 2px 4px 0px 4px; font-size: 0.9em; } code { background-color: #f3f4f4; padding: 0 2px 0 2px; } .md-fences { margin-bottom: 15px; margin-top: 15px; padding-top: 8px; padding-bottom: 6px; } .md-task-list-item > input { margin-left: -1.3em; } @media print { html { font-size: 13px; } table, pre { page-break-inside: avoid; } pre { word-wrap: break-word; } } .md-fences { background-color: #f8f8f8; } #write pre.md-meta-block { padding: 1rem; font-size: 85%; line-height: 1.45; background-color: #f7f7f7; border: 0; border-radius: 3px; color: #777777; margin-top: 0 !important; } .mathjax-block>.code-tooltip { bottom: .375rem; } .md-mathjax-midline { background: #fafafa; } #write>h3.md-focus:before{ left: -1.5625rem; top: .375rem; } #write>h4.md-focus:before{ left: -1.5625rem; top: .285714286rem; } #write>h5.md-focus:before{ left: -1.5625rem; top: .285714286rem; } #write>h6.md-focus:before{ left: -1.5625rem; top: .285714286rem; } .md-image>.md-meta { /*border: 1px solid #ddd;*/ border-radius: 3px; padding: 2px 0px 0px 4px; font-size: 0.9em; color: inherit; } .md-tag { color: #a7a7a7; opacity: 1; } .md-toc { margin-top:20px; padding-bottom:20px; } .sidebar-tabs { border-bottom: none; } #typora-quick-open { border: 1px solid #ddd; background-color: #f8f8f8; } #typora-quick-open-item { background-color: #FAFAFA; border-color: #FEFEFE #e5e5e5 #e5e5e5 #eee; border-style: solid; border-width: 1px; } /** focus mode */ .on-focus-mode blockquote { border-left-color: rgba(85, 85, 85, 0.12); } header, .context-menu, .megamenu-content, footer{ font-family: "Segoe UI", "Arial", sans-serif; } .file-node-content:hover .file-node-icon, .file-node-content:hover .file-node-open-state{ visibility: visible; } .mac-seamless-mode #typora-sidebar { background-color: #fafafa; background-color: var(--side-bar-bg-color); } .md-lang { color: #b4654d; } /*.html-for-mac { --item-hover-bg-color: #E6F0FE; }*/ #md-notification .btn { border: 0; } .dropdown-menu .divider { border-color: #e5e5e5; opacity: 0.4; } .ty-preferences .window-content { background-color: #fafafa; } .ty-preferences .nav-group-item.active { color: white; background: #999; } .menu-item-container a.menu-style-btn { background-color: #f5f8fa; background-image: linear-gradient( 180deg , hsla(0, 0%, 100%, 0.8), hsla(0, 0%, 100%, 0)); } :root {--mermaid-font-zoom:1em ;} @media print { @page {margin: 0 0 0 0;} body.typora-export {padding-left: 0; padding-right: 0;} #write {padding:0;}} 目录 技术概要:InterSystems IRIS 和 UIMA关于 UIMAInterSystems IRIS 如何补充 UIMA Annotation Store(注释存储) InterSystems IRIS NLP在 InterSystems IRIS 中体验 UIMA 用前须知 创建新的命名空间 运行Java Gateway(Java 网关) 体验 UIMA 分析管道 浏览 Annotation Store(注释存储) 通过分析管道发送新文本 向 Aviation.Event 表添加记录 查看Annotation Store(注释存储)中的新条目了解更多关于 UIMA的资料 技术概要:InterSystems IRIS 和 UIMA 本技术概要(First Look)快速介绍了 InterSystems IRIS® 数据平台如何实施和补充非结构化信息管理架构 (UIMA)。在简要概述 UIMA 以及 InterSystems IRIS 如何对其进行补充之后,您将有机会通过基本的动手练习来在实际应用中了解 InterSystems IRIS。 要浏览技术概要(First Look)的所有内容,包括可以在InterSystems IRIS的免费评估实例上执行的内容,请参阅InterSystems 技术概要。 关于 UIMA UIMA 是管理分析非结构化信息(如文本和视频)的标准。对于非结构化信息,计算机通常需要几个步骤才能将信息转化为可操作的结构化数据。例如,扫描的文档将文本转变为机器可读版本之前需要OCR,即使这样,在应用额外的 NLP 策略之前,计算机也不能很好地处理自然语言文本。由于像这样的流程包括本质上非常不同的步骤,因此单个工具不太可能处理所有这些步骤。更可能的情况是,这个流程包括单独模块,这些模块由使用不同技术的不同主体执行,且需要协同工作。在 UIMA 中,这些模块被称为analysis engines(分析引擎)。 因为符合 UIMA 标准的分析引擎都遵循相同的标准,所以它们可以组合成一系列分析器(a UIMA analysis pipeline [UIMA 分析管道]),每个部分做自己最擅长的事情。源非结构化数据在通过此 UIMA 分析管道时不会发生改变,而是沿途生成annotations(注释)。UIMA 标准确保来自一个分析引擎的注释不会干扰来自不同分析引擎的注释。对于文本,这些注释基于文本中的字符位置。UIMA 的互操作性允许您将来自不同供应商和技术的分析引擎组合到一个管道中,而无需编写任何自定义代码,并且由于分析引擎引用原始源数据中的字符位置,因此它们的注释可以组合、比较和推理。UIMA 标准包括运行这些分析引擎中所执行的框架。 除了提供互操作性之外,UIMA 还提供了用于扩展和部署这些分析引擎的框架。这使供应商可以专注于开发分析引擎,而不必担心扩展和部署他们的解决方案。[]{#2_How_InterSystems_IRIS_Complements_UIMA .anchor}UIMA 标准还提供了在分布式架构中调用这些分析引擎的框架。 每个符合 UIMA 的分析引擎都必须附带一个 XML 描述符文件,该文件包含基本识别信息,例如分析引擎的名称和供应商。它还定义了对分析引擎生成的注释进行分类的注释类型。 InterSystems IRIS 如何补充 UIMA InterSystems IRIS 在三个方面补充了 UIMA: 引入功能索引以创建 UIMA 分析管道(analysis pipeline),并在 InterSystems IRIS 表中插入或更新记录时自动为该分析管道提供新文本。 将 UIMA 分析管道生成的注释存储在 InterSystems IRIS 中清晰、可通过 SQL 访问的Annotation Store(注释存储)中。 在 InterSystems IRIS 中体验 UIMA 确保 InterSystems IRIS Natural Language Processing(InterSystems IRIS 自然语言处理, NLP )符合 UIMA 标准,并可用作 UIMA 分析管道中的分析引擎。 创建和调用 UIMA 分析管道 InterSystems IRIS 使用functional index(功能索引)以创建 UIMA 分析管道,该管道使用 InterSystems IRIS 概念,而无需担心实现 Java 接口。功能索引是 InterSystems IRIS 数据库的一项功能,它允许在表中插入或更新记录时执行功能。在这种情况下,功能索引是定义在表的列上,该列包含您希望由 UIMA 分析管道分析的非结构化数据。设置该管道就像将分析引擎描述文件的位置添加到功能索引定义一样简单。 一旦定义了功能索引,只要在索引表列中插入或更新最新数据,InterSystems IRIS 就会自动将非结构化数据输入 UIMA 分析管道。例如,如果在包含报表的列上定义了功能索引,那么将新报表添加到表中后立即对其进行分析。没有[]{#2.2_Annotation_Store .anchor} InterSystems IRIS 中的这一特殊功能,每次您想要分析数据时,都需要在 Java 中以编程方式通过管道发送非结构化数据。 Annotation Store(注释存储) 默认情况下,UIMA 分析管道的结果被存储在冗长繁琐的 XML 文件中。由于 UIMA 标准没有提供更复杂的注释存储方法,InterSystems IRIS 通过使用灵活的、基于 SQL 的存储,将注释放在统一、持久的表中以便后续检索,从而扩展了 UIMA 分析管道。这种存储系统称为Annotation Store(注释存储)。 在您第一次编译包含为创建 UIMA 分析管道定义的功能索引的类时,会自动创建此Annotation Store(注释存储)。它直接链接到包含非结构化数据的原始表的列。 在架构上,Annotation Store(注释存储) 是通过添加一个特殊的分析引擎作为 UIMA 分析管道的最后一个组件来生成的。当您将 UIMA 功能索引添加到 InterSystems IRIS 类时,将会自动生成Annotation Store(注释存储)。在 InterSystems IRIS 之外开发的 UIMA 分析管道也可以将此特殊分析引擎添加到管道末端,以创建Annotation Store(注释存储)。这种实现方式超出了本技术概要(First Look)的讨论范围。 []{#2.3_InterSystems_IRIS_NLP .anchor}您还可以使用XData block在包含功能索引的类中自定义Annotation Store(注释存储)。例如,您可以为每个表定义额外的列和索引。您还可以筛选注释类型,以将它们排除在Annotation Store(注释存储)之外。 InterSystems IRIS NLP InterSystems IRIS Natural Language Processing(InterSystems IRIS 自然语言处理, NLP )被内置到 InterSystems IRIS® 数据平台中,可以在预先不了解这些非结构化文本主题的情况下,对这些文本进行文本分析。它通过应用识别语义实体的特定语言规则来实现这项功能。由于这些规则是专门针对语言而非内容,因此 InterSystems IRIS NLP可以在不使用字典或本体的情况下提供对文本内容的深入分析 您可以使用 InterSystems IRIS NLP 作为 UIMA 分析引擎,为 NLP 概念和上下文生成 UIMA 注释。这些注释与其他 UIMA 分析引擎提供的 UIMA 注释完全兼容。 在 InterSystems IRIS 中体验 UIMA 现在您已经了解了有关 UIMA 的一些基本信息,可以动手体验以了解它在 InterSystems IRIS 中的工作原理。在体验之前,您需要设置环境。 在 InterSystems IRIS 中体验 UIMA 用前须知 首先,请执行以下初步设置任务: 安装 Java Runtime Environment(Java 运行时环境)。 安装 InterSystems IRIS。 创建一个新的 InterSystems IRIS 命名空间。 启动 Java Gateway(Java 网关)。 安装 Java Runtime Environment(Java 运行环境) InterSystems IRIS 进行 UIMA 分析管道分析需要安装Java Runtime Environment(Java 运行时环境,JRE)。还需要一个指向 JRE 安装位置的环境变量。 如果您的机器上尚未安装 JRE,请从 Oracle® 下载并安装最新版本。 创建一个名为JAVA_HOME的环境变量,该变量指向 JRE 安装的位置。例如,在 Windows® 上,使用 Control Panel(控制面板)创建JAVA_HOME环境变量,并定义该变量到 JRE 安装位置的路径。 安装 InterSystems IRIS 要运行 UIMA 分析管道的演示,您需要一个运行中的 InterSystems IRIS 已授权的实例。 有关如何安装和授权 InterSystems IRIS 开发实例的说明,请参阅InterSystems IRIS Basics: Installation (《InterSystems IRIS 基础知识:安装》)。 创建新的命名空间 作为技术概要(First Look)体验的一部分,您将向 InterSystems IRIS 中的命名空间添加一个新类文件。为了使此示例数据与预定义的命名空间分开,请创建一个名为 SAMPLES 的新命名空间来保存与此技术概要(First Look)关联的代码和数据。创建一个新的命名空间: 在浏览器中打开 Management Portal (管理门户),使用 InterSystems IRIS Basics: Connecting an IDE(《INTERSYSTEMS IRIS 基础:连接一个IDE 》)的实例适用的URL。 选择 System Administration(系统管理) > Configuration(配置) > System Configuration(系统配置) > Namespaces(命名空间)。 在 Namespaces(命名空间)页面,选择 Create New Namespace(新建命名空间)。 在 New Namespace(新建命名空间)页面,输入SAMPLES作为新命名空间的名称。 点击 Create New Database(新建数据库),将显示 Database Wizard(数据库向导)。 在 Database Wizard(数据库向导)的第一页,在 Enter the name of your database(输入您的数据库名称)字段,输入您正在创建的数据库的名称,例如 Samplesdb。 输入数据库的目录,例如 C:\InterSystems\IRIS\mgr\Samplesdb 。 点击 Next(下一步)。 点击 Finish(完成)。 回到New Namespace(新命名空间)页面,在Select an existing database for Routines(为 Routine 选择一个现有数据库)下拉菜单,选择您刚刚创建的数据库Samplesdb。 点击靠近页面顶部的 Save(保存),然后在结果日志的末尾单击 Close(关闭)。 UIMA 分析管道 运行Java Gateway(Java 网关) Java Gateway(Java 网关)可以实例化外部 Java object(Java 对象),就好像它是 InterSystems IRIS 中的本机对象一样,可以对其进行操作。InterSystems IRIS 的 UIMA 策略使用Java Gateway(Java 网关),可以从command line (命令行)启动Java Gateway(Java 网关)。例如,在 Windows 上: 打开 Run(运行)对话框。 输入以下命令: %JAVA_HOME%\bin\java -classpath "C:\InterSystems\IRIS\dev\java\lib\JDK18\*;C:\InterSystems\IRIS\dev\java\lib\jackson\*;C:\InterSystems\IRIS \dev\java\lib\uima\*" com.intersystems.gateway.JavaGateway 5555 其中: JAVA_HOME 是指向Java Runtime Environment(Java 运行环境,JRE)安装目录位置的环境变量。 C:\InterSystems\IRIS 是您安装的 InterSystems IRIS 的目录。 JDK18 对应您的 JRE 版本。 如果在UNIX®上运行 ,请记住 -classpath 的句法使用冒号作为分隔符。 体验 UIMA 分析管道 现在您已经完成了准备工作,并已准备好在实际应用中查看 UIMA 分析管道的运行情况。在这次体验中,您将: 添加包含 UIMA 功能索引的类文件 编译包含功能索引的类。 查看 Annotation Store (注释存储)的表。 将非结构化数据添加到示例数据库。 浏览 Annotation Store(注释存储) 以获取分析管道生成的新数据。 添加带有 UIMA 功能索引的类文件 您可以通过为包含非结构化文本的表定义功能索引,向 UIMA 分析管道添加分析引擎。在本次体验中,您将向该分析管道添加 InterSystems IRIS NLP 分析引擎。 在体验的这一部分中,您将创建一个新的类文件。可以在您喜欢的文本编辑器或 IDE(例如 InterSystems Studio 或带有 ObjectScript 扩展名的 Visual Studio Code)中创建类文件。 在 IDE 或文本编辑器中创建一个新文件。 将以下内容复制并粘贴到类文件中: xxxxxxxxxx Class Sample.MyData Extends %Persistent ​ { ​ Property MyText As %String; ​ Index MyIndex On (MyText) As %UIMA.Index(AEDESCRIPTOR = \"classpath:/com/intersystems/uima/annotator/iKnowEngine.xml\"); ​ } 体验 UIMA 分析管道 其中: MyText 是包含非结构化文本的 Sample.MyData 表中的列。 MyIndex 是 UIMA 功能索引。 iKnowEngine.xml是 InterSystems IRIS NLP 分析引擎的描述符文件。 将文件另存为sample.cls。 编译表类 要自动生成 Annotation Store (注释存储),您只需使用InterSystems Terminal(InterSystems 终端)或 IDE 提供的工具编译包含功能索引的类。 建议: 使用 InterSystems Terminal(InterSystems 终端)时,您可以使用 Shift+Insert 命令将剪贴板的内容粘贴到终端命令提示符中。从本指南复制命令并将它们粘贴到终端中以减少错误,这种方式很有用。 加载和编译类: 打开 InterSystems Terminal(InterSystems 终端)。有关为您的实例打开Terminal (终端)的信息,请参阅InterSystems IRIS Basics: Connecting an IDE(《INTERSYSTEMS IRIS 基础:连接一个IDE 》)中的InterSystems IRIS Connection Information (《InterSystems IRIS 连接信息》)。 切换到您为此演示创建的命名空间。例如: set $namespace="samples" 输入以下命令,将类文件加载到命名空间中: do $system.OBJ.Load("<sample-dir>\sample.cls") 其中 <sample-dir> 是您保存 samples.cls 类文件的位置。 输入以下命令,编译您粘贴到sample.cls 的Sample.MyData 类: do $system.OBJ.Compile("Sample.MyData") 浏览 Annotation Store(注释存储) 既然您已经使用 Annotation Store UIMA 功能索引编译了类,可以浏览为保留 InterSystems IRIS NLP 生成的注释而创建的(Annotation Store )注释存储。 在浏览器中使用 URL 为实例打开 Management Portal (管理门户),如InterSystems IRIS Basics: Connecting an IDE(《INTERSYSTEMS IRIS 基础:连接一个IDE 》)的InterSystems IRIS Connection Information(InterSystems IRIS 连接信息)所述。 使用标题中的链接切换到 Samples 命名空间。 参观 UIMA 分析管道 进入 System Explorer(系统资源管理器)> SQL 。 展开左侧窗格中的 Tables(表) 列表。 可以看到 Annotation Store (注释存储) 的三个表。这些表的命名规则对应包含所分析的非结构化文本的表 (Sample.MyData)。 Sample_MyData.Type------包含此存储中使用的Annotation Types (注释类型)的概述。 Sample_MyData.Sofa------包含 sofas,这是 UIMA 分析引擎分析的文本对象。 Sample_MyData.Annotation------包含该分析引擎生成的注释。 您可以修改功能索引定义以创建多个注释表,然后根据注释类型将结果输出到正确的表中。 通过分析管道发送新文本 InterSystems IRIS 中 UIMA 分析管道的强大之处在于,新的非结构化文本会自动通过管道发送以进行分析,并将结果添加到 Annotation Store (注释存储)中。由于您已经创建了 Annotation Store(注释存储),可以看到新纪录如何被添加到 Sample.MyData 表,并生成添加到 Annotation Store的新条目。 向 Aviation.Event 表添加记录 在本步骤中,您将使用 SQL 向示例数据库中的 Sample.MyData 表添加一些非结构化文本。请记住,该表包含MyText 列,您已在该列上定义功能索引。正如您将看到的,当进行此插入操作时,会自动生成注释。 在System Explorer (系统资源管理器 )> SQL页,展开左侧窗格中的 Tables(表) 列表。 选择 Sample.MyData,这是包含通过分析管道发送的非结构化文本的表。 在右侧窗格中,单击 Execute Query(执行查询)标签。 要将新条目插入示例数据库,请在文本框中输入以下查询 xxxxxxxxxx INSERT INTO Sample.MyData (MyText) VALUES (\'First Look unstructured text\') 点击 Execute (执行)。 这会将短语"First Look unstructured text"放入 Sample.MyData 表的 MyText 列中。 了解更多关于 UIMA的资料 查看Annotation Store(注释存储)中的新条目 现在您已将新的非结构化文本添加到示例数据库中,可以查看 Annotation Store 以了解这些文本是如何通过分析管道自动发送的。您可以看到新的非结构化文本和来自 InterSystems IRIS NLP 的注释都添加到了Annotation Store(注释存储)中。 在 System Explorer (系统资源管理器 )> SQL页,展开左侧窗格中的 Tables(表) 列表。 选择 Sample_MyData.Sofa 表。 在右侧窗格中,单击 Open Table(打开表)。 您可以看到添加到 Annotation Store 的新记录。sofaString是由分析管道处理的一段非结构化文本。 点击 Close Window(关闭窗口)。 在左侧窗格中,选择 Sample_MyData.Annotation 表。 点击 Open Table(打开表)。 在coveredText列,您可以看到由 InterSystems IRIS NLP 分析引擎生成的注释。 了解更多关于 UIMA的资料 要了解有关 InterSystems IRIS 如何实施和补充 UIMA 的更多信息,请参阅 Using InterSystems UIMA(《使用 InterSystems UIMA》)。 有关 UIMA 标准的框架、基础设施和组件的详细概述,请参阅 Apache UIMA home page
文章
Jeff Liu · 一月 7, 2021

精华文章---面向 Google Cloud Platform (GCP) 的 InterSystems IRIS 示例参考架构

Google Cloud Platform (GCP) 为基础架构即服务 (IaaS) 提供功能丰富的环境,其作为云提供完备的功能,支持所有的 InterSystems 产品,包括最新的 InterSystems IRIS 数据平台。 与任何平台或部署模型一样,必须留心以确保考虑到环境的各个方面,例如性能、可用性、操作和管理程序。 本文将详细阐述所有这些方面。 以下概述和详细内容由谷歌提供,可在此处找到。   概述 ### GCP 资源 GCP 由一系列物理资产(例如计算机和硬盘驱动器)和虚拟资源(例如虚拟机(VM))组成,它们分布于谷歌遍布全球的数据中心。 每个数据中心的位置都是一个泛区域。 每个区域都是地区的集合,这些地区在该区域内彼此分离。 每个地区都通过一个名称标识,名称由字母标识符和相应区域的名称组成。 这种资源分配带来众多优势,包括发生故障时提供冗余,以及通过将资源配置在客户端附近来减少延迟。 这种分配也引入一些有关如何统筹资源的规则。 ### 访问 GCP 资源 在云计算中,物理硬件和软件变成了服务。 这些服务提供对基础资源的访问。 在 GCP 上开发基于 InterSytems IRIS 的应用程序时,您可混合和匹配这些服务,组合它们来提供您所需的基础架构,然后添加您的代码来实现您要构建的方案。 有关可用服务的详细信息, 可在此处找到。 ### 项目 您分配和使用的任何 GCP 资源必须属于一个项目。 项目由设置、权限和其他描述应用程序的元数据组成。 根据区域和地区规则,单个项目中的资源能够轻松协作,例如通过内部网络进行通信。 每个项目包含的资源在项目边界上保持独立;您只能通过外部网络连接来互连它们。   服务交互 GCP 提供 3 种与服务和资源交互的基本方法。 #### 控制台 Google Cloud Platform 控制台提供基于 web 的图形用户界面,供您管理 GCP 项目和资源。 使用 GCP 控制台,您可创建新项目,或选择现有项目,可使用您在项目环境中创建的资源。 您可以创建多个项目,因此您可以使用项目以任何对您有意义的方式分开您的工作。 例如,如果您想确保只有某些团队成员可以访问项目中的资源,而所有团队成员可以继续访问另一个项目中的资源,则可以开始一个新项目。 命令行界面 如果您喜欢在终端窗口中工作,Google Cloud SDK 提供 gcloud 命令行工具,让您可以访问所需的命令。 gcloud 工具可用于管理您的开发工作流程和 GCP 资源。 有关 gcloud 的详细内容可在此处找到。 GCP 也提供 Cloud Shell,一种基于浏览器的 GCP 交互 shell 环境。 可从 GCP 控制台访问 Cloud Shell。 Cloud Shell 提供: 临时计算引擎虚拟机实例。 从 web 浏览器通过命令行访问实例。 内置代码编辑器。 5 GB 持久性磁盘存储。 预安装 Google Cloud SDK 和其他工具。 支持 Java、Go、Python、Node.js、PHP、Ruby 和 .NET 语言。 Web 预览功能。 访问 GCP 控制台项目和资源的内置授权。   客户端库 Cloud SDK 拥有客户端库,让您轻松创建和管理资源。 GCP 客户端库公开 API 有两个主要目的: 应用 API 提供对服务的访问。 应用 API 针对支持的语言(例如 Node.js 和 Python )进行了优化。 客户端库围绕服务隐喻而设计,因此您可以更自然地使用服务,并编写更少的样板代码。 客户端库还提供身份验证和授权助手。 有关详细信息可在此处找到。 管理 API 提供资源管理功能。 例如,如果您想构建自己的自动化工具,可以使用管理 API。 您还可以使用 Google API 客户端库来访问产品的 API,如 Google Map、Google Drive 和 YouTube。 有关 GCP 客户端库的详细信息可在此处找到。   InterSystems IRIS 示例体系结构 本文部分内容阐述了面向 GCP 的 InterSystems IRIS 部署示例,旨在为特定应用程序的部署抛砖引玉。 这些示例可用作很多部署方案的指南。 此参考体系结构拥有非常强大的部署选项,从最小规模的部署,到满足计算和数据需求的大规模可扩展工作负载,不一而足。 本文还介绍了高可用性和灾难恢复选项以及其他建议的系统操作。 个体可对这些进行相应的修改以支持其组织的标准实践和安全策略。 针对您的特定应用,就基于 GCP 的 InterSystems IRIS 部署,您可联系 InterSystems 进一步探讨。   * * * 示例参考体系结构 以下示例体系结构按照容量和功能逐步升级的顺序讲述了几种不同的配置, 分别为小型开发/生产/大型生产/分片集群生产。先从中小型配置讲起,然后讲述具有跨地区高可用性以及多区域灾难恢复的大规模可扩展性解决方案。 此外,还讲述了一个将 InterSystems IRIS 数据平台的新分片功能用于大规模处理并行 SQL 查询的混合工作负载的示例。 ###   小型开发配置 在本示例中,显示了一个能支持 10 名开发人员和 100GB 数据的小型开发环境,这基本是最小规模的配置。 只要适当地更改虚拟机实例类型并增加持久性磁盘存储,即可轻松支持更多的开发人员和数据。 这足以支持开发工作,并让您熟悉 InterSystems IRIS 功能以及 Docker 容器的构建和编排(如果需要的话)。 小型配置通常不采用具有高度可用性的数据库镜像,但是如果需要高可用性,则可随时添加。   小型配置示例图 示例图 2.1.1-a 显示了图表 2.1.1-b 中的资源。 其中包含的网关只是示例,可做相应地调整以适应您组织的标准网络实践。     下列 GCP VPC 项目资源是针对最小规模的配置提供的。 可根据需求添加或删除 GCP 资源。   小型配置 GCP 资源 下表提供了小型配置 GCP 资源的示例。     需要考虑适当的网络安全和防火墙规则,以防止对 VPC 的不必要访问。 谷歌提供网络安全最佳做法供您入门使用,可在此处找到。   注意:VM 实例需要公共 IP 地址才能访问 GCP 服务。 谷歌建议使用防火墙规则来限制这些 VM 实例的传入,尽管这种做法可能会引起一些问题。   如果您的安全策略确实需要内部 VM 实例,则您需要在网络上手动设置 NAT 代理和相应的路由,以便内部实例可以访问互联网。 务必要明确,您无法使用 SSH 直接完全连接到内部 VM 实例。 要连接到此类内部机器,必须设置具有外部 IP 地址的堡垒实例,然后建立隧道通过它。 可以配置堡垒主机,以提供进入 VPC 的外部入口点。 有关堡垒主机的详细信息,可在此处找到。   产品配置 在本示例中,展示了一个规模较大的产品配置,其采用 InterSystems IRIS 数据库镜像功能来支持高可用性和灾难恢复。 此配置包括 InterSystems IRIS 数据库服务器同步镜像对,该镜像服务器在区域 1 内分为两个地区,用于自动故障转移,在区域 2 内的第三个 DR 异步镜像成员用于灾难恢复,以防万一整个 GCP 区域脱机 。 InterSystems Arbiter 和 ICM 服务器部署在单独的第三个地区,以提高弹性。  此示例体系结构还包括一组可选的负载均衡 web 服务器,用于支持启用 Web 的应用程序。 这些使用 InterSystems 网关的 web 服务器可以根据需要进行缩放。   产品配置示例图 示例图 2.2.1-a 显示了图表 2.2.1-b 中的资源。 其中包含的网关只是示例,可做相应地调整以适应您组织的标准网络实践。     建议将以下 GPC VPC 项目中的资源作为分片集群部署的最低配置。 可根据需求添加或删除 GCP 资源。   产品配置 GCP 资源 下表提供了产品配置 GCP 资源的示例。     大型产品配置 在本示例中,提供了一个大规模可缩放性配置。该配置通过扩展 InterSystems IRIS 功能也引入使用 InterSystems 企业缓存协议 (ECP:EnterpriseCacheProtocol) 的应用程序服务器,实现对用户的大规模横向缩放。 本示例甚至包含了更高级别的可用性,因为即使在数据库实例发生故障转移的情况下,ECP 客户端也会保留会话细节。 多个 GCP 地区与基于 ECP 的应用程序服务器和部署在多个区域中的数据库镜像成员一起使用。 此配置能够支持每秒数千万次的数据库访问和数万亿字节数据。   大型产品配置示例图 示例图 2.3.1-a 显示了图表 2.3.1-b 中的资源。  其中包含的网关只是示例,可做相应地调整以适应您组织的标准网络实践。 此配置中包括一个故障转移镜像对,四个或更多的 ECP 客户端(应用程序服务器),以及每个应用程序服务器对应一个或多个 Web 服务器。 故障转移数据库镜像对在同一区域中的两个不同 GCP 地区之间进行划分,以提供故障域保护,而 InterSystems Arbiter 和 ICM 服务器则部署在单独的第三地区中,以提高弹性。 灾难恢复扩展至第二个 GCP 区域和地区,与上一示例中的情况类似。 如果需要,可以将多个 DR 区域与多个 DR 异步镜像成员目标一起使用。     建议将以下 GPC VPC 项目中的资源作为大型生产部署的最低配置。 可根据需求添加或删除 GCP 资源。   大型产品配置 GCP 资源 下表提供了大型产品配置 GCP 资源的示例。     采用 InterSystems IRIS 分片集群的生产配置 在此示例中,提供了一个针对 SQL 混合工作负载的横向缩放性配置,其包含 InterSystems IRIS 的新分片集群功能,可实现 SQL 查询和表跨多个系统的大规模横向缩放。 本文后面将详细讨论 InterSystems IRIS 分片集群及其功能。 采用 InterSystems IRIS 分片集群的生产配置 示例图 2.4.1-a 显示了图表 2.4.1-b 中的资源。  其中包含的网关只是示例,可做相应地调整以适应您组织的标准网络实践。 此配置中包括四个镜像对,它们为数据节点。 每个故障转移数据库镜像对在同一区域中的两个不同 GCP 地区之间进行划分,以提供故障域保护,而 InterSystems Arbiter 和 ICM 服务器则部署在单独的第三地区中,以提高弹性。 此配置允许从集群中的任何数据节点使用所有的数据库访问方法。 大型 SQL 表数据在物理上跨所有数据节点进行分区,以实现查询处理和数据卷的大规模并行。 将所有这些功能组合在一起,就可以支持复杂的混合工作负载,比如大规模分析 SQL 查询及引入的新数据,所有这一切均在一个 InterSystems IRIS 数据平台中执行。   注意,上面图表中以及下表“资源类型”列中的术语“计算[Engine]”是一个表示 GCP(虚拟)服务器实例的 GCP 术语,将在本文的 3.1节中做进一步介绍。 它并不表示或暗示本文后面所描述的集群体系结构中对“计算节点”的使用。 建议将以下 GPC VPC 项目中的资源作为分片集群部署的最低配置。 可根据需求添加或删除 GCP 资源。 使用分片集群配置 GCP 资源的产品 下表提供了分片集群配置 GCP 资源的示例。     * * * 云概念简介 Google Cloud Platform (GCP) 为基础架构即服务 (IaaS) 提供功能丰富的云环境,使其具备完备的功能,支持所有的 InterSystems 产品,包括支持基于容器的 DevOps 及最新的 InterSystems IRIS 数据平台。 与任何平台或部署模型一样,必须留心以确保考虑到环境的各个方面,例如性能、可用性、系统操作、高可用性、灾难恢复、安全控制和其他管理程序。 本文档将介绍所有云部署涉及的三个主要组件:计算、存储和网络。   计算引擎(虚拟机) GCP 中存在数个针对计算引擎资源的选项,以及众多虚拟 CPU 和内存规范及相关存储选项。 在 GCP 中值得注意的一点是,对给定机器类型中 vCPU 数量的引用等于一个 vCPU,其是虚拟机监控程序层上物理主机中的一个超线程。 就本文档的目的而言,将使用 n1-standard * 和 n1-highmem * 实例类型,这些实例类型在大多数 GCP 部署区域中广泛可用。 但是,对于将大量数据缓存在内存中的大型工作数据集而言,使用 n1-ultramem * 实例类型是不错的选择。 除非另有说明,否则使用默认实例设置(例如实例可用性策略)或其他高级功能。 有关各种机器类型的详细信息,可在此处找到。   磁盘存储 与 InterSystems 产品最直接相关的存储类型是持久性磁盘类型,但是,只要了解并适应数据可用性限制,本地存储可以用于高水平的性能。 还有其他一些选项,例如云存储(存储桶),但是这些选项更特定于单个应用程序的需求,而非支持 InterSystems IRIS 数据平台的操作。 与大多数其他云提供商一样,GCP 对可与单个计算引擎关联的持久性存储施加了限制。 这些限制包括每个磁盘的最大容量、关联到每个计算引擎的持久性磁盘的数量,以及每个持久性磁盘的 IOPS 数量,对单个计算引擎实例 IOPS 设置上限。 此外,对每 GB 磁盘空间设有 IOPS 限制,因此有时需要调配更多磁盘容量才能达到所需的 IOPS 速率。 这些限制可能会随着时间而改变,可在适当时与谷歌确认。 磁盘卷有两种类型的持久性存储类型:“标准持久性”磁盘和“SSD 持久性”磁盘。 SSD 持久性磁盘更适合于那些要求低延迟 IOPS 和高吞吐量的生产工作负载。 标准持久性磁盘对于非生产开发和测试或归档类型的工作负载,是一种更经济的选择。 有关各种磁盘类型及限制的详细信息,可在此处找到。   VPC 网络 强烈建议采用虚拟私有云 (VPC) 网络来支持 InterSystems IRIS 数据平台的各个组件,同时提供正确的网络安全控制、各种网关、路由、内部 IP 地址分配、网络接口隔离和访问控制。 本文档中提供了一个详细的 VPC 示例。 有关 VPC 网络和防火墙的详细信息,可在此处找到。   * * * 虚拟私有云 (VPC) 概述 GCP VPC 与其他云提供商略有不同,其更加简单和灵活。 可在此处找到各概念的比较。 在 GCP 项目中,每个项目允许有数个 VPC(目前每个项目最多允许 5 个),且创建 VPC 网络有两个选项——自动模式和自定义模式。 此处提供每个类型的详细信息。 在大多数大型云部署中,采用多个 VPC 将各种网关类型与以应用为中心的 VPC 进行隔离,并利用 VPC 对等进行入站和出站通信。 有关适合您的公司使用的子网和任何组织防火墙规则的详细信息,强烈建议您咨询您的网络管理员。 本文档不阐述 VPC 对等方面的内容。 在本文档提供的示例中,使用 3 个子网的单一 VPC 用于提供各种组件的网络隔离,以应对各种 InterSystems IRIS 组件的可预测延迟和带宽以及安全性隔离。   网络网关和子网定义 本文档的示例中提供了两种网关,以支持互联网和安全 VPN 连接。 要求每个入口访问都具有适当的防火墙和路由规则,从而为应用程序提供足够的安全性。 有关如何使用路由的详细信息,可在此处找到。 提供的示例体系结构中使用了 3 个子网,它们专与 InterSystems IRIS 数据平台一起使用。 这些单独的网络子网和网络接口的使用为上述 3 个主要组件的每一个提供了安全控制、带宽保护和监视方面的灵活性。 有关各种用例的详细信息,可在此处找到。 有关创建具有多个网络接口的虚拟机实例的详细信息,可在此处找到。   这些示例中包含的子网: 用户空间网络用于入站连接用户和查询 分片网络用于分片节点之间的分片间通信 镜像网络通过同步复制和单个数据节点的自动故障转移实现高可用性。   注意:仅在单个 GCP 区域内具有低延迟互连的多个地区之间,才建议进行故障转移同步数据库镜像。 区域之间的延迟通常太高,无法提供积极的用户体验,特别是对于具有高更新率的部署更如此。   ### 内部负载均衡器 大多数 IaaS 云提供商缺乏提供虚拟 IP (VIP) 地址的能力,这种地址通常用在自动化数据库故障转移设计中。 为了解决这一问题,InterSystems IRIS 中增强了几种最常用的连接方法,尤其是 ECP 客户端和 Web 网关,从而不再依赖 VIP 功能使它们实现镜像感知和自动化。 xDBC、直接 TCP/IP 套接字等连接方法,或其他的直接连接协议,均需要使用类 VIP 地址。 为了支持这些入站协议,InterSystems 数据库镜像技术使用称作<span class="Characteritalic" style="font-style:italic">mirror_status.cxw</span>的健康检查状态页面为 GCP 中的这些连接方法提供自动化故障转移,以与负载均衡器进行交互,实现负载均衡器的类 VIP 功能,仅将流量重定向至活动的主镜像成员,从而在 GCP 内实现完整且强大的高可用性设计。     此处提供了使用负载均衡器实现类 VIP 功能的详细信息。   示例 VPC 拓扑 下图 4.3-a 中的 VPC 布局组合了所有组件,具有以下特点: 利用一个区域内的多个地区实现高可用性 提供两个区域进行灾难恢复 利用多个子网进行网络隔离 包括分别用于互联网和 VPN 连接的单独网关 使用云负载均衡器进行镜像成员的 IP 故障转移   * * * 持久性存储概述 如简介中所述,建议使用 GCP 持久性磁盘,尤其 SSD 持久性磁盘类型。 之所以推荐 SSD 持久性磁盘,是由于其拥有更高的读写 IOPS 速率以及低的延迟,适合于事务性和分析性数据库工作负载。 在某些情况下,可使用本地 SSD,但值得注意的是,本地 SSD 的性能提升会在可用性、耐用性和灵活性方面做出一定的权衡。 可在此处找到本地 SSD 数据持久性方面的详细信息,您可了解何时保存本地 SSD 数据以及何时不保存它们。   LVM 条带化 与其他的云提供商一样,GCP 在每个虚拟机实例的 IOPS、空间容量和设备数量方面都施加了众多存储限制。 有关当前的限制,请查阅 GCP 文档,可在此处找到。 由于这些限制的存在,使用 LVM 条带化实现数据库实例的单个磁盘设备的 IOPS 最大化变得非常必要。 在提供的此示例虚拟机实例中,建议使用以下磁盘布局。 与 SSD 持久性磁盘相关的性能限制可在此处找到。   注意:目前,每个虚拟机实例最多有 16 个持久性磁盘,但 GCP 近期的方案则增至 128 个(测试),这将是令人欣慰的提高。     LVM 条带化的优势在于可以将随机的 IO 工作负载分散到更多的磁盘设备并继承磁盘队列。 以下是如何在 Linux 中将 LVM 条带化用于数据库卷组的示例。 本示例在一个 LVM PE 条带中使用 4 个磁盘,物理盘区 (PE) 大小为 4MB。 或者,如果需要,可以使用更大的 PE 容量。   步骤 1:根据需要创建标准性磁盘或 SSD 持久性磁盘 步骤 2:使用“lsblk -do NAME,SCHED”将每个磁盘设备的 IO 调度器设置为 NOOP 步骤 3:使用“lsblk -do KNAME,TYPE,SIZE,MODEL”识别磁盘设备 步骤4:使用新的磁盘设备创建磁盘卷组 vgcreate s 4M   示例: vgcreate -s 4M vg_iris_db /dev/sd[h-k] 步骤 4:创建逻辑卷 lvcreate n -L -i -I 4MB 示例:lvcreate -n lv_irisdb01 -L 1000G -i 4 -I 4M vg_iris_db 步骤 5:创建文件系统 mkfs.xfs K 示例:mkfs.xfs -K /dev/vg_iris_db/lv_irisdb01 步骤 6:装载文件系统 使用以下装载条目编辑 /etc/fstab /dev/mapper/vg_iris_db-lv_irisdb01    /vol-iris/db    xfs  defaults 0 0 装载 /vol-iris/db 使用上表,每个 InterSystems IRIS 服务器将具有以下配置:2 个 SYS 磁盘、4 个 DB 磁盘、2 个主日志磁盘、2 个备用日志磁盘。   为了增长,LVM 允许在需要的情况下不中断地扩展设备和逻辑卷。 有关持续管理和扩展 LVM 卷的最佳做法,请查阅 Linux 文档。   注意:强烈建议同时为数据库和写入映像日志文件启用异步 IO。 有关在 Linux 上启用的详细信息,请参阅下列社区文章:https://community.intersystems.com/post/lvm-pe-striping-maximize-hyper-converged-storage-throughput   * * * 配置 InterSystems IRIS 新增了 InterSystems Cloud Manager (ICM)。 ICM 执行众多任务,并提供许多用于配置 InterSystems IRIS 数据平台的选项。 ICM 作为 Docker 映像提供,其拥有配置强大的、基于 GCP 云的解决方案所需的一切。 ICM 当前支持以下平台上的配置: Google Cloud Platform (GCP) Amazon Web Services,包括 GovCloud (AWS / GovCloud) Microsoft Azure Resource Manager,包括 Government (ARM / MAG) VMware vSphere (ESXi) ICM 和 Docker 可以从台式机/笔记本电脑工作站运行,也可以具有中央专用的适度“配置”服务器和中央存储库。   ICM 在应用程序生命周期中的作用是“定义->配置->部署->管理” 有关安装和使用 ICM 及 Docker 的详细信息,可在此处找到。   注意:任何云部署都非必须使用 ICM。 完全支持传统的 tar-ball 分布式安装和部署方法。 但是,建议使用 ICM,以简化云部署中的配置和管理。   容器监视 ICM 包含基本的监视工具,其使用 Weave Scope 进行基于容器的部署。 默认情况下不会部署该工具,需要在默认的文件中使用监视器字段指定它。 有关使用 ICM 进行监视、编排和调度的详细信息,可在此处找到。 有关 Weave Scope 的概述和该文档,可在此处找到。   * * * 高可用性 InterSystems 数据库镜像可在任何云环境中提供最高级别的可用性。  有一些选项可以直接在实例级别提供虚拟机弹性。 有关 GCP 中可用的各种政策的详细信息,可在此处找到。 上文中已讨论了云负载均衡器如何通过数据库镜像为虚拟 IP(类 VIP)功能提供自动化 IP 地址故障转移。  云负载均衡器使用<span class="Characteritalic" style="font-style:italic">mirror_status.cxw</span>健康检查状态页面,上文内部负载均衡器部分提到过该页面。  数据库镜像有两种模式——自动故障转移同步镜像、异步镜像。 在本示例中,将介绍同步故障转移镜像。 有关镜像的详细信息,可在此处找到。 最基本的镜像配置是仲裁器控制配置中的一对故障转移镜像成员。 仲裁器放置在同一区域内的第三个地区中,以防止潜在的地区中断影响仲裁器和其中一个镜像成员。 在网络配置中,有多种方法专供设置镜像。 在本示例中,我们将使用本文档前述网络网关和子网定义部分中定义的网络子网。 下一部分内容将提供 IP 地址方案示例,并且基于本部分内容,将仅描述网络接口和指定的子网。   * * * 灾难恢复 InterSystems 数据库镜像将支持灾难恢复的高可用性功能扩展到另一个 GCP 地理区域,以在整个 GCP 区域万一脱机的情况下支持操作弹性。 应用程序如何耐受此类中断取决于恢复时间目标 (RTO) 和恢复点目标 (RPO)。 这些将为设计适当的灾难恢复计划进行的分析提供初始框架。 以下链接中的指南提供了您在为自己的应用程序制定灾难恢复计划时要考虑的事项。 https://cloud.google.com/solutions/designing-a-disaster-recovery-plan 和 https://cloud.google.com/solutions/disaster-recovery-cookbook   异步数据库镜像 InterSystems IRIS 数据平台的数据库镜像提供强大的功能,可在 GCP 地区和区域之间异步复制数据,以帮助支持您的灾难恢复计划的 RTO 和 RPO 目标。 有关异步镜像成员的详细信息,可在此处找到。 与上述高可用性部分中讲述的内容相似,云负载均衡器也使用上文内部负载均衡器部分中提到过的<span class="Characteritalic" style="font-style:italic">mirror_status.cxw</span>健康检查状态页面为虚拟 IP(类 VIP)功能提供自动化 IP 地址故障转移,以进行 DR 异步镜像。 在本示例中,将介绍 DR 异步故障转移镜像,并介绍 GCP 全局负载均衡服务,以便为上游系统和客户端工作站提供单个任播 IP 地址,不分您的 InterSystems IRIS 部署是否在哪个区域或地区中运行。 GCP 的其中一个发展就是负载均衡器的诞生,这是一种软件定义的全局资源,并且不受制于特定的区域。 由于其不是基于实例或设备的解决方案,因此它具有跨区域利用单个服务的独特功能。 有关通过单个任播 IP 进行 GCP 全局负载均衡的详细信息,可在此处找到。   在上述示例中,所有 3 个 InterSystems IRIS 实例的 IP 地址都提供给了 GCP 全局负载均衡器,它会将流量仅定向到承担主要镜像的镜像成员,而不论其位于哪个地区或区域。   * * * 分片集群 InterSystems IRIS 拥有一系列全面的功能来缩放您的应用程序,您可以根据自己工作负载的性质以及所面临的特定性能挑战来单独或组合应用这些功能。 分片功能是这些功能中的一种,可跨多个服务器对数据及其关联的缓存进行分区,从而为查询和数据引入提供灵活、高性价比的性能扩展,同时通过高效的资源利用最大化基础架构的价值。 InterSystems IRIS 分片群集可以为各种应用提供显著的性能优势,尤其对于工作负载包括以下一项或多项的应用更是如此: 大容量或高速数据引入,或两者的组合。 相对较大的数据集、返回大量数据的查询,或两者。 执行大量数据处理的复杂查询,例如扫描磁盘上大量数据或涉及大量计算工作的查询。 这些因素分别都会影响分片的潜在优势,但组合起来使用它们可能会增加优势。 例如,这 3 个因素的组合——快速引入大量数据、大型数据集、检索和处理大量数据的复杂查询——使得当今的许多分析性工作负载非常适合进行分片。 注意,这些特征都与数据有关;InterSystems IRIS 分片的主要功能是缩放数据量。 不过,当涉及某些或所有这些与数据相关的因素的工作负载也经历大量用户的超高查询量时,分片群集也能提供用户量缩放功能。 分片也可以与纵向缩放相结合。   操作概述 分片架构的核心是跨多个系统对数据及其关联的缓存进行分区。 分片集群跨多个 InterSystems IRIS 实例以行方式(称为数据节点)对大型数据库表进行物理上的横向分区,同时允许应用通过任何节点透明地访问这些表,但仍将整个数据集看作一个逻辑并集。 该架构具有 3 个优点:   并行处理:查询在数据节点上并行运行,然后将结果进行合并和组合后,由节点作为完整查询结果返回给连接的应用。许多情况下,这大大提高了执行速度。 分区缓存:每个数据节点都有自己的缓存,专用于它存储的分片表数据分区,再不是单个实例的缓存服务于整个数据集,这大大降低了缓存溢出的风险,并强制执行性能降低式磁盘读取。 并行加载:数据可以并行加载到数据节点,从而减少了引入工作负载和查询工作负载之间的缓存和磁盘争用,提高了两者的性能。   有关 InterSystems IRIS 分片集群的详细信息,可在此处找到。   分片元素和实例类型 分片集群包含至少一个数据节点,如果特定性能或工作负载有需要,则可添加一定数量的计算节点。 这两种节点类型提供简单的构建块,从而实现简单、透明和高效的调整模型。   数据节点 数据节点存储数据。 在物质层面,分片表[1]数据分布在集群中的所有数据节点上,非分片表数据仅物理存储在第一个数据节点上。 这种区分对用户是透明的,唯一可能的例外是,第一个节点的存储消耗可能比其他节点略高,但是由于分片表数据通常会超出非分片表数据至少一个数量级,因此这种差异可以忽略不计。 需要时,可以跨集群重新均衡分片表数据,这通常发生在添加新的数据节点后。 这将在节点之间移动数据的“存储桶”,以实现数据的近似均匀分布。 在逻辑层面,未分片的表数据和所有分片的表数据的并集在任何节点上都可见,因此客户端会看到整个数据集,这与其连接哪个节点无关。 元数据和代码也会在所有数据节点之间共享。 分片集群的基本架构图仅由在集群中看起来统一的数据节点组成。 客户端应用程序可以连接到任何节点,并且可以像在本地一样体验数据。 [1]为方便起见,术语“分片表数据”在整个文档中用于表示支持分片的任何数据模型的“盘区”数据(标记为已分片)。 术语“未分片表数据”和“未分片数据”用于表示处于可分片盘区但却未这样标记的数据,或表示尚不支持分片的数据模型。     数据节点 对于要求低延迟(可能存在不断涌入数据的冲突)的高级方案,可以添加计算节点以提供用于服务查询的透明缓存层。 计算节点缓存数据。 每个计算节点都与一个数据节点关联,为其缓存相应的分片表数据,此外,它还根据需要缓存非分片表数据,以满足查询的需要。     由于计算节点物理上并不存储任何数据,其只是支持查询执行,因此可对其硬件配置文件进行调整以满足需求,例如通过强调内存和 CPU 并将存储空间保持在最低限度。 当“裸露”应用程序代码在计算节点上运行时,引入数据会被驱动程序 (xDBC, Spark) 直接或被分片管理器代码间接转发到数据节点。   * * * 分片集群说明 分片集群部署有多种组合。 下列各图说明了最常见的部署模型。 These diagrams do not include the networking gateways and details and provide to focus only on the sharded cluster components.   基本分片集群 下图是在一个区域和一个地区中部署了 4 个数据节点的最简单分片群集。 GCP 云负载均衡器用于将客户端连接分发到任何分片集群节点。     在此基本模型中,除了 GCP 为单个虚拟机及其连接的 SSD 持久性存储提供的弹性或高可用性外,没有其他弹性或高可用性。 建议使用两个单独的网络接口适配器,一则为入站客户端连接提供网络安全隔离,二则为客户端流量和分片集群通信之间提供带宽隔离。   具有高可用性的基本分片集群 下图是在一个区域中部署了 4 个镜像数据节点的最简单分片集群,每个节点的镜像在地区之间进行了划分。 GCP 云负载均衡器用于将客户端连接分发到任何分片集群节点。 InterSystems 数据库镜像的使用带来了高可用性,其会在该区域内的第二地区中维护一个同步复制的镜像。 建议使用 3 个单独的网络接口适配器,一方面为入站客户端连接提供网络安全隔离,另一方面为客户端流量、分片集群通信、节点对之间的同步镜像流量之间提供带宽隔离。     此部署模型也引入了本文前面所述的镜像仲裁器。   具有单独计算节点的分片集群 下图采用单独的计算节点和 4 个数据节点扩展了分片集群,以此来应对大量的用户/查询并发。 云负载均衡器服务器池仅包含计算节点的地址。 更新和数据引入将像以前一样继续直接更新到数据节点,以维持超低延迟性能,并避免由于实时数据引入而在查询/分析工作负载之间造成资源的干扰和拥挤。 使用此模型,可以根据计算/查询和数据引入的规模单独微调资源分配,从而在“适时”需要的地方提供最佳资源,实现经济而简单的解决方案,而非只是进行计算或数据的调整,浪费不必要的资源。 计算节点非常适合直接使用 GCP 自动缩放分组(亦称自动缩放),允许基于负载的增加或减少自动从托管实例组添加或删除实例。 自动缩放的工作原理为:负载增加时,将更多的实例添加到实例组(扩展);对实例的需求降低时将其删除(缩减)。 有关 GCP 自动缩放的详细信息,可在此处找到。     自动缩放可帮助基于云的应用程序轻松应对流量增加的情况,并在资源需求降低时降低成本。 只需简单地定义自动缩放策略,自动缩放器就会根据测得的负载执行自动缩放。   * * * 备份操作 备份操作有多个选项。 以下 3 个选项可供您通过 InterSystems IRIS 进行 GCP 部署。 下面的前 2 个选项(下文详细说明)采用快照类型的过程,该过程会在创建快照之前将数据库写入操作挂起到磁盘上,然后在快照成功后恢复更新。 可采取以下高级别步骤通过任一快照方法来创建洁净的备份: 通过数据库外部冻结 API 调用暂停对数据库的写入。 创建操作系统和数据磁盘的快照。 通过外部解冻 API 调用恢复数据库写入。 将设施存档备份到备份位置 有关外部冻结/解冻 API 的详细信息,可在此处找到。   注意:本文档未包含备份示例脚本,但您可定期检查发布到 InterSystems 网站上开发者社区的示例。 请访问 www.community.intersystems.com   第三个选项是 InterSystems 在线备份。 这是小型部署的入门级方法,具有非常简单的用例和界面。 但是,随着数据库的增大,建议将使用快照技术的外部备份作为最佳做法,因为其具有以下优势:备份外部文件、更快的恢复时间,以及企业范围的数据和管理工具。 可以定期添加诸如完整性检查之类的其他步骤,以确保洁净且一致的备份。 决定使用哪种选项取决于您组织的运营要求和策略。 InterSystems 可与您详细讨论各种选项。   GCP 持久性磁盘快照备份 可以使用 GCP gcloud 命令行 API 以及 InterSystems 外部冻结/解冻 API 功能实现备份操作。 这允许实现真正的 24x7 全天候操作弹性,并确保洁净常规备份。 有关管理、创建和自动化 GCP 持久性磁盘快照的详细信息,可在此处找到。   逻辑卷管理器 (LVM) 快照 或者,可以在 VM 本身中部署单个备份代理,利用文件级备份,并结合逻辑卷管理器 (LVM) 快照,来使用市面上的许多第三方备份工具。 该模型的主要优点之一是能够对基于 Windows 或 Linux 的 VM 进行文件级恢复。 此解决方案需要注意的几点是,由于 GCP 和大多数其他 IaaS 云提供商都不提供磁带媒体,因此所有的备份存储库对于短期归档均基于磁盘,并能够利用 Blob 或存储桶类型的低成本存储来进行长期保留 (LTR)。 强烈建议您使用此方法来使用支持重复数据删除技术的备份产品,以最有效地利用基于磁盘的备份存储库。 这些具有云支持的备份产品的示例包括但不限于:Commvault、EMC Networker、HPE Data Protector 和 Veritas Netbackup。 注意:InterSystems 不会验证或认可一种备份产品是否优于其他产品。 选择备份管理软件的责任由客户个人决定。   在线备份 对于小型部署,内置在线备份工具也是可行的选择。 该 InterSystems 数据库在线备份实用工具通过捕获数据库中的所有块来备份数据库文件中的数据,然后将输出写入顺序文件。 这种专有的备份机制旨在使生产系统的用户不停机。 有关在线备份的详细信息,可在此处找到。 在 GCP 中,在线备份完成后,必须将备份输出文件和系统正在使用的所有其他文件复制到该虚拟机实例之外的其他存储位置。 存储桶/对象存储是对其很好的命名。 使用 GCP 存储桶有两种选择。 直接使用 gcloud 脚本 API 来复制和操作新创建的在线备份(和其他非数据库)文件。 有关详细信息可在此处找到。 尽管云存储桶属于对象存储,但将存储桶装载为文件系统,并将其用作类似持久性磁盘的功能足以。 有关装载云存储桶(使用云存储 FUSE )的详细信息,可在此处找到。    
公告
Jeff Liu · 五月 21, 2021

InterSystems IRIS、IRIS for Health和HealthShare Health Connect的2021.1版本的预览版现在已经发布!

由于这是一个**预览版**,我们渴望在下个月的通用版发布之前了解您对这个新版本的经验。请通过[开发者社区](https://community.intersystems.com/)分享您的反馈,以便我们能够共同打造一个更好的产品。 **InterSystems IRIS数据平台2021.1**是一个扩展维护(EM)版本。自2020.1(上一个EM版本)以来,在持续交付(CD)版本中增加了许多重要的新功能和改进。请参考[2020.2](https://irisdocs.intersystems.com/iris20202/csp/docbook/DocBook.UI.Page.cls?KEY=GCRN)、[2020.3](https://irisdocs.intersystems.com/iris20203/csp/docbook/DocBook.UI.Page.cls?KEY=GCRN)和[2020.4](https://irisdocs.intersystems.com/iris20204/csp/docbook/DocBook.UI.Page.cls?KEY=GCRN)的发布说明,了解这些内容的概况。 这个版本的增强功能为开发人员提供了更大的自由度,可以用他们选择的语言构建快速和强大的应用程序,并使用户能够通过新的和更快的分析功能更有效地消费大量的信息。 通过InterSystems IRIS 2021.1,客户可以部署**[InterSystems IRIS Adaptive Analytics](https://www.intersystems.com/sg/resources/detail/intersystems-adaptive-analytics/)**,这是一个附加产品,它扩展了InterSystems IRIS,为分析终端用户提供了更大的易用性、灵活性、可扩展性和效率,而不管他们选择何种商业智能(BI)工具。它能够定义一个有利于分析的业务模型,并通过在后台自主构建和维护临时数据结构,透明地加速针对该模型运行的分析查询工作负载。 这个版本中的其他重点新功能包括 * 一套综合的**外部语言网关**,改进了可管理性,现在包括R和Python,可以用你选择的语言构建强大和可扩展的服务器端代码 * **InterSystems Kubernetes Operator**(IKO)为你的环境提供声明式配置和自动化,现在还支持部署InterSystems System Alerting & Monitoring(SAM)。 * **InterSystems API Manager v1.5**,包括改进的用户体验和Kafka支持 * **IntegratedML**的主流版本,使SQL开发人员能够在纯粹的SQL环境中直接构建和部署机器学习模型 **InterSystems IRIS for Health 2021.1**包括InterSystems IRIS的所有增强功能。此外,该版本通过针对FHIR数据解析和评估FHIRPath表达式的API,进一步扩展了该平台对FHIR®标准的广泛支持。这是对2020.1以来发布的重要的FHIR相关功能的补充,包括对**FHIR Profiles**、**FHIR R4 Transforms**和**FHIR客户端API**的支持。   关于所有这些功能的更多细节可以在产品文档中找到: InterSystems IRIS 2021.1 documentation and 文档和发布说明 InterSystems IRIS for Health 2021.1 documentation and 文档和发布说明 HealthShare Health Connect 2021.1 documentation and 文档和发布说明   EM版本带有所有支持平台的经典安装包,以及OCI(Open Container Initiative)又称Docker容器格式的容器镜像。 完整的清单,请参考[支持平台文件](https://docs.intersystems.com/iris20211/csp/docbook/Doc.View.cls?KEY=ISP_technologies#ISP_platforms_server)。 安装包和预览密钥可以从WRC的[预览版本下载网站](https://wrc.intersystems.com/wrc/coDistPreview.csp)获得。   InterSystems IRIS和IRIS for Health的**企业版**的容器镜像以及所有相应的组件都可以从[InterSystems容器注册表](https://docs.intersystems.com/components/csp/docbook/Doc.View.cls?KEY=PAGE_containerregistry)使用以下命令获得。 docker pull containers.intersystems.com/intersystems/iris:2021.1.0.205.0 docker pull containers.intersystems.com/intersystems/irishealth:2021.1.0.205.0 关于可用镜像的完整列表,请参考[ICR文档](https://docs.intersystems.com/components/csp/docbook/Doc.View.cls?KEY=PAGE_containerregistry#PAGE_containerregistry_images)。 **社区版**的容器镜像也可以使用以下命令从[Docker商店](https://hub.docker.com/_/intersystems-iris-data-platform)拉取: docker pull store/intersystems/iris-community:2021.1.0.205.0 docker pull store/intersystems/iris-community-arm64:2021.1.0.205.0 docker pull store/intersystems/irishealth-community:2021.1.0.205.0 docker pull store/intersystems/irishealth-community-arm64:2021.1.0.205.0 另外,所有容器镜像的tarball版本都可以通过WRC的[预览版本下载网站](https://wrc.intersystems.com/wrc/coDistPreview.csp)获得。   InterSystems IRIS Studio 2021.1是一个独立的IDE,用于Microsoft Windows,可以通过WRC的[预览版本下载网站](https://wrc.intersystems.com/wrc/coDistPreview.csp)下载。它与InterSystems IRIS和IRIS for Health 2021.1及以下版本一起使用。InterSystems还支持VSCode-ObjectScript插件,用于用**[Visual Studio Code为InterSystems IRIS开发应用程序](https://intersystems-community.github.io/vscode-objectscript/)**,该插件可用于Microsoft Windows、Linux和MacOS。 其他独立的InterSystems IRIS 2021.1组件,如ODBC驱动程序和Web网关,可从同一页面获得。 该预览版的构建号是2021.1.0.205.0。
文章
Nicky Zhu · 十一月 2, 2021

IRIS 2021 技术文档 First Look 32 -- InterSystems 云管理器(Cloud Manager)

目录 技术概要:InterSystems 云管理器(Cloud Manager) 1 ICM 能为您做什么? 1 ICM 是如何工作的? 1 试一试!使用 ICM 在云中部署 InterSystems IRIS 2 安装 Docker 3 下载 ICM 映像 3 启动 ICM 3 获取云供应商帐户和证书 3 生成安全密钥 4 自定义示例配置文件 4 配置基础设施 9 部署 InterSystems IRIS 10 尝试 ICM 管理命令 11 取消配置基础设施 12 ICM 能做的远不止这些! 13 了解有关 ICM 的更多信息 13 插图目录 图 1: ICM 让一切变得简单 2 图 2: 云供应商的 ICM 默认文件示例 8 图 3: 交互式 ICM 命令 11 技术概要:InterSystems 云管理器(Cloud Manager) 本文向您介绍了 InterSystems 云管理器(InterSystems Cloud Manager,ICM),这是基于 InterSystems IRIS®数据平台的应用程序的端到端云配置和部署解决方案。 作为本指南的一部分,您将使用 ICM 在公共云中配置基础设施,并在该基础设施上部署 InterSystems IRIS。 要浏览所有的技术概要(First Looks),包括那些可以在免费的云实例或 web 实例上执行的技术概要(First Looks),请参见 InterSystems First Looks(《InterSystems 技术概要》)。 ICM 能为您做什么? 欢迎来到云时代(Cloud Age)! 您是否对它的机会虎视眈眈,但又对它的挑战保持警惕?具体来说, 您是否渴望利用云,但又犹豫是否要投入资源进行复杂的迁移? 您是否已经在云中,但还在努力寻找一种方法,以便在各种软件环境中管理部署和版本化您的应用程序? 您是否希望将持续集成和交付引入您的软件工厂,并将 DevOps 方法引入您的部署过程?也就是说,您想把自己从传统实践、库依赖、系统漂移、手动升级和其他开销的限制和风险中解放出来吗? ICM 可以提供帮助! ICM 为您提供了一种简单、直观的方法来配置云基础设施并在其上部署服务,帮助您现在就进入云,而无需进行重大开发或重组。基础设施即代码 (infrastructure as code,IaC) 和容器化部署的优势使得在 Google、Amazon 和 Azure 等公共云平台或私有 VMware vSphere 云上部署基于 InterSystems IRIS 的应用程序变得容易。定义您想要的内容,发布一些命令,剩下的工作由 ICM 来完成。 即使您已经在使用云基础设施、容器,或者同时使用两者,ICM 也可以通过自动化大量手动步骤,极大地减少配置和部署应用程序所需的时间和精力。 ICM 是如何工作的? 在您输入的纯文本配置文件的指导下,ICM 使用 Hashicorp 流行的 Terraform IaC 工具配置您的基础设施,并根据需要配置的主机节点。在下一阶段,ICM 将 InterSystems IRIS 和您的应用程序部署在 Docker 容器中,如果需要,还可以部署其他服务。您想要的部署所需的所有 InterSystems IRIS 配置都是自动执行的。ICM 还可以在现有的虚拟和物理集群上部署容器化服务。 ICM 本身带有一个容器映像,其中包含您所需要的一切。从 InterSystems 的 ICM 映像下载并运行容器,打开命令行,您就可以开始了。ICM 通过组合这些元素,使一切变得简单: 可以用作模板以快速定义所需部署的示例配置文件。 可以向其中添加应用程序的 InterSystems IRIS 映像。 试一试! 使用 ICM 在云中部署 InterSystems IRIS 每个任务的用户友好命令。 管理已配置节点和部署在其上的服务并与之交互的多种方法。 图 1: ICM 让一切变得简单 试一试!使用 ICM 在云中部署 InterSystems IRIS ICM 为您执行许多任务,并为您提供许多选项,以帮助您准确地部署您所需要的内容,所以在生产中使用它需要一定的计划和准备(尽管比手动方法要少得多!)。但配置和部署过程很简单,ICM 可以为您做出许多决定。这一探索旨在让您亲眼看到 ICM 是如何工作的,以及使用 ICM 在亚马逊网络服务(Amazon Web Services,AWS)上部署 InterSystems IRIS 配置是多么容易。虽然这不是一时的工作,但这种探索不应该占用您太多的时间,您可以在机会出现时分阶段进行。 为了让您体验 ICM 而又不至于在细节上陷入困境,我们保持了简单的探索;例如,我们让您尽可能多地使用默认设置。但是,当您把 ICM 引入您的生产系统时,您需要做很多不同的事情,特别是(但不限于)安全方面。所以请确保不要把这种对 ICM 的探索与真实的情况相混淆! 本文档末尾提供的参考资料将使您对在生产中使用 ICM 的情况有一个很好的了解。ICM Guide(《ICM 指南》)提供了使用 ICM 的完整信息和程序,并在适当的地方提供了链接。 这些说明假定您具备以下条件: 特定于容器的 InterSystems IRIS 分片(sharding)许可证并可以访问 InterSystems 软件下载。 亚马逊网络服务(Amazon Web Services,AWS)、谷歌云平台(Google Cloud Platform,GCP)、微软云(Azure)或腾讯云(Tencent)上的帐户。 在配置文件中指定的许多属性在这些云平台上是通用的,但其他属性是特定于平台的。有关这些差异的详细信息可在 ICM Guide(《ICM 指南》)的"ICM Reference(《ICM 参考资料》)"一章中的"Provider-Specific Parameters(特定于供应商的参数)"部分找到。 试一试! 使用 ICM 在云中部署 InterSystems IRIS 安装 Docker ICM 是作为容器映像提供的,它包含您需要的所有内容。因此,对启动 ICM 的 Linux、macOS 或 Microsoft Windows 系统的唯一要求是安装 Docker,并运行 Docker 守护进程,以及系统连接到 Internet。有关在您的平台上安装 Docker 的信息,请参见 Docker 文档中的 Install Docker(《安装 Docker》)。 重要提示: Docker 企业版(Enterprise Edition)和社区版(Community Edition)18.09 及更高版本支持 ICM;企业版(Enterprise Edition)仅支持生产环境。 下载 ICM 映像(Image) 要使用 ICM,您需要将 ICM 映像下载到您正在工作的系统中;这要求您识别将从其中下载它的注册表和访问所需的凭证。同样,对于 ICM 部署 InterSystems IRIS 和其他 InterSystems 组件,它需要相关映像的此信息。ICM 下载映像的注册表必须能被您使用的云供应商访问(也就是说,不能在防火墙后面),而且为了安全起见,必须要求 ICM 使用您提供给它的凭证进行身份验证。有关识别所涉及的注册表和下载 ICM 映像的详细信息,请参见 InterSystems Cloud Manager Guide(《InterSystems 云管理器指南》)中的 Downloading the ICM Image(下载 ICM 映像)。 注意: 启动 ICM 的映像的主要版本和部署的 InterSystems 映像必须匹配。例如,您不能使用 2019.3 版本的 ICM 部署 2019.4 版本的 InterSystems IRIS。 有关在容器中使用 InterSystems IRIS 的简要介绍,包括实际操作经验, 请参见 First Look: InterSystems Products in Containers(《技术概要:容器中的 InterSystems 产品》);有关使用 ICM 以外的方法在容器中部署 InterSystems IRIS 和基于 InterSystems IRIS 的应用程序的详细信息,请参见 Running InterSystems IRIS in Containers(《在容器中运行 InterSystems IRIS》)。 启动 ICM 要在命令行上启动 ICM,使用下面的 docker run 命令,从注册表中拉出(下载)ICM 映像,从中创建一个容器,并启动该容器,例如: docker run --name icm -it --cap-add SYS_TIME containers.intersystems.com/intersystems/icm:2021.1.0.205.0 ICM 容器中的 /Samples 目录,每个供应商都有一个(/AWS,/GCP,等等),旨在使您能够轻松地使用开箱即用的 ICM 进行配置和部署;您可以使用这些目录之一中的示例配置文件 ,也可以从该目录进行配置和部署。然而,由于这些目录在 ICM 容器内,当容器被删除时,其中的数据就会丢失,包括您的配置文件和 ICM 创建并用于管理配置的基础设施的状态目录。在生产中,最好的做法是在启动 ICM 时通过挂载外部卷使用容器外的位置来存储这些数据(请参见 Docker 文档中的 Manage data in Docker(在 Docker 中管理数据))。 获取云供应商帐户和证书 ICM 可以在四个公共云平台上进行配置和部署:亚马逊网络服务(Amazon Web Services,AWS)、谷歌云平台(Google Cloud Platform,GCP)、微软云(Azure)和腾讯云(Tencent)。 登录您的 AWS、GCP、Azure 或 Tencent 帐户。如果您和您的雇主都还没有帐户,您可以进入AWS、GCP、Azure或Tencent 门户页面,快速创建一个免费帐户。 有关获取 ICM 需要向每个云供应商进行身份验证的帐户凭证的信息,请参见 ICM Guide(《ICM 指南》)的 "ICM Reference(《ICM 参考资料》) "一章中的 Security- Related Parameters(安全相关的参数)。 试一试! 使用 ICM 在云中部署 InterSystems IRIS 生成安全密钥 ICM 与配置基础设施的云供应商、配置的节点上的 Docker 以及容器部署后的若干 InterSystems IRIS 服务进行安全的通信。您已经下载或识别了您的云证书;您还需要其他文件来启用安全的 SSH 和 TLS 通信。 您可以使用 ICM 提供的两个脚本来创建您需要的文件,keygenSSH.sh 脚本创建所需的 SSH 文件并将它们放在 ICM 容器的 /Samples/ssh 目录中。keygenTLS.sh 脚本创建所需的 TLS 文件,并将它们放在 /Samples/tls 中。 要在 ICM 命令行上运行这些脚本,请输入以下内容: # keygenSSH.sh Generating keys for SSH authentication. ... # keygenTLS.sh Generating keys for TLS authentication. ... 有关这些脚本生成的文件的更多信息,请参见 ICM Guide(《ICM指南》)中的 Obtain Security-Related Files(获取安全相关的文件)、ICM Security(ICM 安全)和 Security-Related Parameters(安全相关的参数)。 重要提示: 这些生成的密钥是为了方便您在本文体验和其他测试中使用;在生产中,您应该根据您公司的安全策略来生成或获取所需的密钥。这些脚本生成的密钥以及您的云供应商凭证必须完全安全,因为它们提供了对使用它们的任何 ICM 部署的完全访问权限。 自定义示例配置文件 要定义您希望 ICM 部署的配置,您在两个 JSON 格式的配置文件中包含所需的设置:默认文件和定义文件。前者(defaults.json)包含应用于整个部署的设置 --- 例如,您对云供应商的选择 ------而后者(definitions.json)定义了您想要的节点类型以及每种节点的数量,从而决定了您是部署一个分片集群、一个独立的 IRIS 实例,还是其他配置。 ICM 容器中的 /Samples 目录为所有四个云供应商提供示例配置文件。例如,AWS 的示例配置文件位于 /Samples/AWS 目录中。要自定义这些文件,请选择您要使用的供应商,然后按照下面的表格描述,为该供应商修改示例默认值和定义文件。要做到这一点,您可以使用 vi 等编辑器直接在容器内编辑它们,或者在本地命令行上使用 docker cp 命令将它们从容器复制到本地文件系统,然后在您编辑它们后再次返回,例如: docker cp icm:/Samples/AWS/defaults.json . docker cp icm:/Samples/AWS/definitions.json . ... docker cp defaults.json icm:/Samples/AWS docker cp definitions.json icm:/Samples/AWS 重要提示: 字段名和值都是区分大小写的;例如,要选择 AWS 作为云供应商,您必须在默认文件中包含 "Provider":"AWS" ,而不是 "provider": "AWS"、"Provider": "aws"等等。 自定义 defaults.json 下表详细说明了 /Samples 文件中提供的默认文件所需的最小自定义,以及对可选更改的建议。这里没有涉及到的任何设置都可以在示例文件中保持原样。 试一试! 使用 ICM 在云中部署 InterSystems IRIS 每个设置都连接到 ICM Guide(《ICM 指南》)的 "ICM Reference(《ICM 参考资料》)"一章中的 ICM Configuration Parameters(ICM 配置参数)部分中的相关表格,在那里您可以找到更详细的说明;在那里,搜索您想要的参数。最右边的列显示了在示例默认文件中出现的每个参数或参数集。 要查看所有供应商通用的所有设置,请参见该部分的 General Parameters(常规参数) ;要查看特定于特定供应商的所有设置,请参见 Provider-Specific Parameters(特定于供应商的参数)。 下图显示了 AWS、GCP、Azure 和 Tencent 的示例 defaults.json 文件的内容: 设置 说明 示例 defaults.json 文件中的条目 Provider 识别云基础设施供应商;保留示例 defaults.json 中的值。 “Provider”:[”AWS”|"GCP"|"Azure"|"Tencent"], Label 配置节点的命名方案中的字段, Label-Role-Tag-NNNN (请参见下面的 Role);更新以识别部署的所有者和目的, 例如,使用您的公司名称和“TEST”来创建节点名称,如 Acme-DATA-TEST-0001。 "Label": "Sample", Tag (请参见上面的 Label ) "Tag": "TEST", DataVolumeSize 为每个节点配置的持久化数据卷的大小,这可以在 definitions.json 文件中的各个节点定义中被覆盖;接受示例 defaults.json 中的值,除非您在 Tencent 上配置,在这种情况下,将其改为 60。 "DataVolumeSize": "10", SSHUser 在配置节点上具有 sudo 访问权限的非根帐户,用于 ICM 访问; 您可以保留示例 defaults.json 中的默认值, 但如果您在 AWS 或 Tencent 上更改了机器映像(如下),则可能需要更新此条目。 "SSHUser": "ubuntu",(AWS & Tencent) "SSHUser": "sample",(GCP & Azure) SSHPublicKey SSH 公钥的位置。如果您使用了 Generate Security Keys(生成安全密钥)中讨论的密钥生成脚本, 则密钥位于示例文件指定的目录中,因此无需更改;如果您提供了自己的密钥, 请使用 docker cp 将它们从本地文件系统复制到这些位置。 "SSHPublicKey": "/Samples/ssh/insecure-ssh2.pub", SSHPrivateKey SSH 私钥的位置;请参见上面的 SSHPublicKey。 "SSHPrivateKey": "/Samples/ssh/insecure", TLSKeyDir TILS 文件的位置;请参见上面的 SSHPublicKey 。 "TLSKeyDir": "/Samples/tls/", 设置 说明 示例 defaults.json 文件中的条目 DockerVersion 要安装在已配置节点上的 Docker 版本;保持默认值。 "DockerVersion": "5:19.03.8~3-0~ubuntu-bionic", DockerImage 要部署在已配置节点上的映像;更新以反映您在 Identify Docker Repository and Credentials(识别 Docker 存储库和凭证)中识别的存储库和映像信息。 "DockerImage": "containers.intersystems.con/intersystems/iris:2021.1.0.205.0", DockerUser- name Docker- Password 如果在私有存储库中,则下载 DockerImage 指定的映像所需的凭证;请更新以反映您在 Identify Docker Repository and Credentials(识别 Docker 存储库和凭证)中识别的存储库信息和凭证。 "DockerUsername": "xxxxxxxxxxxx", "DockerPassword": "xxxxxxxxxxxx", LicenseDir InterSystems IRIS 许可证的暂存目录;将您的特定于容器的 InterSystems IRIS 分片(sharding)许可证放在这个目录中。 “LicenseDir”: “/Samples/Licenses”, Region, Location (Azure) 供应商用于配置基础设施的计算资源的地理区域;接受示例 defaults.json 中的默认值,或者从供应商那里选择 region 和 zone 的另一个组合(如下)。 "Region": "us-west-1",(AWS) "Region": "us-east1",(GCP) "Location": "Central US",(Azure) "Region": "na-siliconvalley", (Tencent) Zone 所选区域内的可用区(如上);接受示例 defaults.json 中的默认值,或者从供应商那里选择 region 和 zone 的另一个组合。 "Zone": "us-west-1c",(AWS) "Zone": "us-east1-b",(GCP) "Zone": "1",(Azure) "Zone": "na-siliconvalley-1", (Tencent) Machine image (特定于供应商) 配置节点的平台和 OS 模板;接受示例 defaults.json 中的默认值,或者从供应商那里选择 machine image 和 instance type 的不同组合(如下)。 "AMI": "ami-c509eda6",(AWS) "Image": "ubuntu-os-cloud/ubuntu-1804-bionic-v20180617", (GCP) "PublisherName": "Canonical",(Azure) "Offer": "UbuntuServer",(Azure) "Sku": "18.04-LTS",(Azure) "Version": "18.04.201804262",(Azure) "ImageID": "img-pi0ii46r",(Tencent) 设置 说明 示例 defaults.json 文件中的条目 Instance type (特定于供应商) 配置节点的计算资源模板;接受示例 defaults.json 中的值,或者从供应商那里选择 machine image 和 instance type 的不同组合(如上)。 "InstanceType": "m4.large",(AWS) "MachineType": "n1-standard-1",(GCP) "Size": "Standard_DS2_v2",(Azure) "InstanceType": "S2.MEDIUM4",(Tencent) Credentials and account settings (特定于供应商) ICM 向供应商进行身份验证所需的文件或 ID,因供应商而异;更新以指定您的帐户所需的文件位置或 ID(有关说明,请点击供应商链接)。 “Credentials”:”/Samples/AWS/credentials”, (AWS) "Credentials": "/Samples/GCP/sample.credentials",(GCP) "Project": "dp-icmdevelopment",(GCP) "SubscriptionId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",(Azure) "ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",(Azure) "ClientSecret": "xxxxxxxxxxxx/xxxxxxxxxxxxxxx/xxxxxxxxxxxxxx=", (Azure) "TenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",(Azure) "SecretID": "xxxxxxxxxxxx",(Tencent) "SecretKey": "xxxxxxxxxxxx",(Tencent) ISCPassword 已部署的 InterSystems IRIS 映像中预定义帐户的密码;要在部署阶段以交互方式提供带有屏蔽输入的密码(为安全起见建议这样做),请删除此字段,否则更改为您的首选密码。 "ISCPassword": "", Mirror 确定部署在 DATA、DM 和 DS 节点上的 InterSystems IRIS 实例是否被配置为镜像;保留默认值。 “Mirror”: “false” 设置 说明 示例 defaults.json 文件中的条目 UserCPF 指定用于覆盖已部署实例的初始 CPF 设置的配置合并文件。(如果您不熟悉配置合并功能或 CPF,请删除此条目;有关配置合并的信息,请参见 ICM Guide(《ICM**指南》)的 "ICM Reference(《ICM 参考资料》)"一章中的 Deploying with Customized InterSystems IRIS Configurations(使用自定义的 InterSystems IRIS 配置进行部署)。) "UserCPF": "/Samples/cpf/iris.cpf" 下图显示了 AWS、GCP、Azure 和 Tencent 的示例 defaults.json 文件的内容: 图 2: 云供应商的 ICM 默认文件示例 自定义 definitions.json 在 /Samples 目录中的示例 definitions.json 文件(对所有供应商来说都是一样的),定义了一个有两个数据节点和两个计算节点的分片集群,如下所示: 试一试! 使用 ICM 在云中部署 InterSystems IRIS [ { "Role": "DATA", "Count": "2", "LicenseKey": "ubuntu-sharding-iris.key" }, { "Role": "COMPUTE", "Count": "2", "StartCount": "3", "LicenseKey": "ubuntu-sharding-iris.key" } ] Role 字段标识正在配置的节点类型,在这种情况下是 DATA 和 COMPUTE。Count 字段表示要配置多少个该类型的节点;对于 COMPUTE 节点,StartCount 从 0003 开始编号。LicenseKey 字段表示 InterSystems IRIS 许可证文件的名称,该文件位于默认文件中由 LicenseDir 字段指定的目录中。 在这个练习中,删除 COMPUTE 定义,只保留 DATA 定义,如下所示: [ { "Role": "DATA", "Count": "2", "LicenseKey": "ubuntu-sharding-iris.key" } ] 部署分片集群时,所有节点必须有分片(sharding)许可证。使用 docker cp 将分片(sharding)许可证复制到 LicenseDir 指定的容器内的位置,如 /Samples/Licenses,并更新 DATA 节点定义中的 LicenseKey 设置,以指定要使用的许可证密钥。 这些是配置基本分片集群所需的唯一 definitions.json 更改。要配置一个独立的 InterSystems IRIS 实例,请使用此定义: [ { "Role": "DM", "Count": "1", "LicenseKey": "standard-iris.key"" }, ] 配置基础设施 默认情况下,ICM 从当前目录中的配置文件中获取输入,因此您需要做的就是将配置基础设施更改为您选择的供应商的 /Samples 目录,例如 /Samples/AWS,并发出这个命令: icm provision icm provision 命令在您选择的平台上分配和配置主机节点。在配置操作期间,ICM 在状态子目录中创建或更新状态和日志文件,完成后创建 instances.json 文件,作为后续部署和管理命令的输入。 因为 ICM 同时运行多个任务,所以这些步骤(Terraform 计划和应用、复制文件、挂载卷等等)可能不会以相同的顺序在节点上启动和完成。完成后,ICM 提供了一个已经配置好的主机节点的摘要,并输出一个命令行,可以在以后用来删除基础设施,如下所示: Machine IP Address DNS Name Region Zone Acme-DATA-TEST-0001 00.53.183.209 ec2-00-53-183-209.us-west-1.compute.amazonaws.com us-west-1 c Acme-DATA-TEST-0002 00.53.183.185 ec2-00-53-183-185.us-west-1.compute.amazonaws.com us-west-1 c To destroy: icm unprovision [-cleanUp] [-force] 试一试! 使用 ICM 在云中部署 InterSystems IRIS 重要提示: 复制输出中提供的 icm unprovision 命令行,并保存此信息,这样您就可以在取消配置时轻松地复制它。这个输出也出现在 icm.log 文件中。 管理和取消配置您的基础设施所需的文件和目录都在 ICM 容器中,因此当您删除它时就会丢失。因此,在完成基础设施并成功地取消配置之前,您不能删除 ICM 容器。(在生产中,最好的做法是在启动 ICM 时通过挂载外部卷使用容器外的位置来存储这些数据(请参见 Docker 文档中的 Manage data in Docker(在 Docker 中管理数据))。 在配置和部署阶段发生的错误的详细信息写入状态目录子目录中的 terraform.err 文件;当发生错误时,ICM 会将您指向适当的文件,该文件可以帮助您识别问题的原因。 如果 icm provision 由于超时和供应商方面的内部错误,或者配置文件中的错误而没有成功完成,您可以根据需要再次发出该命令,直到 ICM 为所有指定的节点完成所有需要的任务而没有错误。更多信息,请参见 ICM Guide(《ICM 指南》)的 "Using ICM(《使用 ICM》) "一章中的 "Reprovisioning the Infrastructure(重新配置基础设施)"。 有关 icm provision 的更多信息,请参见 ICM Guide(《ICM 指南》)中的 The icm provision Command (icm 配置命令)。 部署 InterSystems IRIS 除了在提供的主机节点上下载映像(Image)并将它们作为容器运行外,ICM 还执行特定于 InterSystems IRIS 的配置和其他任务。要在您已配置的节点上部署 InterSystems IRIS,请留在 /Samples 目录,您在其中自定义了配置文件并配置了基础设施,然后发出以下命令: icm run 默认情况下,icm run 下载并运行配置文件中 DockerImage 字段指定的映像,在这种情况下,是来自 InterSystems 存储库的 InterSystems IRIS 映像;每个容器都被命名为 iris。在实践中,在定义文件中的节点定义中使用不同的 DockerImage 字段(而不是对默认文件中的所有节点使用一次,如此处),可以让您在不同的节点类型上运行不同的映像,除此之外,您还可以使用某些选项多次执行 icm run 命令,以便在每个配置的节点上或只在指定节点上部署具有唯一名称的多个容器。 InterSystems IRIS 在每个节点上启动后,ICM 会执行实例所需的任何配置 --- 例如,重置密码、配置分片(sharding)等。因为 ICM 同时运行多个任务,因此部署过程中的步骤可能不会以相同的顺序在节点上启动和完成。 完成后,ICM 输出一个链接到相应的 InterSystems IRIS 实例的管理门户(Management Portal): $ icm run -definitions definitions_cluster.json Executing command 'docker login' on ACME-DATA-TEST-0001... ...output in /Samples/AWS/state/ACME-DATA-TEST/ACME-DATA-TEST-0001/docker.out ... Pulling image intersystems/iris:2021.1.0.205.0 on ACME-DATA-TEST-0001... ...pulled ACME-DATA-TEST-0001 image intersystems/iris:2021.1.0.205.0 ... Creating container iris on ACME-DATA-TEST-0002... ... Management Portal available at: http://ec2-00-53-183-209.us-west-1.compute.amazonaws.com:52773/csp/sys/UtilHome.csp 在这种情况下,所提供的链接用于数据节点 1,或者用于独立的实例(如果这是您选择的配置)。在浏览器中打开链接,并使用管理门户(Management Portal)探索 InterSystems IRIS。 与 icm provision 一样,如果 icm run 由于超时和供应商方面的内部错误而没有成功完成,您可以再次发出命令;在大多数情况下,反复尝试,部署就会成功。但是,如果错误仍然存在,并且需要手动干预 --- 例如,如果它是由某个配置文件中的错误引起的 --- 在修复问题后,您可能需要删除受影响的一个或多个节点上的持久化 %SYS 数据, 试一试! 使用 ICM 在云中部署 InterSystems IRIS 在重新发布 icm run 命令之前,如 ICM Guide(《ICM 指南》)的 "Using ICM(《使用 ICM》)"一章中的 Redeploying Services(重新部署服务)所述。在出现错误的情况下,请参见 ICM 指向您访问的日志文件,以获得有助于您确定问题原因的信息。 有关 icm run 命令及其选项的更多信息,请参见 ICM Guide(《ICM 指南》)中的 The icm run Command(icm run 命令)。 尝试 ICM 管理命令 ICM 提供了一系列的命令用于 管理配置的基础设施。 管理部署的容器。 与运行在部署的容器中的服务交互,包括 InterSystems IRIS。 这些命令在 ICM Guide(《ICM 指南》)的 Infrastructure Management Commands(基础设施管理命令)、Container Management Commands(容器管理命令)和 Service Management Commands(服务管理命令)小节中有详细的讨论,并在 ICM Commands and Options(ICM 命令和选项)中全面列出。这里提供了一些示例,供您在部署时试用。特别要注意的是,有三个命令(icm ssh、icm exec 和 icm session)可以让您在多个层次上与您部署的节点交互 --- 与节点本身、与部署在它上面的容器以及与容器内运行的 InterSystems IRIS 实例,如下所示: 图 3: 交互式 ICM 命令 在您新部署的 InterSystems IRIS 配置上试试这些命令吧! 列出配置的主机节点: 试一试! 使用 ICM 在云中部署 InterSystems IRIS icm inventory 在每个主机节点上运行 shell 命令: icm ssh -command "df -k" 当一个命令在多个节点上执行时,输出被写入文件,并提供一个输出文件的列表。例如,在这种情况下,如果您部署了分片集群,三个节点各有一个输出文件。 在特定的主机节点上打开交互式 shell: icm ssh -role DATA -interactive 要做到交互,命令必须使用 -interactive 选项并指定单个节点。例如,虽然 icm ssh 可以在多个节点上执行命令,如前面的示例所示,但它只能使用 -interactive 选项在一个节点上打开交互式 shell,如本例所示。代替 -role DM 选项将命令限制在该类型的节点上(在您的部署中只有一个节点),您也可以使用-machine 选项来指定一个特定节点的名称,例如: icm ssh -machine Acme-DATA-TEST-0001 -interactive 显示部署的 InterSystems IRIS 容器的状态: icm ps 当在节点上部署了多个容器时,这个命令会将它们全部列出。 将本地文件复制到 InterSystems IRIS 容器或其中一个容器: icm cp -localPath /Samples/ssh/ssh_notes -remotePath /home/sshuser icm cp -localPath /Samples/ssh/ssh_notes -remotePath /home/sshuser -role DM 在每个 InterSystems IRIS 容器中运行 shell 命令: icm exec -command "ls /irissys/" 在特定的 InterSystems IRIS 容器中打开一个交互式 shell(或运行任何 shell 命令): icm exec -command "bash" -role DATA -interactive 为 InterSystems IRIS 实例打开一个终端(Terminal)窗口(对于单个实例,总是交互式的): icm session -machine Acme-DATA-TEST-0001 针对 InterSystems IRIS 的每个实例,或针对特定的实例,运行 SQL 命令: icm sql -command "SELECT Name FROM Security.Users" icm sql -command "SELECT Name FROM Security.Users" -machine Acme-DATA-TEST-0002 取消配置基础设施 因为 AWS 和其他公共云平台实例会不断产生费用,所以在完成这一体验后,立即取消配置基础设施是很重要的。 要做到这一点,把您从 icm provision 的输出中保存的 icm unprovision 命令复制到 ICM 命令行,例如: $ icm unprovision -cleanUp ICM 能做的远不止这些! 如果您没有从配置输出中保存命令,您可以在工作目录的 icm.log 文件中找到它(例如,/Samples/AWS/icm.log)。-cleanUp 选项在取消配置后会删除状态目录;如果没有这个选项,状态目录会被保留下来。 ICM 能做的远不止这些! 尽管您刚刚完成的 ICM 探索是刻意简化的,但它仍包含了现实世界的基础设施配置和服务部署。添加到您刚刚完成的只是在配置文件中扩展您的部署定义并利用大量命令行选项的问题。例如,您可以 通过定义 AM 节点和 DM 节点,部署应用服务器和数据服务器的分布式缓存集群,或通过仅定义 DM 节点部署独立的 InterSystems IRIS 实例。 部署镜像(mirror) DATA 节点或镜像 DM 节点,只需在默认文件中包含"Mirror":"True",并在定义文件中定义偶数个 DATA 节点或两个 DM 节点和一个 AR(仲裁器)节点。 部署一个包含 COMPUTE 节点的分片集群,以分离查询和数据摄取工作量,同时保持并行处理和分布式缓存的优势,提高两者的性能。 在定义文件中添加 web 服务器(WS)和负载均衡器(LB 或自动)。 通过在定义文件中为不同的节点指定不同的 DockerImage 值,并多次执行 icm run 命令,在不同的节点上部署不同的服务。 通过多次执行 icm run ,同时在命令行中使用 -container 和 -image 选项指定不同的容器名称和映像(包括自定义和第三方映像),在一些或所有节点上部署多个服务。 通过使用第三方工具和内部脚本扩展 ICM 的功能,进一步提高自动化程度,减少工作量。 在已有的虚拟和物理集群上部署服务。 在不部署服务的情况下配置基础设施,只需使用 ICM provision 命令停止。 所有这些可能性以及更多的可能性都在 ICM Guide(《ICM 指南》)中有所涉及。 了解有关 ICM 的更多信息 要了解有关 ICM 和在容器中使用 InterSystems IRIS 的更多信息,请参见 InterSystems Cloud Manager Introduction(《InterSystems 云管理器简介》) (视频) The Benefits of InterSystems Cloud Manager(《InterSystems 云管理器的优势》) (视频) Experience InterSystems IRIS in the Cloud(在云中体验 InterSystems IRIS) (在线体验) InterSystems Cloud Manager Guide(《InterSystems 云管理器指南》) First Look:InterSystems Products in Containers(《技术概要:容器中的 InterSystems 产品》) Running InterSystems Products in Containers(《在容器中运行 InterSystems 产品》)
公告
Jeff Liu · 六月 15, 2021

InterSystems IRIS、IRIS for Health和HealthShare Health Connect 2021.1现已正式发布。

InterSystems非常高兴地宣布,InterSystems IRIS数据平台、InterSystems IRIS for Health和HealthShare Health Connect的2021.1版本现已向我们的客户和合作伙伴全面开放。 这个版本的为开发者提供了更大的自由度,使他们可以用自己选择的编程语言在服务器端和客户端建立快速和强大的应用程序。这个版本还使用户能够通过新的和更快的分析能力更有效地消费大量的信息。 我们期望许多客户和合作伙伴将他们的Caché和Ensemble部署升级到InterSystems的IRIS版本,并尽一切努力使之成为一个平稳和值得的过渡。大多数应用程序在IRIS上运行后会立即看到性能上的好处,甚至在他们探索IRIS带来的许多强大功能之前。 我们诚挚地邀请您参加我们的网络研讨会,介绍新版本的亮点,时间是美国东部时间6月17日上午11点。网络研讨会将被记录下来,并在会后进行 重播。 Release Highlights 在InterSystems IRIS 2021.1,客户可以部署 InterSystems IRIS Adaptive Analytics,Adaptive Analytics是一个附加产品,它扩展了InterSystems IRIS,为商业用户提供了卓越的易用性和自助分析能力,使他们能够对大量数据进行可视化、分析和查询,以获得他们所需的信息,从而做出及时和准确的商业决策,而无需是数据设计或数据管理方面的专家。Adaptive Analytics通过在后台自主构建和维护临时数据结构,透明地加速了针对InterSystems IRIS运行的分析查询工作负载。 该版本中的其他新亮点功能包括。 改进了我们的外部语言服务器的可管理性,现在也包括R和Python。这种网关技术使您能够以您选择的语言稳健和可扩展地利用服务器端代码。 InterSystems Kubernetes Operator(IKO)为您的环境提供声明式配置和自动化,现在还支持部署InterSystems System Alerting & Monitoring(SAM)。 InterSystems API Manager v2.3,包括改进的用户体验、Kafka支持和混合模式。 IntegratedML的主流可用性,使SQL开发人员能够直接在一个纯粹的基于SQL的环境中构建和部署机器学习模型。 支持分片表上的流字段,使您在InterSystems IRIS水平可扩展架构上获得完全的SQL模式灵活性。 一个iris-lockeddown容器镜像,实现了许多安全最佳实践,如禁用管理门户的网络访问和适当的操作系统级权限。 支持用于Oauth 2.0的代码交换的证明密钥(PKCE)。 InterSystems IRIS for Health 2021.1包括InterSystems IRIS的所有改进功能。此外,该版本通过用于解析和评估FHIRPath表达式的API,进一步扩展了该平台对FHIR®标准的全面支持。这是对2020.1以来发布的重要的FHIR相关功能的补充,包括对FHIR Profiles、FHIR R4 Transforms和FHIR客户端API的支持。 该版本还包括HealthShare Health Connect,这是我们基于IRIS for Health的集成引擎,提供大批量的交易支持、流程管理和监控,以支持关键任务应用。有关其功能集与InterSystems IRIS for Health的比较的详细概述,请参见这里。 关于所有这些功能的更多细节可以在产品文档中找到,该文档最近通过方便的目录侧边栏变得更加容易浏览。 InterSystems IRIS 2021.1 文档和发布说明 InterSystems IRIS for Health 2021.1 文档和发布说明 HealthShare Health Connect 2021.1 文档和发布说明 如果您从早期版本升级并使用TLS 1.3,请参见这些升级注意事项。 如何获得软件 InterSystems IRIS 2021.1是一个扩展维护(EM)版本,并附带有适用于所有支持平台的经典安装包,以及OCI(Open Container Initiative)又称Docker格式的容器镜像。 每个产品的完整安装包都可以从WRC的产品下载网站获得。使用 "自定义 "安装选项使用户能够选择他们需要的选项,如InterSystems Studio和IntegratedML,以合理地缩小其安装规模。 InterSystems IRIS和IRIS for Health的企业版容器映像以及所有相应的组件可通过InterSystems容器注册中心使用以下命令获得: docker pull containers.intersystems.com/intersystems/iris:2021.1.0.215.0 docker pull containers.intersystems.com/intersystems/iris-ml:2021.1.0.215.0 docker pull containers.intersystems.com/intersystems/irishealth:2021.1.0.215.0 docker pull containers.intersystems.com/intersystems/irishealth-ml:2021.1.0.215.0 有关可用图像的完整列表,请参考ICR文件。 社区版的容器图像也可以使用以下命令从Docker商店中提取。 docker pull store/intersystems/iris-community:2021.1.0.215.0 docker pull store/intersystems/iris-ml-community:2021.1.0.215.0 docker pull store/intersystems/irishealth-community:2021.1.0.215.0 docker pull store/intersystems/irishealth-ml-community:2021.1.0.215.0 另外,所有容器镜像的tarball版本可以通过WRC的产品下载网站获得。 InterSystems IRIS Studio 2021.1是一个独立的IDE,用于Microsoft Windows,可以通过WRC的产品下载网站下载。它适用于InterSystems IRIS和IRIS for Health 2021.1及以下版本。InterSystems还支持VSCode ObjectScript插件,用于用Visual Studio Code为InterSystems IRIS开发应用程序,该插件可用于Microsoft Windows、Linux和MacOS。 其他独立的InterSystems IRIS 2021.1组件,如ODBC驱动程序和Web网关,可从同一页面获得。 分享您的经验 我们每年只能宣布一个EM版本,所以我们很高兴看到这个版本现在达到了GA的里程碑,并渴望听到你对新软件的体验。请不要犹豫,通过您的客户团队或在开发人员社区上与您联系,对该技术或您使用该技术解决的使用案例提出任何意见。 对于选定的新功能和产品,我们已经建立了早期访问计划,允许我们的用户在软件发布之前进行评估。通过这些有针对性的举措,我们可以向目标受众学习,并确保新产品在发布时能够满足他们的需求。如果您有兴趣参与其中任何一项,请通过您的客户团队或观看开发人员社区进行联系。
文章
Michael Lei · 九月 27, 2024

使用 GitLab 持续交付 InterSystems 解决方案 – 第 7 部分:使用容器的

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题: Git 101 Git 流程(开发流程) GitLab 安装 GitLab 工作流 持续交付 GitLab 安装和配置 GitLab CI/CD 为何使用容器 容器基础架构 使用容器的 CD 在第一篇文章中,我们介绍了 Git 基础知识、深度理解 Git 概念对现代软件开发至关重要的原因,以及如何使用 Git 开发软件。 在第二篇文章中,我们介绍了 GitLab 工作流 – 一个完整的软件生命周期流程,并介绍了持续交付。 在第三篇文章中,我们介绍了 GitLab 安装和配置以及将环境连接到 GitLab 在第四篇文章中,我们编写了 CD 配置。 在第五篇文章中,我们讨论了容器以及使用容器的方式(和原因)。 在第六篇文章中,我们将探讨运行包含容器的持续交付管道所需的主要组件以及这些组件如何协同运行。 在这篇文章中,我们将构建上一篇文章中探讨的持续交付配置。 工作流 在持续交付配置中,我们会: 将代码推送到 GitLab 仓库 构建 docker 镜像 进行测试 将镜像发布到 docker 注册表 将旧容器换为注册表中的新版本 也可以用示意图形式表示此流程: 我们开始吧。 构建 首先,我们需要构建镜像。 我们的代码通常存储在仓库中,CD 配置位于 gitlab-ci.yml 中,但为了提高安全性,我们会在构建服务器上存储几个服务器特定的文件。 GitLab.xml 包含 CD 挂钩代码。 该代码是在上一篇文章中开发的,并在 GitHub 上提供。 这是一个小型库,用于加载代码、运行各种挂钩以及测试代码。 作为更好的替代方案,您可以使用 git 子模块将此项目或类似项目包含到您的仓库中。 最好选择子模块,因为子模块更容易保持最新状态。 另一个替代方案是在 GitLab 上为版本添加标签,并使用 ADD 命令加载这些版本。 iris.key 许可证密钥。 或者,它可以在容器构建过程中下载,而不是存储在服务器上。 将密钥存储在仓库中非常不安全。 pwd.txt 包含默认密码的文件。 将这类文件存储在仓库中也非常不安全。 此外,如果您在单独的服务器上托管生产环境,它可能有不同的默认密码。 load_ci.script 初始脚本,它执行以下任务: 实现操作系统身份验证 加载 GitLab.xml 初始化 GitLab 实用工具设置 加载代码 set sc = ##Class(Security.System).Get("SYSTEM",.Properties) write:('sc) $System.Status.GetErrorText(sc) set AutheEnabled = Properties("AutheEnabled") set AutheEnabled = $zb(+AutheEnabled,16,7) set Properties("AutheEnabled") = AutheEnabled set sc = ##Class(Security.System).Modify("SYSTEM",.Properties) write:('sc) $System.Status.GetErrorText(sc) zn "USER" do ##class(%SYSTEM.OBJ).Load(##class(%File).ManagerDirectory() _ "GitLab.xml","cdk") do ##class(isc.git.Settings).setSetting("hooks", "MyApp/Hooks/") do ##class(isc.git.Settings).setSetting("tests", "MyApp/Tests/") do ##class(isc.git.GitLab).load() halt 请注意,第一行有意留空。 由于某些设置可以是服务器特定的,脚本不会存储在仓库中,而是单独存储。 如果此初始挂钩始终相同,将其存储在仓库中即可。 gitlab-ci.yml 现在,继续持续交付配置: 构建镜像: stage: build tags: - test script: - cp -r /InterSystems/mount ci - cd ci - echo 'SuperUser' | cat - pwd.txt load_ci.script > temp.txt - mv temp.txt load_ci.script - cd .. - docker build --build-arg CI_PROJECT_DIR=$CI_PROJECT_DIR -t docker.domain.com/test/docker:$CI_COMMIT_REF_NAME . 这里会执行哪些操作? 首先,由于 docker build 只能访问基础构建目录的子目录(本例中是仓库根目录),我们需要将“秘密”目录(包含 GitLab.xml、iris.key、pwd.txt 和 load_ci.script 的目录)复制到克隆的仓库中。 接下来,首次终端访问需要用户名/密码,因此我们会将这些信息添加到 load_ci.script 中(这也是 load_ci.script 开头一行留空的原因)。 最后,我们会构建 docker 镜像并适当地为其添加标签:docker.domain.com/test/docker:$CI_COMMIT_REF_NAME 其中,$CI_COMMIT_REF_NAME 是当前分支的名称。 请注意,镜像标签的第一部分应与 GitLab 中的项目名称相同,这样才能在 GitLab 的“注册表”标签页中看到它(“注册表”标签页中提供了关于添加标签的说明)。 Dockerfile 构建 docker 镜像是通过 Dockerfile 完成的,具体如下: FROM docker.intersystems.com/intersystems/iris:2018.1.1.611.0 ENV SRC_DIR=/tmp/src ENV CI_DIR=$SRC_DIR/ci ENV CI_PROJECT_DIR=$SRC_DIR COPY ./ $SRC_DIR RUN cp $CI_DIR/iris.key $ISC_PACKAGE_INSTALLDIR/mgr/ \ && cp $CI_DIR/GitLab.xml $ISC_PACKAGE_INSTALLDIR/mgr/ \ && $ISC_PACKAGE_INSTALLDIR/dev/Cloud/ICM/changePassword.sh $CI_DIR/pwd.txt \ && iris start $ISC_PACKAGE_INSTANCENAME \ && irissession $ISC_PACKAGE_INSTANCENAME -U%SYS < $CI_DIR/load_ci.script \ && iris stop $ISC_PACKAGE_INSTANCENAME quietly 我们从基本的 iris 容器开始。 首先,我们将仓库(和“秘密”目录)复制到容器中。 接下来,我们将许可证密钥和 GitLab.xml 复制到 mgr 目录中。 然后,我们将密码更改为 pwd.txt 中的值。 请注意,此操作会删除 pwd.txt。 之后,实例启动并执行 load_ci.script。 最后,iris 实例停止。 以下是作业日志(部分日志,跳过了加载/编译日志): Running with gitlab-runner 10.6.0 (a3543a27) on docker 7b21e0c4 Using Shell executor... Running on docker... Fetching changes... Removing ci/ Removing temp.txt HEAD is now at 5ef9904 Build load_ci.script From http://gitlab.eduard.win/test/docker 5ef9904..9753a8d master -> origin/master Checking out 9753a8db as master... Skipping Git submodules setup $ cp -r /InterSystems/mount ci $ cd ci $ echo 'SuperUser' | cat - pwd.txt load_ci.script > temp.txt $ mv temp.txt load_ci.script $ cd .. $ docker build --build-arg CI_PROJECT_DIR=$CI_PROJECT_DIR -t docker.eduard.win/test/docker:$CI_COMMIT_REF_NAME . Sending build context to Docker daemon 401.4kB Step 1/6 : FROM docker.intersystems.com/intersystems/iris:2018.1.1.611.0 ---> cd2e53e7f850 Step 2/6 : ENV SRC_DIR=/tmp/src ---> Using cache ---> 68ba1cb00aff Step 3/6 : ENV CI_DIR=$SRC_DIR/ci ---> Using cache ---> 6784c34a9ee6 Step 4/6 : ENV CI_PROJECT_DIR=$SRC_DIR ---> Using cache ---> 3757fa88a28a Step 5/6 : COPY ./ $SRC_DIR ---> 5515e13741b0 Step 6/6 : RUN cp $CI_DIR/iris.key $ISC_PACKAGE_INSTALLDIR/mgr/ && cp $CI_DIR/GitLab.xml $ISC_PACKAGE_INSTALLDIR/mgr/ && $ISC_PACKAGE_INSTALLDIR/dev/Cloud/ICM/changePassword.sh $CI_DIR/pwd.txt && iris start $ISC_PACKAGE_INSTANCENAME && irissession $ISC_PACKAGE_INSTANCENAME -U%SYS < $CI_DIR/load_ci.script && iris stop $ISC_PACKAGE_INSTANCENAME quietly ---> Running in 86526183cf7c . Waited 1 seconds for InterSystems IRIS to start This copy of InterSystems IRIS has been licensed for use exclusively by: ISC Internal Container Sharding Copyright (c) 1986-2018 by InterSystems Corporation Any other use is a violation of your license agreement %SYS> 1 %SYS> Using 'iris.cpf' configuration file This copy of InterSystems IRIS has been licensed for use exclusively by: ISC Internal Container Sharding Copyright (c) 1986-2018 by InterSystems Corporation Any other use is a violation of your license agreement 1 alert(s) during startup. See messages.log for details. Starting IRIS Node: 39702b122ab6, Instance: IRIS Username: Password: Load started on 04/06/2018 17:38:21 Loading file /usr/irissys/mgr/GitLab.xml as xml Load finished successfully. USER> USER> [2018-04-06 17:38:22.017] Running init hooks: before [2018-04-06 17:38:22.017] Importing hooks dir /tmp/src/MyApp/Hooks/ [2018-04-06 17:38:22.374] Executing hook class: MyApp.Hooks.Global [2018-04-06 17:38:22.375] Executing hook class: MyApp.Hooks.Local [2018-04-06 17:38:22.375] Importing dir /tmp/src/ Loading file /tmp/src/MyApp/Tests/TestSuite.cls as udl Compilation started on 04/06/2018 17:38:22 with qualifiers 'c' Compilation finished successfully in 0.194s. Load finished successfully. [2018-04-06 17:38:22.876] Running init hooks: after [2018-04-06 17:38:22.878] Executing hook class: MyApp.Hooks.Local [2018-04-06 17:38:22.921] Executing hook class: MyApp.Hooks.Global Removing intermediate container 39702b122ab6 ---> dea6b2123165 [Warning] One or more build-args [CI_PROJECT_DIR] were not consumed Successfully built dea6b2123165 Successfully tagged docker.domain.com/test/docker:master Job succeeded 请注意,我使用的是 GitLab Shell 执行器,而不是 Docker 执行器。 当您需要从镜像内部提取某些内容时,将使用 Docker 执行器,例如在 Java 容器中构建 Android 应用程序并且只需要一个 apk 时。 在我们的示例中,我们需要整个容器,因此需要使用 Shell 执行器。 因此,我们通过 GitLab Shell 执行器运行 Docker 命令。 运行 我们构建了镜像,接下来要运行镜像。如果是功能分支,我们销毁旧容器并启动新容器即可。 如果是环境,我们可以运行临时容器,在测试成功的情况下,可以替换环境容器(此内容留给读者作为练习)。 脚本如下。 destroy old: stage: destroy tags: - test script: - docker stop iris-$CI_COMMIT_REF_NAME || true - docker rm -f iris-$CI_COMMIT_REF_NAME || true 此脚本会销毁当前运行的容器,并且始终都会成功(默认情况下,如果 docker 尝试停止/移除不存在的容器,则会失败)。 接下来,我们启动新镜像并将其注册为环境。 Nginx 容器 使用 VIRTUAL_HOST 环境变量和 expose 指令(用于了解要代理的端口)自动代理请求。 run image: stage: run environment: name: $CI_COMMIT_REF_NAME url: http://$CI_COMMIT_REF_SLUG. docker.domain.com/index.html tags: - test script: - docker run -d --expose 52773 --env VIRTUAL_HOST=$CI_COMMIT_REF_SLUG.docker.eduard.win --name iris-$CI_COMMIT_REF_NAME docker.domain.com/test/docker:$CI_COMMIT_REF_NAME --log $ISC_PACKAGE_INSTALLDIR/mgr/messages.log 测试 我们来运行一些测试。 test image: stage: test tags: - test script: - docker exec iris-$CI_COMMIT_REF_NAME irissession iris -U USER "##class(isc.git.GitLab).test()" 发布 最后,我们将镜像发布到注册表中 publish image: stage: publish tags: - test script: - docker login docker.domain.com -u dev -p 123 - docker push docker.domain.com/test/docker:$CI_COMMIT_REF_NAME 可以使用 GitLab 秘密变量传递用户名/密码。 现在,我们可以在 GitLab 中看到该镜像: 其他开发者可以从注册表中拉取该镜像。 “环境”标签页上提供了我们所有的环境,可以轻松进行浏览: 结论 在这一系列文章中,我介绍了持续交付的常规方式。 这是一个涉及面非常广的话题,您应将这一系列文章视为方法集合,而不是确定性内容。 如果您想自动构建、测试和交付应用程序,可以选择持续交付(一般情况)和 GitLab(特殊情况)。 利用持续交付和容器,您可以根据需要自定义工作流。 链接 文章的代码 测试项目 完整的 CD 配置 后续内容 就是这些! 希望我已介绍了持续交付和容器的基础知识。 但还有一些主题我没有介绍(可能后面会介绍),特别是关于容器的内容: 数据可以在容器外持久保持,以下是相关文档 kubernetes 等编排平台 InterSystems Cloud Manager 环境管理 – 创建临时环境以进行测试,在功能分支合并后移除旧环境 Docker compose 可以实现多容器部署 缩减 docker 镜像大小并缩短构建时间 ...
文章
Michael Lei · 九月 26, 2024

使用 GitLab 持续交付 InterSystems 解决方案 – 第 1 部分:Git

大家都搭建了测试环境。 有些人很幸运,可以在完全独立的环境中运行生产。 -- 佚名.在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:Git 101Git 流程(开发流程)GitLab 安装GitLab 工作流GitLab CI/CD包含容器的 CI/CD第一部分将介绍现代软件开发的基础 – Git 版本控制系统和各种 Git 流程。Git 101虽然我们将主要探讨软件开发的概况以及 GitLab 如何帮助我们实现这一目标,但 Git,或者说 Git 设计中的几个基础的高级概念对于更好地理解后面的概念非常重要。也就是说,Git 是基于这些概念的版本控制系统(还有更多概念,但这几个概念最为重要):非线性开发意味着,虽然我们的软件是从版本 1 到版本 2、再到版本 3 相继发布的,但实际上从版本 1 到版本 2 的升级是并行完成的 – 多名开发者会同时开发许多功能/错误修复。分布式开发意味着开发者独立于一个中央服务器或其他开发者,可以轻松地在自己的环境中进行开发。合并 – 基于前面提到的两个概念,我们会发现很多不同的版本同时存在,我们需要将它们统一成一个完整的状态。我的意思不是说 Git 发明了这些概念。 Git 并没有发明这些概念, 而是使这些概念变得简单、流行,并加入了多个相关创新概念,也就是说,架构即代码/容器化改变了软件开发。核心 git 术语仓库是存储数据以及关于数据的元信息的项目。“从物理层面来讲”,仓库是磁盘上的目录。仓库用于存储文件和目录。仓库还会存储每个文件的完整变更历史。仓库可以:存储在您自己的计算机本地远程存储在远程服务器上但从 git 的角度来看,本地仓库与远程仓库之间没有特殊的区别。提交是仓库的固定状态。 很显然,如果每次提交都存储仓库的完整状态,我们的仓库很快就会变得非常大。 因此,提交会存储差异,也就是当前提交与其父提交之间的差异。不同的提交可以具有不同数量的父提交:0 个 – 仓库中的第一个提交没有父提交。1 个 – 一切如常 - 我们的提交改变了仓库中的某些内容,就像在父提交期间一样2 个 – 当我们有两个不同的仓库状态时,我们可以将它们合并成一个新状态。 该状态和该提交就会有 2 个父提交。>2 个 – 当我们将 2 个以上的不同仓库状态合并为一个新状态时,就会有 2 个以上的父提交。 这一概念与我们的讨论并没有特别大的关系,但它确实存在。现在,对于父提交,每个与之不同的提交都被称为子提交。 每个父提交可以有任意数量的子提交。分支是对提交的引用(或指针),如下图所示:该图像显示的仓库具有两个提交(灰色圆圈),第二个圆圈是 master 分支的头部。 在我们添加更多提交后,仓库开始变成下图所示的状态:这是最简单的情况。 我们的开发者一次负责处理一个更改。 但通常会有很多开发者同时负责处理不同的功能,我们需要使用提交树显示仓库中的变化。提交树我们从相同的起始状态开始。 仓库具有两个提交:但现在,两名开发者在同时工作,为了避免相互干扰,他们在单独的分支中工作:一段时间后,他们需要合并所做的更改,为此,他们创建了合并请求(也叫拉取请求), 顾名思义,该请求可将两个不同的仓库状态(本例中,我们要将 develop 分支合并到 master 分支中)合并为一个新状态。 接受相应审查并获得批准后,仓库状态如下图所示:开发继续进行:Git 101 - 总结主要概念:Git 是一个非线性的分布式版本控制系统。仓库用于存储数据以及关于数据的元信息。提交是仓库的固定状态。分支是对提交的引用。合并请求(也叫拉取请求)是将两个不同的仓库状态合并为一个新状态的请求。如果您想了解更多关于 Git 的信息,可以阅读相关书籍。Git 流程现在,读者已熟悉基本的 Git 术语和概念,我们来探讨一下如何使用 Git 管理软件生命周期的开发部分。很多实践(称为流程)介绍了使用 Git 的开发流程,但我们只会探讨其中两个:GitHub 流程GitLab 流程GitHub 流程GitHub 流程非常简单。 具体如下:从仓库创建一个分支。将更改提交到新分支从您的分支发送一个拉取请求,其中包含您提议的更改,以发起讨论。根据需要在您的分支上提交更多更改。 您的拉取请求将自动更新。在分支准备好合并后,立即合并拉取请求。我们需要遵守几条规则:master 分支始终可部署(并且可正常运行!)不直接在 master 分支中进行开发在功能分支中进行开发master 分支 == 生产* 环境**需要尽可能频繁地部署到生产环境* 不要与“Ensemble 生产”混淆,这里的“生产”是指正式。** 环境是配置好的代码运行位置,可以是服务器、虚拟机,甚至可以是容器。如下图所示:有关 GitHub 流程的更多信息,请参阅此处。 我们还提供了图解指南。GitHub 流程非常适合小型项目,如果您刚开始使用 Git 流程,可以尝试一下。 不过,GitHub 也会使用 GitHub 流程,因此也可以在大型项目中使用 GitHub 流程。GitLab 流程如果您还没有准备好立即部署到生产环境,GitLab 流程提供 GitHub 流程 + 环境。 具体做法是:在功能分支中进行开发(与上例相同),合并到 master 分支中(与上例相同),但这里有一个不同之处: master 分支仅等同于测试环境。 除此之外,还有链接到可能存在的各种其他环境的“环境分支”。通常存在三个环境(可以根据需要创建更多环境):测试环境 == master 分支预生产环境 == preprod 分支生产环境 == prod 分支进入其中一个环境分支的代码应立即移至相应的环境中,此流程可通过以下方式完成:自动(我们将在第 2 部分和第 3 部分探讨)半自动(与自动方式相同,唯一的区别是应按下按钮授权部署)手动完整的流程是:在功能分支中开发功能。对功能分支进行审查并将其合并到 master 分支中。一段时间(合并了多个功能)后,将 master 分支合并到 preprod 分支中一段时间(用户测试等)后,将 preprod 分支合并到 prod 分支中在我们进行合并和测试时,多个新功能已开发完毕并合并到 master 分支中,因此转到 3。具体如下图所示:有关 GitLab 流程的更多信息,请参阅此处。结论Git 是一个非线性的分布式版本控制系统。Git 流程可用作软件开发周期的准则,有多种 Git 流程可供选择。链接Git 书籍GitHub 流程GitLab 流程Driessen 流程(更全面的流程,用于比较)本文的代码讨论问题您使用 Git 流程吗? 使用哪一种?您为普通项目使用多少个环境?后续内容在接下来的部分中,我们将:安装 GitLab。探讨一些建议的调整。讨论 GitLab 工作流(不要与 GitLab 流程混淆)。敬请关注。
文章
Michael Lei · 九月 27, 2024

使用 GitLab 持续交付 InterSystems 解决方案 – 第 5 部分:为何使用容器

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:Git 101Git 流程(开发流程)GitLab 安装GitLab 工作流持续交付GitLab 安装和配置GitLab CI/CD为何使用容器使用容器的 GitLab CI/CD在第一篇文章中,我们介绍了 Git 基础知识、深度理解 Git 概念对现代软件开发至关重要的原因,以及如何使用 Git 开发软件。在第二篇文章中,我们介绍了 GitLab 工作流 – 一个完整的软件生命周期流程,并介绍了持续交付。在第三篇文章中,我们介绍了 GitLab 安装和配置以及将环境连接到 GitLab在第四篇文章中,我们编写了 CD 配置。在这篇文章中,我们将介绍容器以及使用容器的方法(和原因)。这篇文章假设读者熟悉 docker 和容器的概念。 如果您想了解容器和镜像,请查阅 @Luca.Ravazzolo 撰写的文章。优势使用容器有诸多优势:可移植性效率隔离轻量不可变性下面我们来具体介绍这些优势。可移植性容器将应用程序与运行所需的一切(如配置文件和依赖项)打包在一起。 这样一来,您可以在您本地台式机、物理服务器、虚拟服务器、测试、暂存、生产环境以及公共云或私有云等不同环境中轻松、可靠地运行应用程序。可移植性的另一个优势是,在您构建 Docker 镜像并确认它可以正常运行后,它便可以在任何运行 docker 的位置(目前是 Windows、Linux 和 MacOS 服务器)运行。效率实际上,您只需要运行应用程序进程,而不需要运行所有系统软件等。 容器恰好可以满足这一需求 – 容器仅会运行您明确需要的进程,而不运行其他进程。 由于容器不需要单独的操作系统,占用的资源较少。 虚拟机的大小通常为几千兆字节,而容器的大小通常只有几百兆字节,因此在一台服务器上运行的容器数量要比虚拟机多得多。 由于容器对底层硬件的利用率更高,您需要的硬件更少,从而降低了裸机成本和数据中心成本。隔离容器会将应用程序与其他一切隔离开,同时多个容器可以在同一台服务器上运行,它们可以完全彼此独立。 容器之间的任何交互都应明确声明。 如果一个容器发生故障,它不会影响其他容器,并且可以快速重新启动。 得益于这种隔离机制,安全性也得到了保障。 例如,利用裸机上的 Web 服务器漏洞可能会让攻击者访问整个服务器,但如果使用的是容器,攻击者只能访问 Web 服务器容器。轻量由于容器不需要单独的操作系统,只需几秒钟即可启动、停止或重新启动,从而可以加快所有相关开发流程并缩短生产时间。 您可以更快地开始工作,并且无需花费任何时间进行配置。 不可变性不可变基础架构由不可变组件组成,每次部署时都会替换这些组件,而不是就地更新。 这些组件通过每次部署构建一次的通用镜像启动,可以进行测试和验证。不可变性减少了不一致情况,并且可以轻松地在应用程序的不同状态之间进行复制和移动。 详细了解不可变性。新可能利用上文提到的所有优势,我们可以通过全新的方式管理基础架构和工作流。编排裸机或虚拟机环境存在一个问题 – 它们具有个体性,这一问题会在使用过程中带来许多意外,而这些意外通常不尽如人意。 解决这一问题的方法是基础架构即代码 – 利用 DevOps 团队为源代码使用的版本控制方法,采用描述性模型管理基础架构。通过基础架构即代码,无论环境的起始状态如何,部署命令始终会将目标环境设为相同的配置。 要实现这一点,可自动配置现有目标,也可以丢弃现有目标并重新创建全新的环境。因此,利用基础架构即代码,团队会更改环境描述并对配置模型进行版本控制,而配置模型通常采用 JSON 等归档完好的代码格式。 发布管道会执行该模型,以配置目标环境。 如果团队需要进行更改,则可以编辑源代码,而不是目标环境。借助容器,这一切都可以实现,并且实施起来简单得多。 关闭容器并启动一个新容器只需几秒钟,而配置新虚拟机则需要几分钟。 我甚至都没有提到将服务器回滚到空白状态。扩缩基于之前的描述,您可能认为基础架构即代码本身是静态的。 并不是这样,因为编排工具也可以根据当前工作负载提供横向扩缩(进行更多相同的配置)。 您仅应运行目前需要的容器,并相应地扩缩应用程序。 这样还可以降低成本。结论容器可以简化开发流程。 消除了环境之间的不一致,可以更轻松地进行测试和调试。 可以通过编排构建可扩缩应用程序。可以轻松实现部署或回滚到不可变历史的任意点。组织希望在更高层级上处理任务,其中所有上述问题均已得到解决,并且我们发现调度程序和编排程序以自动方式为我们处理更多的任务。后续内容在下一篇文章中,我们将探讨使用容器进行配置,并创建利用 InterSystems IRIS Docker 容器的 CD 配置。
文章
Michael Lei · 九月 27, 2024

使用 GitLab 持续交付 InterSystems 解决方案 – 第 6 部分:容器基础架构

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:Git 101流程(开发流程)GitLab 安装GitLab 工作流持续交付GitLab 安装和配置GitLab CI/CD为何使用容器?容器基础架构使用容器的 GitLab CI/CD在第一篇文章中,我们介绍了 Git 基础知识,以及为什么对 Git 概念的高层次理解对于现代软件开发如此重要,以及如何使用 Git 开发软件。在第二篇文章中,我们介绍了 GitLab 工作流 – 一个完整的软件生命周期流程和持续交付。在第三篇文章中,我们介绍了 GitLab 的安装和配置以及如何将环境连接到 GitLab。在第四篇文章中,我们编写了 CD 配置。在 第五篇文章中,我们讨论了容器与如何(以及为什么)使用它们。在本文中,我们将探讨运行使用容器的持续交付管道所需的主要组件,以及它们如何协同工作。我们的配置如下所示:在这里,我们可以看到三个主要阶段的分离:构建传送运行构建在之前的部分中,构建通常是增量式 – 我们计算当前环境与当前代码库之间的差异,并修改我们的环境以与代码库相对应。 使用容器时,每次构建都是完整构建。 构建的结果是一个可以通过依赖关系在任何地方运行的镜像。传送在我们的镜像构建并通过测试后,它会被上传到注册表 – 用于托管 docker 镜像的专用服务器。 在那里,它可以替换具有相同标签的旧镜像。 例如,由于对 master 分支的新提交,我们构建了新镜像 (project/version:master),如果测试通过,我们可以用相同标签的新镜像替换注册表中的旧镜像,这样,所有拉取 project/version:master 的人都会获得新版本。运行最后,我们的镜像部署完成。 CI 解决方案(如 GitLab)可以控制此流程,也可以由专门的编排器控制,但目的都一样 – 执行一些镜像,定期检查健康状态,如果有新镜像版本可用就进行更新。查看 docker 网络研讨会以了解这些不同阶段的解释。或者,从提交的角度来看:
文章
Michael Lei · 九月 27, 2024

使用 GitLab 持续交付 InterSystems 解决方案 – 第 9 部分:容器架构

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:Git 101Git 流程(开发流程)GitLab 安装GitLab 工作流持续交付GitLab 安装和配置GitLab CI/CD为何使用容器?容器基础架构使用容器的 CD使用 ICM 的 CD容器架构在本文中,我们将讨论如何构建并部署您自己的容器。Durable %SYS由于容器是临时的,它们不应存储任何应用程序数据。 持久化 %SYS 功能让我们能够实现这一点 – 将设置、配置、%SYS 数据等存储到主机卷上,即:iris.cpf 文件。/csp 目录,包含 Web 网关配置和日志文件。/httpd/httpd.conf 文件,实例的私有 Web 服务器的配置文件。/mgr 目录,包含以下内容:IRISSYS 系统数据库,包括 IRIS.DAT 和 iris.lck 文件、流目录,以及包含 IRISTEMP、IRISAUDIT、IRIS 和 USER 系统数据库的 iristemp、irisaudit、iris 和 user 目录。写入镜像日志文件,IRIS.WIJ。/journal 目录,包含日志文件。/temp 目录,用于存储临时文件。日志文件,包括 messages.log、journal.log 和 SystemMonitor.log。容器架构另一方面,我们需要将应用程序代码存储在我们的容器内,以便在需要时进行升级。这一切使我们实现了如下所示的架构:为了在构建过程中实现这一点,我们至少需要创建一个额外的数据库(用于存储应用程序代码)并将其映射到我们的应用程序命名空间。 在我的示例中,我将使用 USER 命名空间来保存应用程序数据,因为它已经存在且是持久化的。安装程序基于上述内容,我们的安装程序需要执行以下任务:创建 APP 命名空间/数据库将代码加载到 APP 命名空间将我们的应用程序类映射到 USER 命名空间完成所有其他安装(在本例中,我创建了 CSP Web 应用和 REST 应用) Class MyApp.Hooks.Local { Parameter Namespace = "APP"; /// See generated code in zsetup+1^MyApp.Hooks.Local.1 XData Install [ XMLNamespace = INSTALLER ] { <Manifest> <Log Text="Creating namespace ${Namespace}" Level="0"/> <Namespace Name="${Namespace}" Create="yes" Code="${Namespace}" Ensemble="" Data="IRISTEMP"> <Configuration> <Database Name="${Namespace}" Dir="/usr/irissys/mgr/${Namespace}" Create="yes" MountRequired="true" Resource="%DB_${Namespace}" PublicPermissions="RW" MountAtStartup="true"/> </Configuration> <Import File="${Dir}Form" Recurse="1" Flags="cdk" IgnoreErrors="1" /> </Namespace> <Log Text="End Creating namespace ${Namespace}" Level="0"/> <Log Text="Mapping to USER" Level="0"/> <Namespace Name="USER" Create="no" Code="USER" Data="USER" Ensemble="0"> <Configuration> <Log Text="Mapping Form package to USER namespace" Level="0"/> <ClassMapping From="${Namespace}" Package="Form"/> <RoutineMapping From="${Namespace}" Routines="Form" /> </Configuration> <CSPApplication Url="/" Directory="${Dir}client" AuthenticationMethods="64" IsNamespaceDefault="false" Grant="%ALL" Recurse="1" /> </Namespace> </Manifest> } /// This is a method generator whose code is generated by XGL. /// Main setup method /// set vars("Namespace")="TEMP3" /// do ##class(MyApp.Hooks.Global).setup(.vars) ClassMethod setup(ByRef pVars, pLogLevel As %Integer = 0, pInstaller As %Installer.Installer) As %Status [ CodeMode = objectgenerator, Internal ] { Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "Install") } /// Entry point ClassMethod onAfter() As %Status { try { write "START INSTALLER",! set vars("Namespace") = ..#Namespace set vars("Dir") = ..getDir() set sc = ..setup(.vars) write !,$System.Status.GetErrorText(sc),! set sc = ..createWebApp() } catch ex { set sc = ex.AsStatus() write !,$System.Status.GetErrorText(sc),! } quit sc } /// Modify web app REST ClassMethod createWebApp(appName As %String = "/forms") As %Status { set:$e(appName)'="/" appName = "/" _ appName #dim sc As %Status = $$$OK new $namespace set $namespace = "%SYS" if '##class(Security.Applications).Exists(appName) { set props("AutheEnabled") = $$$AutheUnauthenticated set props("NameSpace") = "USER" set props("IsNameSpaceDefault") = $$$NO set props("DispatchClass") = "Form.REST.Main" set props("MatchRoles")=":" _ $$$AllRoleName set sc = ##class(Security.Applications).Create(appName, .props) } quit sc } ClassMethod getDir() [ CodeMode = expression ] { ##class(%File).NormalizeDirectory($system.Util.GetEnviron("CI_PROJECT_DIR")) } } 为了创建非持久化数据库,我会使用 /usr/irissys/mgr 的一个子目录,它不是持久的。 请注意,调用 ##class(%File).ManagerDirectory() 会返回持久化目录的路径,而不是内部容器目录的路径。 持续交付配置 查看第 7 部分以了解完整信息,但我们需要做的就是在现有配置中添加这两行(加粗)代码。 run image: stage: run environment: name: $CI_COMMIT_REF_NAME url: http://$CI_COMMIT_REF_SLUG.docker.eduard.win/index.html tags: - test script: - docker run -d --expose 52773 --volume /InterSystems/durable/$CI_COMMIT_REF_SLUG:/data --env ISC_DATA_DIRECTORY=/data/sys --env VIRTUAL_HOST=$CI_COMMIT_REF_SLUG.docker.eduard.win --name iris-$CI_COMMIT_REF_NAME docker.eduard.win/test/docker:$CI_COMMIT_REF_NAME --log $ISC_PACKAGE_INSTALLDIR/mgr/messages.log volume 参数将主机目录挂载到容器中,ISC_DATA_DIRECTORY 变量告诉 InterSystems IRIS 使用哪个目录。 引用文档内容: 当您使用这些选项运行 InterSystems IRIS 容器时,会发生以下情况: 指定的外部卷被挂载。如果由 ISC_DATA_DIRECTORY 环境变量指定的持久化 %SYS 目录(上述示例中的 iconfig/)已经存在且包含持久化 %SYS 数据,则实例的所有内部指针都会重置到该目录,实例使用其中包含的数据。如果 ISC_DATA_DIRECTORY 环境变量指定的持久化 %SYS 目录已经存在但不包含持久化 %SYS 数据,则不会复制数据,实例使用容器内安装树中的数据运行,这意味着实例特定的数据不是持久的。 因此,您可能需要在脚本中包含对此条件的检查,然后再运行容器。如果 ISC_DATA_DIRECTORY 指定的持久化 %SYS 目录不存在: 指定的持久化 %SYS 目录已创建。将持久化 %SYS 目录内容中列出的目录和文件从其安装位置复制到持久化 %SYS 目录(原始文件仍留在原位)。 实例的所有内部指针都会重置到持久化 %SYS 目录,实例使用其中包含的数据。 更新 当应用程序演进并发布新版本(容器)时,有时您可能需要运行一些代码。 这可能是预编译/后编译挂钩、架构迁移、单元测试,但归根结底,您需要运行任意代码。 这就是为什么您需要一个管理应用程序的框架。 在之前的文章中,我概述了这种框架的基本结构,但它当然可以大幅度扩展以满足特定应用程序的需求。 结论 创建容器化应用程序需要一些思考,但 InterSystems IRIS 提供了几个功能,可以让此流程更加轻松。 链接 索引文章代码测试项目完整 CD 配置带持久化 %SYS 的完整 CD 配置
文章
Michael Lei · 九月 27, 2024

使用 GitLab 持续交付 InterSystems 解决方案 – 第 10 部分:代码之外

经过将近四年的停顿,[我的 CI/CD 系列](https://community.intersystems.com/post/continuous-delivery-your-intersystems-solution-using-gitlab-index)又回来了! 这些年来,我与多个 InterSystems 客户合作,为不同的用例开发 CI/CD 管道。 希望本文中提供的信息对您有所帮助。 此[系列文章](https://community.intersystems.com/post/continuous-delivery-your-intersystems-solution-using-gitlab-index)探讨了使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我们有一系列激动人心的话题要讨论:但今天,我们来谈谈代码之外的事情 – 配置和数据。 # 问题 之前我们探讨过代码提升,这在某种程度上是无状态的 – 我们总是从一个(大概)空实例到完整的代码库。 但有时,我们需要提供数据或状态。 不同的数据类型包括: - 配置:用户、Web 应用、LUT、自定义架构、任务、业务伙伴等 - 设置:环境特定的键值对 - 数据:为了让应用正常工作,通常必须提供引用表等 我们来探讨所有这些数据类型,以及如何先将它们提交到源代码控制工具中,然后进行部署。 # 配置 系统配置分布在许多不同的类中,但 InterSystems IRIS 可以将大多数类导出为 XML。 首先是一个[安全软件包](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&PACKAGE=Security),其中包含以下信息: - Web 应用程序 - DocDB - 域 - 审核事件 - KMIP 服务器 - LDAP 配置 - 资源 - 角色 - SQL 特权 - SSL 配置 - 服务 - 用户 所有这些类都提供 Exists、Export 和 Import 方法,允许您在不同的环境之间移动它们。 一些注意事项: - 用户和 SSL 配置可能包含敏感信息,例如密码。 出于安全原因,通常建议不要将它们存储在源代码控制工具中。 使用 Export/Import 方法来方便地进行一次性传输。 - 默认情况下,Export/Import 方法会将所有内容输出到一个文件中,这可能不适合源代码控制。 这里提供了一个[实用类](https://gist.github.com/eduard93/3a9abdb2eb150a456191bf387c1fc0c3),可用于导出和导入查找表、自定义架构、业务伙伴、任务、凭据和 SSL 配置。 它会每个文件导出一个条目,因此您会得到一个包含 LUT 的目录,另一个包含自定义架构的目录,等等。 对于 SSL 配置,它还会导出以下文件:证书和密钥。 另外值得注意的是,除了导出/导入,您还可以使用 [%Installer](https://community.intersystems.com/post/deploying-applications-intersystems-cache-installer) 或 [Merge CPF](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ACMF) 来创建这些条目中的大多数。 这两个工具还支持创建命名空间和数据库。 Merge CPF 可以调整系统设置,例如全局缓冲区大小。 ## 任务 [%SYS.Task](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYS.Task) 类存储任务并提供 `ExportTasks` 和 `ImportTasks` 方法。 您还可以查看上面的实用类,以逐个导入和导出任务。 请注意,当您导入任务时,如果 `StartDate` 或其他与计划相关的属性在过去的时间里,则可能会遇到导入错误 (`ERROR #7432: Start Date and Time must be after the current date and time`)。 解决方案是将 `LastSchedule` 设置为 `0`,InterSystems IRIS 会将新导入的任务重新安排为在不久的将来运行。 ## 互操作性 互操作性生产包含: - 业务伙伴 - 系统默认设置 - 凭据 - 查找表 前两个可以在 [Ens.Config](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=ENSLIB&PACKAGE=Ens.Config) 软件包中通过 `%Export` 和 `%Import` 方法获取。 使用上面的[实用类](https://gist.github.com/eduard93/3a9abdb2eb150a456191bf387c1fc0c3)导出凭据和查找表。 在最近的版本中,查找表可以通过 [$system.OBJ](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.OBJ) 类导出/导入。 # 设置 [系统默认设置](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ECONFIG_other_default_settings#ECONFIG_other_default_settings_purpose) – 是环境特定设置的默认互操作性机制: > 系统默认设置的目的是简化将生产定义从一个环境复制到另一个环境的过程。 在任何生产中,一些设置的值作为生产设计的一部分确定;通常而言,这些设置在所有环境中都应相同。 不过,其他设置必须根据环境进行调整;这些设置包括文件路径、端口号等。 > > 系统默认设置应仅指定特定于安装 InterSystems IRIS 的环境的值。 相比之下,生产定义应指定在所有环境中都应当相同的设置值。 我强烈建议在生产环境中使用它们。 使用 [%Export](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=ENSLIB&CLASSNAME=Ens.Config.DefaultSettings#%25Export) 和 [%Import](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=ENSLIB&CLASSNAME=Ens.Config.DefaultSettings#%25Import) 传输系统默认设置。 ## 应用程序设置 您的应用程序可能也使用设置。 在这种情况下,我建议使用系统默认设置。 虽然这是一种互操作性机制,但可以通过以下方式访问设置:`%GetSetting(pProductionName, pItemName, pHostClassName, pTargetType, pSettingName, Output pValue)`([文档](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=ENSLIB&CLASSNAME=Ens.Config.DefaultSettings#%25GetSetting))。 您可以编写一个包装器来设置您不关心的默认值,例如: ```objectscript ClassMethod GetSetting(name, Output value) As %Boolean [Codemode=expression] { ##class(Ens.Config.DefaultSettings).%GetSetting("myAppName", "default", "default", , name, .value) } ``` 如果您想要更多类别,还可以公开 `pItemName` 和/或 `pHostClassName` 参数。 可以通过导入、使用系统管理门户、创建 `Ens.Config.DefaultSettings` 类的对象或设置 `^Ens.Config.DefaultSettingsD` 全局进行初始设置。 我的主要建议是将设置放在一个地方(可以是系统默认设置或自定义解决方案),应用程序必须仅通过提供的 API 获取设置。 这样,应用程序本身不知道环境,剩下的工作是为集中式设置存储提供环境特定的值。 为此,在您的仓库中创建一个包含设置文件的设置文件夹,文件名称与环境分支名称相同。 随后在 CI/CD 阶段,使用 `$CI_COMMIT_BRANCH` [环境变量](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html)加载正确的文件。 ``` DEV.xml TEST.xml PROD.xml ``` 如果每个环境有多个设置文件,请使用以环境分支命名的文件夹。 要从 InterSystems IRIS 内获取环境变量值,请[使用](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.Util#GetEnviron) `$System.Util.GetEnviron("name")`。 # 数据 如果您想让一些数据(引用表、目录等)可用,可以通过几种方式实现此目标: - Global导出。 使用二进制 [GOF 导出](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GGBL_managing#GGBL_managing_export)或新的 XML 导出。 使用 GOF 导出时,请记住源系统和目标系统上的区域设置必须匹配(或者至少目标系统上的全局排序规则必须可用)。 [XML 导出](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.OBJ)会占用更多空间。 您可以通过将Global导出为 `xml.gz` 文件进行改进,`$system.OBJ` 方法会根据需要自动(取消)归档 `xml.gz` 文件。 这种方式的主要缺点是数据无法被人类阅读,即使是 XML 也是如此 – 大部分采用 base64 编码。 - CSV。 [导出 CSV](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SQL.StatementResult#%25DisplayFormatted) 并通过 [LOAD DATA](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_loaddata) 导入。 我更喜欢使用 CSV,因为它是最节省存储空间的人类可读格式,并且任何内容都可以导入。 - JSON。 使类[支持 JSON](https://docs.intersystems.com/iris20221/csp/docbook/DocBook.UI.Page.cls?KEY=GJSON_adaptor)。 - XML。 使类[支持 XML](https://docs.intersystems.com/iris20221/csp/docbook/DocBook.UI.Page.cls?KEY=GXMLPROJ_intro) 以将对象投射到 XML 中。 如果您的数据具有复杂结构,请使用该方式。 选择哪种格式取决于您的用例。 这里我按存储效率顺序列出了各个格式,但如果您没有大量数据,那就不必担心。 # 结论 状态为您的 CI/CD 部署管道增加了额外的复杂性,但 InterSystems IRIS 为此提供了大量管理工具。 # 链接 - [实用类](https://gist.github.com/eduard93/3a9abdb2eb150a456191bf387c1fc0c3) - [%Installer](https://community.intersystems.com/post/deploying-applications-intersystems-cache-installer) - [Merge CPF](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ACMF) - [$System.OBJ](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.OBJ) - [系统默认设置](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=ENSLIB&CLASSNAME=Ens.Config.DefaultSettings#%25GetSetting)
文章
Michael Lei · 九月 27, 2024

使用 GitLab 持续交付 InterSystems 解决方案 – 第 11 部分:互操作性

欢迎来到[我的 CI/CD 系列](https://community.intersystems.com/post/continuous-delivery-your-intersystems-solution-using-gitlab-index)的下一个章节,我们将探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 今天,我们来谈谈互操作性。 # 问题 当您有一个有效的互操作性生产时,您有两个独立的流程:一个是处理消息的可以正常运行的生产流程,另一个是更新代码、生产配置和[系统默认设置](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ECONFIG_other_default_settings)的 CI/CD 流程。 显然,CI/CD 流程会影响互操作性。 但问题是: - 更新期间究竟发生了什么? - 我们需要做些什么以在更新期间尽可能缩短或消除生产停机时间? # 术语 - 业务主机 (BH) – 互操作性生产的一个可配置元素:业务服务 (BS)、业务流程(BP、BPL)或业务操作 (BO)。 - 业务主机作业 (Job) – 运行业务主机代码并由互操作性生产管理的 InterSystems IRIS 作业。 - 生产 – 业务主机的互联集合。 - 系统默认设置 (SDS) – 特定于安装 InterSystems IRIS 的环境的值。 - 有效消息 – 当前正在由某个业务主机作业处理的请求。 一个业务主机作业最多只能有一条有效消息。 没有有效消息的业务主机作业处于空闲状态。 # 发生了什么? 我们从生产生命周期开始。 ## 生产启动 首先,可以启动生产。 每个命名空间只能同时运行一个生产,通常而言(除非您真正知道自己在做什么以及为什么这样做),每个命名空间内只能运行一个生产。 不推荐在一个命名空间中于两个或多个不同的生产之间来回切换。 启动生产会启动生产中定义的所有已启用的业务主机。 某些业务主机启动失败不会影响生产启动。 提示: - 可以从系统管理门户或者调用以下代码来启动生产:`##class(Ens.Director).StartProduction("ProductionName")` - 通过实现 `OnStart` 方法可以在生产启动时(在启动任何业务主机作业之前)执行任意代码 - 生产启动是一个可审核事件。 您始终可以在审核日志中查看是谁在何时启动的生产。 ## 生产更新 在生产启动后,[Ens.Director](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=ENSLIB&CLASSNAME=Ens.Director) 会持续监视正在运行的生产。 生产存在两种状态:_目标状态_,在生产类和系统默认设置中定义;以及_运行状态_ – 当前运行的作业及其创建时应用的设置。 如果所需状态与当前状态相同,则一切正常;但如果有差异,就可以(也应该)更新生产。 通常,您会在系统管理门户的“生产配置”页面上看到一个红色的 `Update` 按钮。 更新生产意味着尝试使当前生产状态与目标生产状态匹配。 当您运行 `##class(Ens.Director).UpdateProduction(timeout=10, force=0)` 以更新生产时,它会为每个业务主机执行以下操作: 1. 将有效设置与生产/SDS/类设置进行比较 2. 当且仅当 (1) 显示不匹配时,业务主机会被标记为过时并需要更新。 为每个业务主机运行此操作后,`UpdateProduction` 会构建一组更改: - 要停止的业务主机 - 要启动的业务主机 - 要更新的生产设置 然后,应用这些更改。 通过这种方式,“更新”设置而不更改任何内容不会导致生产停机。 提示: - 可以从系统管理门户或者调用以下代码来更新生产:`##class(Ens.Director).UpdateProduction(timeout=10, force=0)` - 默认的系统管理门户更新超时为 10 秒。 如果您知道处理消息需要更长时间,请调用 `Ens.Director:UpdateProduction` 并设置更长的超时。 - 更新超时是一个生产设置,您可以将其更改为更大的值。 此设置适用于系统管理门户。 ## 代码更新 `UpdateProduction` 不会使用过时的代码更新 BH。 这是一种以安全为导向的行为,但如果您想要在底层代码更改时自动更新所有正在运行的 BH,请按以下步骤操作: 首先,按以下方式加载和编译: ```objectscript do $system.OBJ.LoadDir(dir, "", .err, 1, .load) do $system.OBJ.CompileList(load, "curk", .errCompile, .listCompiled) ``` 现在,`listCompiled` 将包含由于 `u` 标志而实际编译的所有条目(使用 [git 差异](https://github.com/intersystems-ru/GitLab/blob/master/isc/git/Diff.cls)来尽可能减小加载集)。 使用此 `listCompiled` 获取所有已编译类的 $lb: ```objectscript set classList = "" set class = $o(listCompiled("")) while class'="" { set classList = classList _ $lb($p(class, ".", 1, *-1)) set class=$o(listCompiled(class)) } ``` 然后,计算需要重启的 BH 的列表: ```sql SELECT %DLIST(Name) bhList FROM Ens_Config.Item WHERE 1=1 AND Enabled = 1 AND Production = :production AND ClassName %INLIST :classList ``` 最后,在获取 `bhList` 后,停止并启动受影响的主机: ```objectscript for stop = 1, 0 { for i=1:1:$ll(bhList) { set host = $lg(bhList, i) set sc = ##class(Ens.Director).TempStopConfigItem(host, stop, 0) } set sc = ##class(Ens.Director).UpdateProduction() } ``` ## 生产停止 生产可以停止,这意味着向所有业务主机作业发送关闭请求(如果存在,则在它们处理完有效消息后安全地关闭)。 提示: - 可以从系统管理门户或者调用以下代码来停止生产:`##class(Ens.Director).StopProduction(timeout=10, force=0)` - 默认的系统管理门户停止超时为 120 秒。 如果您知道处理消息需要更长时间,请调用 `Ens.Director:StopProduction` 并设置更长的超时。 - 关闭超时是一个生产设置。 您可以将其更改为更大的值。 此设置适用于系统管理门户。 - 通过实现 `OnStop` 方法可以在生产停止时执行任意代码 - 生产停止是一个可审核事件,您始终可以在审核日志中查看是谁在何时停止的生产。 重要的一点是,生产是业务主机的总和: - 启动生产意味着启动所有已启用的业务主机。 - 停止生产意味着停止所有正在运行的业务主机。 - 更新生产意味着计算出过时的业务主机的子集,因此首先停止它们,然后立即重新启动。 此外,新增的业务主机只会启动,从生产中删除的业务主机只会停止。 这会将我们带到业务主机的生命周期。 ## 业务主机启动 业务主机由相同的业务主机作业组成(根据池大小设置的值)。 启动业务主机意味着启动所有业务主机作业。 它们会并行启动。 单个业务主机作业的启动方式如下: 1. 互操作性作业是一个将成为业务主机作业的新进程。 2. 新进程注册为互操作性作业。 3. 业务主机代码和适配器代码加载到进程内存中。 4. 与业务主机和适配器相关的设置加载到内存中。 优先级顺序如下: a. 生产设置(覆盖系统默认设置和类设置)。 b. 系统默认设置(覆盖类设置)。 c. 类设置。 5. 作业准备就绪并开始接受消息。 在完成 (4) 后,作业无法更改设置或代码,因此当您导入新的/相同的代码和新的/相同的系统默认设置时,它不会影响当前正在运行的互操作性作业。 ## 业务主机停止 停止业务主机作业意味着: 1. 互操作性命令作业停止接受更多消息/输入。 2. 如果存在有效消息,业务主机作业具有一定的超时秒数来处理该消息(完成消息会结束 BO 的 `OnMessage` 方法,BS 的 `OnProcessInput`,BPL BP 的状态 `S` 方法以及 BP 的 `On*` 方法)。 3. 如果在超时前有效消息未被处理且 `force=0`,则生产更新会在该业务主机上失败(您会在 SMP 中看到一个红色的 `Update` 按钮)。 4. 如果此列表中的任何一项为真,则表示停止成功: - 没有有效消息 - 有效消息在 `timeout` 之前处理完 - 有效消息在超时之前未处理完但 `force=1` 5. 作业的互操作性取消注册并停止。 ## 业务主机更新 业务主机更新意味着停止当前运行的业务主机作业并启动新作业。 ## 业务规则、路由规则和 DTL 所有业务主机会在新版本的业务规则、路由规则和 DTL 可用时立即开始使用它们。 在这种情况下,不需要重启业务主机。 # 离线更新 不过,有时生产更新需要单个业务主机停机。 ## 规则取决于新代码 考虑接下来的情况。 您有一个当前的路由规则 X,它根据任意标准将消息路由到业务流程 A 或 B。 在新提交中,您同时添加: - 业务流程 C - 新版本的路由规则 X,可以将消息路由到 A、B 或 C。 在此场景下,您不能先加载规则,然后再更新生产。 原因在于,新编译的规则会立即开始将消息路由到业务流程 C,而 InterSystems IRIS 可能尚未编译该规则,或者互操作性尚未更新以供使用。 在这种情况下,您需要禁用包含路由规则的业务主机,更新代码,更新生产,然后再次启用业务主机。 注: - 如果您使用[生产部署文件](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=EGDV_deploying)更新生产,它会自动禁用/启用所有受影响的 BH。 - 对于 InProc 调用的主机,编译会使调用方持有的特定主机的缓存失效。 ## 业务主机之间的依赖关系 业务主机之间的依赖关系至关重要。 假设您有业务流程 A 和 B,其中 A 向 B 发送消息。 在新提交中,您同时添加: - 新版本的流程 A,可以在向 B 发送的请求中设置新属性 X - 新版本的流程 B,可以处理新属性 X 在此场景中,我们必须首先更新流程 B,然后更新流程 A。 您可通过以下两种方式之一完成此操作: - 在更新期间禁用业务主机 - 将更新拆分为两步:首先,仅更新流程 B,然后在单独的更新中开始从流程 A 向流程 B 发送消息。 这个主题一个更具挑战性的变体是,新版本的流程 A 和流程 B 与旧版本不兼容,这需要业务主机停机。 ## 队列 如果您知道更新后某个业务主机将无法处理旧消息,则需要确保在更新前该业务主机队列为空。 为此,请禁用所有向该业务主机发送消息的业务主机,并等到其队列变空。 ## BPL 业务流程中的状态更改 首先,简单介绍一下 BPL BP 的运作方式。 在您编译 BPL BP 后,会在与完整 BPL 类同名的软件包中创建两个类: - `Thread1` 类包含方法 S1、S2、... SN,对应于 BPL 中的活动 - `Context` 类包含所有上下文变量,以及 BPL 将执行的下一个状态(即 `S5`) 此外,BPL 类是持久类,可以存储当前正在处理的请求。 BPL 的工作方式是在 `Thread` 类中执行 `S` 方法并相应地更新 BPL 类表、`Context` 表和 `Thread1` 表,其中一条“正在处理”的消息是 BPL 表中的一行。 请求处理完后,BPL 会删除 BPL、`Context` 和 `Thread` 条目。 由于 BPL BP 是异步的,通过在 `S` 调用之间保存信息并在不同请求之间切换,一个 BPL 作业可同时处理多个请求。 例如,BPL 处理一个请求,直至其到达 `sync` 活动 – 等待来自 BO 的回答。 它会将当前上下文保存到磁盘中,同时将 `%NextState` 属性(位于 `Thread1` 类中)设置为响应活动 `S` 方法,并在 BO 回答前继续处理其他请求。 BO 回答后,BPL 会将上下文加载到内存中,并执行与 `%NextState` 属性中保存的状态对应的方法。 现在,当我们更新 BPL 时会发生什么? 首先,我们需要检查是否至少满足以下两个条件之一: - 更新期间,上下文表为空,这意味着没有正在处理的有效消息。 - 新状态与旧状态相同,或者新状态是在旧状态之后添加的。 如果至少满足一个条件,我们就可以开始更新。 要么没有需要更新后 BPL 处理的更新前请求,要么状态是在结束时添加的,这意味着旧请求也可以进入其中(假设更新前请求与更新后 BPL 活动和处理兼容)。 但是,如果您有正在处理的有效请求,而 BPL 更改了状态顺序,该怎么办? 理想情况下,如果您可以等待,则禁用 BPL 调用方并等待队列为空。 验证上下文表是否也为空。 请记住,队列只显示未处理的请求,而上下文表存储正在处理的请求,因此您可能会遇到一个非常繁忙的 BPL 显示零队列大小的情况,这是正常的。 之后,禁用 BPL,执行更新并启用所有之前禁用的业务主机。 如果无法实现(通常是在 BPL 非常长的情况下,例如,我记得我更新过一个花了大约一周时间处理请求的 BPL,或者更新窗口太短),请使用 [BPL 版本控制](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=EBPLR_process)。 或者,您也可以编写一个更新脚本。 在此更新脚本中,将旧的后续状态映射到新的后续状态并在 `Thread1` 表上运行,以便更新的 BPL 可以处理旧请求。 当然,在更新期间必须禁用 BPL。 也就是说,这是一种极为罕见的情况,通常您不必这样做,但如果您需要这样做,方法就是如此。 # 结论 为了将底层代码更改后实现生产所需的操作数降到最低,互操作性实现了一种复杂的算法。 每次 SDS 更新时,调用具有安全超时的 UpdateProduction。 对于每次代码更新,您都需要决定一种更新策略。 通过使用 [git 差异](https://github.com/intersystems-ru/GitLab/blob/master/isc/git/Diff.cls)减少编译的代码量有助于缩短编译时间,但利用自身“更新”代码并重新编译它或使用相同的值“更新”设置不会触发或要求进行生产更新。 更新和编译业务规则、路由规则和 DTL 可使它们在未进行生产更新的情况下立即可用。 最后,生产更新是一项安全操作,通常不需要停机。 # 链接 - [Ens.Director](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=ENSLIB&CLASSNAME=Ens.Director) - [构建 git 差异](https://github.com/intersystems-ru/GitLab/blob/master/isc/git/Diff.cls) - [系统默认设置](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ECONFIG_other_default_settings) 在本文撰写期间,@James.MacKeith、@Dmitry.Zasypkin 和 @Regilo.Souza 提供了宝贵的帮助,作者对此深表感谢。
公告
Claire Zheng · 七月 30, 2021

Online Meetup:InterSystems 开发者竞赛(人工智能与机器学习)

亲爱的社区开发者们,大家好! 我们真诚邀请您参加 Online Meetup:InterSystems 开发者竞赛(人工智能与机器学习)! 时间: 2021年7月30日(周五) 11:00 AM (美东时间)23:00 (北京时间) 在这场在线Meetup,您将了解到: 此次优胜者的个人履历; 获奖Application的简短Demo; 有关应用技术的开放探讨、问答,以及下次竞赛的一些计划。 我们的发言人名单: @José.Pereira, BI开发者,Shift Consultoria e Sistemas Ltda @Henrique, 系统管理专员/数据库管理员, Sao Paulo Federal Court @Oleh.Dontsov, 全栈开发工程,Tax Sketches SRO @Aleksandr.Kalinin6636, 工程师, LLC "Escape" @Renato.Banzai, 机器学习工程师,Itaú Unibanco @Evgeny.Shvarov, InterSystems 开发者生态经理(Developer Ecosystem Manager) @tomd, InterSystems 产品专家(机器学习) @Raj.Singh5479, InterSystems 产品经理( 开发者体验) @Robert.Kuszewski, InterSystems 产品经理( 开发者体验) 欢迎您在这个特别的会议上与我们的开发人员沟通、交流! ➡️ 注册参会!
公告
Michael Lei · 十二月 30, 2021

第三方合作伙伴产品展示:InterSystems IRIS的“新”监控工具

亲爱的女士们和先生们,在上个月,我改进了我的工具Caché监视器......但首先:名字 选择Caché Monitor这个名字是为了反映该工具与Caché数据库的紧密结合。我想,在可预见的未来,Intersystems Caché这个既定名称很可能会被InterSystems IRIS数据平台完全取代。因此,重塑品牌的步骤成为必要。为了在未来不那么紧密地与一个产品名称联系在一起,我们选择了一个更加中立的新名字: SQL Data Lens 不管叫什么名字,实现与Intersystems产品最紧密的整合仍然是我们的目标。持续的发展也反映在版本号的延续上。上一个版本是Caché Monitor 2.96,下一个版本是SQL Data Lens 3.0。 但这不仅仅是重新命名,而且还集成了许多新的功能,例如 一个很大的进步是,SQL Data Lens现在是基于Java Runtime Environment 11的,有了它,就可以改善对HiDPI的支持。在操作系统层面上配置了系统规模因素,支持每台显示器的DPI! 但也集成了一个新的现代的Flat 外观和Feel 集成,在亮Light和暗Dark两种模式下。 SQL DataLens带有一个最小化的私有JRE(基于JetBrains JDK 11)。 ...等等 请随时从以下网站下载并测试SQL Data Lens,无需任何注册: https://sqldatalens.com/ 我期待着你的经验,建议和想法,这些都非常欢迎。 谢谢您的关注! Andreas https://sqldatalens.com/从链接进去下载不了 我刚试了一下是可以的,而且不用翻墙,看看是否本地浏览器或者其他问题?谢谢! https://sqldatalens.com/download/ 点击download之后,会跳到https://sqldatalens.com/sqldl_files/latest/SQLDataLens_308_20220508.zip这个链接,提示“ Server Error 403 Forbidden ” 那可能得麻烦您到英文社区下面问下,这是我们德国合作伙伴做的:https://community.intersystems.com/post/new-tool-intersystems-iris, 谢谢!