搜索​​​​

清除过滤器
文章
姚 鑫 · 八月 9, 2021

方法关键字SoapRequestMessage,SoapTypeNameSpace,SqlName,SqlProc

# 第八十一章 方法关键字 - SoapRequestMessage 当多个`web方法`具有相同的`SoapAction`时使用此方法。 在默认场景中,该关键字指定请求消息的`SOAP`正文中的顶级元素的名称。 仅适用于定义为`web服务`或`web客户端`的类。 # 用法 要在请求消息的`SOAP`体中指定顶级元素的名称,请使用以下语法: ```java Method name(formal_spec) As returnclass [ WebMethod, SoapAction = "MyAct", SoapRequestMessage="MyReqMessage" ] { //implementation } ``` 其中`soaprequestmessage`是有效的XML标识符。 # 详解 注意:此关键字仅对包装的文档/文字`document/literal`消息有效。 对于包装的文档/文字消息,该关键字指定请求消息的`SOAP`主体中的顶部元素的名称。(默认情况下,包装文档/文字消息。 如果对同一`web服务`中的多个`web方法`使用相同的`SoapAction`值,请指定此关键字。否则,一般不需要这个关键字。 # 与WSDL的关系 `SoapRequestMessage`关键字影响`web服务`的`WSDL`的``部分。例如,考虑以下web方法: ```java Method Add(a as %Numeric,b as %Numeric) As %Numeric [ SoapAction = MyAct,SoapRequestMessage=MyReqMessage, WebMethod ] { Quit a + b } ``` 对于这个web服务,WSDL包含以下内容: ```xml ``` 这些元素在``部分中相应地定义。 默认情况下,如果方法没有指定`SoapRequestMessage`关键字,``部分将改为如下所示: ```xml ``` 如果使用`SOAP`向导从`WSDL` IRIS `web服务`或客户端, IRIS将此关键字设置为适合该WSDL的。 # 对Message的影响 对于前面显示的`web方法`,`web服务`需要以下形式的请求消息: ```xml 12 ``` 相反,如果该方法没有指定`SoapRequestMessage`关键字,则该消息将如下所示: ```xml 12 ``` # 第八十二章 方法关键字 - SoapTypeNameSpace 为此`web方法`使用的类型指定`XML`命名空间。仅适用于定义为`web服务`或`web客户端`的类。 # 用法 若要重写类型的默认`XML`命名空间(当该方法用作web方法时),请使用以下语法: ```java Method name(formal_spec) As returnclass [ SoapTypeNameSpace = "soapnamespace", SoapBindingStyle = document, WebMethod ] { //implementation } ``` 其中`soapnamespace`是命名空间`URI`。请注意,如果`URI`包含冒号(`:`),则该字符串必须加引号。也就是说,可以使用以下内容: ```java Method MyMethod() [ SoapTypeNameSpace = "http://www.mynamespace.org", SoapBindingStyle = document, WebMethod ] ``` 或以下内容: ```java Method MyMethod() [ SoapTypeNameSpace = othervalue, SoapBindingStyle = document, WebMethod ] ``` 但不包括以下内容: ```java Method MyMethod() [ SoapTypeNameSpace = http://www.mynamespace.org, SoapBindingStyle = document, WebMethod ] ``` 重要提示:对于手动创建的`web服务`,该关键字的默认值通常是合适的。当使用SOAP向导从`WSDL`生成`web客户端`或服务时,InterSystems IRIS会将该关键字设置为适合该`WSDL`;如果修改该值,`web客户端`或服务可能不再工作。 # 详解 此关键字指定此`web方法`使用的类型的XML命名空间。 注意:只有当方法使用文档样式绑定时,此关键字才有作用。也就是说,方法(或包含它的类)必须用等于`document`的`SoapBindingStyle`标记。(对于使用`rpc-style`绑定的方法,指定这个关键字是没有意义的。) # 默认 如果省略此关键字,则此方法的类型位于由`web服务`或`客户端`类的`TYPENAMESPACE`参数指定的命名空间中。如果未指定`TYPENAMESPACE`,则类型将位于由`web服务`或客户端的`are`参数指定的命名空间中。 # 与WSDL的关系 `SoapTypeNameSpace`关键字影响`WSDL`的以下部分: ``元素中的命名空间声明。指定的命名空间(例如,`http://www.customtypes.org`)将添加到这里。例如: ```xml ... xmlns:ns2="http://www.customtypes.org" xmlns:s0="http://www.wbns.org" xmlns:s1="http://webservicetypesns.org" ... targetNamespace="http://www.wbns.org" ``` 在本例中,`http://www.customtypes.org`命名空间被分配给前缀`ns2`。 请注意,`WSDL`还像往常一样声明了以下名称空间: - `Web服务`的命名空间(`http://www.wsns.org`),在本例中,它被分配给前缀`s0`,也用作`Web服务`的目标命名空间。 - 网络服务的类型命名空间`http://www.webservicetypesns.org`),在本例中它被分配给`前缀s1`。 如果在`web服务`类中没有指定类型命名空间,则该命名空间不包含在`WSDL`中。 - ``元素,它包含一个``元素,该元素的`targetNamespace`属性等于为`SoapTypeNameSpace`指定的命名空间: ```xml ... ... ``` 相反,如果没有指定`SoapTypeNameSpace`,那么`WSDL`的这一部分将如下所示。请注意,``元素的`targetNamespace`是`web服务`类型的命名空间: ```xml ... ... ``` (此外,如果在`web服务类`中没有指定类型命名空间,则`targetNamespace`将改为`web服务`的命名空间。) # 对消息的影响 `SOAP`消息可能如下所示(为了可读性,添加了换行符和空格): ```xml 3 ``` 请注意,``元素位于`“http://www.customtypes.org”`命名空间中。 相反,如果没有指定`SoapTypeNameSpace`关键字,则消息可以如下所示: ```xml 3 ``` # 第八十三章 方法关键字 - SqlName 覆盖投影`SQL`存储过程的默认名称。 仅当此方法被投影为`SQL`存储过程时应用。 # 用法 要覆盖方法投射为`SQL`存储过程时使用的默认名称,请使用以下语法: ```java ClassMethod name(formal_spec) As returnclass [ SqlProc, SqlName = sqlname ] { //implementation } ``` 其中`sqlname`是`SQL`标识符。 # 详解 如果将此方法投影为`SQL`存储过程,则使用此名称作为存储过程的名称。 # 默认 如果忽略这个关键字, IRIS确定`SQL`名称如下: ```java CLASSNAME_METHODNAME ``` 默认使用大写字母。 但是,在调用存储过程时可以使用任何情况,因为SQL是不区分大小写的。 因此,在下面的示例中,默认的`SQL name`值是`TEST1_PROC1`。 这个默认值是在`SELECT`语句中指定的: ```java Class User.Test1 Extends %Persistent { ClassMethod Proc1(BO,SUM) As %INTEGER [ SqlProc ] { ///definition not shown } Query Q1(KD As %String,P1 As %String,P2 As %String) As %SqlQuery { SELECT SUM(SQLUser.TEST1_PROC1(1,2)) AS Sumd FROM SQLUser.Test1 } } ``` # 第八十四章 方法关键字 - SqlProc 指定是否可以作为`SQL`存储过程调用该方法。 只有类方法(而不是实例方法)可以作为SQL存储过程调用。 # 用法 要指定该方法可以作为`SQL`存储过程调用,请使用以下语法: ```java ClassMethod name(formal_spec) As returnclass [ SqlProc ] { //implementation } ``` 否则,忽略该关键字或将`Not`放在该关键字之前。 # 详解 该关键字指定可以作为`SQL`存储过程调用该方法。 只有类方法(而不是实例方法)可以作为`SQL`存储过程调用。 存储过程由子类继承。 # 默认 如果忽略此关键字,则该方法作为`SQL`存储过程不可用。
文章
姚 鑫 · 八月 15, 2022

第二章 使用管理门户(二)

# 第二章 使用管理门户(二) # 管理门户概述 本节介绍管理门户页面的一些常见布局元素。 注意:在管理门户中的任何位置,将光标移到菜单项上都会显示该项目的描述。 ## 管理门户主页 管理门户主页的标题是 `Welcome, `。在标题旁边,功能区包含以下选项: - 两个视图按钮,可让指定如何在菜单列中显示链接。 - 搜索栏,位于功能区的右侧。当指定一个词并按 Enter 键时,将显示包含该词的所有页面的列表;然后,可以单击要显示的目标页面,而无需浏览子菜单。 以下部分描述了主页的区域: ### 管理门户菜单栏 位于主页左边缘的菜单栏是导航门户的主要方法。 ### 管理门户欢迎窗格 欢迎窗格位于主页的中心,包括经常访问的页面的快捷方式。它包含以下字段: - 收藏夹`Favorites` — 列出选择为收藏夹的管理门户页面(请参阅操作窗格);可以单击每个页面标题直接转到该页面。 - 最近`Recent` — 列出自上次启动 IRIS 以来最近显示的页面。 - `Did you know?` — 显示提示。 - 链接 `Links` - 指向可能想要访问的页面的链接。 ### 管理门户消息窗格 位于主页右侧边缘的消息窗格显示一般系统信息并提供指向系统仪表板的链接。 如果实例是镜像成员,则消息窗格还显示它所属的镜像、其状态和成员类型以及指向镜像监视器的链接。 ## 管理门户标题 页眉位于管理门户中每个页面的顶部,可用于快速导航门户。 标题包含以下链接: - 主页`Home` — 显示管理门户主页。 - 关于`About` — 显示系统概览信息。 - 帮助`Help` — 显示正在查看的页面/主题的在线文档(帮助)。 - 联系方式`Contact` — 显示全球响应中心 (WRC) 的联系方式页面。 - 注销`Logout` — 注销您并带您进入管理门户的登录页面。 - 菜单`Menu` — 根据用户担任的角色显示常见任务列表。 标头还包含有用的信息,例如: - 服务器`Server` — 运行 IRIS 的服务器的名称。 - 命名空间`Namespace` — 当前使用的命名空间的名称。要在不同的命名空间中工作,请单击切换并选择所需的命名空间。 - 用户`User` — 登录到管理门户的用户的名称。要更改用户的密码,请单击名称。 - 许可证`Licensed to` — 出现在许可证密钥信息中的客户名称。 - 实例`Instance` — 服务器上运行的 `IRIS` 实例的名称。 此外,可以显示系统模式标签(例如,测试系统); 管理门户标题的左侧显示正在使用的产品的名称。 ## 管理门户功能区 功能区位于标题正下方,并显示特定于每个页面的不同内容。例如,数据库页面(`System Explorer` > `Databases`)的功能区如下图所示: 功能区的典型内容是: - 正在显示的管理门户页面的标题。 - 当前页面的面包屑,直接列在页面标题上方。路径中列出的每个页面都是一个活动链接,可以使用它返回到先前显示的子菜单/列表。当在页面上进行未保存的更改时,会在面包屑中附加一个星号,例如系统 > 配置 >内存和启动 —(配置设置)*。在离开未保存的更改之前,系统始终会提示进行确认。 注意:页签不会列出路径中的每个页面,并且页签中的页面并不总是与导航菜单中的页面匹配。始终可以通过单击主页返回到管理门户主页并使用搜索工具导航到特定页面,本节稍后将对此进行介绍。 - 允许在页面上执行操作的几个按钮。例如,`Databases` 页面包含按钮 `Integrity Check` 和 `Integrity Log`。 - 刷新按钮,包含有关页面上次更新时间的信息。 ## 系统概述信息 单击管理门户标题上的关于时,将显示一个表格,其中包含以下信息: - 版本 — 此 `IRIS` 实例的特定构建信息,包括平台、构建号和构建日期。 - 配置 - 此实例正在使用的配置 (`.cpf`) 文件的名称和位置。 - 数据库缓存 (`MB`) — 为数据库分配的空间例程缓存 (`MB`) — 为例程分配的空间。 - 日志文件 - 当前日志文件的名称和位置。 - `SuperServer` 端口 — 运行 `IRIS` 服务器的端口号。 - `Web`服务器端口 — 运行私有 `IRIS Web` 服务器的端口号。 - 许可证服务器地址/端口 — `IRIS` 许可证服务器的 `IP` 地址和运行它的端口号。 - 许可给 — 出现在许可密钥信息中的客户名称。 - 集群支持 - 指示此实例是否是集群的一部分。 - 镜像 — 指示此实例是否是镜像的成员。 - `Time System Started` — 上次启动 `InterSystems IRIS` 实例的日期和时间。 - 加密密钥标识符 — 如果激活加密,则为加密密钥的 `GUID`(全局唯一 `ID`)。 - `NLS` 区域设置 — 国家语言支持区域设置。 - 此会话的首选语言 - 管理门户已本地化并可显示的语言的下拉列表。可以通过从下拉列表中选择新的语言来更改显示语言。最初,浏览会话的首选语言是为浏览器指定的语言,如果不支持浏览器语言,则为英语;在特定浏览器中选择首选语言后,即使更改了浏览器语言,该浏览器中的管理门户也会使用该语言。
文章
姚 鑫 · 七月 21, 2021

关键字参数定义,映射定义,属性定义,查询定义

