搜索​​​​

清除过滤器
文章
姚 鑫 · 二月 25, 2022

第六十五章 SQL函数 %INTERNAL

# 第六十五章 SQL函数 %INTERNAL 返回逻辑格式表达式的格式转换函数。 # 大纲 ``` %INTERNAL(expression) %INTERNAL expression ``` # 参数 - `expression` - 要转换的表达式。 字段名、包含字段名的表达式,或返回可转换数据类型(如`DATE`或`%List`)中的值的函数。 # 描述 `%INTERNAL`将表达式转换为逻辑格式,与当前选择模式(显示模式)无关。逻辑格式是数据的内存格式(对其执行操作的格式)。`%INTERNAL`通常用于选择列表`SELECT-ITEM`。 **可以在`WHERE`子句中使用`%INTERNAL`,但强烈建议不要使用`%INTERNAL`,因为使用`%INTERNAL`会阻止在指定字段上使用索引,并且`%INTERNAL`会强制所有比较区分大小写,即使该字段有默认排序规则也是如此。** 应用`%INTERNAL`会将列标题名称更改为诸如“`Expression_1`”之类的值;因此,通常需要指定列名别名,如下面的示例所示。 `%INTERNAL`将数据类型%DATE的值转换为整数数据类型值。`%INTERNAL`将数据类型`%TIME`的值转换为数字`(15,9)`数据类型值。之所以提供此转换,是因为ODBC或JDBC客户端不识别逻辑`%DATE`和`%TIME`值。 `%INTERNAL`是否转换日期取决于日期字段或函数返回的数据类型。`%INTERNAL`转换`CURDATE`、`CURRENT_DATE`、`CURTIME`和`CURRENT_TIME`值。它不转换`CURRENT_TIMESTAMP`、`GETDATE`、`GETUTCDATE`、`NOW`和`$HOROLOG`值。 不能将流字段指定为ObjectScript一元函数(包括所有格式转换函数,`%Internal`除外)的参数。`%INTERNAL`函数允许将流字段作为表达式值,但不对该流字段执行任何操作。 `%INTERNAL`是InterSystems SQL扩展。 要将表达式转换为显示格式,而不考虑当前的选择模式,请使用`%EXTERNAL`函数。要将表达式转换为`ODBC`格式,而不考虑当前的`SELECT`模式,请使用`%ODBCOUT`函数。 # 示例 下面的动态SQL示例以当前选择模式格式返回出生日期(道布)数据值,并使用`%INTERNAL`函数返回相同的数据。出于演示目的,在此程序中,为每次调用随机确定`%SelectMode`值: ```java ClassMethod Internal() { s tStatement = ##class(%SQL.Statement).%New() s tStatement.%SelectMode=$RANDOM(3) if tStatement.%SelectMode=0 {WRITE "Select mode LOGICAL",! } elseif tStatement.%SelectMode=1 {WRITE "Select mode ODBC",! } elseif tStatement.%SelectMode=2 {WRITE "Select mode DISPLAY",! } s myquery = 2 s myquery(1) = "SELECT TOP 5 DOB,%INTERNAL(DOB) AS IntDOB " s myquery(2) = "FROM Sample.Person" s qStatus = tStatement.%Prepare(.myquery) s rset = tStatement.%Execute() d rset.%Display() w !,"End of data" } ``` ```java DHC-APP>d ##class(PHA.TEST.SQLCommand).Internal() Select mode DISPLAY DOB IntDOB 04/25/1990 54536 01/02/2014 63189 01/02/2014 63189 01/28/1978 50066 5 Rows(s) Affected End of data ``` 下面的示例显示了此函数的两种语法形式;在其他方面它们是相同的。它们指定`%LIST`字段的`%EXTERNAL`(显示格式)、`%INTERNAL`(逻辑格式)和`%ODBCOUT`(ODBC格式): ```sql SELECT TOP 10 %EXTERNAL(FavoriteColors) AS ExtColors, %INTERNAL(FavoriteColors) AS IntColors, %ODBCOUT(FavoriteColors) AS ODBCColors FROM Sample.Person ``` ```sql SELECT TOP 10 %EXTERNAL FavoriteColors AS ExtColors, %INTERNAL FavoriteColors AS IntColors, %ODBCOUT FavoriteColors AS ODBCColors FROM Sample.Person ```
文章
shaosheng shengshao · 九月 14, 2022

windows下处理IIS在未安装但Healthshare已安装的时候,部署IIS服务并代理Healthshare

研究Healthshare2018在已经安装完成使用的情况下,部署IIS,并代理平台。之前看到可以通过单独的CSP Gateway安装包进行处理这种问题,该文主要是获取不到该安装包的时候可以如何实现IIS的处理。 第一步:首先按照网上教程部署IIS服务。安装完成之后会在C盘创建一个名叫intepub的包。 第二步:在inetpub包下面直接插入CSPGateway 。这个包是在其他先安装IIS下,后安装Healthshare的CSP网关服务的时候在inetpub下面自动插入的包,直接找一个复制过来。 第三步:打开IIS,创建一个叫csp的网站,按照下图配置。 第四步:在点击模块进行CSPms模块的添加。 接下来是配置好的CSPms的模块。 注意:我们从图中看到的CSPms模块是本机继承的模块,我们在右边添加本机模块的时候并不能找到C:\inetpub\CSPGateway\CSPms.dll这个模块,导致加不进去。那我们就直接在配置文件上面改。找到C:\Windows\System32\inetsrv\config该文件路径下的applicationHost.config文件,为了稳妥起见,我们先备份一下该文件。找到<globalModules>节点,在下面加入<add name="CSPms" image="C:\Inetpub\CSPGateway\CSPms.dll" />找到<modules>节点,在下面加入<add name="CSPms" />找到hiddenSegments节点,找到下面的bin参数,删除<add segment="bin " />,如下图所示。 保存文件,如果保存过程中出现不允许修改的问题,可以把文件贴到桌面,在这个文件上面改造,在copy回去直接替换。再回到iis页面,网站csp的模块里面就会出现对应的CSPms的本机模块。后面进行服务映射,双击点击处理程序映射。 第六步:接下来测试我们打开平台管理页面能不能打开。 如果出现了这个页面,那我们就快成功了,这个错误可能我们大家都有见过。我们打开C:\inetpub\CSPGateway下面的CSP.ini的文件。里面有个 有这个Password字段,因为我们的CSPGateway这个包是从其他服务器上copy过来的,这个密码只能用于那台服务,不能用于这个服务器,那我们可以拿57772端口的apache配置下的这个密码拿过来用。在安装路径下D:\InterSystems\HealthShare\CSP\bin的CSP.ini,也有一个Password字段,把这个密码copy过来放到C:\inetpub\CSPGateway下面的CSP.ini上。重启IIS服务,如果出现权限问题,也把CSP.ini拷贝到桌面,修改好后再贴回去进行替换。再次进行访问,就可以进去桌面了。第七步:点入到CSP网关管理,如果提示出现warning字眼,那是C:\inetpub\CSPGateway下面的用户权限问题。 对CSPGateway右键按照下图示例进行IIS_IUSRS用户权限的添加。对文件夹下的CSP.ini和CSP.log做一样的操作。 好文,欢迎参赛!
文章
姚 鑫 · 一月 25

第七章 C - D 开头的术语

# 第七章 C - D 开头的术语 #### 当前设备 (current device) **系统** 处理 `I/O` 命令的设备。当你登录时,当前设备是你的主设备,通常是你登录时使用的终端或个人计算机。 #### 当前目录 (current directory) **通用** 你当前工作的目录。 #### 基于游标的 SQL (cursor-based SQL) **SQL** 一种嵌入式 `SQL` 查询类型,打开一个游标来处理查询。当你的应用程序需要访问多行数据时,必须使用游标。游标像一个指针——它专注于访问和处理一行数据,然后移动到序列中的下一行。 #### 游标 (cursor) **SQL** 在多行数据中向前移动的迭代器。 #### 自定义存储 (custom storage) **对象(Objects)** 自定义存储允许你通过编写自己的存储接口方法实现来决定对象的存储结构。通常,使用自定义存储的类不会被投射到 `SQL`。 # 以 D 开头的术语 #### 数据库 (database) **通用** `IRIS.DAT` 文件。它可以包含代码和数据。通过全局、包和例程映射,任何给定的数据库都可以被多个命名空间使用。 #### 数据库缓存 (database cache) **系统** 用于缓存从数据库中检索的数据的系统内存(`RAM`),这样相同查询的重复实例可以从内存中而不是存储中检索结果,从而提供显著的性能提升。为了获得最佳性能,数据库缓存应至少与应用程序工作负载的工作集一样大。 #### 数据库加密 (database encryption) **系统** 将 `IRIS` 数据库以加密状态存储在磁盘上的过程。当 `IRIS` 从磁盘读取数据时,数据会在运行时自动解密,以便其合法用户可以访问数据。当数据写入磁盘时,会进行加密。磁盘上的数据也称为静态数据(`at rest`)。 #### 数据库加密密钥 (database-encryption key) **系统** 用于加密 `IRIS` 数据库的 `AES` 密钥。 #### 数据库完整性 (database integrity) **通用** 数据库在其内容或结构上保持未损坏的状态。数据库中的数据有时由于物理完整性退化而变得不可读。内部指针可能由于内部完整性退化而损坏。 #### 数据定义语言 (Data Definition Language) **InterSystems SQL** 数据定义语言(`DDL`)是一种基于命令的语言,用于创建、定义表的结构和删除表。 #### 数据位置 (data location) **系统** 全局所在的数据集。复制全局的全局集合和删除操作的来源是映射到全局数据位置的目录(或目录和系统)。
文章
YuHao Wan · 十一月 5, 2022

Caché实现SM4分组密码算法