# 第五章 参数定义 描述参数定义的结构。 # 介绍 参数定义定义了一个给定类的所有对象都可用的常数值。创建类定义时(或在编译前的任何时候),可以设置其类参数的值。默认情况下,每个参数的值都是空字符串,但是可以在参数定义中指定一个非空值。在编译时,为类的所有实例建立参数值。除了极少数例外,该值不能在运行时更改。 # 详解 参数定义具有以下结构: ```java /// description Parameter name As parameter_type [ keyword_list ] = value ; ``` - `description`描述(可选)旨在显示在“类参考”中。默认情况下,描述为空白。 - `name`(必需)是参数的名称。这必须是有效的类成员名称,并且不能与任何其他类成员名称冲突。 - `parameter_type`(可选)指定参数的用户界面类型,由Studio用于在检查器内为参数提供输入验证。 **这不是类名;参见下一节。在大多数情况下,编译器会忽略这个关键字。** 如果省略参数类型,也要省略单词`As` - `value`(可选)指定参数的值。如果省略值,也要省略等号= - `keyword_list`(可选)是以逗号分隔的关键字列表,用于进一步定义参数。 如果省略此列表,也要省略方括号。 # 参数的允许类型 参数类型`parameter_type` 选项可以是下列值之一: - `BOOLEAN` — true(1)或false(0)值。 - `CLASSNAME` — 有效的类名。 - `COSCODE` — ObjectScript代码。 - `COSEXPRESSION` — 有效的ObjectScript表达式。 **如果参数是`COSEXPRESSION`类型,则在运行时计算该表达式。** 与形参`Type`关键字的大多数其他值不同,这个值影响编译器。 - `COSIDENTIFIER` — 有效的ObjectScript标识符。 - `INTEGER` — 整数值。 - `SQL` — SQL语句 - `SQLIDENTIFIER` — 有效的SQL标识符。 - `STRING` —字符串值。 - `TEXT` — 多行文本值。 - `CONFIGVALUE` -可以在类定义之外修改的参数。 与形参`Type`关键字的大多数其他值不同,这个值影响编译器。 如果参数的类型是`CONFIGVALUE`,那么可以通过`$SYSTEM.OBJ.UpdateConfigParam()`修改参数。 例如,下面的代码更改了参数`MYPARM`(在类`MyApp`中)的值。 `MyClass`的新值为`42`: ```java set sc=$system.OBJ.UpdateConfigParam("MyApp.MyClass","MYPARM",42) ``` **注意,`$SYSTEM.OBJ.UpdateConfigParam()`影响任何新进程所使用的生成的类描述符,但不影响类定义。 如果重新编译类,InterSystems IRIS将重新生成类描述符,该描述符现在将使用包含在类定义中的这个参数的值(从而覆盖通过`$SYSTEM.OBJ.UpdateConfigParam()`所做的更改)。** 也可以省略`parameter_type`,在这种情况下`Inspector`将允许参数的任何值。 ``` /// web服务的名称。 Parameter SERVICENAME = "SOAPDemo" ; ``` # 第六章 映射定义 描述投影定义的结构。 # 介绍 投影定义指示类编译器在编译或删除类定义时执行指定的操作。 投影定义投影类的名称(来自`%Projection.AbstractProjection`)实现方法称为类的编译完成后,当一个类定义中删除(因为它被删除或者因为类即将重新编译)。 # 详情 投影定义有以下结构: ```java /// description Projection name As projection_class (parameter_list) ; ``` - `description`(可选)用于在类引用中显示(但请注意投影目前没有显示在类引用中)。 说明默认为空。 - `Name`(必需)是投影的名称。 这必须是一个有效的类成员名,并且不能与任何其他类成员名冲突。 - `projection_class` (required)是投影类的名称,它是`%Projection.AbstractProjection`的子类。 - `parameter_list`(可选)是一个以逗号分隔的参数及其值列表。 如果指定,这些应该是`projection_class`使用的参数。 如果省略了这个列表,也省略了括号。 - `Keyword_list`(可选)是一个逗号分隔的关键字列表,这些关键字进一步定义了投影。 如果省略了这个列表,也可以省略方括号。 # 第七章 属性定义 描述属性定义的结构。注意,关系是一种属性。 # 介绍 属性包含与类实例相关的信息。可以向对象类添加属性定义。它们在其他类中没有意义。 # 详情 属性定义有以下结构: ```java /// description Property name As classname (parameter_list) [ keyword_list ] ; ``` 或者(对于列表属性): ```java /// description Property name As List Of classname (parameter_list) [ keyword_list ] ; ``` 或者(对于数组属性): ```java /// description Property name As Array Of classname (parameter_list) [ keyword_list ] ; ``` 或者(对于关系属性): ```java /// description Relationship name As classname [ keyword_list ] ; ``` - `description`(可选)用于在类引用中显示。说明默认为空。 - `name`(必需)是属性的名称。 这必须是一个有效的类成员名,并且不能与任何其他类成员名冲突。 - `classname`(可选)是该属性所基于的类的名称。 - `parameter_list`(可选)是参数及其值的逗号分隔列表。如果指定,这些应该是由类名使用的参数,或者是对所有属性都可用的参数。 如果省略此列表,也要省略括号。 - `keyword_list`(对于关系属性是必需的,但在其他方面是可选的)是一个逗号分隔的关键字列表,用于进一步定义属性。 如果省略此列表,也要省略方括号。 注意:分片类不支持属性关系。 ```java Property SSN As %String(PATTERN = "3N1""-""2N1""-""4N") [ Required ]; ``` # 第八章 查询定义 描述查询定义的结构。 # 介绍 类查询是作为类结构一部分的命名查询,可以通过动态SQL进行访问。 **可以在任何类中定义类查询;不需要将它们包含在持久类中。** # 详解 查询定义具有以下结构: ```java /// description Query name(formal_spec) As classname [ keyword_list ] { implementation } ``` - `description`描述(可选)旨在显示在“类别参考”中。默认情况下,描述为空白。 - `name`(必需)是查询的名称。这必须是有效的类成员名称,并且不能与任何其他类成员名称冲突。 - `formal_spec`(可选)指定传递给查询的参数列表。 具体来说,这是通过关联查询类的`Execute()`方法传递给查询的参数列表。 - `classname`(必需)指定此查询使用的查询类。 **对于基于SQL的查询,该值通常为`%SQLQuery`,对于自定义查询,该值通常为%Query。** **注意:分片类不支持自定义类查询。** - `keyword_list`(可选)是逗号分隔的关键字列表,用于进一步定义查询。 如果省略此列表,也要省略方括号。 - `implementation` 实现(可选)是定义查询的零行或多行代码。
文章
姚 鑫 · 六月 14, 2022

第九章 其他参考资料(二)

# 第九章 其他参考资料(二) # 特殊变量 (SQL) 系统提供的变量。 ```sql $HOROLOG $JOB $NAMESPACE $TLEVEL $USERNAME $ZHOROLOG $ZJOB $ZPI $ZTIMESTAMP $ZTIMEZONE $ZVERSION ``` SQL直接支持许多对象脚本特殊变量。这些变量包含系统提供的值。只要可以在SQL中指定文字值,就可以使用它们。 SQL特殊变量名不区分大小写。大多数可以使用缩写来指定。 Variable| Name| Abbreviation| Data Type Returned Use ---|---|---|--- $HOROLOG| $H| %String/VARCHAR |当前进程的本地日期和时间 $JOB| $J| %String/VARCHAR |当前进程的 job ID $NAMESPACE| none| %String/VARCHAR |当前命名空间名称 $TLEVEL| $TL| %Integer/INTEGER|| 当前事务嵌套级别 $USERNAME| none| %String/VARCHAR|当前进程的用户名 $ZHOROLOG| $ZH |%Numeric/NUMERIC(21,6)|自InterSystems IRIS启动后经过的秒数 $ZJOB| $ZJ| %Integer/INTEGER|当前进程的job状态 $ZPI| none| %Numeric/NUMERIC(21,18) |数值常量PI $ZTIMESTAMP |$ZTS| %String/VARCHAR |协调世界时间格式的当前日期和时间 $ZTIMEZONE| $ZTZ| %Integer/INTEGER| 当地时区与GMT的偏移量 $ZVERSION| $ZV| %String/VARCHAR| IRIS的当前版本 # 示例 ```sql SELECT TOP 5 Name,$H FROM Sample.Person ``` 以下示例仅在时区位于大陆内时才返回结果集: ```sql SELECT TOP 5 Name,Home_State FROM Sample.Person WHERE $ZTIMEZONE BETWEEN -480 AND 480 ``` # 字符串操作(SQL) 字符串操作函数和运算符。 SQL 支持多种类型的字符串操作: - 字符串可以通过长度、字符位置或子字符串值进行操作。 - 字符串可以通过指定的分隔符或分隔符字符串来操作。 - 字符串可以通过模式匹配和单词感知搜索来测试。 - 特殊编码的字符串(称为列表)包含嵌入的子字符串标识符,而不使用分隔符。各种 `$LIST` 函数对这些与标准字符串不兼容的编码字符串进行操作。唯一的例外是 `$LISTGET` 函数和 `$LIST` 的单参数和双参数形式,它们将编码字符串作为输入,但将单个元素值作为标准字符串输出。 SQL 支持字符串函数、字符串条件表达式和字符串运算符。 ObjectScript 字符串操作区分大小写。字符串中的字母可以转换为大写、小写或混合大小写。字符串排序规则可以区分大小写,也可以不区分大小写;默认情况下,SQL 字符串排序规则是不区分大小写的 `SQLUPPER`。 SQL 提供了许多字母大小写和排序规则函数和运算符。 当为数字参数指定字符串时,大多数 SQL 函数执行以下字符串到数字的转换: 非数字字符串转换为数字 0;将数字字符串转换为规范数字;并且混合数字字符串在第一个非数字字符处被截断,然后转换为规范数字。 # 字符串连接 以下函数将子字符串连接成字符串: - `CONCAT`:连接两个子字符串,返回一个字符串。 - `STRING`:连接两个或多个子字符串,返回单个字符串。 - `XMLAGG`:连接列的所有值,返回单个字符串。 - `LIST`:连接列的所有值,包括逗号分隔符,返回单个字符串。 - 连接运算符 (`||`) 也可用于连接两个字符串。 # 字符串长度 以下函数可用于确定字符串的长度: - `CHARACTER_LENGTH` 和 `CHAR_LENGTH`:返回字符串中的字符数,包括尾随空格。 `NULL` 返回 `NULL`。 - `LENGTH`:返回字符串中的字符数,不包括尾随空格。 `NULL` 返回 NULL。 - `$LENGTH`:返回字符串中的字符数,包括尾随空格。 `NULL` 返回为 0。 # Truncation and Trim 以下函数可用于截断或修剪字符串。截断限制字符串的长度,删除超出指定长度的所有字符。`Trim`从字符串中删除前导和/或尾随空格。 - `Truncation`: `CONVERT`, `%SQLSTRING`, and `%SQLUPPER`. - `Trimming`: `TRIM`, `LTRIM`, and `RTRIM`. # 子串搜索 以下函数在字符串中搜索子字符串并返回字符串位置: - `POSITION`:按子字符串值搜索,找到第一个匹配项,返回子字符串开始的位置。 - `CHARINDEX`:按子字符串值搜索,找到第一个匹配项,返回子字符串开始的位置。可以指定起点。 - `$FIND`:按子串值搜索,找到第一个匹配项,返回子串结束的位置。可以指定起点。 - `INSTR`:按子字符串值搜索,找到第一个匹配项,返回子字符串开始的位置。可以指定起点和子串出现。 以下函数在字符串中按位置或分隔符搜索子字符串并返回子字符串: - `$EXTRACT`:按字符串位置搜索,返回由开始位置或开始和结束位置指定的子字符串。从字符串的开头搜索。 - `SUBSTRING`:按字符串位置搜索,返回由开始位置或开始和长度指定的子字符串。从字符串的开头搜索。 - `SUBSTR`:按字符串位置搜索,返回由起始位置或起始和长度指定的子字符串。从字符串的开头或结尾搜索。 - `$PIECE`:按分隔符搜索,返回第一个分隔的子字符串。可以指定起点或默认为字符串的开头。 - `$LENGTH`:按分隔符搜索,返回分隔子串的数量。从字符串的开头搜索。 - `$LIST`:在特殊编码的列表字符串上按子字符串计数搜索。它通过子串计数定位子串并返回子串值。从字符串的开头搜索。 - 包含运算符 (`[`) 也可用于确定子字符串是否出现在字符串中。 - `%STARTSWITH` 比较运算符将指定的字符与字符串的开头进行匹配。 # 子串搜索和替换 以下函数在字符串中搜索子字符串并将其替换为另一个子字符串。 - `REPLACE`:按字符串值搜索,用新的子字符串替换子字符串。从字符串的开头搜索。 - `STUFF`:按字符串位置和长度搜索,用新的子字符串替换子字符串。从字符串的开头搜索。 # 字符类型和 Word-Aware 比较 `%PATTERN` 比较运算符将字符串与指定的字符类型模式匹配。
文章
姚 鑫 · 六月 24, 2021

第十七章 加密XML文档

# 第十七章 加密XML文档 本章介绍如何加密XML文档。 提示:发现在此命名空间中启用`SOAP`日志记录非常有用,这样就可以收到有关任何错误的更多信息。 # 关于加密的XML文档 加密的XML文档包括以下元素: - ``元素,其中包含由随机生成的对称密钥加密的加密数据。(使用对称密钥加密比使用公钥加密更有效。) - 至少有一个``元素。每个``元素携带用于加密数据的对称密钥的加密副本;它还包含一个带有公钥的`X.509`证书。拥有匹配私钥的接收方可以解密对称密钥,然后解密``元素。 - (可选)其他明文元素。 ```xml MIICnDCCAYQCAWUwDQYJKo... content omitted J2DjVgcB8vQx3UCy5uejMB ... content omitted LmoBK7+nDelTOsC3 ... content omitted ``` 要创建加密文档,请使用类`%XML.Security.EncryptedData`和`%XML.Security.EncryptedKey`。这些启用XML的类投影到适当名称空间中的有效``和``元素。 # 创建加密的XML文档 创建加密的XML文档的最简单方法如下: 1. 定义并使用可以直接投影到所需XML文档的通用容器类。 2. 创建包含要加密的XML的流。 3. 加密该流,并将其与相应的加密密钥一起写入容器类的相应属性。 4. 为容器类生成XML输出。 ## 加密的前提条件 在加密文档之前,必须创建包含要将加密文档发送到的实体的证书的 IRIS凭据集。在这种情况下,不需要(也不应该拥有)关联的私钥。 ## 容器类的要求 一个通用容器类必须包括以下内容: - 类型为`%XML.Security`的属性。 被投影为``元素的`EncryptedData`。 这个属性将携带加密的数据。 - 至少一个类型为`%XML.Security`的属性。被投影为``元素的`EncryptedKey`。 这些属性将携带相应的密钥信息。 示例如下: ```java Class XMLEncryption.Container Extends (%RegisteredObject, %XML.Adaptor) { Property Data As %XML.Security.EncryptedData(XMLNAME = "EncryptedData"); Property Key As %XML.Security.EncryptedKey(XMLNAME = "EncryptedKey"); Parameter NAMESPACE = "http://www.w3.org/2001/04/xmlenc#"; } ``` ## 生成加密的XML文档 要生成并编写加密文档,请执行以下操作: 1. 创建包含XML文档的流。 为此,通常使用`%XML.Writer`将启用XML的对象的输出写入流。 2. 创建`%SYS.X509Credentials`的至少一个实例,将访问要向其提供加密文档的实体的InterSystems IRIS凭据集。为此,请调用此类的`GetByAlias()`类方法。例如: ```java set credset=##class(%SYS.X509Credentials).GetByAlias("recipient") ``` 若要运行此方法,必须以该凭据集的`OwnerList`中包含的用户身份登录,否则`OwnerList`必须为空。 3. 至少创建`%XML.Security.EncryptedKey`实例。若要创建此类的实例,请使用此类的`CreateX509()`类方法。例如: ```java set enckey=##class(%XML.Security.EncryptedKey).Createx509(credset,encryptionOptions,referenceOption) ``` - `credset`是`%SYS`的实例。 `x509credentials`在刚刚创建的新窗口中打开。 - `encryptionOptions`是`$$$SOAPWSIncludeNone`(还有其他选项,但它们不适用于此场景)。 此宏在`%soap.inc`包含文件中定义。 - `referenceOption`指定了对加密元素的引用的性质。 这里使用的宏在`%soap.inc`包含文件中定义。 4. 在创建`%Library.ListOfObjects`实例,并使用其`Insert()`方法在刚创建插入`%XML.Security.EncryptedKey`实例。 5. 使用`%New()`方法创建`%XML.Security.EncryptedData`实例。例如: ```java set encdata=##class(%XML.Security.EncryptedData).%New() ``` 6. 使用`%XML.Security.EncryptedData的EncryptStream()`实例方法加密在步骤2中创建的流。例如: ```java set status=encdata.EncryptStream(stream,encryptedKeys) ``` - stream 流是在步骤1中创建的流。 - encryptedKeys是在步骤4中创建的密钥列表。 7. 创建并更新容器类的实例。 - 将键列表写入此类的相应属性。 - 将 `%XML.Security.EncryptedData`的实例写入此类的相应属性。 8. 使用`%XML.Writer`为容器类生成输出。 例如,前面显示的`CONTAINER`类还包括以下方法: ```java /// w ##class(XMLEncryption.Container).Demo("E:\temp\SecurityXml.txt") ClassMethod Demo(filename = "", obj = "") { #Include %soap if (obj = "") { s obj = ##class(MyApp.Person).%OpenId(1) } //从此启用XML的对象创建流 set writer = ##class(%XML.Writer).%New() set stream = ##class(%GlobalCharacterStream).%New() set status = writer.OutputToStream(stream) if $$$ISERR(status) {do $System.Status.DisplayError(status) quit } set status = writer.RootObject(obj) if $$$ISERR(status) {do $System.Status.DisplayError(status) quit } do stream.Rewind() set container = ..%New() ; 这就是我们要写出的对象 set cred = ##class(%SYS.X509Credentials).GetByAlias("servercred") set parts =$$$SOAPWSIncludeNone set ref = $$$KeyInfoX509Certificate set key = ##class(%XML.Security.EncryptedKey).CreateX509(cred, parts, ref) set container.Key = key ; 这个细节取决于类 //需要创建一个键列表(本例中仅为一个) set keys = ##class(%Collection.ListOfObj).%New() do keys.Insert(key) set encdata = ##class(%XML.Security.EncryptedData).%New() set status = encdata.EncryptStream(stream, keys) set container.Data = encdata ; 这个细节取决于类 // 为容器写输出 set writer = ##class(%XML.Writer).%New() set writer.Indent = 1 if (filename'="") { set status = writer.OutputToFile(filename) if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit} } set status = writer.RootObject(container) if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit} } ``` 此方法可以接受任何启用XML的类的`OREF`;如果没有提供,则使用默认值。 # 解密加密的XML文件 ## 解密的前提条件 在解密加密的`XML`文档之前,必须同时提供以下两项: - IRIS要使用的受信任证书。 - IRIS凭据集,其私钥与加密中使用的公钥匹配。 ## 解密文档 要解密加密的XML文档,请执行以下操作: 1. 创建`%XML.Reader`实例打开并使用它打开文档。 2. 获取`Document`属性,`%XML.Reader`实例。 其中包含作为DOM的XML文档。 3. 使用阅读器的`correlation()`方法将``元素或元素与类`%XML.Security.EncryptedKey`关联起来。 例如: ```java do reader.Correlate("EncryptedKey","%XML.Security.EncryptedKey") ``` 4. 遍历文档以读取``元素或多个元素。 为此,可以使用阅读器的`Next()`方法,该方法通过引用返回一个导入的对象(如果有的话)。 例如: ```java if 'reader.Next(.ikey,.status) { write !,"Unable to import key",! do $system.OBJ.DisplayError(status) quit } ``` 导入的对象`是%XML.Security.EncryptedKey`的实例。 5. 创建`%Library.ListOfObjects`的实例。 并使用它的`Insert()`方法插入`%XML.Security.EncryptedKey`的实例。 刚从文档中获得的。 6. 调用类`%XML.Security.EncryptedData`的`ValidateDocument()`方法 ```java set status=##class(%XML.Security.EncryptedData).ValidateDocument(.doc,keys) ``` 第一个参数(通过引用返回)是在第2步中检索到的DOM的修改版本。 第二个参数是上一步中的键列表。 7. 可以选择使用`%XML.Writer`为修改后的DOM生成输出。 例如,前面显示的`CONTAINER`类包含以下类方法: ```java ClassMethod DecryptDoc(filename As %String) { #Include %soap set reader = ##class(%XML.Reader).%New() set status = reader.OpenFile(filename) if $$$ISERR(status) {do $System.Status.DisplayError(status) quit } set doc = reader.Document //获取元素 do reader.Correlate("EncryptedKey","%XML.Security.EncryptedKey") if 'reader.Next(.ikey,.status) { write !,"无法导入密钥",! do $system.OBJ.DisplayError(status) quit } set keys = ##class(%Collection.ListOfObj).%New() do keys.Insert(ikey) // 以下步骤返回解密的文档 set status = ##class(%XML.Security.EncryptedData).ValidateDocument(.doc,keys) set writer = ##class(%XML.Writer).%New() set writer.Indent = 1 do writer.Document(doc) quit $$$OK } ```
文章
姚 鑫 · 六月 15, 2021