### 0. 算法概述 SM4算法是一种分组密码算法。其分组长度为128bit,密钥长度也为128bit。加密算法与密钥扩展算法均采用32轮非线性迭代结构,以字(32位)为单位进行加密运算,每一次迭代运算均为一轮变换函数F。SM4算法加/解密算法的结构相同,只是使用轮密钥相反,其中解密轮密钥是加密轮密钥的逆序。 ### 1. 密钥及轮密钥 密钥长度为128比特,表示为MK=(MK(0),MK(1),MK(2),MK(3)),其中MKi(i=0,1,2,3)为字。 轮密钥表示为(rk(0),rk(1),...,rk(31)),其中rk(i)(i=0,...,31)为32比特字。轮密钥由秘钥生成。 ![密钥及轮密钥](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM4-01.png) ### 2. 消息填充分组 首先,将明文转化为字节,由于SM4加密算法按照128个位进行分组,所以很大几率会出现最后一个分组不够128位的情况,需要进行**填充**,填充方式有很多,比如ZeroPadding、PKCS7Padding、PKCS5Padding,不管使用哪种方式,最后每个分组都是128位。每个分组按照**32位**一个字分成四个字。 #### ECB模式与CBC模式 - ECB模式 电子密码本模式,最古老,最简单的模式,将加密的数据分成若干组,每组的大小跟加密密钥相同。不足的部分进行填充。 按照顺序将计算所得的数据连在一起即可,各段数据之间互不影响。 优点:简单,有利于并行计算,误差不会被传递。 缺点:不能隐藏明文的模式,可能对明文进行主动攻击。 - CBC模式 密文分组链接模式,也需要进行分组,不足的部分按照**指定的数据**进行填充。 需要一个**初始化向量**,每个分组数据与上一个分组数据加密的结果进行**异或运算**,最后再进行加密。将所有分组加密的结果连接起来就形成了最终的结果。 优点:不容易进行主动攻击,安全性好于ECB。 缺点:不利于并行计算,误差传递,需要初始化向量。 #### 三种填充方式的比较 某些加密算法要求明文需要按一定长度对齐,叫做**块大小**(BlockSize),比如16字节,那么对于一段任意的数据,加密前需要对最后一个块填充到16 字节,解密后需要删除掉填充的数据。 - **ZeroPadding**,数据长度不对齐时使用**0**填充,否则不填充。 - **PKCS7Padding**,假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是**n**;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小。 - **PKCS5Padding**,PKCS7Padding的子集,块大小固定为**8**字节。 由于使用PKCS7Padding/PKCS5Padding填充时,最后一个字节肯定为填充数据的长度,所以在解密后可以准确删除填充的数据,而使用ZeroPadding填充时,没办法区分真实数据与填充数据,所以只适合以\0结尾的字符串加解密。 ### 3. 迭代运算 本加解密算法由32次迭代运算和1次反序变换R组成。 ![迭代运算](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM4-02.png) #### 3.1 轮函数F和合成置换T ![轮函数和合成置换T](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM4-03.png) ### 4. Caché实现 ``` /// SM4算法是一种分组密码算法。其分组长度为128bit,密钥长度也为128bit。 /// 加密算法与密钥扩展算法均采用32轮非线性迭代结构,以字(32位)为单位进行加密运算,每一次迭代运算均为一轮变换函数F。 /// SM4算法加/解密算法的结构相同,只是使用轮密钥相反,其中解密轮密钥是加密轮密钥的逆序。 /// 本方法适用于 ECB模式,ZeroPadding填充模式 Class Utility.SM4 Extends %RegisteredObject { /// Creator: wyh /// CreatDate: 2022-11-03 /// Description:SM4加密 /// Input: msg:原文 mk:128位密钥 /// Output: 密文 /// Debug: w ##class(Utility.SM4).Encrypt("342622199009262982", "F2D8D966CD3D47788449C19D5EF2081B") ClassMethod Encrypt(msg, mk) { #; 1. 密钥及轮密钥 #; a) 密钥长度为128比特,表示为MK=(MK(0),MK(1),MK(2),MK(3)),其中MKi(i=0,1,2,3)为字 #; b) 轮密钥表示为(rk(0),rk(1),...,rk(31)),其中rk(i)(i=0,...,31)为32比特字。轮密钥由密钥生成。 #; 密钥扩展算法: #; (K(0),K(1),K(2),K(3))=(MK(0)^FK(0),MK(1)^FK(1),MK(2)^FK(2),MK(3)^FK(3)) #; rk(i)=K(i+4)=K(i)^T'(K(i+1)^K(i+2)^K(i+3)^CK(i)),i=0,1,...,31 #; 系统参数FK(0)=(A3B1BAC6),FK(1)=(56AA3350),FK(2)=(677D9197),FK(3)=(B27022DC) #; 固定参数CK(i)(i=0,1,...,31)为: #; 00070E15, 1C232A31, 383F464D, 545B6269, #; 70777E85, 8C939AA1, A8AfB6BD, C4CBD2D9, #; E0E7EEF5, FC030A11, 181F262D, 343B4249, #; 50575E65, 6C737A81, 888F969D, A4ABB2B9, #; C0C7CED5, DCE3EAF1, F8FF060D, 141B2229, #; 30373E45, 4C535A61, 686F767D, 848B9299, #; A0A7AEb5, BCC3CAD1, D8DFE6ED, F4FB0209, #; 10171E25, 2C333A41, 484F565D, 646B7279. s mk = $zcvt(mk, "L") f i = 0 : 1 : 3 d .s MK(i) = $e(mk, 8 * i + 1, 8 * (i + 1)) s FK = "a3b1bac656aa3350677d9197b27022dc" f i = 0 : 1 : 3 d .s FK(i) = $e(FK, 8 * i + 1, 8 * (i + 1)) s CK = "00070e151c232a31383f464d545b626970777e858c939aa1a8afb6bdc4cbd2d9e0e7eef5fc030a11181f262d343b424950575e656c737a81888f969da4abb2b9c0c7ced5dce3eaf1f8ff060d141b222930373e454c535a61686f767d848b9299a0a7aeb5bcc3cad1d8dfe6edf4fb020910171e252c333a41484f565d646b7279" f i = 0 : 1 : 31 d .s CK(i) = $e(CK, 8 * i + 1, 8 * (i + 1)) f i = 0 : 1 : 3 d .s K(i) = ..HexXOR(MK(i), FK(i)) f i = 4 : 1 : 35 d .s K(i) = ..HexXOR(K(i - 4), ..T2(..HexXOR(..HexXOR(..HexXOR(K(i + 1 - 4), K(i + 2 - 4)),K(i + 3 - 4)), CK(i - 4)))) f i = 0 : 1 : 31 d .s rk(i) = K(i + 4) #; 2. 消息填充分组 #; 每组128位,每组再分X(0),X(1),X(2),X(3)作为明文输入. s hex = ..s2hex(msg) s len = $l(hex)/32 s rtn = "" f i = 0 : 1 : len-1 d .k X .s M(i) = $e(hex, 32 * i + 1, 32 * (i + 1)) .f j = 0 : 1 : 3 d ..s X(j) = $e(M(i), 8 * j + 1, 8 * (j + 1)) #; 3. 迭代运算,密文输出(Y(0),Y(1),Y(2),Y(3)) #; a) 32次迭代运算 #; X(i+4)=F(X(i),X(i+1),X(i+2),X(i+3),rk(i)),i=0,1,...,31 #; F(X(i),X(i+1),X(i+2),X(i+3),rk(i))=X(i)^T(X(i+1)^X(i+2)^X(i+3)^rk(i)),i=0,1,...,31 #; b)反序变换 #; (Y(0),Y(1),Y(2),Y(3))=R(X(32),X(33),X(34),X(35))=(X(35),X(34),X(33),X(32)) .f k = 0 : 1 : 31 d ..s X(k + 4) = ..HexXOR(X(k), ..T(..HexXOR(..HexXOR(..HexXOR(X(k + 1), X(k + 2)), X(k + 3)), rk(k)))) .s rtn = rtn_X(35)_X(34)_X(33)_X(32) q rtn } /// Creator: wyh /// CreatDate: 2022-11-03 /// Description:SM4解密 /// 解密变换与加密变换结构相同,不同的仅是轮密钥的使用顺序,解密时使用轮密钥序(rk(31),rk(32),...,rk(0)). /// Input: hex:密文 mk:128位密钥 /// Output: 明文 /// Debug: w ##class(Utility.SM4).Decrypt("5efcbbfdb7a326b340295acb1c0e20fe2622730932bdb5302b5a4ee308944ecc", "F2D8D966CD3D47788449C19D5EF2081B") ClassMethod Decrypt(hex, mk) { s mk = $zcvt(mk, "L") f i = 0 : 1 : 3 d .s MK(i) = $e(mk, 8 * i + 1, 8 * (i + 1)) s FK = "a3b1bac656aa3350677d9197b27022dc" f i = 0 : 1 : 3 d .s FK(i) = $e(FK, 8 * i + 1, 8 * (i + 1)) s CK = "00070e151c232a31383f464d545b626970777e858c939aa1a8afb6bdc4cbd2d9e0e7eef5fc030a11181f262d343b424950575e656c737a81888f969da4abb2b9c0c7ced5dce3eaf1f8ff060d141b222930373e454c535a61686f767d848b9299a0a7aeb5bcc3cad1d8dfe6edf4fb020910171e252c333a41484f565d646b7279" f i = 0 : 1 : 31 d .s CK(i) = $e(CK, 8 * i + 1, 8 * (i + 1)) f i = 0 : 1 : 3 d .s K(i) = ..HexXOR(MK(i), FK(i)) f i = 4 : 1 : 35 d .s K(i) = ..HexXOR(K(i - 4), ..T2(..HexXOR(..HexXOR(..HexXOR(K(i + 1 - 4), K(i + 2 - 4)),K(i + 3 - 4)), CK(i - 4)))) f i = 0 : 1 : 31 d .s rk(i) = K(35 - i) s len = $l(hex)/32 s rtn = "" f i = 0 : 1 : len-1 d .k X .s M(i) = $e(hex, 32 * i + 1, 32 * (i + 1)) .f j = 0 : 1 : 3 d ..s X(j) = $e(M(i), 8 * j + 1, 8 * (j + 1)) .f k = 0 : 1 : 31 d ..s X(k + 4) = ..HexXOR(X(k), ..T(..HexXOR(..HexXOR(..HexXOR(X(k + 1), X(k + 2)), X(k + 3)), rk(k)))) .s rtn = rtn_X(35)_X(34)_X(33)_X(32) q ..hex2str(rtn) } /// 非线性变换τ构成 /// τ由4个并行的S盒,设输入A=(a0,a1,a2,a3),输出为B=(b0,b1,b2,b3) /// (b0,b1,b2,b3)=τ(A)=(Sbox(a0),Sbox(a1),Sbox(a2),Sbox(a3)) /// w ##class(Utility.SM4).tau("942600f0") ClassMethod tau(a) { f i = 0 : 1 : 7 d .s a(i) = $e(a, i + 1) s s(0) = "d690e9fecce13db716b614c228fb2c05" s s(1) = "2b679a762abe04c3aa44132649860699" s s(2) = "9c4250f491ef987a33540b43edcfac62" s s(3) = "e4b31ca9c908e89580df94fa758f3fa6" s s(4) = "4707a7fcf37317ba83593c19e6854fa8" s s(5) = "686b81b27164da8bf8eb0f4b70569d35" s s(6) = "1e240e5e6358d1a225227c3b01217887" s s(7) = "d40046579fd327524c3602e7a0c4c89e" s s(8) = "eabf8ad240c738b5a3f7f2cef96115a1" s s(9) = "e0ae5da49b341a55ad933230f58cb1e3" s s(10) = "1df6e22e8266ca60c02923ab0d534e6f" s s(11) = "d5db3745defd8e2f03ff6a726d6c5b51" s s(12) = "8d1baf92bbddbc7f11d95c411f105ad8" s s(13) = "0ac13188a5cd7bbd2d74d012b8e5b4b0" s s(14) = "8969974a0c96777e65b9f109c56ec684" s s(15) = "18f07dec3adc4d2079ee5f3ed7cb3948" f i = 0 : 1 : 15 d .f j = 0 : 1 : 15 d ..s s(i, j) = $e(s(i), 2 * j + 1, 2 * (j + 1)) s rtn = "" f i = 0 : 1 : 3 d .s r = ..hex2int(a(2 * i)) .s c = ..hex2int(a(2 * i + 1)) .s rtn = rtn _ s(r, c) return rtn } /// 线性变换L /// 非线性变换τ的输出是线性变换L的输入.设输入为B,输出为C. /// C=L(B)=B^(B
文章
姚 鑫 · 二月 4, 2021

第二十四章 Caché 变量大全 $ZA 变量