第七章 控制命名空间分配的外观

# 第七章 控制命名空间分配的外观 # 控制命名空间分配的外观 除了控制命名空间分配外,还可以控制命名空间分配在XML输出中的显示方式。具体地说,可以控制以下内容: ## 显式名称空间分配与隐式名称空间分配 将元素和属性分配给命名空间时,XML中有两种等效的表示形式,由编写器实例的`SuppressXmlns`属性控制。 为一个名为`Person`的对象生成XML输出,该对象被分配给名称空间`“http://www.person.org”`(通过前面讨论的`namespace`类参数)。 使用缺省输出(`SuppressXmlns`等于0)的示例如下: ```java Uberoth,Amanda Q. 1952-01-13 ``` 另一种可能的形式完全相同,如下所示。 这是使用`SuppressXmlns`等于1生成的,它确保显式分配给名称空间的每个元素都显示为该名称空间的前缀。 ```java Uberoth,Amanda Q. 1952-01-13 ``` 请注意,此属性仅影响命名空间分配的显示方式;它不控制如何分配任何命名空间。如果不使用命名空间,则此参数无效。 ## 为命名空间指定自定义前缀 当为对象生成XML输出时,系统会根据需要生成命名空间前缀。第一个名称空间前缀是`s01`,下一个是`s02`,依此类推。可以指定不同的前缀。为此,请在启用XML的对象本身的类定义中设置`XMLPREFIX`参数。此参数有两个效果: - 它确保在XML输出中声明指定的前缀。也就是说,即使没有必要这样做,它也会被声明。 - 它使用该前缀,而不是在其他情况下会看到的自动生成的前缀。 # 控制空字符串(`""`)的导出方式 为对象启用XML时,需要指定将空值和空字符串投影到XML的方式 其中一个选项是在支持xml的类中将`XMLIGNORENULL`设置为`“RUNTIME”`(不区分大小写)。 在这种情况下,当使用%XML.Write的`RuntimeIgnoreNull`属性的值来确定如何处理任何等于`""`的属性,如下所示: - **如果编写器的`RuntimeIgnoreNull`属性为0(默认值),则`XMLNIL`参数控制如何导出该属性。`XMLNIL`是一个类参数和一个属性参数;属性参数优先。** - 如果`XMLNIL`为0(默认值),则不投影特性。也就是说,它不包含在XML文档中。 - 如果`XMLNIL`为1,并且该属性用作元素,则该属性将按如下方式导出: ``` ``` - 如果`XMLNIL`为1并且特性用作属性,则不会输出特性。 - 如果编写器的`RuntimeIgnoreNull`属性为1,则该属性将导出为空元素或空属性(其导出方式与值`$char(0)`相同,后者始终导出为空元素或空导出)。 除非`XMLIGNORENULL`在启用xml的类中是`“RUNTIME”`,否则编写器的`RuntimeIgnoreNull`属性是无效的。 ## 示例:`RuntimeIgnoreNull`为0(默认值) ```java Class EmptyStrings.Export Extends (%Persistent, %XML.Adaptor) { Parameter XMLNAME="Test"; Parameter XMLIGNORENULL = "RUNTIME"; ///把这个作为一个元素 ///XMLNIL is 0, the default Property Property1 As %String; ///把这个作为一个元素 ///XMLNIL is 0, the default Property Property2 As %String(XMLPROJECTION = "ATTRIBUTE"); ///将其作为XMLNIL=1的元素 Property Property3 As %String(XMLNIL = 1); ///将其作为XMLNIL=1的属性进行项目 Property Property4 As %String(XMLNIL=1,XMLPROJECTION="ATTRIBUTE"); } ``` 如果创建了这个类的一个新实例(并且没有设置任何属性的值),然后使用 `%XML.Writer`输出,如下所示: ```java ``` ## 例如:`RuntimeIgnoreNull`为1 ```java ``` **在本例中,因为`RuntimeIgnoreNull`为1,所以没有使用`XMLNIL`参数。 相反,`""`被导出为空属性或空元素。** # 导出类型信息 默认情况下,XML编写器不写入类型信息。有两个选项可用于在输出中包括类型信息: - 编写器的`OutputTypeAttribute`属性。如果此属性为1,则编写器包括其写入的对象内所有元素的XML类型信息(但不包括对象本身)。例如: ```java Petersburg,Greta U. 1949-05-15 ``` 请注意,相应的命名空间将添加到XML文档的根。 - `Object()`和`RootObject()`方法的`className`参数。此参数用于指定对象的预期`ObjectScript`类型(类名)。 如果参数与实际类型相同,则编写器不包括对象的类型信息。 如果参数与实际类型不同,编写器将包括对象的实际XML类型(默认为类名)。例如,假设`Test2.PersonWithAddress`实例编写输出,并将`className`参数指定为`MyPackage.MyClass`。因为`MyPackage.MyClass`与实际的类名不同,所以编写器生成以下输出: ```java Avery,Robert H. Ukiah 82281 ``` # 生成SOAP编码的XML 对于`%XML.Writer`,`Format`属性控制输出的整体格式。这是以下选项之一: - `“literal”`,即默认值,在本书的大多数例子中都使用了它。 - `“encoded”`,按照SOAP 1.1标准中的描述进行编码。 - `“encoded12”`,按照SOAP 1.2标准中的描述进行编码。 ## 创建内联引用 在编码格式中,任何对象值属性都被作为引用包含,被引用的对象被导出为单独的元素。 要将这些属性内联导出,而不是作为单独的元素,请将`ReferencesInline`属性设置为1。 如果格式是`“literal”`,`ReferencesInline`属性没有效果。 # 导出后控制unswizling 当导出一个支持xml的持久对象时,系统会像往常一样自动将所有需要的信息混合到内存中;该信息包括对象值属性。导出对象后,InterSystems IRIS将消除任何对象列表,但(默认情况下)不会消除单个对象引用。 对于大对象,这可能导致``错误。 在这种情况下,要使任何单个对象引用不被混合,请在支持xml的类中设置`XMLUNSWIZZLE`参数,如下所示: ```java Parameter XMLUNSWIZZLE = 1; ``` 该参数默认值为0。 # 控制元素的关闭 只包含属性的元素可以用以下任一方式表示: ```java ``` `Object()`方法始终使用第一个语法导出元素。如果需要使用此处显示的第二种语法关闭元素,请手动编写对象,如本章前面的“手动构造元素”中所述。 咨询一下,我也是初学,有没有类似于oracle的exp远程备份数据库的方法?
文章
王喆 👀 · 九月 13, 2022

IRIS快速查询服务思路分享

背景 作为集成平台厂商,在医院同其它系统联调的时候所做的事情中,多的不是开发代码而是查消息。我先演示一下目前我正在使用的IRIS查消息的方式: 例子1: 需要看【个人信息注册服务】 我只需要在框中输入【个人信息注册】回车 点击【查看消息】,显示的是消息里面的内容,如图所示: 点击【查看流程】,显示的是IRIS消息可视化的页面,如图所示: 例子2: 需要查询患者ID为【2874621】这患者在集成平台调用了哪些服务 我只需要选择下拉选择【患者ID】,然后输入【2874621】,回车 这个是我们以用户需求的角度直观的查询到指定的消息,IRIS也有这个功能—消息查看器,它是如何使用的呢?首先,我们得知道这条消息使用了哪些Production组件,其次我们需要了解这个消息使用的实体类的结构。比如同样查这个人的消息,我需要输入request.Body.PatientID=“2874621”,选择消息类。如果我需要查多个服务的我还需要多选消息类 …… 本文不是来介绍如何使用消息查看器的,各位大概知道就好。 程序分析与设计思路 原始消息查看器的使用 我们先使用IRIS自带的【消息查看器】查患者ID为【2874621】个人信息注册服务,如图所示: 选时间,输入图中所示的条件去检索。步骤上好像也挺简单的,但是这里有两个前提条件,一是我得知道每个服务对应的消息类是哪个。二是我知道这个消息类里面患者ID对应的字段是哪个,而且这个字段不能在循环里面(好像说了3个条件 ~ 哈哈),如何处理摆在了我们的眼前。 SQL分析 使用IRIS自带的【显示查询】功能(这个如何开启使用可以看我的另外一篇文章 https://cn.community.intersystems.com/node/525741 ),如图所示: 找个位置格式化一下: 可以看到从这段SQL可以看出3点: (1)IRIS消息记录的表是【Ens.MessageHeader】 (2)IRIS保存消息是给实体本身建立了一个表 比如上述的例子: 【BKIP_PatientInfo_MSG.PatientInfoRegister】 (3)head.MessageBodyId = BKIP_PatientInfo_MSG.PatientInfoRegister.%ID 通过这3条,我们可以得到下面这张图的信息: 倘若我们把SQL按照如上图所示的方式处理,然后反应在页面上供用户选择;患者ID也由用户选择或者输入;这样确实直观的解决了。但是,效率,上述消息查看器在用户选择时间的时候首先查到两个ID让查询就在这两个主键ID之间查找,增加了效率,我们当然可以使用。那么它是必须的么,去掉的话我们速度一定变慢?这是其一。第二点,如果患者ID是在循环中,甚至是在循环的循环中…… 消息查看器好像就没办法了(当然如果有大佬可以解决请接受我的膝盖)。 需要解决的问题 对照表,服务和消息类的对照表 如何把循环中的字段作为关键字进行查询 第一个问题就是解决方案,新建一张表维护服务和消息类的关系,第二个问题:我们要去看一下Ens.MessageHeader这张表: 好像看不出什么。。。 大家可以看看这个图 在图中红框框住的部分是一样的,同时我们进入消息可视化,如图: 我们可以不可以说这个会话ID就是代表了这个服务的这条消息呢?我们要查指定服务的指定人的消息可以理解为查这个会话ID也就是SessionID。我们把思路换一下,如果我把循环中的患者ID遍历出来存入一张表中,这一行的数据我有sessionID、患者ID、服务名。当我想查患者ID为【2874621】的个人信息注册服务得到sessionID之后,直接跳到可视化追踪,如果把患者ID换成医嘱ID也是一样的处理,其它关键字段也是一样,问题是不是迎刃而解? 程序设计(思路) 新建一个索引表 字段为 SessionID、服务名、属性名、属性值、创建时间,如图所示: 然后在每次服务被调用的时候取出我们需要的属性名、属性值和SessionID存入这张表中,如图所示: 我们在页面上进行查询的时候只需要编写如图所示的SQL,省略号代表and后面的条件。 后续的REST接口和前端的页面在此就【略】,大家可以参考我的另外一篇文章提供的思路编写提供出来Restful接口(https://cn.community.intersystems.com/node/525561 )。 总结 总的来说,我们思路是我们把原本数据量变小,把原本的多重循环的问题维护在一张单表的多行数据中去。这样把原本的多表联合查询改成了单表查询。这速度不快的飞起?目前这边只做思路分享,相信程序设计上大家肯定有自己的一套方式,我这边暂时不做过多展示。都看到这里了,给我点一个赞吧!!! 可以考虑使用ElasticSearch做全文搜索 是的,用ES是最好的。但是ES中有一个很重要的概念叫倒排索引,就是拆分词语存入倒排索引的库,方便在检索的时候分析,和我这边的把一个消息关键字段拆分成索引去检索是否有一点异曲同工捏。当然还是那句话ES是最好的,只不过我这边不需要那么高性能,而且也没试过IRIS结合ES去使用,这里只做分享哈。 还可以考虑用iknow(现在叫InterSystems NLP)实现全文检索
文章
姚 鑫 · 六月 17, 2023

第六十章 镜像中断程序 - 使用主 ISCAgent 的日志数据进行 DR 提升和手动故障转移

# 第六十章 镜像中断程序 - 使用主 `ISCAgent` 的日志数据进行 `DR` 提升和手动故障转移 ## 使用主 `ISCAgent` 的日志数据进行 `DR` 提升和手动故障转移 如果 `IRIS A` 的主机系统正在运行,但 `IRIS` 实例没有且无法重新启动,您可以使用以下过程在通过升级后使用来自 `IRIS A` 的最新日志数据更新升级的 `IRIS C IRIS A` 的 `ISCAgent`。 1. 推广 `IRIS C`,选择 `IRIS A` 作为故障转移伙伴。 `IRIS C` 被提升为故障转移成员,从 `IRIS A` 的代理获取最新的日志数据,并成为主要成员。 2. 重新启动 `IRIS A` 上的 `IRIS` 实例,它作为备份重新加入镜像。 3. 在 `IRIS A` 重新加入镜像并变为活动状态后,可以使用使用升级的 DR 异步临时替换故障转移成员中描述的过程,将所有成员返回到它们以前的角色,首先是正常关闭 `IRIS C` ,然后在 `IRIS B` 的配置参数文件的 `[MirrorMember]` 部分中设置 `ValidatedMember=0`(请参阅配置参数文件参考中的 `[MirrorMember]`),将 `IRIS B` 重新启动为 `DR` 异步,将 `IRIS B` 提升为备份,并以 `DR` 异步方式重新启动 `IRIS C`。 注意:如果 `IRIS A` 的主机系统已关闭,但 `IRIS B` 的主机系统已启动,尽管其 `IRIS` 实例未运行,请按照手动故障转移到活动备份中所述在 `IRIS B` 上运行 `^MIRROR` 例程以确定 是否`IRIS B` 在发生故障时是一个活动备份。如果是这样,使用前面的过程,但在升级期间选择 `IRIS B` 作为故障转移伙伴,允许 `IRIS C` 从 `IRIS B` 的 `ISCAgent` 获取最新的日志数据。 ## 使用来自日志文件的日志数据进行 DR 提升和手动故障转移 如果 `IRIS A` 和 `IRIS B` 的主机系统都已关闭,但可以访问 `IRIS A` 的日志文件,或者 `IRIS B` 的日志文件和消息日志可用,您可以使用最新的日志数据更新 `IRIS C`从升级前的初级开始,使用以下过程。 1. 使用 `IRIS A` 或 `IRIS B` 的最新日志文件更新 `IRIS C`,如下所示: - 如果 `IRIS A` 的日志文件可用,则将最新的镜像日志文件从 `IRIS A` 复制到 `IRIS C`,从 `IRIS C` 上的最新日志文件开始,并包括来自 `IRIS A` 的任何后续文件。例如,如果 `MIRROR -MIRRORA-20180220.001` 是 `IRIS C` 上的最新文件,复制 `MIRROR-MIRRORA-20180220.001` 和 `IRIS A` 上的任何更新文件。 - 如果 `IRIS A` 的日志文件不可用但 `IRIS B` 的日志文件和消息日志可用: 1. 确认`IRIS B`很可能已被捕获,如下所示: a. 确认当`A`及其代理不可用时,`B`同时断开与 A的连接。可以通过在`Messages.log`文件中搜索类似于以下内容的消息来检查 `IRIS B`断开连接的时间: ```java MirrorClient: Primary AckDaemon failed to answer status request ``` b. 通过在其 `messages.log` 文件中搜索类似于以下内容的消息,确认 IRIS B 在断开连接时是活动备份: ```java Failed to contact agent on former primary, can't take over ``` 注意:`messages.log` 文件中的如下消息表明 `IRIS B` 在断开连接时未处于活动状态: ```java nonactive Backup is down ``` 当无法确认它是否已被追上时强制提升的 `DR` 异步成为主数据库可能会导致它成为主数据库而没有镜像生成的所有日志数据。因此,一些全局更新操作可能会丢失,而其他镜像成员可能需要从备份中重建。 2. 如果可以确认 `IRIS B` 处于活动状态,请将最新的镜像日志文件从 `IRIS B` 复制到 `IRIS C`,从 `IRIS C` 上的最新日志文件开始,然后包括来自 `IRIS B` 的所有后续文件。例如,如果 `MIRROR-MIRRORA-20180220.001` 是 `InterSystems IRIS C` 上的最新文件,请从 `IRIS C` 复制 `MIRROR-MIRRORA-20180220.001` 和任何更新的文件。检查文件的权限和所有权,并在必要时更改它们以匹配现有日志文件。 2. 在不选择故障转移合作伙伴的情况下将 `IRIS C` 提升为故障转移成员。 `IRIS C` 成为主要的。 3. 当 `IRIS A` 和 `IRIS B` 的问题得到修复时,尽早并在重新启动 `IRIS` 之前,在每个成员上的 `IRIS` 实例的配置参数文件的 `[MirrorMember]` 部分中设置 `ValidatedMember = 0`(参见 `[ MirrorMember]` 在配置参数文件参考)。说明指出,此更改是必需的。完成此操作后,在每个成员上重新启动 `IRIS`,从 `IRIS A`(最近成为主成员的成员)开始。 1. 如果成员在 `IRIS` 重新启动时作为备份或 `DR` 异步加入镜像,则不需要进一步的步骤。任何在故障成员上但不在当前主成员上的日志数据都已被丢弃。 2. 如果在 `IRIS` 实例重新启动时成员无法加入镜像,如重建镜像成员中描述的引用不一致数据的消息日志消息所示,则成员上的最新数据库更改晚于存在于上的最新日志数据 `IRIS C` 成为主要时。要解决此问题,请按照该部分中的描述重建成员。 4. 在大多数情况下,`DR` 异步系统不是主要故障转移成员的合适永久主机。在 `IRIS A` 和 `IRIS B` 重新加入镜像后,使用使用升级的 `DR` 异步临时替换故障转移成员中描述的过程将所有成员返回到它们以前的角色。如果 `IRIS A` 或 `IRIS B` 作为备份重新启动,则在备份处于活动状态时从正常关闭 `IRIS C` 开始,以故障转移到备份;如果 `IRIS A` 或 `IRIS B` 都重新启动为 `DR` 异步,将其中一个提升为备份,然后在 `IRIS C` 上执行正常关闭。将另一个以前的故障转移成员提升为备份,然后将 `IRIS C` 作为 `DR` 异步重启。
文章
Guangliang Zhang · 十月 21, 2022

基于cconsole.log的cache数据库的实时监控

cache数据库自身带有系统监控Portal界面,但需要运维人员定期主动查看才能获取监控信息。当系统故障发生时,容易出现由于没有及时获取故障信息而不能及时处理,从而导致造成的影响扩大。本文研究通过解析cache数据库控制台日志(cconsole.log)进行监控信息获取并主动推送微信或短信实现cache数据库主动实时监控。 cache数据库在运行时会将所有控制台消息包括一般消息、系统错误、某些操作系统错误和网络错误都会发送到控制台日志文件,通过操作员控制台工具从其他系统远程启动的作业的成功或失败等信息也会写入控制台日志,因此通过对控制台日志的解析即可获取所需要监控信息。具体步骤方法如下: 解析控制台日志 控制台日志默认存储在install-dir\mgr路径下。 根据cache版本不同,使用的读取方法也不同。对于cache2016版本以上,系统提供了EnsLib.SQL.Snapshot类,可以直接获取日志的行和列信息,非常方便。对于cache2010及以下版本则无此方法,需要使用%File文件读取方法。 随着系统运行时间增加,控制台日志也会不断增大,造成每次检索时间加大,且不容易找出最近的监控信息,作出有效监控。较好的方式是每次解析时均从上次的节点继续进行,以便获取新的有效的监控信息。可以使用指定global来记录每次执行的最后一行ID,下次执行时从ID+1行开始解析来达到目的。对于控制台日志,EnsLib.SQL.Snapshot方法里提供了方法以获取当前行,对于cache2010及以下版本,则需要记录最后一次处理的内容,再次处理的时先找到本条内容的位置,从此位置继续处理。代码示例如下。 cache2016及以上版本代码示例: ClassMethod GetAlertInfo() As %String{ //cconsole.log存储路径 set FilePath="D:\InterSystems\HealthShare\mgr\cconsole.log" //使用EnsLib.SQL.Snapshot读取日志 set snap=##class(EnsLib.SQL.Snapshot).%New() set snap.MaxRowsToGet=-1 do snap.ImportFile(FilePath,," ") do Consolelogshow do snap.Clean() quit $$$OK //处理日志内容Consolelogshow set colsCount=snap.ColCount //获取上一次处理的最后一行行号 set snap.%CurrentRow=$g(^AlertCurrentRow("Consolelog","CurrentRow")) k ^AlertCurrentRow("Consolelog","PID") while snap.Next() { set alertLevel=snap.GetData(3) set pid=snap.GetData(2) set pid=$p(pid,"(",2) set pid=$p(pid,")",1) continue:pid="" //0级别信息不处理 continue:((alertLevel=0)) //进程号已存在不处理,即只获取每次每个进程的第一条告警/错误信息 continue:($d(^AlertCurrentRow("Consolelog","PID",pid))) //记录已处理的进程号 set ^AlertCurrentRow("Consolelog","PID",pid)=1 //定义并组装监控消息内容 set alertInfo="" i ((alertLevel=1)||(alertLevel=2)||(alertLevel=3)){ f i=4:1:colsCount{ set alertInfo=alertInfo_" "_snap.GetData(i) } set alertInfo=snap.GetData(1)_":"_alertLevel_":"_alertInfo continue:((alertLevel=1)&&(alertInfo'["Warning")) //发送监控信息至信息推送平台,如微信或短信等 set rt=..sendAlertInfo(alertInfo) } //记录当前处理的行 set ^AlertCurrentRow("Consolelog","CurrentRow")=snap.%CurrentRow } quit $$$OK} cache2010及以下版本代码示例: ClassMethod GetAlertInfo() As %String{ //使用%File读取日志 set file=##class(%File).%New("/cachesys/mgr/cconsole.log") set Status= file.Open("R") k ^AlertCurrentRow("Consolelog","PID") //获取上次处理的信息内容 set LastPosition=$g(^AlertCurrentRow("Consolelog","LastPosition")) s:LastPosition="" LastPosition=0 set LastPosition=LastPosition+1 set int=0 while 'file.AtEnd{ i int=0{ //跳转到上次处理的最后位置 do file.MoveTo(LastPosition) } set file.LineTerminator=$c(10) set text= file.ReadLine(,.sc) set int=int+1 //解析日志 set pid=$p(text," ",2) set pid=$p(pid,"(",2) set pid=$p(pid,")",1) set alertLevel=$p(text," ",3) s:text'="" textLast=text //定义并组装监控消息内容 i ((alertLevel=1)&&(alertLevel["Warning"))||(alertLevel=2)||(alertLevel=3){ set length=$length(text," ") set alert="",alertInfo="" f i=4:1:length{ set alert=$p(text," ",i) i i=4{ set alertInfo=alert }else{ set alertInfo=alertInfo_" "_alert } } set alertInfo=$p(text," ",1)_": "_alertLevel_": "_pid_":"_alertInfo set alertInfo=$zcvt(alertInfo,"O","UTF8") i '$d(^AlertCurrentRow("Consolelog","PID",pid)){ //记录已处理的进程号 set ^AlertCurrentRow("Consolelog","PID",pid)=1 //发送监控信息至信息推送平台,如微信或短信等 set rt=..sendAlertInfo(alertInfo) } } } //记录最后处理的行内容 set ^AlertCurrentRow("Consolelog","LastPosition")=file.FindAt(1,textLast) do file.Close() quit $$$OK} 控制台日志在记录信息时,经常会记录一系列问题。记录模式为时间+进程ID+信息级别+详细信息。如图1所示。信息级别从0级到3级,问题严重程度依次增加。为0的一般为正常运行信息,也是占日志主要部分的内容。问题级别为1的为警告信息。问题级别为2的为错误信息。问题级别为3的为严重错误信息。因此信息级别为1以上均是监控需要关注的信息。需要将此部分信息及时发出。 图1 cconsole.log普通记录示例 有时候控制台日志也会有其它格式的信息出现,如图2所示。此类信息说明有比较严重的异常行为出现,需要重点关注,因为一般将其全部发出。 图2 cconsole.log特殊记录示例 监控信息推送 控制台日志会对一个进程的多个操作进行记录,如果将所有信息发出,则会造成信息量过大而找不到重点。一般将每个进程的第一个告警或错误信息发出即可。处理方式见上述示例代码。 将获取到所需要发送的监控信息调取微信(如图3)或短信(如图4)或其它信息推送终端的接口即可进行信息推送。 图3 微信告警示例 图4 短信告警示例 任务部署 将程序部署成定时任务,按照所需自定义监控频率即可实现cache数据库的主动实时监控。如图5所示。 图5 监控任务示例 确实升级后的Cache强了很多 这个极实用,推荐大伙收藏。 实用,收藏
文章
Michael Lei · 二月 14

FHIR 用例集: 打破数字医疗壁垒,实现高质量发展

FHIR 用例集: 打破数字医疗壁垒,实现高质量发展 --促进互联互通,改进工作流程,提高数据洞察 简介 HL7® FHIR®(快速医疗互操作性资源)是以电子方式访问、交换和管理医疗信息的国际标准。与以往的标准不同,FHIR 可让帮助行业从业者轻松构建创新应用程序,有效地收集、汇总和分析来自不同来源的各种医疗保健和管理数据。医疗机构、社保/保险公司、政府机构、生命科学公司、医疗设备制造商和医疗科技等多种主体利用 FHIR 来简化信息流、提高数据洞察力、改善临床效果和业务成果。 FHIR 基于 JSON、HTTP 和 REST 等流行的网络技术。有了 FHIR,没有医疗信息化背景的软件开发人员也能使用熟悉的开发工具和开源技术,快速、轻松地满足政府机构、临床医生、研究人员、医疗行业从业者以及各类市场主体的数据需求。 FHIR 是一种灵活、适应性强的医疗数据模型,可轻松定制,以实现各种用例的互操作性。FHIR 由称为 "资源 "的离散、可计算的数据对象组成,以实现最佳效率。通过 FHIR 资源,应用程序可以访问单个医疗记录元素,而无需检索摘要文档中包含的所有数据。 本文回顾了 FHIR 的实际应用,并提供了 InterSystems 客户如何使用 FHIR 连接不同系统、加速数字化转型和提高数据洞察力的真实案例。 FHIR 商机无限 FHIR 正在改变医疗健康数据的访问和交换。无论您是为政府、医疗机构、公共卫生机构、保险公司还是厂商工作,FHIR 都能帮助您高效地获取、检索和共享来自电子病历系统、智能医疗设备、可穿戴设备、临床试验和公共卫生监测系统等不同来源的医疗数据。 当前应用和未来的 FHIR 用例FHIR支持实现大量的不同业务场景。您可以在各种部署场景中将 FHIR 用于各种目的。下面的列表总结了 FHIR 在不同行业领域的一些当前应用和潜在的未来用例。 医疗机构 应用场景: 患者数据访问 API机会:可以基于FHIR资源和技术框架实现卫健委互联互通三年攻坚计划以及国家数据局"数据要素x医疗行业"三年行动计划中提到的相关电子健康档案共享、检验检查互认、医疗行业数据要素流通、交易等战略目标,通过基于标准的 (FHIR) API 让患者以程序化的方式访问其健康数据(病史、化验结果、治疗计划等),以及未来可能的全国统一医疗健康档案超级APP(患者端)。 应用场景: 临床决策支持机会: 使用 FHIR 改善临床决策系统的洞察力。将实时电子病历数据安全传输到第三方系统进行分析并返回建议,帮助临床医生做出明智决策。与以往的标准和方法不同,使用 FHIR,您可以将临床决策支持功能直接嵌入电子病历,以简化流程。 应用场景: 医疗机构与支付方(医保/保险公司)的合规数据交换 机会: 利用 FHIR 自动化医疗机构与支付方之间的数据交换。消除资源密集、耗时的人工流程(降低飞行检查和审计成本)。允许医疗机构直接将电子病历数据转发给支付方,无需人工干预。 使用案例: 临床试验和研究机会: 使用 FHIR 无缝共享临床试验招募和分析所需的患者数据,加快临床研究进程。 设备制造商、医疗科技公司和应用开发商 用例:远程医疗和远程监控机会: 使用 FHIR 可将患者数据从家用医疗设备安全地传输给医疗服务提供者,以便他们有效地远程监控和管理患者。 用例: 移动医疗应用程序机遇: 患者可以在手机端访问在不同医院治疗的电子病历,并且确保患者数据的隐私和安全。 用例: 慢性病管理应用程序机会: 使用 FHIR 在医疗服务提供者之间无缝共享患者数据,以实现一致的监控和协调的护理计划。 用例: 药物管理应用程序机会: 为临床医生和护理人员创建多功能药物管理应用程序。使用 FHIR 在区域全民健康信息平台之间高效共享处方信息、用药计划和药房记录。 生命科学公司、政府机构和付款人 用例:健康信息交换机会: 使用 FHIR,政府、公共卫生、保险公司等可高效开展电子健康档案/电子病历共享调阅数据,以进行质量评估、护理差距识别、理赔裁定以及开展潜在的数据交易等。 用例: 护理计划机会: 利用 FHIR,让跨机构护理团队--医生、家庭医疗工作者、社区护理人员、家庭成员等--能够无缝交换信息。让不同的医疗保健系统进行有效沟通。确保所有护理团队成员都能获得最新的患者信息。 用例: 公共卫生报告机会: 使用 FHIR 有效地汇总和共享患者数据,以进行监控和人口健康管理,从而简化公共卫生报告。利用电子病历批量检索功能。(注:该功能自 2022 年起已成为所有美国电子病历系统的强制性要求,在WHO、OECD、欧盟、亚洲、港澳台等地区也正在逐步推广普及) 以上只是部分FHIR的用例,有了FHIR,从业者可以打开无限想象空间,创建丰富多样、互联互通的数字医疗创新应用。
文章
姚 鑫 · 二月 24, 2021

第四十六章 Caché 变量大全 ^$GLOBAL 变量

# 第四十六章 Caché 变量大全 ^$GLOBAL 变量 提供有关全局变量和进程私有全局变量的信息。 # 大纲 ```java ^$|nspace|GLOBAL(global_name) ^$|nspace|G(global_name) ^$||GLOBAL(global_name) ^$||G(global_name) ``` # 参数 - `|nspace|` 或 `[nspace]` - 可选-扩展SSVN引用,可以是显式名称空间名称,也可以是隐含名称空间。必须计算为带引号的字符串,该字符串括在方括号(`[“nspace”]`)或竖线(`|“nspace”|`)中。命名空间名称不区分大小写;它们以大写字母存储和显示。 - global_name 计算结果为包含无下标全局名称的字符串的表达式。全局名称区分大小写。使用`^$||global()`语法时,与进程专用全局名称相对应的无下标全局名称:`^a`表示`^||a`。 # 描述 可以将`^$GLOBAL`用作`$DATA`、`$ORDER`和`$QUERY`函数的参数,以返回有关当前名称空间(默认名称空间)或指定名称空间中是否存在全局变量的信息。还可以使用`^$global`返回有关存在进程私有全局变量的信息。 ## 进程私有全局变量 可以使用`^$global`获取有关所有命名空间中是否存在进程私有全局变量的信息。可以将进程专用全局的查找指定为`^$||global`或`^$|“^”|global`。 例如,要获取有关进程私有全局`^||a`及其后代的信息,可以指定`$DATA(^$||global(“^a”))`。进程私有全局变量不是特定于名称空间的,因此在定义进程私有全局变量时,无论当前名称空间如何,此查找都会返回有关`^||a`的信息。 请注意,`^$GLOBAL`不支持在`GLOBAL_NAME`本身中指定进程专用全局语法。使用进程专用全局语法指定`GLOBAL_NAME`会导致``错误。 # 参数 ## nspace 此可选参数允许`^$GLOBAL`查找在另一个命名空间中定义的`GLOBAL_NAME`。这称为扩展SSVN参考。可以显式地将命名空间名称指定为带引号的字符串文字、变量,也可以通过指定隐含的命名空间来指定。命名空间名称不区分大小写。可以使用方括号语法`[“user”]`或环境语法`|“user”|`。Nspace分隔符前后不允许有空格 可以使用以下方法测试是否定义了命名空间: ```java DHC-APP>WRITE ##class(%SYS.Namespace).Exists("USER") 1 DHC-APP>WRITE ##class(%SYS.Namespace).Exists("LOSER") 0 ``` 以使用`$NAMESPACE`特殊变量来确定当前名称空间。更改当前名称空间的首选方式是新建`$NAMESPACE`,然后设置`$NAMESPACE=“nspace ename”`。 ## global_name 计算结果为包含无下标全局名称的字符串的表达式。全局变量区分大小写。 - `^$global(“^a”)`:`global_name“^a”`在当前名称空间中查找此全局名称及其后代。它不查找进程私有全局`“^||a”`。 - `^$|"USER"|GLOBAL("^a")`:global_name `"^a"`在`“user”`名称空间中查找此全局名称及其后代。它不查找进程-私有全局`"^||a"`。 -` ^$||GLOBAL("^a")`:global_name `"^a"`在所有名称空间中查找进程私有全局`"^||a"`及其后代。它不查找全`"^a"`。 # 示例 以下示例显示如何将`^$GLOBAL`用作`$DATA`、`$ORDER`和`$QUERY`函数的参数。 ## 作为`$DATA`的参数 `^$GLOBAL`作为`$DATA`的参数返回一个整数值,表示指定的全局名称是否作为`^$GLOBAL`节点存在。下表显示了`$DATA`可以返回的整数值。 Value | Meaning ---|--- 0| 全局名称不存在 1| 全局名称是包含数据但没有子代的现有节点。 10| 全局名称是没有数据但具有子代的现有节点。 11| 全局名称是包含数据的现有节点,并且具有子代。 下面的示例测试当前命名空间中是否存在指定的全局变量: ```java /// d ##class(PHA.TEST.SpecialVariables).GLOBAL() ClassMethod GLOBAL() { KILL ^GBL WRITE $DATA(^$GLOBAL("^GBL")),! SET ^GBL="test" WRITE $DATA(^$GLOBAL("^GBL")),! SET ^GBL(1,1,1)="subscripts test" WRITE $DATA(^$GLOBAL("^GBL")) } ``` ```java DHC-APP>d ##class(PHA.TEST.SpecialVariables).GLOBAL() 0 1 11 ``` 下面的示例测试user命名空间中是否存在指定的全局变量: ```java /// d ##class(PHA.TEST.SpecialVariables).GLOBAL1() ClassMethod GLOBAL1() { SET $NAMESPACE="USER" SET ^GBL(1)="test" SET $NAMESPACE="%SYS" WRITE $DATA(^$|"USER"|GLOBAL("^GBL")) } ``` ```java DHC-APP>d ##class(PHA.TEST.SpecialVariables).GLOBAL1() 10 ``` 下面的示例测试任何命名空间中是否存在指定的进程私有全局变量: ```java /// d ##class(PHA.TEST.SpecialVariables).GLOBAL2() ClassMethod GLOBAL2() { SET $NAMESPACE="USER" SET ^||PPG(1)="test" SET $NAMESPACE="%SYS" WRITE $DATA(^$||GLOBAL("^PPG")) } ``` ```java DHC-APP>d ##class(PHA.TEST.SpecialVariables).GLOBAL2() 10 ``` ## 作为`$ORDER`的参数 `$ORDER(^$|nspace|GLOBAL( global_name),direction)` `^$GLOBAL`作为`$ORDER`的参数,将排序序列中的下一个或上一个全局名称返回到指定的全局名称。如果`^$GLOBAL`中不存在这样的全局名称节点,`$ORDER`将返回空字符串。 注意:`$ORDER(^$GLOBAL(NAME))`不会从IRISSYS数据库返回`%global names`。 Direction参数指定是返回下一个全局名称还是返回上一个全局名称。如果不提供方向参数,InterSystems IRIS会将排序顺序中的下一个全局名称返回给您指定的全局名称。 以下子例程搜索当前名称空间,并将全局名称存储在名为global的本地数组中。 ```java /// d ##class(PHA.TEST.SpecialVariables).GLOBAL3() ClassMethod GLOBAL3() { GLOB SET NAME="" WRITE !,"以下全局变量在 ",$NAMESPACE FOR I=1:1 { SET NAME=$ORDER(^$GLOBAL(NAME)) WRITE !,NAME QUIT:NAME="" SET GLOBAL(I)=NAME } WRITE !,"全部完成" QUIT } ``` ```java DHC-APP>d ##class(PHA.TEST.SpecialVariables).GLOBAL3() 以下全局变量在 DHC-APP ^%ISCWorkQueue ^%cspSession ^%qCacheMsg ^%qCacheMsgNames ^%qCacheObjectErrors ^%qCacheObjectKey ^%qCacheObjectQualifier ^%qCacheSQL ^%qHTMLElementD ^%qJavaMetaDictionary ^%qMgtPortal.Index ^%qPublicSuffix ^%qStream ^%qcspRule ^A ^AA Visible+4^%SYS.GD DHC-APP> ``` ## 作为`$QUERY`的参数 `^$GLOBAL`作为`$QUERY`的参数,按排序顺序将下一个全局名称返回到指定的全局名称。如果`^$GLOBAL`中不存在这样的全局名称作为节点,则`$QUERY`将返回空字符串。 注意:`$QUERY(^$GLOBAL(NAME))`不会从IRISSYS数据库返回`%GLOBAL NAMES`。 在以下示例中,用`user`命名空间中存在三个全局变量(`^GBL1`、`^GBL2`和`^GBL3`)。 ```java /// d ##class(PHA.TEST.SpecialVariables).GLOBAL4() ClassMethod GLOBAL4() { NEW $NAMESPACE SET $NAMESPACE="USER" SET (^GBL1,^GBL2,^GBL3)="TEST" NEW $NAMESPACE SET $NAMESPACE="%SYS" WRITE $QUERY(^$|"USER"|GLOBAL("^GBL1")),! WRITE $QUERY(^$|"USER"|GLOBAL("^GBL2")) NEW $NAMESPACE SET $NAMESPACE="USER" KILL ^GBL1,^GBL2,^GBL3 } ``` ```java DHC-APP>d ##class(PHA.TEST.SpecialVariables).GLOBAL4() ^$|"USER"|GLOBAL("^GBL2") ^$|"USER"|GLOBAL("^GBL3") ``` ## 作为`MERGE`的参数 `^$GLOBAL`作为`MERGE`命令的源参数,将全局目录复制到目标变量。`Merge`将每个全局名称添加为具有空值的目标下标。下面的示例显示了这一点: ```java MERGE gbls=^$GLOBAL("") ZWRITE gbls ``` ```java ... gbls("^zlgsql")="" gbls("^zlgtem")="" gbls("^zlgtem1")="" gbls("^zlgtem4")="" gbls("^zlgtemp")="" gbls("^zlgtemp1")="" gbls("^zlgtemp3")="" gbls("^zlgtemp5")="" gbls("^zlgtmp")="" gbls("^zlj")="" gbls("^zll")="" gbls("^zltmp")="" gbls("^zmc")="" gbls("^znum")="" gbls("^zpeterc")="" gbls("^zsb")="" gbls("^zseq")="" gbls("^zstock")="" gbls("^ztTmp")="" gbls("^ztrap1")="" gbls("^zwb1")="" gbls("^zwhtmp")="" gbls("^zx")="" gbls("^zx1")="" gbls("^zx2")="" gbls("^zxdd")="" gbls("^zyb")="" gbls("^zyb1")="" gbls("^zyb2")="" gbls("^zyl")="" gbls("^zzTT")="" gbls("^zzdt")="" gbls("^zzp")="" gbls("^zzy")="" gbls("^zzz")="" ```
文章
Michael Lei · 四月 24, 2022

基于Docker的Apache Web Gateway

# 基于Docker的Apache Web Gateway Hi 社区 在本文中,我们将基于Docker程序化地配置一个Apache Web Gateway,使用。: * HTTPS protocol. * TLS\SSL to secure the communication between the Web Gateway and the IRIS instance. ![image](/sites/default/files/inline/images/net-schema-01.png) 我们将使用两个镜像:一个用于Web网关,第二个用于IRIS实例。 所有必需的文件都在这 [GitHub repository](https://github.com/lscalese/docker-webgateway-sample). 我们从git clone开始: ```bash git clone https://github.com/lscalese/docker-webgateway-sample.git cd docker-webgateway-sample ``` ## 准备系统 为了避免权限方面的问题,你的系统需要一个用户和一个组: * www-data * irisowner 需要与容器共享证书文件。 如果你的系统中不存在这些文件,只需执行: ```bash sudo useradd --uid 51773 --user-group irisowner sudo groupmod --gid 51773 irisowner sudo useradd –user-group www-data ``` ## 生成证书 在这个示例中,我们使用以下三个证书: 1. HTTPS web server usage. 2. TLS\SSL encryption on Web Gateway client. 3. TLS\SSL encryption on IRIS Instance. 有一个随时可用的脚本来生成它们。. 然而,你应该自定义证书的主题;只需编辑这个文件 [gen-certificates.sh](https://github.com/lscalese/docker-webgateway-sample/blob/master/gen-certificates.sh) . 这是 OpenSSL `subj` argument的结构: 1. **C**: Country code 2. **ST**: State 3. **L**: Location 4. **O**: Organization 5. **OU**: Organization Unit 6. **CN**: Common name (basically the domain name or the hostname) 可以随意改动这些值. ```bash # sudo is needed due chown, chgrp, chmod ... sudo ./gen-certificates.sh ``` 如果一切都OK,应该能看到两个带证书的新目录 `./certificates/` and `~/webgateway-apache-certificates/` with certificates: | File | Container | Description | |--- |--- |--- | | ./certificates/CA_Server.cer | webgateway,iris | Authority server certificate| | ./certificates/iris_server.cer | iris | Certificate for IRIS instance (used for mirror and wegateway communication encryption) | | ./certificates/iris_server.key | iris | Related private key | | ~/webgateway-apache-certificates/apache_webgateway.cer | webgateway | Certificate for apache webserver | | ~/webgateway-apache-certificates/apache_webgateway.key | webgateway | Related private key | | ./certificates/webgateway_client.cer | webgateway | Certificate to encrypt communication between webgateway and IRIS | | ./certificates/webgateway_client.key | webgateway | Related private key | 请记住,如果有自签名的证书,浏览器会显示安全警报。 显然,如果你有一个由认证机构交付的证书,你可以用它来代替自签的证书(尤其是Apache服务器证书) ## Web Gateway 配置文件 让我们来看看配置文件. ### CSP.INI 你能看到在 `webgateway-config-files` 目录下 CSP.INI 文件. 将被推到镜像里, 但内容可以在runtime被修改. 可以把这个文件作为模版. 在这个示例中,以下参数将在容器启动时被覆盖: * Ip_Address * TCP_Port * System_Manager 更多细节请参考 [startUpScript.sh](https://github.com/lscalese/docker-webgateway-sample/blob/master/startUpScript.sh) . 大致上,替换是通过`sed`命令行进行的. 同时, 这个文件包含 SSL\TLS 配置来确保与 IRIS 实例的通信: ``` SSLCC_Certificate_File=/opt/webgateway/bin/webgateway_client.cer SSLCC_Certificate_Key_File=/opt/webgateway/bin/webgateway_client.key SSLCC_CA_Certificate_File=/opt/webgateway/bin/CA_Server.cer ``` 这些语句都比较重要. 我们必需确保证书文件可用. 我们稍后将在`docker-compose`文件中用一个卷来做这件事. ### 000-default.conf 这是一个Apache 配置文件. 允许使用HTTPS协议并将HTTP请求重定向到HTTPS. 证书和私钥文件在这个文件里设置: ``` SSLCertificateFile /etc/apache2/certificate/apache_webgateway.cer SSLCertificateKeyFile /etc/apache2/certificate/apache_webgateway.key ``` ## IRIS 实例 对我们 IRIS实例, 我们仅仅配置最低要求来允许SSL\TLS 和Web Gateway 之间的通信; 这涉及到: 1. `%SuperServer` SSL Config. 2. Enable SSLSuperServer security setting. 3. Restrict the list of IPs that can use the Web Gateway service. 为简化配置, config-api 用一个简单的JSON 配置文件. ```json { "Security.SSLConfigs": { "%SuperServer": { "CAFile": "/usr/irissys/mgr/CA_Server.cer", "CertificateFile": "/usr/irissys/mgr/iris_server.cer", "Name": "%SuperServer", "PrivateKeyFile": "/usr/irissys/mgr/iris_server.key", "Type": "1", "VerifyPeer": 3 } }, "Security.System": { "SSLSuperServer":1 }, "Security.Services": { "%Service_WebGateway": { "ClientSystems": "172.16.238.50;127.0.0.1;172.16.238.20" } } } ``` 不需要做任何动作. 在容器启动时这个配置会自动加载. ## tls-ssl-webgateway 镜像 ### dockerfile ``` ARG IMAGEWEBGTW=containers.intersystems.com/intersystems/webgateway:2021.1.0.215.0 FROM ${IMAGEWEBGTW} ADD webgateway-config-files /webgateway-config-files ADD buildWebGateway.sh / ADD startUpScript.sh / RUN chmod +x buildWebGateway.sh startUpScript.sh && /buildWebGateway.sh ENTRYPOINT ["/startUpScript.sh"] ``` 默认的 entry point是 `/startWebGateway`, 但是在启动webserver前需要执行一些操作. 记住我们的 CSP.ini 文件只是个 `模版`, 并且我们需要在启动时改变一些参数 (IP, port, system manager) . `startUpScript.sh` 将执行这些变化并启动初始 entry point 脚本 `/startWebGateway`. ## 启动容器 ### docker-compose 文件 启动容器之前, 必须修改好`docker-compose.yml` 文件: * `**SYSTEM_MANAGER**` 必须配好授权的IP来访问 **Web Gateway Management** https://localhost/csp/bin/Systems/Module.cxw 基本就是你自己的IP地址 (可以是一个用逗号分开的列表). * `**IRIS_WEBAPPS**` 必须配好 CSP 应用列表. 这个表用空格隔开, 例如: `IRIS_WEBAPPS=/csp/sys /swagger-ui`. 默认, 只有 `/csp/sys` 被暴露. * 80和 443 端口映射好. 如果你的系统中已经使用了这些端口,请将调整为其他端口. ``` version: '3.6' services: webgateway: image: tls-ssl-webgateway container_name: tls-ssl-webgateway networks: app_net: ipv4_address: 172.16.238.50 ports: # change the local port already used on your system. - "80:80" - "443:443" environment: - IRIS_HOST=172.16.238.20 - IRIS_PORT=1972 # Replace by the list of ip address allowed to open the CSP system manager # https://localhost/csp/bin/Systems/Module.cxw # see .env file to set environement variable. - "SYSTEM_MANAGER=${LOCAL_IP}" # the list of web apps # /csp allow to the webgateway to redirect all request starting by /csp to the iris instance # You can specify a list separate by a space : "IRIS_WEBAPPS=/csp /api /isc /swagger-ui" - "IRIS_WEBAPPS=/csp/sys" volumes: # Mount certificates files. - ./volume-apache/webgateway_client.cer:/opt/webgateway/bin/webgateway_client.cer - ./volume-apache/webgateway_client.key:/opt/webgateway/bin/webgateway_client.key - ./volume-apache/CA_Server.cer:/opt/webgateway/bin/CA_Server.cer - ./volume-apache/apache_webgateway.cer:/etc/apache2/certificate/apache_webgateway.cer - ./volume-apache/apache_webgateway.key:/etc/apache2/certificate/apache_webgateway.key hostname: webgateway command: ["--ssl"] iris: image: intersystemsdc/iris-community:latest container_name: tls-ssl-iris networks: app_net: ipv4_address: 172.16.238.20 volumes: - ./iris-config-files:/opt/config-files # Mount certificates files. - ./volume-iris/CA_Server.cer:/usr/irissys/mgr/CA_Server.cer - ./volume-iris/iris_server.cer:/usr/irissys/mgr/iris_server.cer - ./volume-iris/iris_server.key:/usr/irissys/mgr/iris_server.key hostname: iris # Load the IRIS configuration file ./iris-config-files/iris-config.json command: ["-a","sh /opt/config-files/configureIris.sh"] networks: app_net: ipam: driver: default config: - subnet: "172.16.238.0/24" ``` Build and start: ```bash docker-compose up -d --build ``` `tls-ssl-iris 和 tls-ssl-webgateway 容器应该启动好了.` ## 测试 Web Access ### Apache 默认页 打开网页 [http://localhost](http://localhost). 你将自动被重定向到[https://localhost](https://localhost). 浏览器显示安全警告. 如果是自签署的证书,这是正常的,接受并继续. ![image](/sites/default/files/inline/images/apache-web-gateway-with-docker-02.png) ### Web Gateway 管理页面 打开 [https://localhost/csp/bin/Systems/Module.cxw](https://localhost/csp/bin/Systems/Module.cxw) 并测试服务器连接. ![image](/sites/default/files/inline/images/apache-web-gateway-with-docker-03.png) ### 管理门户 打开 [https://localhost/csp/sys/utilhome.csp](https://localhost/csp/sys/utilhome.csp) ![image](/sites/default/files/inline/images/apache-web-gateway-with-docker-04.png) 赞! Web Gateway 例子跑起来了! ## IRIS Mirror 与vWeb Gateway 在上一篇文章中,我们建立了一个镜像环境,但网络网关是一个缺失的部分。 现在,我们可以改进这一点。 一个包括Web Gateway和一些更多改进的资源库就可以用了 [iris-miroring-with-webgateway](https://github.com/lscalese/iris-mirroring-with-webgateway) : 1. 证书不再是即时生成的,而是在一个单独的过程中生成的. 2. IP地址被docker-compose和JSON配置文件中的环境变量所取代, 变量被定义在'.env'文件中. 3. 这个repository 可以作为一个模板来使用. 查看 repository文件 [README.md](https://github.com/lscalese/iris-mirroring-with-webgateway) 来运行以下环境: ![image](https://github.com/lscalese/iris-mirroring-with-webgateway/blob/master/img/network-schema-01.png?raw=true)
文章
Jingwei Wang · 七月 21, 2022

DeepSee 的开发 - 第四部分 - 创建主题区

一个主题区是一个子立方体,可以选择覆盖项目的名称。你定义一个主题区是为了使用户能够关注较小的数据集,出于安全原因或其他原因。本章讨论了以下主题。 简介 在本教程中,我们创建了两个主题区域,按邮政编码划分患者: Patient Set A: 居住在邮政编码为32006, 32007, or 36711区域的患者 Patient Set B: 居住在邮政编码为34577 or 38928区域的患者 创建主题领域 要创建主题区域,请做以下工作。 在模型中,点击 "新建"。 选中 "主题区域"。 对于主题区名称,键入Patient Set A 对于主题区的类名,输入 Tutorial.SubjectA 对于基础立方体,点击浏览并选择 Tutorial。 单击 OK。 在一个单独的浏览器标签或窗口中,访问分析器,然后做以下工作。 展开HomeD。 把ZIP Code放到过滤器框中。这就在数据透视表的正上方增加了一个过滤框。 在该过滤框中,点击搜索按钮,然后选择 32006, 32007, 和 36711。 然后点击'为透视表显示当前查询'按钮(笔记本带一个笔的图标) 系统会显示一个对话框,显示分析器所使用的MDX查询。 SELECT FROM [PPatients] %FILTER %OR({[HOMED].[H1].[ZIP CODE].&[32006],[HOMED].[H1].[ZIP CODE].&[32007],[HOMED].[H1].[ZIP CODE].&[36711]}) 将%FILTER后面的文本复制到系统剪贴板上。 点击确定。 在模型中,点击标有Patient Set A的一行。 在详细信息栏中,将复制的文本粘贴到 过滤器 中。 %OR({[HOMED].[H1].[ZIP Code].&[32006],[HOMED].[H1].[ZIP Code].&[32007],[HOMED].[H1].[ZIP Code].&[36711]}) 点击保存,然后点击确定。 编译该主题区。 对于第二个主题区,重复前面的步骤,并作如下改动。 对于课题区名称,键入Patient Set B 对于主题区的类名,键入Tutorial.SubjectB 对另外两个邮政编码重复前面的步骤。因此,对于Filter,使用以下内容。 %OR({[HOMED].[H1].[ZIP Code].&[34577],[HOMED].[H1].[ZIP Code].&[38928]}) 检查主题领域 现在我们检查一下我们所创建的主题领域。 在分析器中,点击左上角立方体按钮,选择Patient Set A。 单击 "确定"。然后分析器显示所选主题区的内容。 注意,总的记录数没有你的Tutorial基本立方体那么高。 在模型内容区,展开HomeD维度,ZIP Code级别,以及City级别。都没有之前的Tutorial基本立方体数据那么多。 对患者组B重复前面的步骤。 当您展开HomeD维度、ZIP Code级别,以及City级别。也没之前的Tutorial基本立方体数据那么多。 常见的过滤器表达式 在这一节中,我们在分析器中试验常见的过滤器,看看它们对生成的查询的影响。 在分析器中,打开Tutorial立方体。 分析器把立方体和主题区都称为主题区。它们之间的正式区别只有在你创建它们时才有意义。 点击新建。 分析器显示计数(记录的计数)。 在添加过滤器之前,让我们看看当前的查询是如何定义的,以便我们有一个比较的基础。 展开ColorD和Favorite Color。 把Orange拖到过滤器。 分析器现在只使用最喜欢的颜色是Orange的患者。 点击‘为透视表显示当前查询’按钮(笔记本加一个笔图标)。然后系统显示以下查询。 SELECT FROM [TUTORIAL] %FILTER [ColorD].[H1].[Favourite Color].&[Orange] %FILTER关键字限制了查询。%FILTER后面的片段是一个过滤表达式。 点击确定。 给过滤器添加另一种颜色。点击过滤器中橙色旁边的X。这样就可以删除该过滤器。 把 "Favourite Color "拖到过滤器中。这就在数据透视表的正上方增加了一个过滤器框。 在该过滤框中,点击搜索按钮(放大镜图标),然后选择橙色和紫色。 系统现在只使用最喜欢的颜色是橙色或最喜欢的颜色是紫色的患者(注意,计数比单独的橙色要高)。 再次显示查询文本。现在你应该看到以下内容。 SELECT FROM [TUTORIAL] %FILTER %OR({[COLORD].[H1].[FAVOURITE COLOR].&[Orange],[COLORD].[H1].[FAVOURITE COLOR].&[Purple]}) 在这种情况下,过滤器的表达式如下。 %FILTER %OR({[COLORD].[H1].[FAVOURITE COLOR].&[Orange],[COLORD].[H1].[FAVOURITE COLOR].&[Purple]}) %OR函数是InterSystems公司的一项优化;该函数的参数是一个集合。 这个集合被大括号{}所包围,由一个逗号分隔的元素列表组成。在这种情况下,该集合包含两个成员表达式。一个集合表达式指的是由该集合的元素所表示的所有记录。在本例中,该集合指的是所有最喜欢的颜色是橙色的患者和所有最喜欢的颜色是紫色的患者。 点击确定。 使用过滤器下拉列表,清除紫色旁边的复选框。现在分析器只使用最喜欢的颜色是橙色的患者。 展开AllerD和Allergies。将模具拖到过滤器,在最喜欢的颜色的下面。这个透视表只显示最喜欢的颜色是橙色和对霉菌过敏的患者。 再次显示查询文本。现在你应该看到以下内容。 SELECT FROM [TUTORIAL] %FILTER NONEMPTYCROSSJOIN([AllerD].[H1].[Allergies].&[mold],[COLORD].[H1].[FAVOURITE COLOR].&[Orange]) MDX函数NONEMPTYCROSSJOIN结合了两个成员,并返回结果元组。该元组只访问属于两个给定成员的记录。 现在你已经看到了三种最常见的过滤表达式。 当你使用一个成员表达式作为过滤器时,系统只访问属于这个成员的记录。你可以写一个成员表达式,如下所示。 [dimension name].[hierarchy name].[level name].&[member key] 或者。 [dimension name].[hierarchy name].[level name].[member name] dimension name是一个维度的名称。 hierarchy name是一个层次结构的名称。您可以省略层次结构的名称。如果你这样做,查询会使用在这个维度中定义的具有给定名称的第一层。 level name是该层次结构中的一个层次的名称。你可以省略层次名称。如果你这样做,查询会使用在这个维度中定义的具有给定名称的第一个成员。 member key是给定层次中成员的键。这通常与成员名称相同。 member name是给定级别中成员的名称。 关于更多过滤规则,请看用DeepSee使用MDX和DeepSee MDX参考。
文章
Frank Ma · 六月 13, 2022

利用IRIS IntegratedML(一体化机器学习)来预测肾病的Web应用

肾脏疾病可以从一些医学界熟知的参数中发现。这样,为了帮助医学界和计算机系统,特别是人工智能,科学家Akshay Singh发表了一个非常有用的数据集,用于训练肾脏疾病检测/预测方面的机器学习(ML)算法。这份出版物可以在最大和最知名的ML数据库Kaggle上找到,网址是https://www.kaggle.com/datasets/akshayksingh/kidney-disease-dataset。 关于数据集 该肾脏疾病数据集有以下元数据信息(来源:https://www.kaggle.com/datasets/akshayksingh/kidney-disease-dataset): 它有400行,有25个特征,如红细胞、足部水肿、糖等等。 其目的是对病人是否患有慢性肾脏病进行分类。 分类是基于一个名为 "classification "(分类)的属性,属性值是 "ckd"(慢性肾脏病)或 "notckd"(不是慢性肾脏病)。 数据集作者对数据集进行了清洗,包括将文本映射为数字和其他一些变化。在清洗之后,数据集作者做了一些EDA(探索性数据分析),然后将数据集分为训练和测试两部分,并在上面应用模型。据观察,最初的分类结果并不令人满意。因此,数据集的作者没有放弃有Nan(非数)值的行,而是用lambda函数将其替换为每一列的模式。之后,数据集作者又将数据集分为训练集和测试集,并对其应用模型。这一次的结果更好,我们看到随机森林和决策树是表现最好的,准确率为1.0,错误分类率为0。分类的性能是通过打印混淆矩阵、分类报告和准确性来衡量的。 数据集信息 (来源: https://archive.ics.uci.edu/ml/datasets/chronic_kidney_disease): 我们使用以下表述来收集数据集 age - age(年龄)bp - blood pressure(血压)sg - specific gravity(比重)al - albumin(白蛋白)su - sugar(糖)rbc - red blood cells(红血球)pc - pus cell(脓细胞)pcc - pus cell clumps(脓细胞团块)ba - bacteria(细菌)bgr - blood glucose random(血糖随机)bu - blood urea(血尿素)sc - serum creatinine(血清肌酐)sod - sodium(钠)pot - potassium(钾)hemo - hemoglobin(血红蛋白)pcv - packed cell volume(填充细胞体积)wc - white blood cell count(白血球计数)rc - red blood cell count(红细胞计数)htn - hypertension(高血压)dm - diabetes mellitus(糖尿病)cad - coronary artery disease(冠状动脉疾病)appet - appetite(食欲)pe - pedal edema(足部水肿)ane - anemia(贫血)class - class(类) 属性信息 (来源: https://archive.ics.uci.edu/ml/datasets/chronic_kidney_disease): 我们使用24+类 = 25 (11个数字类型,14个名义类型)1.Age 年龄(数字)岁数2.Blood Pressure 血压(数字)血压单位:mm/Hg3.Specific Gravity 比重(数字)sg - (1.005,1.010,1.015,1.020,1.025)4.Albumin 白蛋白(名义)al - (0,1,2,3,4,5)5.Sugar 糖 (数字)su - (0,1,2,3,4,5)6.Red Blood Cells 红血球 (名义)rbc - (normal,abnormal) 红细胞 - (正常,异常)7.Pus Cell 脓细胞 (名义)pc - (normal,abnormal)(正常、异常)8.Pus Cell clumps 脓细胞团块 (名义) pcc - (present,notpresent) (出现、未出现)9.Bacteria 细菌(名义)ba - (present,notpresent) (出现、未出现)10.Blood Glucose Random 血糖随机(数字)bgr 单位 mgs/dl11.Blood Urea 血尿素(数字)bu 单位mgs/dl12.Serum Creatinine 血清肌酸酐(数字)sc 单位 mgs/dl13.Sodium 钠 (数字)sod 单位 mEq/L14.Potassium 钾 (数字)pot 单位 mEq/L15.Hemoglobin 血红蛋白(数字)hemo 单位 gms16.Packed Cell Volume 包容细胞体积(数字)17.White Blood Cell Count白血球计数 (数字)wc 单位 cells/cumm 18.Red Blood Cell Count 红细胞计数(数字)rc 单位 millions/cmm19.Hypertension 高血压(名义)htn - (yes,no) (是,否)20.Diabetes Mellitus 糖尿病(名义)dm - (yes,no) (是,否)21.Coronary Artery Disease 冠状动脉疾病(名义)cad - (yes,no) (是,否)22.Appetite 食欲(名义)appet - (good,poor) (好,差)23.Pedal Edema 踏板水肿(名义)pe - (yes,no) (是,否)24.Anemia 贫血(名义)ane - (yes,no) (是,否)25.Class 类 (名义)class - (ckd,notckd) (慢性肾脏病,不是慢性肾脏病) 从Kaggle获取肾脏数据 使用Health-Dataset(健康数据集)应用程序,可以把肾脏病数据从Kaggle加载到IRIS表中: https://openexchange.intersystems.com/package/Health-Dataset. 要做到这一点,在你的module.xml项目中,设置依赖关系(Health Dataset的ModuleReference): Module.xml with Health Dataset application reference <?xml version="1.0" encoding="UTF-8"?> <Export generator="Cache" version="25"> <Document name="predict-diseases.ZPM"> <Module> <Name>predict-diseases</Name> <Version>1.0.0</Version> <Packaging>module</Packaging> <SourcesRoot>src/iris</SourcesRoot> <Resource Name="dc.predict.disease.PKG"/> <Dependencies> <ModuleReference> <Name>swagger-ui</Name> <Version>1.*.*</Version> </ModuleReference> <ModuleReference> <Name>dataset-health</Name> <Version>*</Version> </ModuleReference> </Dependencies> <CSPApplication Url="/predict-diseases" DispatchClass="dc.predict.disease.PredictDiseaseRESTApp" MatchRoles=":{$dbrole}" PasswordAuthEnabled="1" UnauthenticatedEnabled="1" Recurse="1" UseCookies="2" CookiePath="/predict-diseases" /> <CSPApplication CookiePath="/disease-predictor/" DefaultTimeout="900" SourcePath="/src/csp" DeployPath="${cspdir}/csp/${namespace}/" MatchRoles=":{$dbrole}" PasswordAuthEnabled="0" Recurse="1" ServeFiles="1" ServeFilesTimeout="3600" UnauthenticatedEnabled="1" Url="/disease-predictor" UseSessionCookie="2" /> </Module> </Document> </Export> 预测肾脏疾病的网络前端和后端应用程序 进入Open Exchange应用程序链接 (https://openexchange.intersystems.com/package/Disease-Predictor) 并遵循以下步骤: 用Clone/git 将repo拉到本地的任一目录中 $ git clone https://github.com/yurimarx/predict-diseases.git 打开该目录下Docker终端,并运行: $ docker-compose build 运行IRIS容器: $ docker-compose up -d 进入管理门户执行查询,训练AI模型: http://localhost:52773/csp/sys/exp/%25CSP.UI.Portal.SQL.Home.zen?$NAMESPACE=USER 创建用于训练的VIEW: CREATE VIEW KidneyDiseaseTrain AS SELECT age, al, ane, appet, ba, bgr, bp, bu, cad, classification, dm, hemo, htn, pc, pcc, pcv, pe, pot, rbc, rc, sc, sg, sod, su, wc FROM dc_data_health.KidneyDisease 使用view视图创建AI模型 CREATE MODEL KidneyDiseaseModel PREDICTING (classification) FROM KidneyDiseaseTrain 训练模型: TRAIN MODEL KidneyDiseaseModel 访问 http://localhost:52773/disease-predictor/index.html ,使用疾病预测器的前台预测疾病,如下: 幕后工作 预测肾脏病的后端类方法 InterSystems IRIS 允许你执行SELECT,使用之前创建的模型进行预测。 Backend ClassMethod to predict Kidney Disease /// Predict Kidney Disease ClassMethod PredictKidneyDisease() As %Status { Try { Set data = {}.%FromJSON(%request.Content) Set %response.Status = 200 Set %response.Headers("Access-Control-Allow-Origin")="*" Set qry = "SELECT PREDICT(KidneyDiseaseModel) As PredictedKidneyDisease, " _"age, al, ane, appet, ba, bgr, bp, bu, cad, dm, " _"hemo, htn, pc, pcc, pcv, pe, pot, rbc, rc, sc, sg, sod, su, wc " _"FROM (SELECT "_data.age_" AS age, " _data.al_" As al, " _"'"_data.ane_"'"_" AS ane, " _"'"_data.appet_"'"_" AS appet, " _"'"_data.ba_"'"_" As ba, " _data.bgr_" As bgr, " _data.bp_" AS bp, " _data.bu_" AS bu, " _"'"_data.cad_"'"_" As cad, " _"'"_data.dm_"'"_" As dm, " _data.hemo_" AS hemo, " _"'"_data.htn_"'"_" AS htn, " _"'"_data.pc_"'"_" As pc, " _"'"_data.pcc_"'"_" As pcc, " _data.pcv_" AS pcv, " _"'"_data.pe_"'"_" AS pe, " _data.pot_" As pot, " _"'"_data.rbc_"'"_" As rbc, " _data.rc_" AS rc, " _data.sc_" AS sc, " _data.sg_" As sg, " _data.sod_" As sod, " _data.su_" AS su, " _data.wc_" AS wc)" Set tStatement = ##class(%SQL.Statement).%New() Set qStatus = tStatement.%Prepare(qry) If qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT} Set rset = tStatement.%Execute() Do rset.%Next() Set Response = {} Set Response.classification = rset.PredictedKidneyDisease Set Response.age = rset.age Set Response.al = rset.al Set Response.ane = rset.ane Set Response.appet = rset.appet Set Response.ba = rset.ba Set Response.bgr = rset.bgr Set Response.bp = rset.bp Set Response.bu = rset.bu Set Response.cad = rset.cad Set Response.dm = rset.dm Set Response.hemo = rset.hemo Set Response.htn = rset.htn Set Response.pc = rset.pc Set Response.pcc = rset.pcc Set Response.pcv = rset.pcv Set Response.pe = rset.pe Set Response.pot = rset.pot Set Response.rbc = rset.rbc Set Response.rc = rset.rc Set Response.sc = rset.sc Set Response.sg = rset.sg Set Response.sod = rset.sod Set Response.su = rset.su Set Response.wc = rset.wc Write Response.%ToJSON() Return 1 } Catch err { write !, "Error name: ", ?20, err.Name, !, "Error code: ", ?20, err.Code, !, "Error location: ", ?20, err.Location, !, "Additional data: ", ?20, err.Data, ! Return 0 } } 现在,任何web应用都可以使用该预测并显示结果。您可以到预测疾病应用程序的前端文件夹中查看源代码。
文章
Hao Ma · 五月 24, 2023

Caché Mirroring 101:简要指南和常见问题解答

镜像101 Caché 镜像是一种可靠、廉价且易于实施的高可用性和灾难恢复解决方案,适用于基于 Caché 和 Ensemble 的应用程序。镜像在广泛的计划内和计划外中断情况下提供自动故障转移,应用程序恢复时间通常限制在几秒钟内。逻辑数据复制消除了存储作为单点故障和数据损坏的根源。升级可以在很少或没有停机时间的情况下执行。 但是,部署 Caché 镜像确实需要大量规划,并且涉及许多不同的过程。与任何其他关键基础设施组件一样,操作镜像需要持续监控和维护。 您可以通过两种方式使用本文:作为常见问题列表,或作为理解和评估镜像、规划镜像、配置镜像和操作镜像的简要顺序指南。每个答案都包含指向每个主题的详细讨论以及每个任务的分步过程的链接。 当您准备好开始规划镜像部署时,您的起点应该始终是Caché 高可用性指南“镜像”一章的镜像架构和规划部分。 经常问的问题 了解和评估镜像 镜像有什么好处? 镜像能否部署在虚拟化环境中? 镜像可以部署在云端吗? 镜像的基本设计是什么? 数据库副本如何与实时生产数据库同步? 自动故障转移是如何触发的?有没有它没有涵盖的情况? 镜像是否提供灾难恢复? 规划镜像 如何规划镜像的架构?将包括哪些成员,他们将在哪里? 哪些网络和延迟注意事项申请?镜像需要什么样的网络配置? 在故障转移时将应用程序连接重定向到新主节点的选项有哪些? 镜像中的 Caché 实例有哪些兼容性要求? 如何将现有数据库迁移到镜像? 如果将镜像部署在虚拟化环境中,我应该考虑什么? 配置镜像 我需要考虑哪些配置准则? 如何保护镜像? 如何配置镜像虚拟IP地址(镜像VIP)? 我在哪里以及如何安装仲裁器? 如何安装和启动 ISCAgent? 如何创建和配置镜像? 如何创建镜像数据库?如何将现有数据库添加到镜像? 如何确保 ECP 在故障转移后重定向应用程序服务器连接? 当镜像 VIP 不可用时(例如在云中),我如何确保重定向应用程序连接? 如何将 Caché Shadow转换为镜像? 我应该查看哪些其他配置细节? 管理镜像 如何监控镜像的运行? 如何修改镜像?我能做什么调整? 我可以在镜像中添加成员吗?消除一?如何完全删除镜像? 如果我需要暂时从镜像中删除成员怎么办? 我必须一次升级镜像吗?我必须把镜子从生产中取出来做吗? 我应该了解哪些其他镜像或镜像相关的管理程序和细节? 镜像中断程序 了解和评估镜像 镜像有什么好处? 对于基于 Caché 和 Ensemble 的应用程序,存在三种实现高可用性的主要方法: 故障转移集群、 虚拟化 HA和 Caché 镜像。前两者最大的缺点是依赖共享存储,存储失败后果不堪设想;可选的存储级冗余可以改善这一点,但也可以延续某些类型的数据损坏。此外,软件升级需要大量的停机时间,对于许多故障,应用程序恢复时间可能有几分钟。 通过使用两个具有独立存储和逻辑数据复制的物理独立系统,镜像避免了共享存储问题,升级不需要停机或停机时间很短,应用程序恢复时间通常为几秒钟。这种方案还提供可靠和强大的灾难恢复能力,灾难恢复站点(DR)可以位于距生产数据中心任何适当的距离。 镜像的主要限制是它只复制数据库本身;应用程序所需的外部文件需要额外的解决方案,安全和配置管理目前是分散的。 以下资源提供了这些 HA 方法的详细分析和比较,以及有关镜像优势的更多信息: 系统故障转移策略( Caché 高可用性指南) 高可用性策略(白皮书) 业务连续性的高可用性(视频) 缓存镜像:高可用性的冒险(视频) 镜像:吞吐量架构(在线学习) InterSystems Caché:数据库镜像:执行概述(白皮书) 镜像介绍(在线学习) HealthShare:通过镜像实现高可用性(在线学习) 镜像能否部署在虚拟化环境中? 镜像经常部署在虚拟化环境中。镜像通过自动故障转移对计划内或计划外中断提供即时响应,而虚拟化 HA 软件会在机器或操作系统意外中断后自动重启托管镜像成员的虚拟机。从而允许故障成员快速重新加入镜像以充当备份(或在必要时接管为主)。 有关使用此方法的信息,请参阅 InterSystems 白皮书高可用性策略。 镜像可以部署在云端吗? 镜像可以有效部署在云端。由于云网络限制,使用虚拟 IP 地址(镜像 VIP)在故障转移后重定向应用程序连接通常是不可能的,但这可以使用负载均衡器等网络流量管理器有效克服。 镜像的基本设计是什么? 一个 Caché 镜像通常包括物理上独立的主机上的两个 Caché 实例,称为故障转移成员;镜像自动将主角色分配给一个,而另一个成为备份。应用程序更新主数据库,而镜像使备份数据库与主数据库保持同步。 当主服务器发生故障或不可用时,备份服务器会自动接管主服务器,并将应用程序连接重定向到它。当主实例恢复运行时,它会自动成为备份实例。 操作员启动的人工切换可用于在计划的维护或升级停机期间保持可用性。 镜像可选地包含称为asyncs的其他成员,用于灾难恢复以及商业智能和数据仓库目的。 一个镜像也可以只使用一个故障转移成员和一定数量的异步,例如当灾难恢复是主要目标时。 数据库副本如何与实时生产数据库同步? 镜像的备份成员和异步成员使用日志文件(Journal文件)与主成员保持同步,日志文件包含自上次备份以来对 Caché 实例中的数据库所做更改的时间顺序记录。在镜像中,来自主数据库的日志文件被发送到其他成员并dejournaled日志记录——也就是说,其中记录的更改被应用到数据库的本地副本,使它们与主数据库保持同步。 日志记录从主数据库到备份的传输是同步的,主数据库在关键点等待备份的确认。这使故障转移成员保持紧密同步,并且备份处于活动状态(Active),并准备好接管为主。异步从主服务器异步接收日志数据,因此有时可能会滞后一些日志记录。 自动故障转移是如何触发的?有没有它没有涵盖的情况? 只有在确认主服务器在没有人工干预的情况下不能再作为主服务器运行时,备份服务器才能自动接管。当故障转移成员之间的直接通信中断时,备份从第三方系统( 仲裁器)获得帮助以确认这一点,仲裁器与两个故障转移成员保持独立联系。 此外,如果备份无法确认其拥有或无法从主服务器获取最新的日志数据,则无法发生自动故障转移。在每个故障转移主机上独立于 Caché 实例运行的代理进程,称为ISCAgents ,参与自动故障转移逻辑和机制的这一方面和其他方面。 假设仲裁器正常运行,几乎所有计划外的主机故障都包括在内;只有将故障转移成员彼此隔离并与仲裁器隔离的网络故障,才能阻止活动备份接管发生故障或不可用的主要成员。 镜像是否提供灾难恢复? 一种类型的异步镜像成员是灾难恢复 (DR) 异步。 DR 异步具有主数据库上所有镜像数据库的副本,并且可以随时提升为故障转移成员。当中断导致镜像没有正常运行的故障转移成员时,您可以手动切换到被提升后的 DR 异步;数据丢失的程度将取决于发生中断时 DR 异步落后于主服务器多远,以及前主服务器的主机系统是否正常运行,是否允许它获取额外的日志数据。提升的 DR 异步也可用于许多其他计划内和计划外中断情况。 规划镜像 如何规划镜像的架构?将包括哪些成员,他们将在哪里? 镜像的大小、成员资格和物理分布将取决于您部署它的原因以及许多基础设施和操作因素,允许多种可能的配置 具有两个故障转移成员的镜像通过自动故障转移提供高可用性。在可选的异步成员中,一个或多个 DR 异步可以提供数据安全和灾难恢复能力,而报告异步用于数据挖掘和商业智能等目的。单个报告异步最多可以属于 10 个独立的镜像,从而使其可以充当企业范围的数据仓库,将来自不同位置的相关数据库集合在一起。 如果不需要自动故障转移,镜像也可以包含一个故障转移成员和多个用于灾难恢复和报告目的的异步。 一个镜像最多可以包含 16 个成员。因为故障转移成员之间需要低延迟连接,因此通常位于同一地点,但异步成员可以位于本地或单独的数据中心,包括为 DR 异步上的数据提供最大安全性的地理位置偏远的位置。 一台主机上可以安装多个镜像成员,但需要额外规划。 哪些网络和延迟注意事项适用?镜像需要什么样的网络配置? 主要的网络配置考虑因素包括可靠性、带宽和网络延迟,这是应用程序性能的重要考虑因素。选择对主要成员传输给其他成员的日志数据进行压缩是通常但不必须的做法。 每个镜像成员都有几个不同的网络地址,用于不同的目的,在规划支持您的镜像所需的网络配置之前,应该很好地理解这些地址。 包含在单个数据中心、机房或校园内的镜像以及涉及双数据中心和地理上分离的灾难恢复的镜像的示例镜像和网络配置将帮助您定义所需的网络配置。 在故障转移时将应用程序连接重定向到新主节点的选项有哪些? 镜像和 Caché 内置了几个自动重定向选项,包括使用虚拟 IP 地址 (VIP) 进行镜像、将 ECP 数据服务器标识为镜像连接,以及镜像感知 CSP 网关。 镜像 VIP 通常是一种非常有效的解决方案,但确实需要一些提前规划,尤其是在网络配置方面。 还提供一系列外部技术选项,包括使用网络流量管理器(例如负载平衡器) 、自动或手动 DNS 更新、应用程序级编程和用户级程序。 镜像中的 Caché 实例有哪些兼容性要求? 在确定要添加到镜像的系统之前,请务必查看Caché 实例和平台字节顺序兼容性的要求。由于故障转移成员可以随时交换主要和备份的角色,因此它们应该尽可能相似; CPU 和内存配置应该相同或接近,存储子系统应该具有可比性。 如何将现有数据库迁移到镜像? 任何 Caché 数据库都可以轻松添加到镜像中;它所需要的只是能够备份和恢复数据库,或复制其CACHE.DAT文件。程序在下一节中说明。 如果将镜像部署在虚拟化环境中,我应该考虑什么? 在虚拟化环境中使用镜像时,规划虚拟镜像成员主机与物理主机和存储之间的正确关系很重要;镜像和虚拟化平台方面也有重要的操作考虑因素。 配置镜像 我需要考虑哪些配置指南? 如果您计划配置镜像虚拟 IP 地址 (VIP) ,InterSystems 建议将故障转移成员配置为使用相同的超级服务器端口和Web 服务器端口。 主要故障转移成员上的 Caché 实例配置(例如用户、角色、名称空间和映射)或未镜像的数据(例如与 SQL 网关和 Web 服务器配置相关的文件)都不会被其他镜像成员上的镜像复制。因此,在发生故障转移时启用备份或任何 DR 异步成员(可能被提升)以接管主服务器所需的任何设置或文件必须在这些成员上手动复制并根据需要进行更新。 不要在配置为镜像成员的任何系统上禁用 Internet 控制消息协议 (ICMP);镜像依靠 ICMP 来检测成员是否可达。 由于日志记录是镜像同步的基础,因此必须监视和优化故障转移成员上的日志记录性能并通常遵循日志记录最佳实践。特别是,InterSystems 建议您增加所有镜像成员上的共享内存堆大小(Shared memory heap size)。 如何保护镜像? 保护镜像通信的主要方法是 SSL/TLS,它使用 X.509 证书加密镜像内的所有流量。强烈建议使用 SSL/TLS 安全性。要在镜像上启用 SSL/TLS,您必须首先在每个镜像成员上创建一个镜像 SSL/TLS 配置;您可能会发现在创建镜像之前执行此操作最方便。启用 SSL/TLS 时,添加到镜像的每个成员都必须在主服务器上获得授权;成员的 X.509 证书更新时也是如此。 对于使用 SSL/TLS 的镜像的另一层保护,您可以激活日志加密。这意味着日志记录在主服务器上创建时使用其活动加密密钥之一进行加密,并在其他成员取消日志记录之前解密。备份和所有异步必须激活相同的密钥,备份和 DR 异步也必须使用它来加密数据。 配置镜像使用的网络的方式对镜像的安全性也有重要影响。 如何配置镜像虚拟IP地址(镜像VIP)? 镜像 VIP 是通过在创建和添加成员到镜像或修改镜像时指定详细信息来配置的,但是需要一些准备工作,包括所需信息的标识以及镜像成员的主机和 Caché 实例的可能配置。 我在哪里以及如何安装仲裁器? 仲裁器的位置应尽量减少仲裁器和故障转移成员意外同时中断的风险(如果两个故障转移都失败,则仲裁器变得无关紧要),因此其位置主要取决于故障转移成员的位置。单个系统可以配置为多个镜像的仲裁器,前提是它的位置适合每个镜像。托管镜像的一个或多个故障转移或 DR 异步成员的系统不应配置为该镜像的仲裁者。 任何运行 2015.1 或更高版本 ISCAgent 的系统,包括托管一个或多个 Caché 2015.1 或更高版本实例的系统,都可以配置为仲裁器。您可以准备任何其他受支持的系统(OpenVMS 系统除外),包括托管 2015.1 之前的 Caché 实例的系统,通过安装 ISCAgent将其配置为仲裁器。 如何安装和启动 ISCAgent? ISCAgent 随 Caché 自动安装,因此安装在任何镜像成员上。但是,必须将代理配置为在每个镜像成员上的系统启动时启动。 如何创建和配置镜像? 配置镜像是一个多步骤的过程: 创建镜像并配置第一个故障转移成员 配置第二个故障转移成员(如果需要) 授权第二个故障转移成员,如果使用 SSL/TLS(推荐) 配置异步镜像成员(如果需要,DR 或报告) 授权新的异步成员,如果使用 SSL/TLS(推荐) 在完成这些步骤中的任何一个之后,您可以在镜像监视器中查看镜像的状态以确认结果是否符合预期。 如何创建镜像数据库?如何将现有数据库添加到镜像? 在将数据库添加到镜像之前,您可能需要查看某些镜像数据库注意事项,这些注意事项与哪些内容可以镜像和哪些内容不能镜像、镜像和Shadow的同时使用、镜像数据库属性的传播以及镜像下每个实例的最大数据库数有关。 创建镜像数据库和添加现有数据库的过程是不同的,因为对镜像数据库的更改记录在镜像日志文件中,这与非镜像日志文件不同。如果数据库创建为镜像数据库,它从一开始就使用镜像日志文件,这使得通过在每个镜像成员上创建具有相同镜像名称的镜像数据库,可以很容易地将新数据库添加到镜像中。 当您将现有的非镜像数据库添加为主数据库上的镜像数据库时,它会从使用非镜像日志文件切换到镜像日志文件。因此,您不能简单地在其他成员上创建数据库,因为镜像无法将非镜像日志文件传送给其他成员。取而代之的是,在将数据库添加到主数据库的镜像后,您必须将其备份并在其他成员上恢复,或者将其CACHE.DAT文件复制到其他成员。 如何确保 ECP 在故障转移后重定向应用程序服务器连接? 无论您是否配置了镜像 VIP,您都可以通过将镜像 ECP 数据服务器配置为连接到它的每个 ECP 应用程序服务器上的镜像连接来确保 ECP 连接被重定向到新的主服务器。 (应用服务器不使用 VIP;因为它定期从指定主机收集信息,它会自动检测故障转移并切换到新的主服务器。) 当无法使用镜像 VIP 时(例如在云中),如何重定向应用程序连接? 只有当镜像成员位于同一网络子网上时才能使用镜像 VIP,而当它们位于不同的数据中心时通常不会出现这种情况。出于类似的原因,VIP 通常不是云中部署的选项。 可以使用一系列外部技术替代方案,包括使用负载均衡器(物理或虚拟)等网络流量管理器,可用于实现与 VIP 相同级别的透明度,向客户端应用程序提供单个地址或设备。其他可能的机制包括自动或手动 DNS 更新、应用程序级编程和用户级程序。 如何将 Caché Shadow转换为镜像? 镜像提供了一个Shadow到镜像实用程序,允许您将Shadow源和目标以及它们之间映射的Shadow数据库转换为具有主数据库、备份或异步数据库和镜像数据库的镜像。 我应该查看哪些其他配置细节? 虽然默认值通常是所需的全部,但您可能希望自定义 ISCAgent 端口号。 在主要故障转移成员上,您可能希望将代码从现有的^ZSTU或^ZSTART例程移动到用户定义的^ZMIRROR 例程,它允许您为特定镜像事件实现自定义的、特定于配置的逻辑和机制,以便它是直到镜像初始化后才执行。 将镜像与 Ensemble 一起使用时,您应该了解具有镜像数据的 Ensemble 命名空间的特殊要求以及 Ensemble Autostart 在镜像环境中的功能。 管理镜像 如何监控镜像的运行? 您可以在任何镜像成员的 Caché 管理门户中加载的Mirror Monitor提供有关的详细信息 镜像及其每个成员的运行状态,包括使用 SSL/TLS 时成员的 x.509 DN。 在故障转移成员上,两个故障转移成员的网络地址和仲裁器连接状态,以及仲裁器的地址;在异步上,报告异步所属的镜像。 在备份和异步成员上, 日志数据从主数据传输的状态和日志数据的Dejournaling,以及日志数据从主数据到达的速率。 加载镜像监视器的成员上镜像数据库的状态。 Mirror Monitor 还允许您执行许多操作,包括查看和搜索成员的日志文件、 将 DR 异步提升为故障转移成员或将备份降级为 DR 异步,以及激活、赶上和删除镜像数据库。 您可以在镜像成员的%SYS命名空间中使用 Caché 系统状态例程 ( ^%SS ) 来监视其镜像通信进程。 如何修改镜像?我可以修改什么? 在主服务器上编辑镜像以更改镜像的配置(包括 SSL/TLS、镜像 VIP 等)并在网络配置更改时更新成员的网络地址。您还必须编辑主服务器上的镜像以授权其他成员上的 X.509 证书更新。 在异步上编辑镜像以更改异步类型,将报告异步添加到另一个镜像,并进行其他特定于异步的更改。 您可以使用Mirror Monitor从任何成员(且仅该成员)的镜像中删除镜像数据库,尽管其影响因所涉及的成员类型而异。 我可以在镜像中添加成员吗?删除一个?如何完全删除镜像? 您始终可以将异步成员添加到镜像中,最多可添加 16 个成员。如果你有一个故障转移成员和少于 15 个异步,你总是可以添加一个备份。您还可以通过将 DR 异步提升为故障转移成员来替换备份,这会自动将当前备份降级为 DR 异步。 您可以编辑任何成员的镜像以从镜像中删除该成员。要完全删除镜像,您必须按特定顺序删除成员并采取其他步骤。 如果我需要暂时从镜像中删除成员怎么办? 您可以使用镜像监视器通过断开成员与镜像的连接来无限期地停止备份或异步成员上的镜像,例如进行维护或(在异步情况下)减少网络负载。 在异步上,您还可以暂停镜像中所有数据库的Dejournaling,而不暂停从主数据库到异步数据库的日志数据传输。 我必须一次升级镜像吗?我必须把镜像从生产中取出来做吗? 镜像的所有故障转移和 DR 异步成员必须是相同的 Caché 版本,并且只能在镜像升级期间有所不同。一旦升级的成员成为主要成员,您就无法使用其他故障转移成员或任何 DR 异步成员,直到它们也升级为止。通常,最佳做法是同时将报告异步升级到同一版本。 您选择的升级过程取决于您是进行维护版本升级、 不对镜像数据库进行任何更改的主要升级,还是对镜像数据库进行更改的主要升级。所提供的程序旨在最大限度地减少应用程序停机时间;在前两种情况下,您通常可以完全避免停机时间,而在后一种情况下,它通常仅限于执行计划的故障转移和进行所需的镜像数据库更改所需的时间。 当您在计划停机期间进行重大升级并且不需要最小化应用程序停机时间时,您可能还想使用一个更简单的过程。 我应该了解哪些其他镜像或镜像相关的管理程序和细节? 您可以在未使用SSL/TLS 的镜像上启用安全性,只要每个成员都具有有效的镜像 SSL/TLS 配置。 您可以为未使用它的镜像激活日志加密,只要该镜像使用 SSL/TLS 安全性并且用于加密主要日志数据的活动加密密钥在备份和所有异步中也处于活动状态。 根据您的硬件和网络配置,您可能需要调整镜像的服务质量超时(QoS 超时)设置,这在故障转移机制中起着重要作用。通常,如果需要更快地响应中断,则可以在部署在具有专用本地网络的物理(非虚拟化)主机上的镜像上减小此设置。 如果绝大多数镜像数据库更新由高度压缩的数据(如压缩图像)或加密数据组成,则日志数据压缩预计不会有效,因此可能会浪费 CPU 时间。在这种情况下,您可以选择配置或修改镜像以将日志数据设置为Uncompressed 。 (使用 Caché 数据库加密或日志加密不是选择压缩的一个因素。) 如果主要成员和其他镜像成员之间的网络延迟成为问题,您可以通过微调操作系统 TCP 参数来减少它,以允许主要成员和备份/异步成员分别建立适当大小的发送和接收缓冲区. ^MIRROR 例程为所有镜像任务提供了管理门户的命令行替代方案。 SYS.Mirror API 提供了以编程方式调用通过管理门户和^MIRROR例程可用的镜像操作的方法。 镜像中断程序 有关处理各种计划内和计划外镜像中断情况的建议过程的概述,请参阅镜像中断过程。