# <center> 第二十四章 Caché 变量大全 $ZA 变量 包含当前设备上最后一次读取的状态。 # 大纲 ```java$ZA``` # 描述 `$ZA`包含当前设备上最后一次读取的状态。 不能使用`SET`命令修改此特殊变量。尝试这样做会导致`<SYNTAX>`错误。 # 注意 ## `$ZA`,带终端I / O `$ZA`被实现为一系列位标志,每个位表示一条特定的信息。下表显示了可能的值、它们的含义,以及如何使用模(`#`)和整数除(`\`)运算符测试它们: 位 | 测试| 含义---|---|---0 |`$ZA#2`| `<CTRL-C>`已到达,无论是否启用中断。1 |`$ZA\2#2`| 读取超时。2 |`$ZA\4#2`| I/O错误。8 |`$ZA\256#2`| Caché检测到无效的转义序列。9 |`$ZA\512#2`| 硬件检测到奇偶校验或成帧错误。11 |`$ZA\2048#2`| 进程已与其主设备断开连接。12 |`$ZA\4096#2`|对于COM端口:CTS(清除发送)。从调制解调器发送到其计算机的信号,表示传输可以进行。对于TCP设备:设备在服务器模式下运行。13 |`$ZA\8192#2`|对于COM端口:DSR(数据集就绪)。从调制解调器发送到其计算机的信号,表示调制解调器已准备好运行。对于TCP设备:设备当前处于与远程主机通话的已连接状态。14 |`$ZA\16384#2`| 如果为真,则设置振铃。15 |`$ZA\32768#2`| 载波检测设置为真。16 |`$ZA\65536#2`|CE_BREAK COM端口错误状态。17 |`$ZA\131072#2`| CE_FRAME COM端口错误状态。18 |`$ZA\262144#2`| CE_IOE COM端口错误状态。19 |`$ZA\524288#2`| CE_OVERRUN COM端口错误状态。20 |`$ZA\1048576#2`| CE_RXPARITY COM端口错误状态。21 |`$ZA\2097152#2`| CE_TXFULL COM端口错误状态。22 |`$ZA\4194304#2`| TXHOLD通讯端口错误状态。在`ClearCommError()`返回的错误掩码中,如果下列任一字段为真,则设置为:fCtsHold、fDsrHold、fRlsdHold、fXoffHold、fXoffSent。`24 & 25 |$ZA\16777216#4|`Caché请求DTR(数据终端就绪)设置:0 = DTR关闭。 1 = DTR =开。 2 = DTR握手。设置为(1)时,表示已准备好发送和接收数据。 尽管`$ZA`显示的许多条件都是错误,但它们不会通过捕获`$ZTRAP`来中断程序的流程。 (具有中断功能的`<CTRL-C>`会陷阱到`$ZTRAP`。)与这些错误有关的程序在每次读取后必须检查`$ZA`。 COM端口使用位12至15、24和25报告调制解调器控制引脚的状态。无论端口的Caché调制解调器控制检查是打开还是关闭,都可以执行此操作。用户可以通过设置`OPEN`或`USE`命令`portstate`参数(具体为字节8)来启用或禁用COM端口的`$ZA`错误报告。如果启用了错误报告,则端口错误状态将在位16到22中报告。 可以使用`%SYSTEM.Process`类的`DisconnectErr()`方法来检测当前进程的调制解调器断开连接。可以通过设置`Config.Miscellaneous`类的`DisconnectErr`属性来建立系统范围的默认行为。 ## 带磁带I / O的$ZA 对于磁带I / O,`$ZA`中的位字段指示错误和特殊情况。在每个引用磁带设备的命令之后,Caché更新`$ZA`。 下表显示了磁带I/O的`$ZA`位的含义。请注意Trap列。字母Y表示`<MAGTAPE>`错误。如果设置了`$ZTRAP`变量,则Caché会发出相关的`$ZTRAP`错误代码。 位 | 值| 陷阱 |含义注意---|---|---|---0 |1| Y| 逻辑错误(读写混合),用于在读取和写入之间切换,或者关闭然后打开设备,或者发出前进空格、退格键或倒带命令。2 |4| N| 写保护始终反映`OPEN`或`USE`只读参数的状态。此位不反映磁带的物理写保护状态(写环或写锁定),因为许多版本的UNIX®在尝试实际写入磁带之前不会通知磁带写保护。如果尝试打开不带只读参数的写保护9磁道磁带,则Caché会设置此位并以只读方式打开磁带。未出现错误。3 |8| Y| 错误摘要错误摘要是导致Caché错误的所有条件(在Trap下标记为Y的所有条件)的逻辑或。5 |32| N| 磁带的开始[BOT]在UNIX®系统上,该位在倒带时设置,并在打开磁带时清除。6 |64| N| On Line 7 |128| Y|控制器或驱动器错误。10 |1024| N|磁带结尾[EOT]在大多数UNIX®平台上不支持。14 |16384| Y| Tape MarkCaché在遇到Read,Read Block,Forward Space或Backspace上的磁带标记时将Tape Mark位置1。这会将“错误摘要”位置1,并在“读取”,“读取标签”和“读取块”上将陷阱陷阱为`$ZTRAP`。15 |32768| Y| 磁带未准备好 一些位指示错误条件,而另一些位指示不一定产生错误的条件。为了监视这些非错误情况,程序必须在每次磁带操作后测试`$ZA`的相应位。例如,如果程序可能写在磁带末尾之外,则它必须检查位10(磁带末尾)。 要测试某个位,请将`$ZA`除以表中该位列出的值,然后执行模2运算。例如,以下命令检查是否设置了位14(磁带标记): ```javaUSE 47 IF $ZA\16384#2 {DO Endfile}``` 其中16384等于2等于14的幂,而#2表示模2运算。由于任何等于0的幂等于1,因此无需除数即可检查位0(逻辑错误)。例如: ```javaUSE 47 GOTO Logerr:$ZA#2```
文章
Nicky Zhu · 一月 8, 2021

Web 服务业务操作客户端 – 响应超时行为

调用 Web 服务的过程中,在期望的时间内未返回响应时,后续发生的情况由业务操作的几个设置来控制。(请注意,这也与非 SOAP 简单 HTTP 调用等有关)   3 个主要设置为: 响应超时 指定从远程 Web 服务器获取响应的超时时间。 重试间隔 尝试与 Ensemble 之外的目标进行连接之间等待的秒数。 故障超时 尝试与 Ensemble 之外的目标保持连接的总秒数。 经过此秒数时间后,业务操作将丢弃消息数据并返回错误代码。   归纳一下就是这样: 我们将等待 Web 服务器的响应,时间为“响应超时”所用的时间。 如果此时间过后未收到响应,我们将在“重试间隔”时间过后再次调用 Web 服务器。 如此继续尝试,直到时间(从第一次尝试算起)达到“故障超时”设置的时间秒数。   以下举例进行说明:   假定以下设置:   就是说: 响应超时 - 等待 7 秒以响应 重试间隔 - 每 10 秒重试一次 故障超时 – 30 秒后“放弃”重试   假定在 8 秒后返回响应,则情况如下: 在 00:00,我们进行第一次调用 在 00:07,由于未返回任何响应,我们在内部确认发生了“响应超时”错误(并在“事件日志”中记录“错误事件”),并根据重试策略和设置再次尝试 – “故障超时”尚未到达,因此引发了“需要重试标志”。 [在 00:08,Web 服务器返回一个响应,但由于我们已经将其记录为超时错误,因此我们不会收到此响应] 在 00:10,重试间隔时间已到,并且由于引发了“需要重试标志”,我们将再次调用 Web 服务。 在 00:17,如果仍未返回任何响应,我们将再次达到“响应超时”时间(请注意,在上面的“00:08/第 3 段中,返回的响应已被“忽略/丢弃”),因此我们再次在内部将此认为是错误(但这次不会再添加一个“错误事件日志”条目,只会在第一次尝试时记录此错误,而不是每次重试失败时都记录),由于我们尚未达到“故障超时”时间,因此需要再次引发“需要重试标志”。 [在 00:18,Web 服务器返回一个响应,这次我们还是接收不到] 在 00:20,又达到重试间隔时间,我们将尝试第 3 次调用。 在 00:27,仍无响应,再次发生“响应超时”错误,需要重试(仍未达到“故障超时”)。 [在 00:28,服务器未发回响应] 在 00:30,再次达到重试间隔时间,我们进行第 4 次尝试(也是最后一次)。 在 00:37,“响应超时”再次发生 – 这次,已达到“故障超时”时间,因此我们不会引发“需要重试标志”,而是放弃 – 我们在事件日志中记录一个错误事件,指出已达到故障超时时间,并且返回业务操作调用错误。   以下是上述示例的调用“证据”。   首先是服务器端 [来自 SOAP 日志] – 可以看到,其收到了 4 次调用/请求,均相隔 10 秒,每次在请求发出 8 秒后返回一个响应: * * * 05/31/2016 14:18:45 ********************* Input to Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:18:53 ********************* Output from Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:18:55 ********************* Input to Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:03 ********************* Output from Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:05 ********************* Input to Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:13 ********************* Output from Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:15 ********************* Input to Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:23 ********************* Output from Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   现在,从 Ensemble BO/客户端,可以看到,有 4 次尝试,均相隔 10 秒,每次在 7 秒后记录响应超时错误。   客户端   05/31/2016 14:18:45 ********************* Output from Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:18:52 ********************* Input to Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ERROR #5922: Timed out waiting for response string**** SOAP client return error. method=GetResponse, action=http://tempuri.org/Test.WSTimeouts.WebService.GetResponse      ERROR #5922: Timed out waiting for response   05/31/2016 14:18:55 ********************* Output from Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:02 ********************* Input to Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ERROR #5922: Timed out waiting for response string**** SOAP client return error. method=GetResponse, action=http://tempuri.org/Test.WSTimeouts.WebService.GetResponse      ERROR #5922: Timed out waiting for response   05/31/2016 14:19:05 ********************* Output from Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:12 ********************* Input to Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ERROR #5922: Timed out waiting for response string**** SOAP client return error. method=GetResponse, action=http://tempuri.org/Test.WSTimeouts.WebService.GetResponse      ERROR #5922: Timed out waiting for response   05/31/2016 14:19:15 ********************* Output from Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:22 ********************* Input to Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ERROR #5922: Timed out waiting for response string**** SOAP client return error. method=GetResponse, action=http://tempuri.org/Test.WSTimeouts.WebService.GetResponse      ERROR #5922: Timed out waiting for response   以下是对 Ensemble 的可视化跟踪: 以下是事件日志条目:   以下为跟踪事件的日志示例[您可能需要放大以更好地读取文字]: 由此,可以看到上述示例的一些“内部运作”: 在日志行 ID #684,执行了首次调用 – 时间:17:09:16。 然后,在 7 秒之后 (09:23),我们收到响应超时错误 (#685)。 然后,操作记录了此错误 (#687),并确定等待 3 秒钟,直到达到重试间隔时间;10 秒重试间隔时间减去 7 秒响应超时时间 (#688-#690)。 经过 3 秒等待( 09:26;#691)之后,进行第 2 次尝试 (#692),结果相同,继续重复操作,直到第 4 次尝试 (#704) 。 在第 4 次尝试失败(09:53;#705)之后,由于达到超时时间(30 秒),因此未进行另一次尝试。  
文章
姚 鑫 · 一月 31, 2023

第六十二章 使用 SNMP 监控 IRIS

# 第六十二章 使用 SNMP 监控 IRIS 本附录描述了 `IRIS` 数据平台和 `SNMP`(简单网络管理协议)之间的接口。 `SNMP` 是一种通信协议,作为一种管理 `TCP/IP` 网络(包括单个网络设备和一般计算机设备)的方法已获得广泛接受。它的流行扩大了它作为许多企业管理工具的底层结构和协议的用途。这是它对 `IRIS` 的主要重要性:一种向各种管理工具提供管理和监控信息的标准方法。 `SNMP` 既是一种标准消息格式,也是管理对象的一组标准定义。它还提供用于添加自定义管理对象的标准结构,这是 `IRIS` 用来定义其管理信息以供其他应用程序使用的功能。 # 将 `SNMP` 与 `IRIS` 结合使用 `SNMP` 定义了客户端-服务器关系,其中客户端(网络管理应用程序)连接到在远程网络设备或计算机系统上执行的服务器程序(称为 `SNMP` 代理)。客户端请求并从该代理接收信息。有四种基本类型的 `SNMP` 消息: - `GET` – 获取特定托管对象的数据 - `GETNEXT` – 获取分层树中下一个管理对象的数据,允许系统管理员遍历设备的所有数据 - `SET`——为特定的管理对象设置值 - `TRAP`——被管理设备或系统发送的异步警报 `SNMP MIB`(管理信息库)包含管理对象的定义。每个设备都会发布一个文件,也称为其 `MIB`,该文件定义了它支持的标准 `MIB` 的哪一部分,以及托管对象的任何自定义定义。对于 `IRIS`,这是位于 `install-dir\SNMP` 目录中的 ISC-IRIS.mib 文件。 # `IRIS` 作为子代理 `SNMP` 客户端连接到 `SNMP` 代理,该代理正在侦听众所周知的地址端口 `161`。由于客户端希望连接到该特定端口,因此计算机系统上只能有一个 `SNMP` 代理。为了允许访问系统上的多个应用程序,开发人员可以实现主代理,它可以扩展或连接到多个子代理。 已将 `IRIS SNMP` 接口实施为子代理,旨在通过 `SNMP` 主代理进行通信。 `IRIS` 支持的大多数操作系统都提供一个 `SNMP` 主代理,它可以通过某种方式扩展以支持多个子代理。然而,这些代理中的许多都以专有且不兼容的方式实现了它们的可扩展性。 `IRIS` 使用代理扩展性 (`AgentX`) 协议实现其子代理,该协议是 `IETF` 提议的标准,如 `RFC 2741`中所述。 一些标准的 `SNMP` 主代理支持 `AgentX`。如果操作系统提供的 `SNMP` 主代理与 `AgentX` 不兼容,可以将其替换为公共域 `Net-SNMP` 代理。 注意:`Windows` 标准代理例外,它不支持 `AgentX`,`Net-SNMP` 版本可能不适合。对于这个例外,提供了一个 `Windows` 扩展代理 `DLL`,`iscsnmp.dll`,它处理标准 `Windows SNMP` 服务扩展 `API` 和 `IRIS AgentX` 服务器之间的连接。 # 在 `IRIS` 中管理 `SNMP`。 由于 `SNMP` 是标准协议, `IRIS` 子代理的管理是最少的。最重要的任务是验证系统上的 `SNMP` 主代理是否与代理扩展性 (`AgentX`) 协议兼容(请参阅 `IRIS` 作为子代理)并且它处于活动状态并正在侦听标准 `AgentX TCP` 端口 `705` 上的连接。 `Windows`系统,系统自动安装一个`DLL`来连接标准的`Windows SNMP`服务。验证 `Windows SNMP` 服务是否已自动或手动安装和启动。 重要提示:一些 `SNMP` 主代理,特别是 `Linux` 上的 `Net-SNMP`,默认情况下不启用 `AgentX`,并且一旦启用,默认情况下不使用 `TCP` 端口 `705`。对于 `Net-SNMP`,必须修改 `snmpd.conf` 文件以启用与 `IRIS` 子代理的通信。最新版本的 `Net-SNMP` 还实现了 `VACM`(基于视图的访问控制模型)安全性,默认情况下,只允许访问 `mib-2.system` 子树; `IRIS` 子代理启动并正常运行,但没有 `SNMP` 请求转发到 `IRIS`。必须扩展 `snmpd.conf` 中定义的“视图”以包括 `IRIS MIB` 子树。 接下来,使用以下步骤启用监控服务: 1. 导航到管理门户中的服务页面(系统管理 > 安全 > 服务)。 2. 单击 `%Service_Monitor` 服务。 3. 选中启用服务复选框并单击保存。 4. 返回到服务列表页面并确保启用了 `%Service_Monitor` 服务。 最后,使用以下步骤将 `IRIS SNMP` 子代理配置为在 `IRIS` 启动时自动启动: 1. 导航到管理门户中的监控设置页面(系统管理 > 配置 > 其他设置 > 监控)。 2. 为系统启动时启动 `SNMP` 代理设置选择是,然后单击保存。 3. 当编辑此设置时,`SNMP` 接口的 `IRIS` 端会立即停止和启动。 还可以使用 `^SNMP` 例程手动或以编程方式启动和停止 `IRIS SNMP` 子代理: ```java Do start^SNMP(,) Do stop^SNMP ``` 其中 `` 是连接的 `TCP` 端口(默认为 `705`),`` 是 `TCP` 端口读取超时值(默认为 `20` 秒)。在达到 `` 值之前, `IRIS` 会在 `install-dir\mgr` 目录下的 `SNMP.LOG` 文件中记录在建立连接或应答请求时遇到的任何问题。 注意:当 `SNMP` 主代理重新启动时,可能需要使用 `^SNMP` 例程手动重新启动 `IRIS SNMP` 子代理,如前所述。
文章
Michael Lei · 七月 6, 2021

精华文章--虚拟化大型数据库 - VMware CPU 容量规划

供应商或内部团队要求说明如何为 VMware vSphere 上运行的_大型生产数据库_进行 CPU 容量规划。 总的来说,在调整大型生产数据库的 CPU 规模时,有几个简单的最佳做法可以遵循: - 为每个物理 CPU 核心规划一个 vCPU。 - 考虑 NUMA 并按理想情况调整虚拟机规模,以使 CPU 和内存对于 NUMA 节点是本地的。 - 合理调整虚拟机规模。 仅在需要时才添加 vCPU。 通常,这会引出几个常见问题: - 由于使用超线程技术,VMware 创建的虚拟机的 CPU 数量可以是物理 CPU 数量的两倍。 那不就是双倍容量吗? 创建的虚拟机不应该有尽可能多的 CPU 吗? - 什么是 NUMA 节点? 我应该在意 NUMA 吗? - 虚拟机应该合理调整规模,但我如何知道什么时候合理? 我以下面的示例回答这些问题。 但也要记住,最佳做法并不是一成不变的。 有时需要做出妥协。 例如,大型生产数据库虚拟机很可能不适合 NUMA 节点,但我们会看到,其实是没问题的。 最佳做法是指必须针对应用程序和环境进行评估和验证的准则。 虽然本文中的示例是在 InterSystems 数据平台上运行的数据库,但概念和规则通常适用于任何大型(怪兽)虚拟机的容量和性能规划。 有关虚拟化最佳做法以及有关性能和容量规划的更多帖子,请参见 [InterSystems 数据平台和性能系列的其他帖子列表](https://cn.community.intersystems.com/post/intersystems-数据平台的容量规划和性能系列文章)。 # 怪兽虚拟机 本帖主要是关于部署_怪兽虚拟机_,有时也称为 _Wide 虚拟机_。 高事务数据库的 CPU 资源要求意味着它们通常部署在怪兽虚拟机上。 > 怪兽虚拟机是指虚拟 CPU 或内存多于物理 NUMA 节点的虚拟机。 # CPU 架构和 NUMA 当前的英特尔处理器架构采用非统一内存架构 (NUMA)。 例如,本帖中用来运行测试的服务器有: - 两个 CPU 插槽,每个插槽一个 12 核处理器(英特尔 E5-2680 v3)。 - 256 GB 内存(16 条 16GB RDIMM) 每个 12 核处理器都有自己的本地内存(128GB RDIMM 及本地高速缓存),还可以访问同一主机中其他处理器上的内存。 每个由 CPU、CPU 高速缓存和 128GB RDIMM 内存组成的 12 核套装都是一个 NUMA 节点。 为了访问其他处理器上的内存,NUMA 节点通过快速互连来连接。 处理器上运行的进程访问本地 RDIMM 和缓存内存的延迟比跨互连访问其他处理器上的远程内存的延迟要低。 跨互连访问会增加延迟,因此性能不一致。 同样的设计也适用于具有两个以上插槽的服务器。 一台四插槽英特尔服务器有四个 NUMA 节点。 ESXi 了解物理 NUMA,ESXi CPU 调度器设计为优化 NUMA 系统的性能。 ESXi 使性能最大化的方法之一是在物理 NUMA 节点上创建数据本地性。 在我们的示例中,如果虚拟机有 12 个 vCPU,并且内存不到 128GB,ESXi 将分配该虚拟机在一个物理 NUMA 节点上运行。 这就形成了规则: > 如果可能,将虚拟机规模调整为使 CPU 和内存对于 NUMA 节点是本地的。 如果需要比 NUMA 节点规模大的怪兽虚拟机也没有问题,ESXi 可以很好地自动计算和管理要求。 例如,ESXi 将创建能够智能调度到物理 NUMA 节点上的虚拟 NUMA 节点 (vNUMA),以获得最佳性能。 vNUMA 结构对操作系统公开。 例如,如果您有一台具有两个 12 核处理器的主机服务器和一个具有 16 个 vCPU 的虚拟机,ESXi 可能会使用每个处理器上的 8 个物理核心来调度虚拟机 vCPU,操作系统(Linux 或 Windows)将看到两个 NUMA 节点。 同样重要的是,应合理调整虚拟机的规模,并且分配的资源不要超过所需的资源,否则会导致资源浪费和性能损失。 除了有助于调整 NUMA 的规模,具有高(但安全的)CPU 利用率的 12 vCPU 虚拟机比具有中低 CPU 利用率的 24 vCPU 虚拟机更高效、性能更好,特别是该主机上还有其他虚拟机需要调度并且争用资源时。 这也再次强化了该规则: > 合理调整虚拟机规模。 __注意:__英特尔和 AMD 的 NUMA 实现有区别。 AMD 每个处理器有多个 NUMA 节点。 我已经有一段时间没有在客户服务器中看到 AMD 处理器了,但是如果你有这些处理器,请检查 NUMA 布局,作为规划的一部分。 ## Wide 虚拟机和授权 为实现最佳 NUMA 调度,请配置 Wide 虚拟机; 2017 年 6 月更正:按每个插槽 1 个 vCPU 配置虚拟机。 例如,默认情况下,一个具有 24 个 vCPU 的虚拟机应配置为 24 个 CPU 插槽,每个插槽一个核心。 > 遵守 VMware 最佳做法规则。 请参见 [VMware 博客上的这篇文章以查看示例。 ](https://blogs.vmware.com/performance/2017/03/virtual-machine-vcpu-and-vnuma-rightsizing-rules-of-thumb.html) 该 VMware 博客文章进行了详细介绍,但是作者 Mark Achtemichuk 建议遵循以下经验法则: - 虽然有许多高级 vNUMA 设置,但只有极少数情况下需要更改其默认值。 - 总是将虚拟机 vCPU 数配置为反映每插槽核心数,直到超过单个物理 NUMA 节点的物理核心数。 - 当需要配置的 vCPU 数量超过 NUMA 节点中的物理核心数量时,将 vCPU 均匀分配到最少数量的 NUMA 节点上。 - 当虚拟机规模超过物理 NUMA 节点时,不要分配奇数数量的 vCPU。 - 不要启用 vCPU 热添加,除非您不介意禁用 vNUMA。 - 不要创建规模大于主机物理核心总数的虚拟机。 Caché 授权以核心数为准,因此这不是问题,但是对于除 Caché 以外的软件或数据库,指定虚拟机有 24 个插槽可能会对软件授权产生影响,因此必须与供应商核实。 # 超线程和 CPU 调度器 超线程 (HT) 经常在讨论中出现,我听过“超线程使 CPU 核心数量翻倍”。 这在物理层面上显然是不可能的,物理核心有多少就是多少。 超线程应该被启用,并会提高系统性能。 预计应用程序性能可能会提高 20% 或更多,但实际数字取决于应用程序和工作负载。 但肯定不会翻倍。 正如我在 [VMware 最佳实践](https://cn.community.intersystems.com/post/intersystems-数据平台和性能-–-第-9-篇-intersystems-iris-vmware-最佳实践指南)中所述,_调整大型生产数据库虚拟机规模_的一个很好的起点是假定 vCPU 拥有服务器上完整的物理核心专用资源 — 在进行容量规划时基本忽略超线程。 例如: > 对于一台 24 核主机服务器,可规划总共多达 24 个 vCPU 的生产数据库虚拟机,且可能还有余量。 在您花时间监测应用程序、操作系统和 VMware 在峰值处理期间的性能后,您可以决定是否进行更高度的虚拟机整合。 在最佳做法帖子中,我将规则表述为: > 一个物理 CPU(包括超线程)= 一个 vCPU(包括超线程)。 ## 为什么超线程不会使 CPU 翻倍 英特尔至强处理器上的超线程是在一个物理核心上创建两个_逻辑_ CPU 的方法。 操作系统可以有效地针对两个逻辑处理器进行调度 — 如果一个逻辑处理器上的进程或线程正在等待,例如等待 IO,则物理 CPU 资源可以被另一个逻辑处理器使用。 在任何时间点都只能有一个逻辑处理器运行,因此虽然物理核心得到了更有效的利用,但_性能并没有翻倍_。 在主机 BIOS 中启用超线程后,当创建虚拟机时,可以为每个超线程逻辑处理器配置一个 vCPU。 例如,在一台启用了超线程的物理 24 核服务器上,可以创建具有多达 48 个 vCPU 的虚拟机。 ESXi CPU 调度器将通过首先在独立的物理核心上运行虚拟机进程来优化处理(同时仍然考虑 NUMA)。 在以后的帖子中,我将探讨在怪兽数据库虚拟机上分配比物理核心数更多的 vCPU 是否有助于扩展。 ### 协同停止和 CPU 调度 在监测主机和应用程序性能后,您可以决定是否让主机 CPU 资源过载。 这是否是一个好主意在很大程度上取决于应用程序和工作负载。 了解调度器和要监测的关键指标有助于确保没有使主机资源过载。 我有时听说,要让虚拟机正常运行,空闲逻辑 CPU 的数量必须与虚拟机中的 vCPU 数量相同。 例如,一个 12 vCPU 虚拟机必须“等待”12 个逻辑 CPU“可用”,才能继续执行。 不过应该注意,ESXi 在版本 3 之后就不是这样了。 ESXi 对 CPU 使用宽松的协同调度,以提高应用程序性能。 由于多个协作线程或进程经常相互同步,不一起调度它们可能会增加操作的延迟。 例如,在自旋循环中,一个线程等待被另一个线程调度。 为了获得最佳性能,ESXi 尝试将尽可能多的同级 vCPU 一起调度。 但是,当有多个虚拟机在整合环境中争用 CPU 资源时,CPU 调度器可以灵活地调度 vCPU。 如果一些 vCPU 的进展比同级 vCPU 领先太多(这个时间差称为偏移),领先的 vCPU 将决定是否停止自身(协同停止)。 请注意,协同停止(或协同启动)的是 vCPU,不是整个虚拟机。 这种机制即使在资源有些过载的情况下也非常有效,但正如您所预期,CPU 资源过载太多将不可避免地影响性能。 我在后面的示例 2 中展示了一个过载和协同停止的例子。 记住,这不是虚拟机之间全力争夺 CPU 资源的竞赛;ESXi CPU 调度器的工作是确保 CPU 共享、保留和限制等策略被遵守,同时最大限度地提高 CPU 利用率,并确保公平性、吞吐量、响应速度和可伸缩性。 关于使用保留和共享来确定生产工作负载优先级的讨论不在本帖范围之内,而且取决于应用程序和工作负载组合。 如果我以后发现任何特定于 Caché 的建议,我可能会重新讨论这个话题。 有许多因素会影响到 CPU 调度器,本节只是简单提一下。 要深入了解,请参见帖子末尾的参考资料中的 VMware 白皮书及其他链接。 # 示例 为了说明不同的 vCPU 配置,我使用一个基于浏览器的高事务速率医院信息系统应用程序运行了一系列基准测试。 与 VMware 开发的 DVD 商店数据库基准测试的概念类似。 基准测试的脚本是根据现场医院实施的观测值和指标创建的,包括高使用率的工作流程、事务和使用最多系统资源的组件。 其他主机上的驱动虚拟机以设置的工作流程事务速率执行具有随机输入数据的脚本,来模拟 Web 会话(用户)。 1 倍速率的基准为基线。 速率可以按比例递增和递减。 除了数据库和操作系统指标外,一个很好的用来衡量基准数据库虚拟机性能的指标是在服务器上测量的组件(也可以是事务)响应时间。 一个组件示例是一部分最终用户屏幕。 组件响应时间增加意味着用户将开始看到应用程序响应时间变差。 性能良好的数据库系统必须为最终用户提供_一致的_高性能。 在下面的图表中,我针对一致的测试性能进行测量,并通过对 10 个最慢的高使用率组件的响应时间取平均值来表示最终用户体验。 预计平均组件响应时间为亚秒级,用户屏幕可能由一个组件组成,或者复杂的屏幕可能有多个组件。 > 请记住,您始终针对峰值工作负载进行规模调整,并且为意外的活动峰值留出缓冲区。 我通常以平均 80% 的峰值 CPU 利用率为目标。 基准测试硬件和软件的完整列表在帖子末尾。 ## 示例 1. 合理调整规模 - 每个主机一个怪兽虚拟机 可以创建一个可以使用主机服务器所有物理核心的数据库虚拟机,例如 24 物理核心主机上的 24 vCPU 虚拟机。 数据库虚拟机不会在 Caché 数据库镜像中“裸机”运行服务器以实现 HA,也不会引入操作系统故障转移集群的复杂性,而是包含在 vSphere 集群中实现管理和 HA,例如 DRS 和 VMware HA。 我见过有客户遵循老派的思维,根据五年硬件寿命结束时的预期容量来确定主数据库虚拟机的规模,但从上文可知,最好合理调整规模;如果虚拟机没有过度调整,性能和整合度会更好,并且管理 HA 将更容易;如果需要维护或主机出现故障,并且数据库怪兽虚拟机必须迁移或在其他主机上重启,想想俄罗斯方块的玩法就知道了。 如果预计事务速率显著增加,可以在计划维护期间提前增加 vCPU。 > 注意,“热添加”CPU 选项会禁用 vNUMA,因此不要将其用于怪兽虚拟机。 考虑下图显示的在 24 核主机上进行的一系列测试。 对于这个 24 核系统,3 倍事务速率是甜蜜点和容量规划目标。 - 主机上运行一个虚拟机。 - 使用了四种虚拟机规模来展示 12、24、36 和 48 vCPU 的性能。 - 尽可能对每种虚拟机规模都运行一系列事务速率(1 倍、2倍、3 倍、4 倍、5 倍)。 - 性能/用户体验以组件响应时间(条形图)的形式显示。 - 客户机虚拟机的 CPU 利用率百分比为平均值(线条)。 - 所有虚拟机规模中,主机 CPU 利用率都在 4 倍速率时达到 100%(红色虚线)。 ![24 物理核心主机 单个客户机虚拟机平均 CPU 百分比和组件响应时间 ](https://community.intersystems.com/sites/default/files/inline/images/single_guest_vm.png "单个客户机虚拟机") 这个图表中有许多信息,但我们可以关注几个有趣的事情。 - 24 vCPU 虚拟机(橙色)平稳地增加到目标 3 倍事务速率。 在 3 倍速率时,客户机内虚拟机的平均 CPU 利用率为 76%(峰值为 91% 左右)。 主机 CPU 利用率并不比客户机虚拟机高多少。 在 3 倍速率之前,组件响应时间非常稳定,因此用户很满意。 就我们的目标事务速率而言 — _这个虚拟机已合理调整规模_。 关于合理规模调整先说这么多,那么增加 vCPU 也就是使用超线程又会如何。 性能和可伸缩性有可能翻倍吗? 简短回答是_不可能!_ 在这种情况下,可以通过查看 4 倍以上速率的组件响应时间来了解答案。 虽然在分配了更多逻辑核心 (vCPU) 后性能“更好”,但仍然不平稳,不像 3 倍速率之前那样一致。 4 倍速率时,用户将报告响应时间变慢,无论分配多少个 vCPU。 请记住,在 4 倍速率时,_主机_曲线已经持平于 100% CPU 利用率,如 vSphere 所报告。 在 vCPU 数量较多的情况下,即使客户机内 CPU 指标 (vmstat) 报告低于 100% 利用率,对于物理资源来说情况也并非如此。 请记住,客户机操作系统不知道它是虚拟化的,它只是报告它所看到的资源。 另外,客户机操作系统也看不到超线程,所有 vCPU 都表现为物理核心。 关键是,数据库进程(在 3 倍事务速率时有 200 多个 Caché 进程)非常繁忙,并且非常高效地使用处理器,逻辑处理器没有很多空闲资源来调度更多工作,或将更多虚拟机整合到该主机。 例如,很大一部分 Caché 处理是在内存中进行的,因此没有很多 IO 等待。 所以,虽然可以分配比物理核心更多的 vCPU,但由于主机已经被 100% 利用,并不会获益许多。 Caché 非常擅长处理高工作负载。 即使主机和虚拟机的 CPU 利用率达到 100%,应用程序仍在运行,并且事务速率仍在提高 — 扩展不是线性的,如我们所见,响应时间越来越长,用户体验将受到影响 — 但应用程序不会“一落千丈”,尽管情况不是很好,但用户仍可以工作。 如果您的应用程序对响应时间不是那么敏感,那么很高兴地告诉您,您可以将其推向边缘甚至更远,并且 Caché 仍然可以安全地工作。 > 请记住,您不会想要以 100% CPU 运行数据库虚拟机或主机。 您需要容量来应对虚拟机的意外峰值和增长,而 ESXi 虚拟机监控程序需要资源来进行所有网络、存储和其他活动。 我总是针对 80% CPU 利用率的峰值进行规划。 即便如此,vCPU 的规模最多也只调整到物理核心数,这样即使在极端情况下,仍然有余量让 ESXi 虚拟机监控程序处理逻辑线程。 > 如果您运行超融合 (HCI) 解决方案,还必须考虑主机级别的 HCI CPU 要求。 有关详细信息,请参见我[先前关于 HCI](https://community.intersystems.com/post/intersystems-data-platforms-and-performance-%E2%80%93-part-8-hyper-converged-infrastructure-capacity "previous post on HCI") 的帖子。 部署在 HCI 上的虚拟机的基本 CPU 规模调整与其他虚拟机相同。 请记住,您必须在您自己的环境中使用您的应用程序验证和测试所有内容。 ## 示例 2. 资源过载 我看到过客户站点报告应用程序性能“慢”,而客户机操作系统却报告有空闲的 CPU 资源。 记住,客户机操作系统并不知道它是虚拟化的。 不幸的是,客户机内指标(例如 vmstat 在 pButtons 中报告的指标)可能具有欺骗性,您还必须获得主机级指标和 ESXi 指标(例如 `esxtop`)才能真正了解系统运行状况和容量。 如上面的图表所示,当主机报告 100% 利用率时,客户机虚拟机可能报告较低的利用率。 36 vCPU 虚拟机(红色)在 4 倍速率时报告 80% 平均 CPU 利用率,而主机报告 100%。 即使规模调整合理的虚拟机也可能出现资源短缺的情况,例如,如果在启动后有其他虚拟机迁移到主机上,或者由于 DRS 规则配置不当而导致资源过载。 为了显示关键指标,在下面的一系列测试中,我进行了以下配置: - 主机上运行两个数据库虚拟机。 - - 一个 24 vCPU 虚拟机以恒定的 2 倍事务速率运行(图表上未显示)。 - - 一个 24 vCPU 虚拟机以 1 倍、2 倍、3 倍事务速率运行(图表上显示这些指标)。 在另一个数据库使用资源的情况下;在 3 倍速率时,客户机操作系统 (RHEL 7) vmstat 只报告 86% 平均 CPU 利用率,运行队列大小平均只有 25。 然而,该系统的用户将大声抱怨,因为组件响应时间随着进程变慢而迅速增加。 如下图所示,协同停止和就绪时间说明了为什么用户性能如此糟糕。 就绪时间 (`%RDY`) 和协同停止 (`%CoStop`) 指标显示 CPU 资源在目标 3 倍速率下大幅过载。 这实际并不奇怪,因为_主机_以 2 倍速率运行(其他虚拟机),_而_该数据库虚拟机以 3 倍速率运行。 ![](https://community.intersystems.com/sites/default/files/inline/images/overcommit_3.png "过载的主机") 该图表明,当主机上的 总 CPU 负载增加时,就绪时间也会增加。 > 就绪时间是指虚拟机已准备好运行,但由于 CPU 资源不可用而无法运行的时间。 协同停止也会增加。 没有足够的空闲逻辑 CPU 来允许数据库虚拟机运行(正如我在上面的超线程部分详细说明的那样)。 最终结果是由于对物理 CPU 资源的争用而导致处理延迟。 我曾在一个客户站点看到过这种情况,当时通过 pButtons 和 vmstat 获取的支持视图只显示了虚拟化的操作系统。 虽然 vmstat 报告还有 CPU 余量,但用户的性能体验非常糟糕。 这里的教训是,直到 ESXi 指标和主机级视图可用,才能诊断出真正的问题;一般的集群 CPU 资源短缺导致的 CPU 资源过载,以及使情况变得更糟的不良 DRS 规则,会使高事务数据库虚拟机一起迁移并使主机资源不堪重负。 ## 示例 3. 资源过载 在此示例中,我使用了一个以 3 倍事务速率运行的基准 24 vCPU 数据库虚拟机,然后使用两个以恒定 3 倍事务速率运行的 24 vCPU 数据库虚拟机。 虚拟机的平均基准 CPU 利用率(见上面的示例 1)为 76%,主机则为 85%。 单个 24 vCPU 数据库虚拟机会使用全部 24 个物理处理器。 运行两个 24 vCPU 虚拟机意味着这两个虚拟机将争用资源,并使用服务器上的全部 48 个逻辑执行线程。 ![](https://community.intersystems.com/sites/default/files/inline/images/overcommit_2vm.png "过载的主机") 请记住,在运行单个虚拟机时,主机并没有被 100% 利用,我们仍然可以看到,当两个非常繁忙的 24 vCPU 虚拟机试图使用主机上的 24 个物理核心(即使开启了超线程)时,吞吐量和性能显著下降。 尽管 Caché 非常有效地使用了可用的 CPU 资源,但每个虚拟机的数据库吞吐量仍然下降了 16%,更重要的是,组件(用户)响应时间增加了 50% 以上。 ## 总结 本帖的目的是回答几个常见问题。 要深入了解 CPU 主机资源和 VMware CPU 调度器,请参见下面的参考部分。 虽然有许多专业级的调整,并且要深入研究 ESXi 才能榨干系统的最后一点性能,但基本规则非常简单。 对于_大型生产数据库_: - 为每个物理 CPU 核心规划一个 vCPU。 - 考虑 NUMA 并按理想情况调整虚拟机规模,以使 CPU 和内存对于 NUMA 节点是本地的。 - 合理调整虚拟机规模。 仅在需要时才添加 vCPU。 如果您想要整合虚拟机,请记住,大型数据库非常繁忙,在高峰期会大量使用 CPU(物理和逻辑)。 在您的监视系统告诉您安全之前,不要超额预定 CPU。 ## 参考 - [VMware 博客 - 怪兽虚拟机何时过载 vCPU:pCPU](https://blogs.vmware.com/vsphere/2014/02/overcommit-vcpupcpu-monster-vms.html) - [2016 NUMA 深入研究系列介绍](http://frankdenneman.nl/2016/07/06/introduction-2016-numa-deep-dive-series) - [VMware vSphere 5.1 中的 CPU 调度器](http://www.vmware.com/content/dam/digitalmarketing/vmware/en/pdf/techpaper/vmware-vsphere-cpu-sched-performance-white-paper.pdf) ## 测试 我在一个 vSphere 集群上运行了本帖中的示例,该集群包括连接到一个全闪存阵列的双处理器 Dell R730。 在示例运行期间,网络或存储没有出现瓶颈。 - Caché 2016.2.1.803.0 PowerEdge R730 - 2 个 Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz - 16 条 16GB RDIMM,2133 MT/s,双列,x4 数据宽度 - SAS 12Gbps HBA 外部控制器 - 超线程 (HT) 开启 PowerVault MD3420,12G SAS,2U-24 驱动器 - 24 个 960GB 固态硬盘 SAS 读取密集型 MLC 12Gbps 2.5 英寸热拔插驱动器,PX04SR - 2 个控制器,12G SAS,2U MD34xx,8G 缓存 VMware ESXi 6.0.0 build-2494585 - 按照最佳实践配置虚拟机;VMXNET3、PVSCSI 等 RHEL 7 - 大页面 基准 1 倍速率下平均每秒 700,000 gloref(每秒数据库访问次数)。 24 vCPU 在 5 倍速率下平均每秒超过 3,000,000 gloref。 测试以老化方式进行,直到达到稳定的性能,然后进行 15 分钟采样并取平均值。 > 这些示例只是为了说明理论,您必须使用自己的应用程序进行验证!
公告
Claire Zheng · 一月 16, 2023

来看看你的“开发者社区2022战报” 吧!

亲爱的开发者社区成员们, 我们非常高兴地与您分享最新的开发者社区功能! 🔥 您的 2022 年回顾 🔥 现在,您可以生成个性化“2022 年活动概要”——体现了您在2022年在开发者社区的主要活动,包括发帖数量、评论、视图、热门和最喜欢的帖子/标签等等! 如何查看?有两种方式。 方式一:在您主页顶部会发现一个新横幅: 单击“深入了解”按钮查看您当年的个人亮点!比如: 方式二:点击右上角头像,进入个人主页后即可查看 快来回顾你的2022吧!欢迎在评论分享自己的“2022战报”!
问题
jiang yucong · 七月 26, 2023

如何下载cache-2016的安装包?

从哪里可以下载到cache-2016的安装包?包括windows、macOS以及Linux 您好,目前Cache 2016的安装包仅面向我们的现有客户提供。如果您是我们的客户,请联系我们的售后团队support@intersystems.com;如果您还不是,请联系我们的销售团队GCDPSales@intersystems.com; 谢谢!
文章
Qiao Peng · 一月 5, 2021

增强型日志监视器

各位开发者们大家好! 此前,我向各位介绍了一个非常好用的运行分析监控面板,它能使消息处理过程中的关键指标可视化,例如入站/出站消息的数量和平均处理时间等。 现在,我想用一项许多人已熟悉的工作流程,来展示一个增强型日志监视器——将警告信息作为Production中的消息来处理。我们可以通过创建路由规则来实现对告警消息的过滤和路由,并运用预先构建的组件(例如电子邮件适配器等)来发送粒度级别的通知。 如你所知,监视和管理警告信息是确保任何应用程序平稳运行的关键。对诸如HealthShare和IRIS医疗版这样支撑医疗系统运转的一级应用程序和集成引擎来说对告警信息的处理更显得尤为重要。 让我们先来梳理一下InterSystems产品中已经附带的警告信息监视和管理工具: 通过名为Ens.Alert的组件,你可以使用警告处理器(Alert Processor)为Production中的各类接口配置自定义警报。 系统监视器(System Monitor )能显示Production关键性能指标的实时状态。 日志监视管理器(Log Monitor Manager (^MONMGR) utility )程序能根据消息日志(现在InterSystems IRIS上称messages.log,以往称cconsole.log)生成各种严重级别的通知消息,再通过电子邮件将该通知发送给预设好的收件人。 Production监视器(Production Monitor )显示当前正在运行的Production及其接口(输入/输出连接)、队列、活动作业、事件日志、活动图表等的实时状态。 镜像监视器(Mirror Monitor)显示每个镜像及其构件的运行状态、镜像数据库状态以及关键镜像指标(例如日志传入速率)信息。 尽管由我制作的增强型日志监控器与上述这些日志监控器管理器(^MONMGR)非常相似,它的好处在于给用户提供了一个熟悉的界面和对警告信息的精准路由及管理能力——每个写进消息日志(messages.log文件)的告警条目都会被转换成一条Production里的消息,再按路由规则(Ens.Alert)精细过滤出特定的警告。这些警告可以通过Production中的操作(Business Operation)使用邮件和短信等方式发送出去。不仅如此,现在你还可以在Production中的邮件适配器设置来轻松编辑通知的收件人。 例如:日志监控管理器(^MONMGR)已经具备了按照指定的最低严重性级别发出警告的功能,你可以通过设置在发生二级日志事件时自动向系统管理员发出警告。 如果使用我即将介绍的增强型日志监视器,你就能进一步细化过滤,做到不是所有二级事项都发出,而是只在一个实例的发生了镜像故障转移切换(二级事件范畴下的一个具体情况)时才发出警告。在这个例子中,我们假设该系统部署了由镜像实现的高可用/灾备功能,并且包含这个日志监视器Production需要运行在每个镜像成员中的非镜像命名空间中。 使用增强型日志监控器前,请先从OpenExchange下载示例代码。下载的文件为xml格式,可以直接导入。导入时请转到管理门户,并导航至“Interoperability”->“管理”->“部署变化”->“部署”。 现在点选“Open Local Deployment”选项来打开从OpenExchange下载的xml文件,并在加载后单击“部署” 导航到刚刚部署的“Interoperability”(互操作性->列表->Production)。请勿在设定好全局^lasttimestamp(后面再做说明)之前启动Production。你应该能在Production中看到以下三个组件: “测试”服务 这其中包含了我所使用的定制底层代码(JK.MONMGR.CustomService class.)该代码会持续检查message.log文件是否被加入了新的行,再为每个新加的行项目创建Ens.AlertRequest消息并将其发送至Ens.Alert。你可以使用它的适配器设置来设定呼叫间隔——即在消息日志(messages.log文件)中检查新行的频率。为了能让设定值尽量贴近实际频率,你可以选则诸如1或5这样较小的整数。 “Ens.Alert”进程 这是一个名叫“Ens.Alerts”的路由规则,你可以利用它把特定的警告(基于警告文本)从消息日志路由到“EnsLib.EmailAlertOperation”以发送邮件通知。请注意要在条件中包含AlertText的内容(即Document.AlertText [ “”后面双引号内的警告文字)。你还可以创建其他的附加规则,也可以用DTL把警告消息转换为向下游发送的电子邮件模板或其他格式的通知。 “EnsLib.EmailAlertOperation”操作 这是一个预先构建好的出站电子邮件操作(BO),让你能直接发送邮件通知。你可以利用Production配置中的邮件适配器设置来设定要发送电子邮件的地址列表,SMTP服务器/端口以及凭据等。 启动Production前应先设置全局^ lasttimestamp以记录下此工具检查最后一行时的时间戳。你需要按“月/日/年 时:分:秒”的格式进行设置–例如,从终端输入: >> set ^lasttimestamp = “08/28/2020 08:00:00” 现在可以启动这个Production来亲身体会它的功效了!你还可以通过修改示例代码来满足你的特定需求。 如有任何疑问,请在下方留言评论或与我们的销售工程师联系!
文章
姚 鑫 · 七月 2, 2021

第二十五章 添加和使用XSLT扩展函数

# 第二十五章 添加和使用XSLT扩展函数 # 自定义错误处理 当出现错误时,XSLT处理器(`Xalan`或`Saxon`)执行当前错误处理程序的`error()`方法,将消息作为参数发送到该方法。类似地,当发生致命错误或警告时,XSLT处理器会根据需要执行`datalError()`或`Warning()`方法。 对于所有这三种方法,默认行为是将消息写入当前设备。 要自定义错误处理,请执行以下操作: - 对于`Xalan`或`Saxon`处理器,在创建`%XML.XSLT.ErrorHandler`的子类。在这个子类中,根据需要实现`Error()`、`FatealError()`和`Warning()`方法。 这些方法中的每一个都接受单个参数,即包含由XSLT处理器发送的消息的字符串。 这些方法不返回值。 - 要在编译样式表时使用此错误处理程序,请创建子类的实例,并在编译样式表时在参数列表中使用它。 - 若要在执行XSLT转换时使用此错误处理程序,请创建子类的实例,并在使用的`Transform`方法的参数列表中使用它。 # 指定样式表使用的参数 要指定样式表使用的参数,请执行以下操作: 1. 创建`%ArrayOfDataTypes`的实例在。 2. 调用此实例的`SetAt()`方法将参数及其值添加到此实例。对于`SetAt()`,将第一个参数指定为参数值,将第二个参数指定为参数名称。 根据需要添加任意多个参数。 ```java Set tParameters=##class(%ArrayOfDataTypes).%New() Set tSC=tParameters.SetAt(1,"myparameter") Set tSC=tParameters.SetAt(2,"anotherparameter") ``` 3. 将此实例用作`Transform`方法的`pParms`参数。 可以不使用`%ArrayOfDataType`,而是使用 IRIS多维数组,该数组可以具有任意数量的具有以下结构和值的节点: Node| Value ---|--- arrayname("parameter_name") |Value of the parameter named by parameter_name # 添加和使用XSLT扩展函数 可以在InterSystems IRIS中创建`XSLT`扩展函数,然后在样式表中使用它们,如下所示: - 对于`XSLT2.0`(`Saxon`处理器),可以使用名称空间`com.intersystems.xsltgateway.XSLTGateway`中的`evaluate`函数或名称空间`http://extension-functions.intersystems.com`中的`evaluate`函数 - 对于`XSLT1.0`(`Xalan`处理器),只能在名称空间`http://extension-functions.intersystems.com`中使用`evaluate`函数 默认情况下(举个例子),后一个函数反转它接收到的字符。但是,通常不使用默认行为,因为实现了一些其他行为。要模拟多个单独的函数,需要传递一个选择器作为第一个参数,并实现一个开关,该开关使用该值选择要执行的处理。 在内部,`evaluate`函数作为XSLT回调处理程序中的方法(`evaluate()`)实现。 要添加和使用XSLT扩展函数,请执行以下操作: 1. 对于`Xalan`或`Saxon`处理器,在创建`%XML.XSLT.CallbackHandler`的子类。在这个子类中,根据需要实现`evaluate()`方法。请参阅下一小节。 2. 在样式表中,声明`evaluate`函数所属的命名空间,并根据需要使用`evaluate`函数。请参阅下一小节。 3. 执行XSLT转换时,创建子类的实例,并在使用的`Transform`方法的参数列表中使用它。请参阅“执行XSLT转换”。 ## 实现evaluate()方法 在内部,调用`XSLT`处理器的代码可以将任意数量的位置参数传递给当前回调处理程序的`evaluate()`方法,该方法将它们作为具有以下结构的数组接收: Node| Value ---|--- Args| 参数数量 Args(index) |位置索引中参数的值 该方法只有一个返回值。返回值可以是: - 标量变量(如字符串或数字)。 - 流对象。这允许返回超过字符串长度限制的超长字符串。流必须包装在新窗口中的`%XML.XSLT.StreamAdapter`实例中,使XSLT处理器能够读取流。以下是部分示例: ```java Method evaluate(Args...) As %String { //create stream ///... // create instance of %XML.XSLT.StreamAdapter to // contain the stream Set return=##class(%XML.XSLT.StreamAdapter).%New(tStream) Quit return } ``` ## 在样式表中使用计算 要在XSLT中使用XSLT扩展函数,必须在XSLT样式表中声明扩展函数的名称空间。对于InterSystems evaluate函数,此命名空间是`http://extension-functions.intersystems.com`或`com.intersystems.xsltgateway.XSLTGateway`,如前所述。 下面的示例显示使用evaluate的样式表: ```xml ``` ## 使用ISC:计算缓存 XSLT2.0网关将`evaluate`函数调用缓存在`isc:evaluate`缓存中。缓存的默认最大大小为`1000`个项目,但可以将大小设置为不同的值。此外,还可以清除缓存、转储缓存,还可以从`%List`中预先填充缓存。使用以下格式: - 缓存条目总数 - 对于每个条目: 1. 求值参数总数 2. 所有求值参数 3. 计算值 缓存还包括可缓存的函数名称的过滤器列表。请注意以下事项: - 可以在筛选器列表中添加或删除函数名。 - 可以清除过滤器列表。 - 可以通过设置一个布尔值来覆盖筛选器列表,该布尔值将缓存每个`evaluate`调用。 将函数名添加到筛选器列表不会限制求值缓存的大小。可以对同一函数进行任意数量的调用,但具有不同的参数和返回值。函数名和参数的每个组合都是求值缓存中的一个单独条目。 可以使用`%XML.XSLT2.Transformer`中的方法来操作求值缓存。 # 使用XSL转换向导 Studio提供了一个执行XSLT转换的向导,当希望快速测试样式表或自定义XSLT扩展函数时,该向导非常有用。要使用此架构向导,请执行以下操作: 1. Tools > Add-Ins > XSLT Schema Wizard. 2. 指定以下必需的详细信息: - 对于XML文件,选择浏览以选择要转换的XML文件。 - 对于XSL文件,选择浏览以选择要使用的XSL样式表。 - 对于呈现为,选择文本或XML以控制转换的显示方式。 3. 如果已在要在此转换中使用的创建了`%XML.XSLT.CallbackHandler`的子类,请指定以下详细信息: - 对于XSLT Helper Class中的第一个下拉列表,选择一个命名空间。 - 对于XSLT Helper Class中的第二个下拉列表,选择该类。 4. 选择Finish(完成)。 对话框底部显示转换后的文件。可以从该区域复制和粘贴。 5. 要关闭此对话框,请选择取消。
文章
Qiao Peng · 十月 22, 2022

通用SQL业务服务和业务操作

1. 新的系统SQL业务服务/业务操作 接连SQL数据源和操作SQL数据目标是常见的集成业务场景。使用SQL适配器监控SQL数据源和操作SQL目标库时,我们需要开发自定义BS或BO,写不少代码。例如开发自定义SQL服务需要: 1. 开发响应消息类,用于承接SQL快照数据; 2. 开发自定义业务服务BS类,用于将SQL快照按字段赋值给对应的消息,并将消息发送给目标(业务流程或业务操作)。 而要开发自定义SQL操作,更麻烦些: 1. 开发请求和响应消息类,用于向BO传输数据和接收返回数据; 2. 开发自定义业务服务BO类,设置消息响应表,根据不同请求消息类型编写方法; 3. 在方法中根据请求消息数据拼写SQL语句; 4. 在方法中将SQL执行结果存入响应消息。 虽然很简单,但编程过程枯燥乏味。而且当修改SQL语句时,还要修改对应的消息类和BS/BO类。 从2021.2开始,InterSystems IRIS增加了2套系统通用SQL业务服务和SQL业务操作: BS EnsLib.SQL.Service.GenericService 使用SQL语句 BS EnsLib.SQL.Service.ProcService 使用存储过程 BO EnsLib.SQL.Operation.GenericOperation 使用SQL语句 BO EnsLib.SQL.Operation.ProcOperation 使用存储过程 只要直接将它们加入Production,就可以直接使用了,甚至无需再定义消息,真正实现无代码开发,nice! 2. 如何使用系统SQL业务服务/业务操作 下面我们来看看它们是怎么实现的,以及如何使用它们。 2.1 SQL业务服务 直接将EnsLib.SQL.Service.GenericService或EnsLib.SQL.Service.ProcService加入Production的BS中,并设置数据源、SQL语句和发送目标: 一步搞定,且无需使用IDE开发任何一行代码! 那么SQL业务操作发送给目标业务组件的是什么消息呢? 默认是Ens.StreamContainer,而结果集数据是以JSON表达并放在其Stream属性(流类型)下的,正好用到JSON免schema的特性。类似于: 如果你已经有一个消息类,想让SQL业务服务将结果集记录放在这个消息里?只要设置SQL业务服务的“消息类”即可: 当然,你自己定义的消息属性名要和返回的SQL结果集字段名一致,否则会报错。如果消息是以前定义好的,那么通过SQL语句使用AS来修改字段名最简单,例如SELECT NAME AS Display... 对如何操作JSON不熟? 在InterSystems IRIS/Cache'里,系统类%DynamicObject就是JSON对象,它有方法%FromJSON可以直接将JSON字符串/字符流转为JSON对象。所以可以直接以对象的方式操作JSON,就像下面在业务流程里将Ens.StreamContainer的Stream属性转为JSON对象使用: 2.2 SQL 业务操作 同样,SQL业务操作也无需代码开发。直接将EnsLib.SQL.Operation.GenericOperation或EnsLib.SQL.Operation.ProcOperation加入Production,进行配置即可。 配置项除了SQL数据目标的连接信息外,就是要执行的SQL语句了: 在 查询 中添加SQL语句,如果SQL语句中有任何参数需要传入,用"?" 代表。例如 Insert into QP.Patient(Gender,Name) values(?,?) 如果SQL需要输入参数,在 输入参数 中按顺序添加需要的参数,一般情况下是使用请求消息里的属性,在需要用的属性名前面加 *,说明这个参数来自请求消息。例如*Sex,*Name。 好,配置完成,依然没写一行代码! 但慢着,我们没有对这个BO定义请求消息呀? 我们不需要事先声明任何请求消息类,EnsLib.SQL.Operation.GenericOperation和EnsLib.SQL.Operation.ProcOperation可以接受任何类型的请求消息! 那么像上面的例子中提到的传入参数*Sex,*Name,是不是我必须要事先建一个请求消息类,里面有Sex和Name属性? 完全不必要!你可以直接通过系统请求消息Ens.StreamContainer、Ens.StringContainer、Ens.StringRequest传入JSON字符串/字符流即可,SQL业务操作会自动将JSON实例化为JSON对象。因此上面例子,可以传入Ens.StringRequest,只要JSON字符串里有Sex和Name这2个数据即可。 例如你可以直接测试这个BO: 是不是非常方便! * 注意:当使用Ens.StreamContainer、Ens.StringContainer、Ens.StringRequest 这些请求消息时,数据一定要是JSON格式的字符串/字符流,否则会得到报错信息! 那么SQL执行返回的结果在哪里? BO默认的响应消息是StreamContainer,因此执行结果都以JSON格式在它里面。 如果执行的SQL是INSERT/UPDATE/DELETE,那么返回的只有SQL语句影响的行数,放在NumRowsAffected里,如下: 如果执行的SQL是SELECT,则返回的是结果集如下: 如果你已经有定义过的消息类用于接收返回的SQL结果集,可以设置其“响应类”属性: SQL业务操作会将返回结果集的第一行按字段名对应保存到响应消息的属性上。 *注意:当SQL返回的结果集有多行记录时,这种使用用户自定义响应消息的情况下,会在事件日志中记录一条信息,并只将结果集第一行对应到自定义响应消息中! * 信息更新:在IRIS 2023.1及之后的版本中,配置页面与上面的截图有一点变化: 1. 输入参数改为逐一通过“添加”按钮进行添加,从而避免书写错误 2. 请求消息类可以指定了。如果您有自己定义好的请求消息,可以在下图“RequestClass”中选择。如果你继续想使用Ens.StreamContainer、Ens.StringContainer、Ens.StringRequest 这些系统请求消息时,请保留其为空。 今后连接SQL数据源和数据目标时,使用这2套系统SQL业务服务和业务操作,可以快乐地玩耍了!
文章
Michael Lei · 五月 3, 2022

Amazon EKS, IRIS 高可用与备份

所有源代码均在: https://github.com/antonum/ha-iris-k8s 在上一篇文章中,我们讨论了如何在k8s集群上建立具有高可用性的IRIS,基于分布式存储,而不是传统的镜像。作为一个例子,那篇文章使用了Azure AKS集群。在这一篇中,我们将继续探讨k8s上的高可用配置。这一次,基于Amazon EKS(AWS管理的Kubernetes服务),并将包括一个基于Kubernetes 快照进行数据库备份和恢复的选项。 安装 开始干活. 首先需要一个AWS账户,安装 AWS CLI, kubectl 和 eksctl 工具. 要创建新的集群,请运行以下命令: eksctl create cluster \ --name my-cluster \ --node-type m5.2xlarge \ --nodes 3 \ --node-volume-size 500 \ --region us-east-1 这个命令需要大约15分钟,部署EKS集群并使其成为你的kubectl工具的默认集群。你可以通过运行以下代码来验证你的部署: kubectl get nodes NAME STATUS ROLES AGE VERSION ip-192-168-19-7.ca-central-1.compute.internal Ready <none> 18d v1.18.9-eks-d1db3c ip-192-168-37-96.ca-central-1.compute.internal Ready <none> 18d v1.18.9-eks-d1db3c ip-192-168-76-18.ca-central-1.compute.internal Ready <none> 18d v1.18.9-eks-d1db3c 下一步是安装Longhorn分布式存储引擎. kubectl create namespace longhorn-system kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.1.0/deploy/iscsi/longhorn-iscsi-installation.yaml --namespace longhorn-system kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml --namespace longhorn-system 最后安装 IRIS : kubectl apply -f https://github.com/antonum/ha-iris-k8s/raw/main/tldr.yaml 现在,你就有了一个功能齐全的EKS集群,并安装了Longhorn分布式存储和IRIS。 你可以回到上一篇文章,尝试对集群和IRIS部署进行各种破坏,看看系统如何自我修复。请看模拟故障Simulate the Failure部分。 Bonus #1 IRIS on ARM IRIS EKS和Longhorn都支持ARM架构,所以我们可以使用AWS Graviton 2实例部署相同的配置,基于ARM架构。 你只需要将EKS节点的实例类型改为 "m6g "系列,将IRIS镜像改为基于ARM的。 eksctl create cluster \--name my-cluster-arm \--node-type m6g.2xlarge \--nodes 3 \--node-volume-size 500 \--region us-east-1 tldr.yaml containers: #- image: store/intersystems/iris-community:2020.4.0.524.0 - image: store/intersystems/irishealth-community-arm64:2020.4.0.524.0 name: iris 或只是用: kubectl apply -f https://github.com/antonum/ha-iris-k8s/raw/main/tldr-iris4h-arm.yaml 这就行了! 你就有了一个在在ARM平台上运行的IRIS Kubernetes集群. Bonus #2 - 备份与恢复 生产级架构的一个经常被忽视的部分是创建数据库的备份,并在需要时快速恢复和克隆这些备份的能力。 在Kubernetes中,常见的方式是使用持久化卷快照。 首先--你需要安装所有需要的k8s组件: #Install CSI Snapshotter and CRDs kubectl apply -n kube-system -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml kubectl apply -n kube-system -f https://github.com/kubernetes-csi/external-snapshotter/raw/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml kubectl apply -n kube-system -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml kubectl apply -n kube-system -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml kubectl apply -n kube-system -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml 下一步 - 为Longhorn配置S3 bucket凭证 (请查看 详细指导): #Longhorn backup target s3 bucket and credentials longhorn would use to access that bucket #See https://longhorn.io/docs/1.1.0/snapshots-and-backups/backup-and-restore/set-backup-target/ for manual setup instructions longhorn_s3_bucket=longhorn-backup-123xx #bucket name should be globally unique, unless you want to reuse existing backups and credentials longhorn_s3_region=us-east-1 longhorn_aws_key=AKIAVHCUNTEXAMPLE longhorn_aws_secret=g2q2+5DVXk5p3AHIB5m/Tk6U6dXrEXAMPLE 下面的命令将从上一步骤中获取环境变量,并使用它们来配置Longhorn备份。 #configure Longhorn backup target and credentials cat <<EOF | kubectl apply -f - apiVersion: longhorn.io/v1beta1 kind: Setting metadata: name: backup-target namespace: longhorn-system value: "s3://$longhorn_s3_bucket@$longhorn_s3_region/" # backup target here --- apiVersion: v1 kind: Secret metadata: name: "aws-secret" namespace: "longhorn-system" labels: data: # echo -n '<secret>' | base64 AWS_ACCESS_KEY_ID: $(echo -n $longhorn_aws_key | base64) AWS_SECRET_ACCESS_KEY: $(echo -n $longhorn_aws_secret | base64) --- apiVersion: longhorn.io/v1beta1 kind: Setting metadata: name: backup-target-credential-secret namespace: longhorn-system value: "aws-secret" # backup secret name here EOF 它可能看起来很多,但它基本上是告诉Longhorn使用一个特定的S3 bucket,用指定的凭证来存储备份的内容。 搞定! 如果你现在进入Longhorn的用户界面,你就可以创建备份,恢复等等。 关于如何连接到Longhorn用户界面的快速练习: kubectl get pods -n longhorn-system # note the full pod name for 'longhorn-ui-...' pod kubectl port-forward longhorn-ui-df95bdf85-469sz 9000:8000 -n longhorn-system 这将把到Longhorn UI的流量转发到你的本地 http://localhost:9000 程序化的 备份/恢复 通过Longhorn用户界面进行备份和恢复可能是足够好的第一步--但我们将更进一步,使用k8s Snapshot APIs,以编程方式进行备份和恢复. 首先 - 快照本身. iris-volume-snapshot.yaml apiVersion: snapshot.storage.k8s.io/v1beta1 kind: VolumeSnapshot metadata: name: iris-longhorn-snapshot spec: volumeSnapshotClassName: longhorn source: persistentVolumeClaimName: iris-pvc 这个卷快照指的是源卷 "iris-pvc",我们确实在IRIS部署中使用。因此,只要应用这个就可以立即开始备份过程. 在快照之前/之后执行IRIS Write Daemon Freeze/Thaw是一个好主意. #Freeze Write Daemon echo "Freezing IRIS Write Daemon" kubectl exec -it -n $namespace $pod_name -- iris session iris -U%SYS "##Class(Backup.General).ExternalFreeze()" status=$? if [[ $status -eq 5 ]]; then echo "IRIS WD IS FROZEN, Performing backup" kubectl apply -f backup/iris-volume-snapshot.yaml -n $namespace elif [[ $status -eq 3 ]]; then echo "IRIS WD FREEZE FAILED" fi #Thaw Write Daemon kubectl exec -it -n $namespace $pod_name -- iris session iris -U%SYS "##Class(Backup.General).ExternalThaw()" 恢复过程是非常直接的。它基本上是创建一个新的PVC,并指定快照为源。 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: iris-pvc-restored spec: storageClassName: longhorn dataSource: name: iris-longhorn-snapshot kind: VolumeSnapshot apiGroup: snapshot.storage.k8s.io accessModes: - ReadWriteOnce resources: requests: storage: 10Gi 然后你就根据这个PVC创建一个新的部署。检查github repo中的这个测试脚本,它将依次进行。 创建全新的IRIS部署 向IRIS添加一些数据 冻结写Daemon,拍摄快照,解冻写Daemon 在快照的基础上,创建一个IRIS部署的克隆 验证所有的数据是否还在那里 验证所有的数据是否还在那里,现在你将有两个相同的IRIS部署,一个是另一个的克隆备份。 Enjoy!
文章
Qiao Peng · 十二月 7, 2023

通用TCP业务服务和业务操作

TCP作为OSI 7层的传输层的通信协议,其使用上不像更上层的通信协议那么方便,因为TCP操作的不是数据包,它操作的是数据流。因此有多种将TCP数据流“解释”为数据包(消息)的方法。 InterSystems IRIS提供了多种TCP适配器,用于不同的“解释”,例如EnsLib.TCP.FramedInboundAdapter使用特定的首尾字符做为分隔、EnsLib.TCP.CountedInboundAdapter使用固定的长度进行分隔... 同时,InterSystems IRIS提供了多种开箱即用的TCP业务服务和业务操作,方便接入和发送TCP数据。这里我们介绍常见的使用特定的首尾字符做为分隔的TCP业务服务和业务操作。 1. 通用TCP业务服务和业务操作 EnsLib.TCP.Framed.PassthroughService和EnsLib.TCP.Framed.PassthroughOperation是一组使用特定的首尾字符做为分隔TCP数据流的通用业务服务和业务操作。EnsLib.TCP.Framed.PassthroughService业务服务会将TCP数据封装在Ens.StreamContainer发送给业务流程或业务操作;而EnsLib.TCP.Framed.PassthroughOperation业务操作发送并接收Ens.StreamContainer类型的数据。 2. 使用EnsLib.TCP.Framed.PassthroughService业务服务 2.1 向production中加入通用TCP业务服务 增加通用TCP业务服务,只需要在Production配置页面的服务中添加EnsLib.TCP.Framed.PassthroughService。 建议加入Production时,给业务服务起一个名字,用于代表具体的业务,例如是连接到设备的TCP服务,可以命名为TCPforDevice(可以考虑的命名规则 - 接口方式+业务系统)。如果未命名,默认会使用类名作为业务服务名。 2.2 配置通用TCP业务服务 主要的设置项是以下几个: 1. Port:接收TCP数据的端口,例如图中的65530端口 2. Target Config Names:TCP服务发送消息的目标,可以是业务流程或业务操作 3. Message Frame Start:标记TCP开始的字符或字符串,用10进制ASCII码表示。如果有多个字符,字符ASCII间用逗号分隔。图例中为ASCII 10,也就是“退格符”。 4. Message Frame End:标记TCP结束的字符或字符串,用10进制ASCII码表示。如果有多个字符,字符ASCII间用逗号分隔。图例中为ASCII 28和13,也就是“文件分隔符”和“回车符”。 5. Remove Framing: 是否把标记TCP起止的字符删除后再发送到后续业务组件。建议选中 6. Discard Incorrect Framing:丢弃使用不正确起始字符的TCP数据。建议在调试阶段取消选中,测试完成后再选中 7. Frame Acknowledgement:是否要发送Frame 通知,可以取消选中 启用该业务服务后,既可以接收了TCP请求了。 2.3 测试通用TCP业务服务 可以使用TCP客户端进行发送数据的测试。这里使用Packet Sender向IRIS发布的65530端口,发送以下的数据,注意前后的TCP分段起止字符: 然后就可以到消息可视化追踪的页面查看接收的消息。可以看到它是Ens.StreamContainer类型的消息,而且已经把起止字符去掉了: 3. 使用EnsLib.TCP.Framed.PassthroughOperation业务操作 3.1 向production中加入通用TCP业务操作 同样,可以直接将EnsLib.TCP.Framed.PassthroughOperation加入production。 3.2 配置通用TCP业务操作 主要的设置项是以下几个: 1. IP Address:TCP服务器的IP地址 2. Port:TCP服务器的TCP端口,例如图中的65530端口。这里用65530端口,就是发送到上面我们建立的通用TCP业务服务。 3. Message Frame Start:标记TCP开始的字符或字符串,用10进制ASCII码表示。如果有多个字符,字符ASCII间用逗号分隔。图例中为ASCII 10,也就是“退格符”。业务操作会自动在发送的TCP数据头部加入这些开始字符。 4. Message Frame End:标记TCP结束的字符或字符串,用10进制ASCII码表示。如果有多个字符,字符ASCII间用逗号分隔。图例中为ASCII 28和13,也就是“文件分隔符”和“回车符”。业务操作会自动在发送的TCP数据尾部加入这些开始字符。 5. Remove Framing: 是否把收到的TCP响应数据中标记TCP起止的字符删除。建议选中 6. Discard Incorrect Framing:丢弃使用不正确起始字符的TCP响应数据。建议在调试阶段取消选中,测试完成后再选中 启用该业务操作后,既可以发送TCP请求了。 3.3 测试通用TCP业务操作 因为TCP业务操作的请求消息是Ens.StreamContainer,里面是流数据,为了方便测试,我们建立一个测试用的业务流程,里面组织好数据并调用通用TCP业务操作。 3.3.1 创建一个新的业务流程,设置其请求消息为Ens.StringRequest,用于测试时传入TCP数据。并为其上下文增加一个名为DataBody、类型为%Stream.GlobalCharacter(可持久化的字符流类型)的属性: 3.3.2 在业务流程中增加一个代码流程(<code>),将请求消息的字符串数据写入上下文的DataBody字符流: Do context.DataBody.Write(request.StringValue) 注意行首加空格。 3.3.3 然后在业务流程中再加入一个调用流程(<call>),调用上面已经加入production的业务操作,例如TCPtoWMS,并设置请求和响应消息为Ens.StreamContainer或Ens.StreamContainer。 3.3.4 配置RESTtoLIS业务操作的请求消息(Request) 可以直接点击构建请求消息(Request Builder)按钮,使用图形化拖拽建立请求消息: 将左边上下文context里的DataBody拖拽到callrequest的Stream属性上。 3.3.5 将业务流程编译后加入到Production。 3.3.6 测试这个业务流程,输入测试字符串。然后查看消息追踪,可以看到类似这样的: 这是测试业务流程/业务操作的消息追踪: 因为TCP数据发给了同一个production下的通用业务服务,因此还会看到这样的一笔业务服务的消息追踪: