搜索​​​​

清除过滤器
问题
water huang · 四月 17, 2021

ensemble 2016 如何快速的调用dll文件。

如图 dll放在 我调用的方式如下 期待能够在ensemble里面能便捷的调用dll,各种语言开发的dll,至少能支持c#生成的dll, 在Ensemble 2016中,使用$ZF(-4)来操作DLL是Caché的Callout网关的底层实现之一。这种方式比较通用。当然,如果这些DLL(ActiveX/COM)注册在Ensemble服务器上(Windows服务器),还有别的调用方式:使用Caché Activate 网关,用Studio来产生这些DLL的代理类,然后您就可以像使用Ensemble/Caché类一样使用这些DLL里的方法了。 使用Caché Activate 网关 这个并不好用。服务器重启后 需要重启网关,还很可能需要重新导入dll来生成代理类。因此这样的方式 我已经弃用,或者说 “可能需要重新导入dll来生成代理类” 这个是我操作不对才导致这样的结果?使用$ZF(-4)来操作DLL,这个dll具有一些特殊要求?比如? 如果dll重新编译过,那么是需要重新生成代理类的;其它情况下并不需要。另外,Ensemble 2016里有.net 网关,通过Studio来建立dll代理类。不过它依然需要在dll代码发生变化时重新生成代理类。 这也是后续版本(尤其是InterSystems IRIS)推出动态对象网关的原因:通过动态网关就不用生成代理类了,从而避免因为.net/java端发生代码变更而需要重新生成代理类。$ZF()对DLL没有特殊要求。
文章
Louis Lu · 一月 7, 2021

FAQ 常见问题系列--RHEL V7.2 上的 Caché 进程故障

**RHEL V7.2 上的 Caché 进程故障** InterSystems WRC 处理了几个有关进程错误引发的问题,这些问题可以归因于 Red Hat Linux 最近的一次更新。 RHEL V7.2 (systemd-219-19.el7.x86_64) 中实现的一个新功能可能导致操作系统 IPC(进程间通信)信号量在 非系统用户注销时被解除分配(系统用户,即 UID 编号小于 1000 的用户除外)。 Caché 在内部利用 IPC 信号量来控制 Caché 进程的运行(例如,当尝试唤醒 Caché 进程时)。 这通过“semop”系统服务来实现,如果操作系统意外删除了 Caché 用于进行 IPC 通讯的信号量,则进程可能会出现错误。 如果发生这种情况,在 cconsole.log 中会找到以下证据: “System error while trying to wake-up a process, code = 22”(尝试唤醒进程时系统出错,代码 = 22) 以及在 Caché SYSLOG 中也会记录相应的错误,例如以下典型示例: Err   Process    Date/Time           Mod Line  Routine            Namespace 22    39761      09/29/2016 04:41:27PM 61  359   BF0+1359^Ens.Queue.1 HSBUS 这最终可能导致 Caché 的运行实例处于挂起状态。 以下是 Redhat 提供的一篇文章的链接,文中给出了有关此功能的详细信息以及禁用该功能的方法: [https://access.redhat.com/solutions/2062273](https://access.redhat.com/solutions/2062273 "Follow link")   此问题已在 systemd-219-19.el7_2.4(通过 RHBA-2016-0199 发布 ()中修复。
公告
Claire Zheng · 一月 25, 2022

恭喜Louis喜获重磅证书——HL7 FHIR R4 Proficiency Exam

亲爱的开发者们, 很高兴同大家分享一个好消息!我们中文社区版主、InterSystems高级销售工程师Louis(@Louis Lu)于近日顺利通过“HL7 FHIR R4 Proficiency Exam”并取得资格认证证书! HL7 FHIR(R4)能力证书可以证明在最新和最热门的HL7标准方面达到行业公认的专业水平。考试涵盖了以下内容:FHIR原则;FHIR资源的基本概念;交换机制;一致性和实施指导;如何使用术语;如何建立安全和可靠的FHIR解决方案;FHIR维护过程;以及如何使用和处理FHIR许可和知识产权(IP)。 FHIR®(快速医疗互操作性资源)是HL7的下一代标准框架,它建立并结合了HL7第二版(V2)、第三版(V3)和临床文档架构(CDA®)产品线的最佳功能,同时利用了最新的网络标准并注重于实际实施的能力。FHIR适合在各种情况下使用,如手机应用程序、云通信、基于电子健康记录(EHR)的数据共享、大型医疗机构间服务器通信等等。FHIR还可以支持使用一个标准涵盖尽可能多的应用场景,实现医疗信息化真正意义的互操作性,是智慧医院数字化转型,实现组装式应用创新的重要基础。 一起来恭喜Louis(@Louis Lu)吧✿✿ヽ(°▽°)ノ✿✿✿ヽ(°▽°)ノ✿(*^▽^*) 恭喜🎉🎉
文章
Lele Yang · 一月 30, 2022

FAQ 常见问题系列 -- 系统管理篇 Linux OOM Killer问题

Linux内核机制OOM Killer,也即Out of Memory Killer, 顾名思义,该机制的主要职能就是当内存不足时,选择并杀掉一些进程,以使系统继续运行。 Caché/Ensemble/IRIS的多个客户曾经遇到过与此相关的系统宕机,宕机的直接原因是数据库核心写进程Write Daemon被OOM Killer选中并杀掉了,在我们的日志文件中可以看到如下信息, 06/15/21-10:50:31:035 (13579) 3 Daemon WRTDMN (pid 13588) died. Freezing system 06/15/21-10:52:25:940 (13601) 2 System Process 'WRTDMN' terminated abnormally (pid 13588) 与之对应,在操作系统的日志文件中可以看到如下记录, Jun 15 10:50:34 localhost kernel: Free swap = 0kB Jun 15 10:50:34 localhost kernel: Total swap = 20479996kB Jun 15 10:50:34 localhost kernel: 16777102 pages RAM Jun 15 10:50:34 localhost kernel: 0 pages HighMem/MovableOnly Jun 15 10:50:34 localhost kernel: 324506 pages reserved Jun 15 10:50:30 localhost kernel: cache invoked oom-killer: gfp_mask=0x42d0, order=3, oom_score_adj=0 Jun 15 10:50:35 localhost kernel: Out of memory: Kill process 13588 (cache) score 127 or sacrifice child InterSystems在后续的IRIS版本中(从IRIS2021.1.0开始)已经对此做了优化,以使Write Daemon不那么容易被OOM Killer选中。但是要从根本上解决该问题,还是应当重新审视系统的内存分配,如Huge Page,共享内存等,检查Linux内存相关参数,如vm.swappiness,vm.dirty_background_ratio,vm.dirty_ratio等,以使系统可以在内存使用方面达到最大的效用。
文章
Michael Lei · 三月 10, 2023

如何使用最新的 IRIS Native SDK for Python 用 Python 遍历 IRIS Global数据结构

InterSystems IRIS 2022.2 具有适用于 Python 的原生 SDK (https://docs.intersystems.com/iris20222/csp/docbook/Doc.View.cls?KEY=PAGE_python_native)。 我们知道如何使用 IRIS Object Script $Order 函数遍历Global数据结构。 SET key= "" FOR { SET key= $ORDER ( ^myglobal (key)) QUIT :key= "" WRITE !, ^myglobal (key) } 如何使用 IRIS Native SDK 从 Python 执行相同的操作?这里有一个代码示例: import iris args = { 'hostname' : '127.0.0.1' , 'port' : 51772 , 'namespace' : 'USER' , 'username' : '_SYSTEM' , 'password' : 'SYS' } conn = iris.connect(**args) # Create an iris object irispy = iris.createIRIS(conn) # Create a global array in the USER namespace on the server irispy.set( 'A' , 'root' , 'foo' , 'SubFoo' ) irispy.set( 123 , 'root' , 'bar' , 'lowbar' , 'UnderBar' ) irispy.set( 124 , 'root' , 'bar' , 'lowbar' , 'UnderBar2' ) irispy.set( "hi" , 'root' , 'bar' , 'lowbar' ) irispy.set( "hi again" , 'root' , 'bar3' ) # Read the values from the database and print them subfoo_value = irispy.get( 'root' , 'foo' , 'SubFoo' ) underbar_value = irispy.get( 'root' , 'bar' , 'lowbar' , 'UnderBar' ) underbar2_value = irispy.get( 'root' , 'bar' , 'lowbar' , 'UnderBar2' ) lowbar_value = irispy.get( 'root' , 'bar' , 'lowbar' ) bar3_value = irispy.get( 'root' , 'bar3' ) print( 'Created two values: ' ) print( ' root("foo","SubFoo")=' , subfoo_value) print( ' root("bar","lowbar","UnderBar")=' , underbar_value) print( ' root("bar","lowbar","UnderBar2")=' , underbar2_value) print( ' root("bar","lowbar")=' , lowbar_value) print( ' root("bar3")=' , bar3_value) direction = 0 # direction of iteration (boolean forward/reverse) next_sub = chr( 0 ) # start at first possible subscript subs = [] print( "\n Iterating root \n" ) isDef = irispy.isDefined( 'root' , *subs) while isDef: next_sub = irispy.nextSubscript( False , 'root' , *subs, next_sub) # get first subscript if next_sub == None : # we finished iterating nodes on this tree branch, move a level up if len(subs) == 0 : # no more things to iterate break next_sub = subs.pop( -1 ) # pop last subscript in order to continue iterating this level if irispy.isDefined( 'root' , *subs, next_sub) == 11 : print( 'root(' ,*subs, next_sub, ')=' ,irispy.get( 'root' , *subs, next_sub)) continue continue isDef = irispy.isDefined( 'root' , *subs, next_sub) if isDef in [ 10 , 11 ]: # keep building subscripts for depth first search subs.append(next_sub) next_sub = chr( 0 ) continue elif isDef == 1 : # reached a leaf node, print it print( 'root(' ,*subs, next_sub, ')=' ,irispy.get( 'root' , *subs, next_sub)) else : # def 0 is not really expected print( "error" ) irispy.kill( 'root' ) conn.close() exit( -1 ) # Delete the global array and terminate irispy.kill( 'root' ) # delete global array root conn.close()
文章
姚 鑫 · 五月 10, 2021

第四章 多维存储的SQL和对象使用(二)

# 第四章 多维存储的SQL和对象使用(二) # 索引 持久化类可以定义一个或多个索引;其他数据结构用于提高操作(如排序或条件搜索)的效率。InterSystems SQL在执行查询时使用这些索引。InterSystems IRIS对象和SQL在执行`INSERT`、`UPDATE`和`DELETE`操作时自动维护索引内的正确值。 ## 标准索引的存储结构 标准索引将一个或多个属性值的有序集与包含属性的对象的对象`ID`值相关联。 例如,假设我们定义了一个简单的持久化`MyApp.Person`类,该类具有两个文本属性和一个关于其`Name`属性的索引: ```java Class MyApp.Person Extends %Persistent { Index NameIdx On Name; Property Name As %String; Property Age As %Integer; } ``` 如果我们创建并保存此`Person`类的多个实例,则生成的数据和索引全局变量类似于: ```java // data global ^MyApp.PersonD = 3 // counter node ^MyApp.PersonD(1) = $LB("",34,"Jones") ^MyApp.PersonD(2) = $LB("",22,"Smith") ^MyApp.PersonD(3) = $LB("",45,"Jones") // index global ^MyApp.PersonI("NameIdx"," JONES",1) = "" ^MyApp.PersonI("NameIdx"," JONES",3) = "" ^MyApp.PersonI("NameIdx"," SMITH",2) = "" ``` 请注意有关全局索引的以下事项: 1. 默认情况下,它被放在一个全局变量中,全局变量的名称是后面附加`“i”`(表示索引)的类名。 2. 默认情况下,第一个下标是索引名;这允许将多个索引存储在同一全局中,而不会发生冲突。 3. 第二个下标包含整理后的数据值。在这种情况下,使用默认的`SQLUPPER`排序函数对数据进行排序。这会将所有字符转换为大写(不考虑大小写进行排序),并在前面加上一个空格字符(强制所有数据作为字符串进行排序)。 4. 第三个下标包含包含索引数据值的对象的对象ID值。 5. 节点本身是空的;所有需要的数据都保存在下标中。请注意,如果索引定义指定数据应与索引一起存储,则将其放置在全局索引的节点中。 该索引包含足够的信息来满足许多查询,比如按姓名列出所有`Person`类。 # 位图索引 位图索引类似于标准索引,不同之处在于它使用一系列位字符串来存储与索引值对应的一组对象`ID`值。 ## 位图索引的逻辑运算 位字符串是一个包含一组特殊压缩格式的位(`0`和`1`值)的字符串。 InterSystems IRIS包含一组有效创建和使用位字符串的函数。 这些都列在下表中: 位操作 函数 | 描述 ---|--- `$Bit`| 在位串中设置或获取位。 `$BitCount` | 计算位串中的位数。 `$BitFind` | 查找位串中下一个出现的位。 `$BitLogic` | 对两个或多个位串执行逻辑(`AND`, `OR`)操作。 在位图索引中,位字符串中的顺序位置对应于索引表中的行(对象`ID`号)。 对于给定值,位图索引维护一个位字符串,在给定值存在的每一行中包含`1`,在没有给定值的每一行中包含`0`。 请注意,位图索引只适用于使用系统分配的默认存储结构的对象,数值型对象`ID`值。 例如,假设我们有一个类似如下的表: ID| State| Product ---|---|--- 1| MA| Hat 2| NY| Hat 3| NY| Chair 4| MA| Chair 5| MA| Hat 如果`State`和`Product`列有位图索引,则它们包含以下值: `State`列上的位图索引包含以下位字符串值: ``` MA 1 0 0 1 1 NY 0 1 1 0 0 ``` 注意,对于值`“MA”`,在与`State`等于`“MA”`的表行对应的位置(1、4和5)中有一个1。 类似地,`Product`列上的位图索引包含以下位字符串值(注意,这些值在索引中被排序为大写): ``` CHAIR 0 0 1 1 0 HAT 1 1 0 0 1 ``` InterSystems SQL Engine可以通过对这些索引维护的位串进行迭代、计算位内位数或执行逻辑组合(`AND, or`)来执行许多操作。 例如,要找到`State`等于`“MA”`、`Product`等于`“HAT”`的所有行,SQL引擎可以简单地将适当的位串与逻辑`and`组合在一起。 除了这些索引之外,系统还维护一个额外的索引,称为“区段索引”,对于存在的每一行包含1,对于不存在的行(如已删除的行)包含0。 这用于某些操作,如否定。 ## 位图索引的存储结构 位图索引将一个或多个属性值的有序集合与一个或多个包含与属性值对应的对象`ID`值的位字符串相关联。 例如,假设我们定义了一个简单的持久`MyApp`。 `Person`类具有两个文字属性和`Age`属性上的位图索引: ```java Class MyApp.Person Extends %Persistent { Index AgeIdx On Age [Type = bitmap]; Property Name As %String; Property Age As %Integer; } ``` 如果我们创建并保存这个`Person`类的几个实例,得到的数据和索引全局变量类似于: ```java // data global ^MyApp.PersonD = 3 // counter node ^MyApp.PersonD(1) = $LB("",34,"Jones") ^MyApp.PersonD(2) = $LB("",34,"Smith") ^MyApp.PersonD(3) = $LB("",45,"Jones") // index global ^MyApp.PersonI("AgeIdx",34,1) = 110... ^MyApp.PersonI("AgeIdx",45,1) = 001... // extent index global ^MyApp.PersonI("$Person",1) = 111... ^MyApp.PersonI("$Person",2) = 111... ``` 关于全局索引,请注意以下几点: 1. 默认情况下,它被放置在一个全局变量中,全局变量的名称是类名,后面附加一个`“I”`(表示`Index`)。 2. 默认情况下,第一个下标是索引名;这允许多个索引存储在同一个全局中,而不会发生冲突。 3. 第二个下标包含经过整理的数据值。在这种情况下,不应用排序函数,因为这是数字数据的索引。 4. **第三个下标包含块编号;为了提高效率,位图索引被分成一系列位串,每个位串包含表中大约`64000`行的信息。这些位串中的每一个都被称为块。** 5. 节点包含位串。 另请注意:因为该表有一个位图索引,所以会自动维护一个区索引。该盘区索引存储在索引`GLOBAL`中,并使用前缀有`“$”`字符的类名作为其第一个下标。 ## 位图索引的直接访问 下面的示例使用类区索引来计算存储的对象实例(行)的总数。注意,它使用`$ORDER`来迭代区索引的块(每个块包含大约64000行的信息): ```java ClassMethod Count1() As %Integer { New total,chunk,data Set total = 0 Set chunk = $Order(^Sample.PersonI("$Person",""),1,data) While (chunk '= "") { Set total = total + $bitcount(data,1) Set chunk = $Order(^Sample.PersonI("$Person",chunk),1,data) } Quit total } ``` ```java DHC-APP>w ##class(PHA.TEST.SQL).Count1() 208 ```
文章
姚 鑫 · 四月 1, 2021

第十四章 使用SQL Shell界面(三)

# 第十四章 使用SQL Shell界面(三) # SQL元数据、查询计划和性能指标 ## 显示元数据 SQL Shell支持`M`或`Metadata`命令以显示有关当前查询的元数据信息。 对于每个结果集项目,此命令列出以下元数据:列名称(SQL字段名称),键入(ODBC数据类型整数代码),PRE(精度或最大长度),比例(最大分数数字),`NULL(BOOLEAN:1 = NULL允许,0 =不允许空值)`,标签(标题标签,请参阅列别名),表(SQL表名称),架构(架构名称),`CTYPE`(客户端数据类型,请参阅`%SQL.statementColumn ClientType`属性)。 ## SHOW STATEMENT 可以执行查询,然后发出show语句或显示`st`以显示准备好的SQL语句。默认情况下,必须执行查询。可以避免通过设置`executemode =延迟`执行查询,从而发出查询,然后发出`show`语句sql shell命令。 显示声明信息包含实现类(缓存查询名称),参数(一个以逗号分隔的实际参数值,如上面条款和`WHERE`子句文字值),和语句文本(文字文本的SQL命令,包括字母大小写和参数值)。 ## EXPLAIN and Show Plan 有两种方式显示SQL查询的查询计划; 如果需要,两者都可以显示备用的查询计划。 - EXPLAIN:前言用解释命令选择SELECT查询。例如: ```sql SQL]USER>>EXPLAIN SELECT Name FROM Sample.MyTable WHERE Name='Fred Rogers' ``` - SHOW PLAN:发出查询,然后发出show plan shell命令。例如: ```sql SQL]USER>>SELECT Name FROM Sample.MyTable WHERE Name='Fred Rogers' SQL]USER>>SHOW PLAN ``` EXPLAIN SQL命令显示有关指定选择查询的查询计划信息而不执行查询。EXPLAIN Alt允许显示备用查询计划。EXPLAIN Stat返回性能统计信息以及查询计划。EXPLAIN只能用于返回选择查询的查询计划;它不会返回用于执行查询操作的`Insert`,`Update`或`DELETE`语句等其他命令的查询计划。 Show Plan SQL shell命令允许显示SQL Shell成功发布的上次查询的查询计划信息。显示计划可用于执行查询操作的任何SQL命令,包括选择,插入,更新和删除。默认情况下,必须执行查询。可以避免通过设置`executemode=deferred`,执行查询,发出查询,然后发出以下SQL shell命令之一: - SHOW PLAN、SHOW PL(或简单的SHOW)显示关于当前查询的查询计划信息。 查询计划可用于调试和优化查询的性能。 它指定查询的执行方式,包括索引的使用和查询的成本值。 可以返回查询计划的语句有:`SELECT`、`DECLARE`、`non-cursor UPDATE or DELETE`、`INSERT…SELECT`。 该命令有一个`V` (VERBOSE)选项。 - 显示PLANALT显示当前查询的备用显示计划。 该命令有一个`V` (VERBOSE)选项。 可以使用`$SYSTEM.SQL.Explain()`方法从ObjectScript生成查询计划。 ## SQL Shell Performance 成功执行一个SQL语句后,SQL Shell会显示四个语句准备值(`times(s)/globals/cmds/disk`)和四个语句执行值(`times(s)/globals/cmds/disk`): - 语句准备时间是指准备动态语句所花费的时间。 这包括生成和编译语句所花费的时间。 它包括在语句缓存中查找语句所花费的时间。 因此,如果执行了一条语句,然后按编号或名称回收,回收语句的准备时间接近于零。 如果一条语句已经准备好并执行,然后通过发出GO命令重新执行,那么重新执行时的准备时间为零。 - 经过的执行时间是从调用`%execute()`到`%Display()`返回所经过的时间。 它不包括输入参数值的等待时间。 语句`globals`是全局引用的计数,`cmds`是执行的SQL命令的计数,`disk`是磁盘延迟时间,单位是毫秒。 SQL Shell为准备操作和执行操作保留单独的计数。 这些性能值只在`“DISPLAYMODE”`设置为`“currentdevice”`,`“MESSAGES”`设置为`“ON”`时显示。 这些是SQL Shell的默认设置。 # Transact-SQL支持 默认情况下,SQL Shell执行InterSystems SQL代码。 但是,SQL Shell可以用来执行`Sybase`或MSSQL代码。 ## Setting DIALECT 默认情况下,SQL Shell将代码解析为InterSystems SQL。 可以使用`SET DIALECT`来配置SQL Shell以执行`Sybase`或`MSSQL`代码。 若要更改当前方言,请将`“方言”`设置为`Sybase`、`MSSQL`或IRIS。 默认值是`Dialect=IRIS`。 这些设置的方言选项不区分大小写。 下面是一个从SQL Shell中执行MSSQL程序的例子: ```java DHC-APP>DO $SYSTEM.SQL.Shell() SQL Command Line Shell ---------------------------------------------------- The command prefix is currently set to: . Enter q to quit, ? for help. DHC-APP>>SET DIALECT MSSQL dialect = MSSQL DHC-APP>>SELECT TOP 5 name + '-' + ssn FROM Sample.Person 1. SELECT TOP 5 name + '-' + ssn FROM Sample.Person Expression_1 yaoxin-111-11-1117 xiaoli-111-11-1111 姚鑫-111-11-1112 姚鑫-111-11-1113 姚鑫-111-11-1114 5 Rows(s) Affected statement prepare time(s)/globals/lines/disk: 0.1989s/46546/257369/114ms execute time(s)/globals/lines/disk: 0.0032s/24/676/3ms --------------------------------------------------------------------------- ``` `Sybase`和`MSSQL`方言支持这些方言中有限的SQL语句子集。 它们支持`SELECT`、`INSERT`、`UPDATE`和`DELETE`语句。 它们对永久表支持`CREATE TABLE`语句,但对临时表不支持。 支持创建视图。 支持创建触发器和删除触发器。 但是,如果`CREATE TRIGGER`语句部分成功,但在类编译时失败,则此实现不支持事务回滚。 支持`CREATE PROCEDURE`和`CREATE FUNCTION`。 ## Setting COMMANDPREFIX 可以使用`SET COMMANDPREFIX`指定必须追加到后续SQL Shell命令的前缀(通常是单个字符)。 在SQL Shell提示符发出的SQL语句中不使用此前缀。 这个前缀的目的是防止SQL Shell命令和SQL代码语句之间的歧义。 例如,`SET`是一个SQL Shell命令; `SET`也是`Sybase`和`MSSQL`中的SQL代码语句。 默认情况下,没有命令前缀。 要建立命令前缀,设置`COMMANDPREFIX=prefix`,指定的前缀不带引号。 要恢复为没有命令前缀,设置`COMMANDPREFIX=""`。 以下示例显示了命令前缀/(斜杠字符)被设置、使用和恢复的示例: ```java DHC-APP>>SET COMMANDPREFIX=/ commandprefix = / DHC-APP>>/SET LOG=ON log = xsql19388.log DHC-APP>> > 1>>SELECT TOP 3 Name,Age 2>>FROM Sample.Person 3>>/GO 2. SELECT TOP 3 Name,Age FROM Sample.Person Name Age yaoxin 30 xiaoli 姚鑫 7 3 Rows(s) Affected statement prepare time(s)/globals/lines/disk: 0.0595s/46282/256257/9ms execute time(s)/globals/lines/disk: 0.0003s/3/714/0ms --------------------------------------------------------------------------- DHC-APP>>/SET COMMANDPREFIX commandprefix = / DHC-APP>>/SET COMMANDPREFIX="" commandprefix = "" DHC-APP>>SET COMMANDPREFIX commandprefix = ``` 当设置了命令前缀时,除了`?`、`#`和`GO`之外的所有SQL Shell命令都需要该命令前缀; 可以使用或不使用命令前缀发出这三个SQL Shell命令。 当发出`SET`或`SET COMMANDPREFIX`命令时,SQL Shell将显示当前命令前缀,作为SQL Shell初始化的一部分,并且在`?` 命令选项显示。 ## 运行命令 SQL Shell `RUN`命令执行SQL脚本文件。 在发出运行命令之前必须设置方言,以指定IRIS (InterSystems SQL)、Sybase (Sybase TSQL)或MSSQL (Microsoft SQL); 默认的方言是IRIS。 可以调用`RUN scriptname`,也可以只调用`RUN`,然后提示输入脚本文件名。 `RUN`加载脚本文件,然后准备并执行文件中包含的每个语句。 脚本文件中的语句必须分隔,通常用`GO`行或分号(`;`)分隔。 `RUN`命令提示指定分隔符。 SQL脚本文件结果显示在当前设备上,也可以显示在日志文件中。 还可以生成一个包含准备失败语句的文件。 `RUN`命令返回指定这些选项的提示符,示例如下: ```java [SQL]USER>>SET DIALECT=Sybase dialect = Sybase [SQL]USER>>RUN Enter the name of the SQL script file to run: SybaseTest Enter the file name that will contain a log of statements, results and errors (.log): SyTest.log SyTest.log Many script files contain statements not supported by IRIS SQL. Would you like to log the statements not supported to a file so they can be dealt with manually, if applicable? Y=> y Enter the file name in which to record non-supported statements (_Unsupported.log): SyTest_Unsupported.log Please enter the end-of-statement delimiter (Default is 'GO'): GO=> Pause how many seconds after error? 5 => 3 Sybase Conversion Utility (v3) Reading source from file: Statements, results and messages will be logged to: SyTest.log . . . ``` ## TSQL例子 下面的SQL Shell示例创建了一个`Sybase`过程`AvgAge`。 它使用`Sybase EXEC`命令执行这个过程。 然后,它将方言更改为InterSystems IRIS,并使用InterSystems SQL `CALL`命令执行相同的过程。 ```java DHC-APP>>SET DIALECT Sybase dialect = Sybase DHC-APP>> > 1>>CREATE PROCEDURE AvgAge 2>>AS SELECT AVG(Age) FROM Sample.Person 3>>GO 3. CREATE PROCEDURE AvgAge AS SELECT AVG(Age) FROM Sample.Person statement prepare time(s)/globals/lines/disk: 0.0173s/8129/22308/4ms execute time(s)/globals/lines/disk: 0.0436s/3844/23853/34ms --------------------------------------------------------------------------- DHC-APP>>EXEC AvgAge 4. EXEC AvgAge Dumping result #1 Aggregate_1 50.68137254901960784 1 Rows(s) Affected statement prepare time(s)/globals/lines/disk: 0.0086s/8096/21623/0ms execute time(s)/globals/lines/disk: 0.1131s/90637/458136/19ms --------------------------------------------------------------------------- DHC-APP>>SET DIALECT=IRIS Dialect 'iris' is not supported. ```
文章
Hao Ma · 五月 31, 2021

精华文章---HealthConnect中创建HTTP服务

# HealthConnect中创建HTTP服务端 这里我说说怎么在HealthConnect上开发HTTP服务。 作为消息引擎,HealthConnect会需要从一个接口接收HTTP请求发送到另一个接口,中间做消息转换,路由等等,目的的接口可能是HTTP,或者SOAP,REST等等。这里只介绍HTTP服务的内容,也就是最简单的两种实现: ## 第一种:实现客户定制的HTTP服务业务服务组件(Business Servie) 创建Business Service类,继承EnsLib.HTTP.Service, 如下面的示例: ```java Class SEDemo.IO.HTTP.ServiceExample1 Extends EnsLib.HTTP.Service { Parameter ADAPTER; Method OnProcessInput(pInput As %Stream.Object, Output pOutput As %Stream.Object) As %Status { //创建Ensemble消息发送给其他组件 set pRequest=##class(Ens.StreamContainer).%New() Set tSC=pRequest.StreamSet(pInput) set tSC= ..SendRequestAsync("Dummy1",pRequest,.pResponse) //创建返回Stream,发送给调用方 set pOutput=##class(%Stream.GlobalCharacter).%New() do pOutput.Write("yes, I recieved request") Quit tSC } } ``` 详细说明: **使用CSP机制接收请求,不要用HTTP的Inbound Adpater,这样能得到** 如果您学习过在Ensemble上开发SOAP接口,一定对代码里的***"Parameter ADAPTER;"***不陌生,它的作用是确定不要使用父类里的Adapter。 EnsLib.HTTP.Service有两个工种模式,一个是使用内置适配器是Ens.HTTP.InboundAdapter,还有一个是使用CSP机制,从CSP Gateway接收请求。下面的图中黄色的箭头是用适配器接收消息,这时业务服务可以定义工种的URL,端口,SSL等等;下面红色的箭头是所谓的”CSP请求“,也就是HTTP请求经过Web服务器,CSP Gateway, 到Web Application, 再被EnsLib.HTTP.Service收到。 使用CSP机制有更好的安全性和性能,所以在HealthConnect中任何HTTP的服务端接口我们都推荐CSP机制,包括HTTP接口,SOAP接口, REST接口。这些接口的开发都不要使用对应的InboundAdapter。 有关CSP Gateway的原理,还可以参见在线文档或者我的另一技术文章:[Web Gateway介绍](https://cn.community.intersystems.com/post/webgateway%E7%B3%BB%E5%88%971-web-gateway%E4%BB%8B%E7%BB%8D) 注意的是:当不使用Adapter时,Production页面的组件配置中很多项目会消失,这些是Adapter的属性,比如允许的IP, 端口,包括编码等等。因为不用Adapter,您也不用定义IP,端口号;只有编码,可以在BS的代码里实现。实现的操作可以参考Adapter的设置: https://docs.intersystems.com/healthconnectlatest/csp/docbook/Doc.View.cls?KEY=EHTTP_settings_inbound **OnProcessInput()的入参pInput** 业务组件收到的HTTP请求由pInput传入,真正的类型是%CSP.GlobalBinaryStream,它的父类是已经不推荐使用的流类型%GlobalBinaryStream。用%Stream.Object作为pInput的对象类型是合适的,这是一个新版本的Stream对象的超类,可以是任意类型的流。上面代码里业务服务组件发出的Ensemble消息的类型是StreamContainer,如果你看看消息跟踪的类型,你会发现里面流的类型是”GB",也就是一个%GlobalBinaryStream类型的流。 pInput对象的属性Stream里存放的是HTTP Body,而HTTP头放在Attributes属性里,如下图所示: **如何获得请求里的消息头** 以下是用pInput.GetAttributeList()得到的Attributes的内容: ``` 还有这么个操作,就是单独创建一个Web Application, 配置DispatchClass,来接入一个Web服务。我觉得完全没有必要,而且新版本中Web Application的菜单里只剩下了为REST分配分派类的选择,因此这里就不说这个了。 ## 第二种: 使用EnsLib.HTTP.GenericService预置业务服务组件 使用预置的,开发好的组件意味着不用写代码,配置一下就可以使用。相应的,灵活性上就差了,大概只适合简单的透传,转发,路由。如果要修改数据包内容,http头内容,编码转换等等,需要要在其他的组件上,比如production中消息经过的BP, BO中实现。 EnsLib.HTTP.GenericService的父类是EnsLib.HTTP.Service,因此它也是可以使用Adapter机制或者CSP标准机制。如前面所述,我们需要用CSP机制,调用的URL必须包含**?CfgItem=组件配置名**,比如我在Production里面配置了组件”GenericService1", 那么我访问的请求就应该是: http://localhost/csp/healthshare/demo/EnsLib.HTTP.GenericService.cls?CfgItem=GenericHTTPService1 还有,因为我们不修改代码,所以**组件配置项中会有Adapter的配置,比如端口号,SSL配置,直接忽略它们,不用理睬。** 。如果您看到配置项上有”字符集",显示的是UTF-8,可是没起作用,请不要奇怪,这个是Adapter的配置,您用CSP机制时它是不起作用的。 EnsLib.HTTP.GenericService向其他业务组件发出的Ensemble消息的类型是**EnsLib.HTTP.GenericMessage**。以下是一个POST请求被业务服务组件发送给BO的消息样例。 ``` { âserial_idâ: âC20114062017025â, âtake_timeâ: â2019-01-02 15:04:05â, âpos_timeâ: â2019-01-02 15:04:05â, âtypeâ: â1â, âwarnâ: â0â, âfile_idâ: â9cd586eef356c71f64b82a190b469e69â, âfile_nameâ: âA1012014400596520160714190338.hlyâ, âfile_pathâ: â/service/data/TE9100Yâ, âbegin_timeâ: â2016-07-04 20:17:21â, âend_timeâ: â2016-07-04 20:18:21â, âlengthâ: â60â, âsizeâ: â9650â, âresultâ: â窦æ§å¿ç, æ¬æ¬¡å¿çµçæµæªè§å¼å¸¸â } GB /csp/healthshare/demo/ GenericHTTPService1 1.1 POST 1 CfgItem=GenericHTTPService1 CfgItem=GenericHTTPService1 RAW /csp/healthshare/demo/EnsLib.HTTP.GenericService.cls */* gzip, deflate, br Basic X3N5c3RlbTpTWVM= no-cache keep-alive 532 text/plain CSPSESSIONID-SP-52773-UP-csp-healthshare-=0000000100006fSAbXykPFTd0NFNEoaUH94y07Phmq0V92aeDg; CSPBrowserId=F4nsRY6yDGVipvQ84sDN3w; CSPWSERVERID=I33xYkI3 172.16.58.200:52773 0e0bf4a7-868b-4e69-bbac-bfd89909fe99 PostmanRuntime/7.28.0 ``` 使用这个组件要注意的几点: - 上面的数据包里有中文乱码,我是故意这么做的,就是提醒您:强调一下选用这个组件时,如果要修改数据,可以在其他的组件处理。如果我做一个简单的HTTP转发,我可以选用EnsLib.HTTP.GenericService和EnsLib.HTTP.GenericOperation这一对预置组件,这样BS,BO就免开发了。而如果中间要做中文编码的转换,我会插入一个BP, 专门处理EnsLib.HTTP.GenericMessage里的中文转码。 - 组件的"启动标准请求"配置项:要使用CSP Gateway请求机制时应该勾选。 - "持久消息已发送INPROC"配置项( PersistInProcData) 这个选项是专用于以InProc模式同步调用时是否要持久化Ensemble消息的选项,默认是保存。如果设置成off,那么HealthConnect即不保留消息头,也不保留消息体,在消息查看器里无法查看,也不能重传。 - “保持标准请求分区”配置项 也是用于InProc模式调用,是否保留到BO定义的外部系统的连接。和上面的"持久消息已发送INPROC"配置项一样,很少被用到,保留默认的选中就可以。 - "没有字符集转换”配置项 控制CSP Gateway是否对%reponse消息里的文本按content-type的类型转码,默认是不勾选,也就是保留CSP Gateway的转码功能。 - ”One-Way"配置项 如果客户端不期待响应消息,那么选中这个选项后,EnsLib.HTTP.GenericService收到请求转发的同时会送一个Http状态码202,意思是”服务器已接受请求,但尚未处理“. 默认是不选中这个配置。
文章
姚 鑫 · 四月 22, 2021

第五章 优化查询性能(一)

# 第五章 优化查询性能(一) InterSystems SQL自动使用查询优化器创建在大多数情况下提供最佳查询性能的查询计划。该优化器在许多方面提高了查询性能,包括确定要使用哪些索引、确定多个`AND`条件的求值顺序、在执行多个联接时确定表的顺序,以及许多其他优化操作。可以在查询的`FROM`子句中向此优化器提供“提示”。本章介绍可用于评估查询计划和修改InterSystems SQL将如何优化特定查询的工具。 InterSystems IRIS®Data Platform支持以下优化SQL查询的工具: - `SQL Runtime Statistics`用于生成查询执行的运行时性能统计信息 - 索引分析器,用于显示当前命名空间中所有查询的各种索引分析器报告。这显示了InterSystems SQL将如何执行查询,可以全面了解索引是如何使用的。此索引分析可能表明应该添加一个或多个索引以提高性能。 - 查询执行计划:显示SQL查询(查询计划)的最佳(默认)执行计划,并可选地显示该SQL查询的备用查询计划以及统计信息。用于显示查询计划的工具包括SQL `EXPLAIN`命令、`$SYSTEM.SQL.ExPlan()`方法以及管理门户和SQL Shell中的各种`Show Plan`工具。查询计划和统计数据是在准备查询时生成的,不需要执行查询。 可以使用以下选项来指导查询优化器,方法是设置配置默认值或在查询代码中编码优化器“提示”: - 管理所有条件的子句选项中提供的索引优化选项,或单个条件前面的`%NOINDEX`。 - SQL代码中指定的注释选项,使优化器覆盖该查询的系统范围编译选项。 - 在每个查询或系统范围的基础上可用的并行查询处理允许多处理器系统在处理器之间划分查询执行。 以下SQL查询性能工具将在本手册的其他章节中介绍: - 缓存查询,使动态SQL查询能够重新运行,而无需在每次执行查询时准备查询的开销。 - SQL语句来保留最新编译的嵌入式SQL查询。在“SQL语句和冻结计划”一章中。 - 冻结计划以保留嵌入式SQL查询的特定编译。使用此编译,而不是使用较新的编译。在“SQL语句和冻结计划”一章中。 以下工具用于优化表数据,因此可以对针对该表运行的所有查询产生重大影响: - 定义索引可以显著提高对特定索引字段中数据的访问速度。 - `ExtentSize`、`Selective`和`BlockCount`用于在用数据填充表之前指定表数据估计;此元数据用于优化未来的查询。 - `Tune Table`用于分析已填充的表中的代表性表数据;生成的元数据用于优化未来的查询。 本章还介绍如何将查询优化计划写入文件,以及如何生成SQL故障排除报告以提交给InterSystems WRC。 # 管理门户SQL性能工具 IRIS管理门户提供对以下SQL性能工具的访问。有两种方式可以从管理门户系统资源管理器选项访问这些工具: - 选择工具,然后选择SQL性能工具。 - 选择SQL,然后选择工具下拉菜单。 从任一界面中您都可以选择以下SQL性能工具之一: - SQL运行时统计信息,以生成查询执行的性能统计信息。 - 索引分析器,用于显示当前命名空间中所有查询的各种索引分析器报告。这显示了InterSystems SQL将如何执行查询,可以全面了解索引是如何使用的。此索引分析可能表明应该添加一个或多个索引以提高性能。 - 备用显示计划:显示SQL查询的可用备用查询计划以及统计信息。 - 生成报告以向InterSystems Worldwide Response Center(WRC)客户支持部门提交SQL查询性能报告。要使用此报告工具,必须首先从WRC获得WRC跟踪号。 - 导入报告允许查看SQL查询性能报告。 # SQL运行时统计信息 可以使用SQL运行时统计信息来衡量系统上运行的SQL查询的性能。SQL运行时统计信息衡量`SELECT`、`INSERT`、`UPDATE`和`DELETE`操作(统称为查询操作)的性能。SQL运行时统计信息(SQL Stat)是在准备查询操作时收集的。请参阅使用SQL运行时统计信息工具。 默认情况下,SQL运行时统计信息的收集处于关闭状态。必须激活统计信息收集。强烈建议指定超时以结束统计信息收集。激活统计信息收集后,必须重新编译(准备)现有的动态SQL查询,并重新编译包含嵌入式SQL的类和例程。 性能统计信息包括`ModuleName`、`ModuleCount`(模块被调用的次数)、`RowCount`(返回的行数)、`TimeSpent`(执行性能,单位为秒)、`GlobalRefs`(全局引用数)、`LinesOfCode`(执行的行数)和`ReadLatency`(磁盘读取访问时间,单位为毫秒)。 可以显式清除SQL Stats数据。清除缓存查询会删除所有相关的SQL统计数据。删除表或视图会删除所有相关的SQL Stats数据。 注意:系统任务在所有名称空间中每小时自动运行一次,以将特定于进程的SQL查询统计信息聚合到全局统计信息中。因此,全局统计信息可能不会反映一小时内收集的统计信息。可以使用管理门户监视此每小时一次的聚合或强制其立即发生。要查看此任务上次完成和下次调度的时间,请依次选择系统操作、任务管理器、任务调度,然后查看更新SQL查询统计信息任务。可以单击任务名称查看任务详细信息。在`Task Details`(任务详细信息)显示中,可以使用Run(运行)按钮强制立即执行任务。 # 使用SQL运行时统计信息工具 可以使用以下任一方法从管理门户显示系统范围内的SQL查询的性能统计信息: - 选择系统资源管理器,选择工具,选择SQL性能工具,然后选择SQL运行时统计信息。 ![image](/sites/default/files/inline/images/1_38.png) - 选择系统资源管理器,选择SQL,然后从工具下拉菜单中选择SQL运行时统计信息。 ## Settings “设置”选项卡显示当前系统范围的SQL运行时统计信息设置以及此设置的过期时间。 Change Settings(更改设置)按钮允许设置以下统计信息收集选项: - 收集选项:可以将统计信息收集选项设置为0、1、2或3.0。0=关闭统计信息代码生成;1=为所有查询打开统计信息代码生成,但不收集统计信息;2=仅记录查询外部循环的统计信息(在主模块打开和关闭时收集统计信息);3=记录查询的所有模块级别的统计信息。 - 从0到1:更改SQL Stats选项后,需要编译包含SQL的例程和类以执行统计代码生成。对于xDBC和动态SQL,必须清除缓存查询以强制重新生成代码。 - 要从1变为2:只需更改SQL Stats选项即可开始收集统计信息。这使可以在运行的生产环境中启用SQL性能分析,并将中断降至最低。 - 从1到3(或从2到3):更改SQL Stats选项后,需要编译包含SQL的例程和类,以记录所有模块级别的统计信息。对于xDBC和动态SQL,必须清除缓存查询以强制重新生成代码。选项3通常仅用于非生产环境中已识别的性能较差的查询。 - 从1、2或3变为0:要关闭统计代码生成,不需要清除缓存的查询。 - 超时选项:如果收集选项为2或3,可以按已用时间(小时或分钟)或按完成日期和时间指定超时。可以用分钟或小时和分钟指定运行时间;该工具将指定的分钟值转换为小时和分钟(100分钟=1小时40分钟)。默认值为50分钟。日期和时间选项默认为当天午夜(23:59)之前。强烈建议指定超时选项。 - 重置选项:如果收集选项为2或3,则可以指定超时值到期时要重置为的收集选项。可用选项为0和1。 ## 查询测试 查询测试选项卡允许输入SQL查询文本(或从历史记录中检索),然后显示该查询的SQL统计信息和查询计划。查询测试包括查询的所有模块级别的SQL统计信息,而与收集选项设置无关。 输入一个SQL查询文本,或使用`Show History`按钮检索一个。 可以通过单击右边的圆形“X”圆来清除查询文本字段。 使用`Show Plan With SQL Stats`按钮执行。 默认情况下,后台复选框中的“运行`Show Plan`进程”未被选中,这是大多数查询的首选设置。 仅对长时间、运行缓慢的查询选择此复选框。 当这个复选框被选中时,你会看到一个进度条显示“请等待…”的消息。 当运行一个长查询时,带有SQL Stats和`Show History`按钮的`Show Plan`消失,而显示一个`View Process`按钮。 单击`View Process`将在新选项卡中打开流程详细信息页面。 在流程详细信息页面中,可以查看该流程,并可以暂停、恢复或终止该流程。 流程的状态应该反映在显示计划页面上。 当流程完成后,显示计划会显示结果。 `View Process`按钮消失,带有SQL Stats的`Show Plan`和`Show History`按钮重新出现。 使用查询测试显示的语句文本包括注释,不执行文字替换。 ## 查看统计信息 `View Stats`(查看统计信息)选项卡为提供了在此系统上收集的运行时统计信息的总体视图。 可以单击任何一个`View Stats`列标题对查询统计信息进行排序。然后,可以单击SQL语句文本以查看所选查询的详细查询统计信息和查询计划。 使用此工具显示的语句文本包括注释,不执行文字替换。`ExportStatsSQL()`和`Show Plan`显示的语句文本会去掉注释并执行文字替换。 ### 清除统计信息按钮 清除统计信息按钮清除当前名称空间中所有查询的所有累积统计信息。它会在SQL运行时统计信息页上显示一条消息。如果成功,则会显示一条消息,指示已清除的统计信息数量。如果没有统计信息,则会显示无要清除的消息。如果清除不成功,则会显示一条错误消息。 ## 运行时统计信息和显示计划 SQL运行时统计信息工具可用于显示包含运行时统计信息的查询的显示计划。 可以使用`Alternate Show Plans`工具将显示计划与统计数据进行比较,从而显示查询的运行时统计信息。备用显示计划工具在其显示计划选项中显示查询的估计统计信息。如果激活了收集运行时统计信息,则其`Compare Show Plans with Stats`选项将显示实际的运行时统计信息;如果运行时统计信息未处于活动状态,则此选项将显示估计统计信息。
文章
姚 鑫 · 五月 20, 2021

第一章 发送HTTP请求

# 第一章 发送HTTP请求 本主题介绍如何发送`HTTP`请求(如`POST`或`GET`)和处理响应。 # HTTP请求简介 可以创建`%Net.HttpRequest`的实例来发送各种`HTTP`请求并接收响应。此对象相当于Web浏览器,可以使用它发出多个请求。它会自动发送正确的`cookie`,并根据需要设置`Referer`标头。 要创建HTTP请求,请使用以下常规流程: 1. 创建`%Net.HttpRequest`的实例。 2. 设置此实例的属性以指示要与之通信的Web服务器。基本属性如下: - 服务器指定Web服务器的IP地址或计算机名称。默认值为`localhost`。 **注意:不要将`http://`或`https://`作为服务器值的一部分。这将导致错误`#6059:无法打开到服务器http:/的TCP/IP套接字`。** 3. 可以选择设置HTTP请求的其他属性和调用方法,如指定其他HTTP请求属性中所述。 4. 然后,通过调用`%Net.HttpRequest`实例的`get()`方法或其他方法来发送HTTP请求,如“发送HTTP请求”中所述。 可以从实例发出多个请求,它将自动处理cookie和Referer标头。 注意:如果创建此HTTP请求是为了与生产出站适配器(`EnsLib.HTTP.Outbound Adapter`)一起使用,那么请改用该适配器的方法来发送请求。 5. 如果需要,使用`%Net.HttpRequest`的同一实例发送其他HTTP请求。默认情况下,InterSystems IRIS使TCP/IP套接字保持打开状态,以便可以重复使用套接字,而无需关闭和重新打开它。 以下是一个简单的示例: ```java /// w ##class(PHA.TEST.HTTP).Get() ClassMethod Get() { set request=##class(%Net.HttpRequest).%New() set request.Server="tools.ietf.org" set request.Https=1 set request.SSLConfiguration="yx" set status=request.Get("/html/rfc7158") d $System.Status.DisplayError(status) s response = request.HttpResponse s stream = response.Data q stream.Read() } ``` # 提供身份验证 如果目标服务器需要登录凭据,则HTTP请求可以包括提供凭据的HTTP `Authorization`标头。 如果使用的是代理服务器,还可以指定代理服务器的登录凭据;为此,请设置`ProxyAuthorization`属性 ## 使用HTTP 1.0时对请求进行身份验证 对于HTTP 1.0,要验证HTTP请求,请设置`%Net.HttpRequest`实例的用户名和密码属性。然后,该实例使用基本访问身份验证基于该用户名和密码创建HTTP `Authorization`标头(RFC 2617)。此`%Net.HttpRequest`发送的任何后续请求都将包括此头。 **重要提示:请确保还使用SSL。在基本身份验证中,凭据以base-64编码形式发送,因此易于读取。** ## 在使用HTTP 1.1时对请求进行身份验证 对于HTTP 1.1,要验证HTTP请求,在大多数情况下,只需设置`%Net.HttpRequest`实例的用户名和密码属性。当`%Net.HttpRequest`的实例收到`401 HTTP`状态代码和`WWW-Authenticate`标头时,它会尝试使用包含支持的身份验证方案的`Authorization`标头进行响应。使用为IRIS支持和配置的第一个方案。默认情况下,它按以下顺序考虑这些身份验证方案: 1. 协商(SPNEGO和Kerberos,根据RFC 4559和RFC 4178) 2. NTLM(NT LAN Manager身份验证协议) 3. 基本认证(RFC 2617中描述的基本接入认证) 重要:如果有可能使用基本身份验证,请确保也使用SSL(参见“使用SSL进行连接”)。 在基本身份验证中,凭据以base-64编码的形式发送,因此很容易读取。 在Windows上,如果没有指定`Username`属性,IRIS可以使用当前登录上下文。 具体来说,如果服务器使用401状态码和用于`SPNEGO`、`Kerberos`或`NTLM`的`WWW-Authenticate`头响应,那么IRIS将使用当前操作系统用户名和密码创建`Authorization`头。 具体情况与HTTP 1.0不同,如下所示: 1. 如果认证成功,IRIS更新`%Net`的`CurrentAuthenticationScheme`属性。 `HttpRequest`实例来指示它在最近的身份验证中使用的身份验证方案。 2. 如果尝试获取方案的身份验证句柄或令牌失败,IRIS会将基础错误保存到`%Net.HttpRequest`实例的`AuthenticationErrors`属性中。此属性的值为`$List`,其中每一项都具有格式`scheme ERROR: message` 仅HTTP 1.1支持协商和`NTLM`,因为这些方案需要多次往返,而HTTP 1.0要求在每个请求/响应对之后关闭连接。 ### Variations 如果知道服务器允许的一个或多个身份验证方案,则可以通过包括`Authorization`标头来绕过服务器的初始往返行程,该标头包含所选方案的服务器的初始令牌。为此,请设置`%Net.HttpRequest`实例的`InitiateAuthentication`属性。对于此属性的值,请指定服务器允许的单个授权方案的名称。使用下列值之一(区分大小写): - Negotiate - NTLM - Basic 如果要自定义要使用的身份验证方案(或更改其考虑顺序),请设置`%Net.HttpRequest`实例的`AuthenticationSchemes`。对于此属性的值,请指定以逗号分隔的身份验证方案名称列表(使用上一个列表中给出的准确值)。 ## 直接指定授权标头 对于HTTP 1.0或HTTP 1.1(如果适用于场景),可以直接指定HTTP `Authorization`标头。具体地说,可以将`Authorization`属性设置为等于正在请求的资源的用户代理所需的身份验证信息。 如果指定`Authorization`属性,则忽略用户名和密码属性。 ## 启用HTTP身份验证的日志记录 要启用HTTP身份验证的日志记录,请在终端中输入以下内容: ```java set $namespace="%SYS" kill ^ISCLOG set ^%ISCLOG=2 set ^%ISCLOG("Category","HttpRequest")=5 ``` 日志条目将写入`^ISCLOG global`中.。要将日志写入文件(以提高可读性),请输入以下内容(仍在`%SYS`命名空间内): ```java do ##class(%OAuth2.Utils).DisplayLog("filename") ``` 其中,`filename`是要创建的文件的名称。该目录必须已存在。如果该文件已经存在,它将被覆盖。 要停止日志记录,请输入以下内容(仍在`%SYS`命名空间内): ```java set ^%ISCLOG=0 set ^%ISCLOG("Category","HttpRequest")=0 ``` # 指定其他HTTP请求属性 在发送HTTP请求之前(请参阅发送HTTP请求),可以指定其属性,如以下各节所述: 可以为`%Net.HttpRequest`的所有属性指定默认值,如最后列出的部分中所指定。 ## Location属性 `Location`属性指定从Web服务器请求的资源。如果设置此属性,则在调用`Get()`, `Head()`, `Post()`, 或 `Put()`方法时,可以省略location参数。 例如,假设正在向url `http://machine_name/test/index.html`发送一个HTTP请求 在这种情况下,将使用下列值: `%Net.HttpRequest`的示例属性 Properties |Value ---|--- Server| machine_name Location| test/index.html ## 指定Internet媒体类型(Media Type)和字符编码(Character Encoding) 可以使用以下属性指定`%Net.HttpRequest`实例及其响应中的Internet媒体类型(也称为MIME类型)和字符编码: - Content-Type指定`Content-Type`标头,该标头指定请求正文的Internet媒体类型。默认类型为None。 可能的值包括`application/json`、`application/pdf`、`application/postscript`、`image/jpeg`、`image/png`、`multipart/form-data`、`text/html`、`text/plan`、`text/xml`等等 - ContentCharset属性控制请求的任何内容(例如,`text/html`或`text/xml`)类型时所需的字符集。如果不指定此属性,InterSystems IRIS将使用InterSystems IRIS服务器的默认编码。 注意:如果设置此属性,则必须首先设置`ContentType`属性。 - `NoDefaultContentCharset`属性控制在未设置`ContentCharset`属性的情况下是否包括文本类型内容的显式字符集。默认情况下,此属性为False。 如果此属性为true,则如果有文本类型的内容,并且没有设置`ContentCharset`属性,则内容类型中不包括任何字符集;这意味着字符集iso-8859-1用于消息输出。 - WriteRawMode属性影响实体正文(如果包含)。它控制请求正文的写入方式。默认情况下,此属性为False,并且InterSystems IRIS以请求标头中指定的编码写入正文。如果此属性为true,则InterSystems IRIS以原始模式写入正文(不执行字符集转换)。 - `ReadRawMode`属性控制如何读取响应正文。默认情况下,此属性为False,并且InterSystems IRIS假定正文在响应标头中指定的字符集中。如果此属性为true,InterSystems IRIS将以原始模式读取正文(不执行字符集转换)。 ## 使用代理服务器 可以通过代理服务器发送HTTP请求。要设置此设置,请指定HTTP请求的以下属性: - `ProxyServer`指定要使用的代理服务器的主机名。如果此属性不为空,则将HTTP请求定向到此计算机。 - `ProxyPort`指定代理服务器上要连接到的端口。 - `ProxyAuthorization`指定`Proxy-Authorization`标头,如果用户代理必须使用代理验证其自身,则必须设置该标头。对于该值,请使用正在请求的资源的用户代理所需的身份验证信息。 - `ProxyHTTPS`控制HTTP请求是针对HTTPS页面还是针对普通HTTP页面。如果未指定代理服务器,则忽略此属性。此属性将目标系统上的默认端口更改为代理端口443。 - `ProxyTunes`指定是否通过代理建立到目标HTTP服务器的隧道。如果为true,则请求使用HTTP CONNECT命令建立隧道。代理服务器的地址取自`ProxyServer`和`ProxyPort`属性。如果`ProxyHttps`为true,则隧道建立后,系统间IRIS将协商SSL连接。在这种情况下,由于隧道与目标系统建立直接连接,因此将忽略https属性。 ## 使用SSL进行连接 `%Net.HttpRequest`类支持SSL连接。要通过SSL发送请求,请执行以下操作: 1. 将`SSLConfiguration`属性设置为要使用的已激活SSL/TLS配置的名称。 2. 还要执行以下操作之一,具体取决于是否使用代理服务器: - 如果未使用代理服务器,请将`https`属性设置为true。 - 如果使用的是代理服务器,请将`ProxyHTTPS`属性设置为true。 在这种情况下,要使用到代理服务器本身的`SSL`连接,请将https属性设置为true。 请注意,当使用到给定服务器的`SSL`连接时,该服务器上的默认端口假定为443(HTTPS端口)。例如,如果没有使用代理服务器,并且https为true,则会将Default Port属性更改为443。 ### 服务器身份检查 默认情况下,当`%Net.HttpRequest`实例连接到SSL/TLS安全的Web服务器时,它会检查证书服务器名称是否与用于连接到服务器的`DNS`名称匹配。如果这些名称不匹配,则不允许连接。此默认行为可防止“中间人”攻击,在RFC 2818的3.1节中进行了描述;另请参阅RFC 2595的2.4节。 **若要禁用此检查,请将`SSLCheckServerIdentity`属性设置为0。** ## `HTTPVersion`、`Timeout`、`WriteTimeout`和`FollowRedirect`属性 `%Net.HttpRequest`还提供以下属性: `HTTPVersion`指定请求页面时使用的HTTP版本。默认值是`"HTTP/1.1"`。你也可以使用`“HTTP/1.0”`。 `Timeout`指定等待web服务器响应的时间,以秒为单位。 缺省值是30秒。 `WriteTimeout`指定等待Web服务器完成写入的时间(以秒为单位)。默认情况下,它将无限期等待。可接受的最小值为2秒。 `FollowRedirect`指定是否自动跟踪来自Web服务器的重定向请求(由300-399范围内的HTTP状态代码发出信号)。如果使用的是GET或HEAD,则默认值为TRUE;否则为FALSE。 ## 指定HTTP请求的默认值 可以为`%Net.HttpRequest`的所有属性指定默认值。 - 要指定适用于所有名称空间的默认值,请设置全局节 `^%SYS("HttpRequest","propname")`,其中`“PropName”`是属性的名称。 - 要为一个名称空间指定默认值,请转到该名称空间并设置节点`^SYS("HttpRequest","propname")` (`^%SYS`全局设置会影响整个安装,`^SYS`全局设置会影响当前命名空间。) 例如,要为所有名称空间指定默认代理服务器,请设置全局节`^%SYS("HttpRequest","ProxyServer")` 您好,我想请教一下如下错误是什么原因造成的呢? 错误 #6085: 无法写入使用SSL/TLS配置'xxxxxx'的套接字,报告'SSL/TLS error in SSL_connect(), SSL_ERROR_SSL: protocol error, error:0A000126:SSL routines::unexpected eof while reading'错误
文章
Hao Ma · 一月 12, 2023

IRIS, Caché监控指导 - 诊断报告和性能报告

InterSystems公司的技术支持中心WRC(World Response Center)提供的服务包括故障报修,升级和数据迁移支持等等。当客户报告了系统故障或性能问题给WRC时, 会被要求收集以下的两份报告,以了解系统的运行情况和性能表现,它们是:***诊断报告(Diagnostic Report)和系统性能报告***。 ## 诊断报告(Diagnostic Report) 有关诊断报告,您需要知道: 1. **诊断报告是当前系统的运行状况的数据收集。** 2. **是给InterSystems技术支持工程师的,维护人员基本不需要读它。** 3. **当出现紧急故障需要重启系统时,先做一次诊断报告的收集,会对WRC在故障过后分析故障原因提供极大的便利。** #### 报告收集的步骤 进入管理门户页面,**“系统管理>诊断报告”(System Operation > Diagnostic Reports)**,点击**运行**。 - 报告收集通常**需要5-10分钟** - 执行开始后屏幕会出现提示:诊断报告在…时间运行.报告将储存在…目录中。成功后可以在“系统管理>任务管理器>任务历史“看到记录收集成功的记录。 - 在运行前,您可以选择报告存放的位置(Diectory for archived reports). - 如果不填写,**默认报告保存的目录是install-dir\mgr** ⚠️ :收集报告由内部用户irisuser执行,所以您选择的存放目录要有irisuser的读写权限。如果没有,点击“运行”时您并不能得到 提示错误,需要到“系统管理>任务管理器>任务历史“, 才能发现其实报告收集的任务没有执行。 - 如果有公网连接,可以配置邮件信息,报告会直接发送给WRC - 如果开设了WRC工单,请输入WRC工单号码 - 万一您已经无法登录管理门户,还是有紧急的故障要收集诊断报告,您需要[在Terminal执行命令收集诊断报告(附录1)][1]。 #### 报告的内容细节 诊断报告是一个HTML文件,名字是license中您的机构名称+时间, 比如这样:MyCompany202301051144.html 请记住2点: 1. 收集的数据分基本信息(Basic information)和高级信息(Advanced Information)。对于维护人员,基本信息可以在操作维护的各个页面上查看,没必要去读报告。而高级信息,不要求维护人员读懂或者分析内容。 具体内容如果如下,您可以选择跳过,或者从文档中了解更多的内容:[在线文档:Caché的诊断报告内容](https://docs.intersystems.com/ens201815/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_diagnostic#GCM_diagnostic_contents), [在线文档:IRIS的诊断报告内容](https://docs.intersystems.com/irisforhealth20211/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_diagnostic#GCM_diagnostic_contents)。 >报告的内容是系统当前的运行状态,主要包括: > >- 配置信息: > > 主要有服务器硬件信息,操作系统信息,许可证信息,实例的配置情况(CPF文件),数据库的配置,bin目录下的文件等等 > >- 当前系统的基本运行情况 > > - 数据库大小,占用的百分比 > - Journal的信息 > - 许可证的使用情况 > - 安全设置,审计(Audit)日志 > - 网络的状态。比如在Linux系统,其实是执行 `netstat -an` 的输出结果 > - 进程的状态 > - 内存的状态, cstat, core dump等等 > - GloStat ..., 不一一罗列 2. **cconsole.log**,或者**messages.log** 不包含在诊断报告中。通常情况下, 您需要把这个日志文件和诊断报告一起提交WRC #### 问题和回答 Q: 需要定期做诊断报告吗? A: 定期做系统健康检查吧,里面包括了诊断报告 Q: 要创建定时任务做诊断报告吗? A: 没必要。除非您要指定一个时间,比如要了解凌晨1AM的系统运行状况。 ## 性能报告(SystemPerformance Report) 有关性能报告,您需要了解: - 收集的数据包括操作系统的性能指标,Caché的设置,Caché性能指标等等。Caché性能指标大多数是每秒平均值。 - 报告收集 - Windows服务器:在Termial执行收集命令。 - Linux/Unix服务器:除了Terminal执行收集命令外,可以在管理门户设置定时任务(Task)执行。 > *为什么Windows服务器不能使用定时任务:* > > 性能报告的收集中会使用Windows系统的Perform.exe,也就是Windows系统性能监视程序。从用户管理门户设置Task, 或者使用当前非管理员权限的用户打开Terminal收集性能报告,会因权限不足导致内部调用Windows Perfom的失败,也就无法得倒操作系统的性能指标。 - 因为只是将系统默认收集的指标打包归档,所以它**对系统性能影响很小**。可以在任何时间执行。 - 即使没有故障要处理,定期的收集性能报告也是InterSystems推荐的。定期的报告保留了一个系统正常运行时的性能基线,便于今后的性能故障的原因分析,以及了解资源耗费情况的趋势。 如果操作中需要了解更多的内容,请参考在线文档:[Monitoring Performance Using ^SystemPerformance](https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_systemperf)(for IRIS), [Monitoring Performance Using ^pButtons](https://docs.intersystems.com/ens201815/csp/docbook/Doc.View.cls?KEY=GCM_pbuttons)(for Caché)。 ### 在Terminal收集性能报告 #### Windows环境 1. 打开一个以管理员权限运行的Windows命令窗口。一般是找到CMD, 右键, “以管理员身份运行“。 2. 输入命令进入Caché或者IRIS终端。 - Caché: ` - IRIS : `iris console instanceName` 或者 `iris terminal instanceName` 3. 在%sys命名空间执行以下命令Routine: - Caché: `do ^pButtons` (见下面的例子) - IRIS : `do ^SystemPerfromance` ```zsh %SYS>do ^pButtons Current log directory: c:\intersystems\hsap\mgr\/intersystems\ Windows Perfmon data will be left in raw format. Available profiles: 1 12hours - 12 hour run sampling every 10 seconds 2 24hours - 24 hour run sampling every 10 seconds 3 30mins - 30 minute run sampling every 1 second 4 4hours - 4 hour run sampling every 5 seconds 5 8hours - 8 hour run sampling every 10 seconds 6 test - A 5 minute TEST run sampling every 30 seconds select profile number to run: 2 Collection of this sample data will be available in 86520 seconds. The runid for this data is 20200123_142501_24hours. %SYS>do Collect^pButtons Current Performance runs: 20200123_142501_24hours ready in 24 hours 1 minute 48 seconds nothing available to collect at the moment. ``` > `do Collect^pButtons`命令显示程序执行的状态。这是一个24小时的收集任务,显示的执行时间*“ready in 24 hours 1 minute 48 seconds”*比24小时略长,多出的2分钟用于^pButtons将整理日志并将数据转换为html格式。 #### Linux/Unix环境 进入iris terminal并执行收集命令`do ^pButtons`或者`do ^SystemPerformance` , 以下是在 ```bash $ iris session iris Node: af34ddc4655b, Instance: IRIS USER>zn "%sys" %SYS>do ^SystemPerformance Current log directory: /usr/irissys/mgr/ Available profiles: 1 12hours - 12 hour run sampling every 10 seconds 2 24hours - 24 hour run sampling every 10 seconds 3 30mins - 30 minute run sampling every 1 second 4 4hours - 4 hour run sampling every 5 seconds 5 8hours - 8 hour run sampling every 10 seconds 6 test - A 5 minute TEST run sampling every 30 seconds Select profile number to run: ``` ### 管理页面创建任务收集性能报告 对于Linux/Unix系统上安装的IRIS或者Caché, 到管理门户的“系统操作>任务管理器>新任务“,创建的新任务, ![image](/sites/default/files/inline/images/image-20230111132102826.png) 选项中,命名空间为“%SYS", 任务类型为”运行传统任务”(RunLegacyTask), 执行代码:: - Caché: `do run^pButtons(“ProfileName”)` - IRIS: `do run^SystemPerformance(“ProfileName”)` ProfileName也就是收集数据的时间长度, Profile定义数据采样的时间长度和频率,选项为:30mins, 4hours, 8hours, 12hours, 24hours, test。 - **24hours**: 最常用。任务执行的起始时间无所谓。如果设为零点,这样最后得到的数据图横轴是0点到24点,讨好细节控。 - **test**: 多用于24小时的收集工作前的预操作,查看5分钟的收集数据结果。 ### 其他有关报告收集的注意事项 1. 多个收集任务可并行。 比如为了分析某个性能问题, 客户可以从早上9点执行两个收集任务,一个4小时的任务收集早晨业务繁忙时段的数据,一个24小时的任务收集全天的数据。 2. 默认报告保存位置是***"/mgr"***文件夹。 一个24小时报告的尺寸通常在**10MB - 100MB**。如果要保留的报告很多,可以考虑执行命令修改保存目录(注意确保Caché具有该目录的写权限)。 `%SYS>do setlogdir^pButtons("/somewhere_with_lots_of_space/perflogs/")` 3. 还可以创建另一个定时任务,清除历史报告,只保留一段时间的。 4. 在一个长的报告收集过程中,可以使用`Preview^SystemPerformance`命令在[不中断收集的情况下,读取已经收集的性能报告的数据。如果需要, 请阅读[在线文档中生成报告的部分](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_systemperf#GCM_systemperf_produce)。 5. 如果可以要收集整个商业周期,比如一周或一个月的数据,需要[创建新性能采样profile(附录2)][2]。 6. 最后, 有一些内部的开关可以设置^pButtons采集更多的数据,比如硬盘访问的更详细的记录。通常这样的操作可能要消耗大量资源,请在WRC指导上使用。 ### 性能报告的内容 性能报告是一个html文件, 默认的文件名为服务器,实例名称, 时间的组合,比如bjse01_IRIS_20220628_103554_30mins.html。 内容主要为: - 配置, cpf文件,license等等 - 系统级别问题诊断,比如系统hang, 网络问题,性能问题等。(有兴趣可以去在线文档了解“cstat"或者“irisstat") - Caché,IRIS性能表现的采样, 比如Global的读写速度, Rdration等指标,有兴趣可以去在线文档了解“mgstat') - 操作系统的性能数据 - Windows Performance Monitor - Linux Info, ps, sar, vmstat... 具体内容请阅读: [InterSystems IRIS Performance Data Report for Microsoft Windows Platforms](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_systemperf#GCM_systemperf_reports_windows_) , [InterSystems IRIS Performance Data Report for Linux Platforms](https://docs.intersystems.com/iris20222/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_systemperf#GCM_systemperf_reports_linux) ### 性能报告内容的展现 性能报告是一个html文件。内容中的简单的数据,可以直观的在html中阅读, 但表格数据,比如cpu, 内存使用,IO, Global访问等等, 展现,或者说画图工具是有必要的。 **如果维护人员或用户想阅读性能报告**,可以考虑以下工具: [性能报告数据转csv](https://community.intersystems.com/post/extracting-pbuttons-data-csv-file-windows) : 这是一个windows PowerShell的程序, 方便使用EXCEL展现性能数据。 [YASPE](https://github.com/murrayo/yaspe) : 作者是Murrayo Oldfield,开源的工具,代码是python, 提供docker image下载。操作简单。数据可以生成图表,像下面的样子,而且可以zoom in/out。 ![example](https://github.com/murrayo/yaspe/raw/main/images/Glorefs_and_CPU_Utilisation_-_Sample_site_name_Monday_17_Jan_2022.png) WRC内部工具:如果您有了一个WRC工单,并且就当前问题提交了性能报告。您可以请你的技术支持提供一个报告中数据图表呈现。WRC问题有生成图表的工具,但仅供内部使用。 ## 附录 [1]: 在Terminal执行命令收集诊断报告 > 万一您已经无法登录管理门户,还是有紧急的故障要收集诊断报告,您需要在Terminal操作。 > > 这个操作命令叫**Buttons**. 据说,名字的出处是最初开发者任务收集报告应该简单的像“按一个按钮”,所以后面开发的性能报告,干脆就被称为**pButtons**, p是performance的字头。因为被使用的可能性太小,iris的文档中其实已经找不到Buttons的内容。下面的步骤只是简单的给您一个印象。如果有一天不幸您非要用它,WRC会详细的指导您。 > > ```sh > USER>zn "%sys" > %SYS>do ^Buttons > (以下步骤省略)... > ``` > > *执行^Buttons可以选择收集某一个命名空间的ensemble production的信息。如果有需要,请按WRC的要求操作。* [2]: 创建新性能采样profile > Profile定义收集数据的时长和频率, 比如24hours的profile是每10秒采样一次运行24小时。如果可以希望收集其他时间长度的数据,或者减小采样频率以减小结果文件的大小,可以考虑自己定制Profile。比如下面的my_24hours_30sec: > > ```sh > //创建24小时配置文件,30秒采样间隔。30sec*2880=1440mins=24hours > %SYS>write $$addprofile^pButtons("My_24hours_30sec","24 hours 30 sec interval",30,2880) > %SYS>do ^pButtons > Current log directory: /db/backup/benchout/pButtonsOut/ > Available profiles: > 1 12hours - 12 hour run sampling every 10 seconds > 2 24hours - 24 hour run sampling every 10 seconds > 3 30mins - 30 minute run sampling every 1 second > 4 4hours - 4 hour run sampling every 5 seconds > 5 8hours - 8 hour run sampling every 10 seconds > 6 My_24hours_30sec- 24 hours 30 sec interval > 7 test - A 5 minute TEST run sampling every 30 seconds > > select profile number to run: > ```
文章
Jeff Liu · 五月 15

基于 Kubernetes 而非传统Mirror的IRIS 高可用部署

在本文中,我们将使用基于分布式存储的 Kubernetes 部署来构建一个 IRIS 的高可用配置,而不使用“传统的”IRIS Mirror。 这种部署将能够容忍与基础架构相关的故障,如节点、存储和可用区故障。 所描述的方法可以大大降低部署的复杂性,代价是 RTO的略微延长。 图 1 - 传统镜像与采用分布式存储的 Kubernetes 本文的所有源代码均可在 https://github.com/antonum/ha-iris-k8s 下载TL;DR 假设您有一个正在运行的 3 节点集群,并且您对 Kubernetes 有一定了解 – 请继续: kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml kubectl apply -f https://github.com/antonum/ha-iris-k8s/raw/main/tldr.yaml 如果您不确定上面两行有什么作用,或者没有可执行这些命令的系统,请跳转至“高可用性要求”部分。 我们将在后面详细介绍。 第一行安装 Longhorn - 开源分布式 Kubernetes 存储。 第二行安装 InterSystems IRIS ,将基于 Longhorn 的卷用于 Durable SYS。 等待所有 pod 进入运行状态。 kubectl get pods -A 您现在应该能通过 http://<IRIS Service Public IP>:52773/csp/sys/%25CSP.Portal.Home.zen(默认密码为“SYS”)访问 IRIS 管理门户,并通过以下命令访问 IRIS 命令行: kubectl exec -it iris-podName-xxxx -- iris session iris 模拟故障 现在开始制造一些混乱。 但在操作之前,先尝试将一些数据添加到数据库中,并确保当 IRIS 重新上线后数据仍然存在。 kubectl exec -it iris-6d8896d584-8lzn5 -- iris session iris USER>set ^k8stest($i(^k8stest))=$zdt($h)_" running on "_$system.INetInfo.LocalHostName() USER>zw ^k8stest ^k8stest=1 ^k8stest(1)="01/14/2021 14:13:19 running on iris-6d8896d584-8lzn5" 我们的“混乱工程”从这里开始: # Stop IRIS - Container will be restarted automatically kubectl exec -it iris-6d8896d584-8lzn5 -- iris stop iris quietly # Delete the pod - Pod will be recreated kubectl delete pod iris-6d8896d584-8lzn5 # "Force drain" the node, serving the iris pod - Pod would be recreated on another node kubectl drain aks-agentpool-29845772-vmss000001 --delete-local-data --ignore-daemonsets --force # Delete the node - Pod would be recreated on another node # well... you can't really do it with kubectl. Find that instance or VM and KILL it. # if you have access to the machine - turn off the power or disconnect the network cable. Seriously! 高可用性要求 我们正在构建可以容忍以下故障的系统: 容器/VM 内的 IRIS 实例。 IRIS – 级别故障。 pod/容器故障。 个别集群节点暂时不可用。 一个典型的例子是可用区临时下线。 个别集群节点或磁盘的永久性故障。 基本上,是我们刚才在“模拟故障”部分中尝试实现的场景。 如果发生上述任何一种故障,系统应该在没有任何人工干预的情况下保持在线,并且没有数据丢失。 从技术上说,数据持久性的保证是有限制的。 IRIS 本身可以根据应用程序内的日志循环和事务使用情况提供:https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=GCDI_journal#GCDI_journal_writecycle。无论如何,我们讨论的是低于两秒的 RPO(恢复目标时间)。 系统的其他组件(Kubernetes API 服务、etcd 数据库、LoadBalancer 服务、DNS 等等)不在讨论范围内,它们通常由 Azure AKS 或 AWS EKS 等托管 Kubernetes 服务管理,因此我们假定它们已经高度可用。 换一个角度看 – 我们负责处理个别的计算和存储组件故障,并假设其余故障由基础设施/云提供商处理。 架构 在谈到 InterSystems IRIS 的高可用性时,传统的建议是使用镜像。 使用镜像时,有两个始终在线的 IRIS 实例同步复制数据。 每个节点都维护一个完整的数据库副本,如果主节点宕机,用户将重新连接到备份节点。 本质上,在镜像方法中,IRIS 负责计算和存储的冗余。 利用部署在不同可用区的Mirror,镜像方法提供了容忍计算和存储故障所需的冗余,并且实现了只有几秒的出色 RTO(目标恢复时间或系统在故障后重新上线所需的时间)。 您可以在以下网址找到在 AWS 云上部署 IRIS Mirror的模板:https://community.intersystems.com/post/intersystems-iris-deployment%C2%A0guide-aws%C2%A0using-cloudformation-template Mirror的缺点是设置、执行备份/恢复程序比较复杂,而且缺少对安全设置和本地非数据库文件的复制。 Kubernetes 等容器编排器通过部署对象提供计算冗余,在出现故障时会自动重启有故障的 IRIS Pod/容器。 所以在 Kubernetes 架构图上只能看到一个 IRIS 节点在运行。 我们没有让另一个 IRIS 节点始终保持运行,而是将计算可用性外包给 Kubernetes。 Kubernetes 将确保当原始 pod 因任何原因发生故障时,重新创建 IRIS pod。 图 2 故障转移方案 到目前为止还不错... 如果 IRIS 节点发生故障,Kubernetes 就会创建一个新节点。 根据集群的情况,发生计算故障后,让 IRIS 重新上线需要 10 到 90 秒的时间。 相对于Mirror只需要几秒即可恢复,这确实有些退步,但如果万一发生中断时您可以容忍这一点,那么回报就是复杂度大大降低。 无需配置镜像。 无需担心安全设置和文件复制。 说实话,如果您在容器内登录,在 Kubernetes 中运行 IRIS,您甚至不会注意到您正在高可用环境中运行。 一切都像单实例 IRIS 部署一样。 等等,那存储呢? 我们还要处理数据库... 无论我们想象到什么样的故障转移方案,我们的系统都应该确保数据持久性。 Mirror依赖于 IRIS 节点本地的计算。 如果节点故障或只是暂时不可用 – 该节点的存储也会变得如此。 这就是IRIS 需要在Mirror配置内解决 IRIS 层面的数据复制的原因。 我们需要的存储不仅能在容器重启后保留数据库的状态,而且能针对节点或整个网段(可用区)宕机等事件提供冗余。 就在几年前,这个问题还没有简单的答案。 不过从上图可以猜到 – 我们现在有了这样的答案。 它称为分布式容器存储。 分布式存储将多个基础主机卷抽象成一个联合存储,供 k8s 集群的每个节点使用。 在本文中,我们使用 Longhorn https://longhorn.io;它免费、开源且相当容易安装。 但是您也可以看看其他提供相同功能的产品,例如 OpenEBS、Portworx 和 StorageOS。 Rook Ceph 是另一个可以考虑的 CNCF 孵化项目。 在高端领域 – 有企业级存储解决方案,如 NetApp、PureStorage 等。 分步指南 在 TL;DR 部分中,我们一次性安装了整套系统。 附录 B 将指导您完成逐步安装和验证过程。 Kubernetes 存储 让我们往回退一步,从总体上谈谈容器和存储,以及 IRIS 如何融入其中。 默认情况下,容器内的所有数据都是暂时的。 当容器消亡时,数据也会消失。 在 docker 中,可以使用卷的概念。 本质上,它允许将主机操作系统上的目录公开给容器。 docker run --detach --publish 52773:52773 --volume /data/dur:/dur --env ISC_DATA_DIRECTORY=/dur/iconfig --name iris21 --init intersystems/iris:2020.3.0.221.0 在上面的示例中,我们启动了 IRIS 容器,并使主机本地的“/data/dur”目录可以被“/dur”挂载点上的容器访问。 所以,容器在该目录内存储的任何内容都会保留下来,并可在下次容器启动时使用。 在 IRIS 方面,我们可以通过指定 ISC_DATA_DIRECTORY 来指示 IRIS 将所有需要在容器重启后存活的数据存储在特定目录中。 Durable SYS 是您可能需要在文档 https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=ADOCK#ADOCK_iris_durable_running 中查找的 IRIS 功能的名称 在 Kubernetes 中,语法有所不同,但概念是相同的。 以下是 IRIS 的基本 Kubernetes 部署。 apiVersion: apps/v1 kind: Deployment metadata: name: iris spec: selector: matchLabels: app: iris strategy: type: Recreate replicas: 1 template: metadata: labels: app: iris spec: containers: - image: store/intersystems/iris-community:2020.4.0.524.0 name: iris env: - name: ISC_DATA_DIRECTORY value: /external/iris ports: - containerPort: 52773 name: smp-http volumeMounts: - name: iris-external-sys mountPath: /external volumes: - name: iris-external-sys persistentVolumeClaim: claimName: iris-pvc 在上面的部署规范中,“volumes”部分列出了存储卷。 可以在容器外部通过“iris-pvc”等 persistentVolumeClaim 访问它们。 volumeMounts 在容器内公开了此卷。 “iris-external-sys”是将卷挂载绑定到特定卷的标识符。 在现实中,我们可能有多个卷,此名称即用来区分各个卷。 如果您愿意,还可以叫它“steve”。 我们已经熟悉的环境变量 ISC_DATA_DIRECTORY 指示 IRIS 使用一个特定挂载点来存储所有需要在容器重启后存活的数据。 现在,我们来看一下持久卷声明 iris-pvc。 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: iris-pvc spec: storageClassName: longhorn accessModes: - ReadWriteOnce resources: requests: storage: 10Gi 相当直接。 请求 10 GB,只能在一个节点上以读/写方式挂载,使用“longhorn”存储类。 storageClassName: longhorn 在这里实际上很关键。 我们看一下我的 AKS 集群上可用的存储类: kubectl get StorageClass NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE azurefile kubernetes.io/azure-file Delete Immediate true 10d azurefile-premium kubernetes.io/azure-file Delete Immediate true 10d default (default) kubernetes.io/azure-disk Delete Immediate true 10d longhorn driver.longhorn.io Delete Immediate true 10d managed-premium kubernetes.io/azure-disk Delete Immediate true 10d 默认安装了 Azure 的几个存储类,还有一个来自于 Longhorn,是我们在第一个命令中安装的: kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml 如果在持久卷声明定义中注释掉 #storageClassName: longhorn,则将使用当前标记为“default”的存储类,它是一个常规 Azure 磁盘。 为了说明为什么需要分布式存储,让我们重复本文开头所述的没有 longhorn 存储的“混乱工程”实验。 前两种情况(停止 IRIS 和 删除 Pod)将成功完成,系统将恢复到运行状态。 尝试耗尽或终止节点将使系统进入故障状态。 #forcefully drain the node kubectl drain aks-agentpool-71521505-vmss000001 --delete-local-data --ignore-daemonsets kubectl describe pods ... Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 57s (x9 over 2m41s) default-scheduler 0/3 nodes are available: 1 node(s) were unschedulable, 2 node(s) had volume node affinity conflict. 基本上,Kubernetes 会尝试重启集群上的 IRIS pod,但最初启动该 pod 的节点不可用,另外两个节点存在“卷节点相关性冲突”。 对于这种存储类型,卷只在最初创建它的节点上可用,因为它基本上与节点主机上可用的磁盘绑定。 使用 longhorn 作为存储类时,“强制耗尽”和“节点终止”实验均会成功,并且 IRIS pod 很快会恢复运行。 为了实现这一目标,Longhorn 控制了集群的 3 个节点上的可用存储,并将数据复制到全部三个节点上。 如果其中一个节点永久不可用,Longhorn 会迅速修复集群存储。 在我们的“节点终止”场景中,系统立即使用其余的两个卷副本在其他节点上重启 IRIS pod。 然后,AKS 提供一个新节点来替换丢失的节点,一旦准备就绪,Longhorn 就会介入,并在新节点上重建所需数据。 一切都是自动的,不需要您参与。 图 3 Longhorn 在替换的节点上重建卷副本。 更多有关 k8s 部署的信息 我们看一下部署的其他几个方面: apiVersion: apps/v1 kind: Deployment metadata: name: iris spec: selector: matchLabels: app: iris strategy: type: Recreate replicas: 1 template: metadata: labels: app: iris spec: containers: - image: store/intersystems/iris-community:2020.4.0.524.0 name: iris env: - name: ISC_DATA_DIRECTORY value: /external/iris - name: ISC_CPF_MERGE_FILE value: /external/merge/merge.cpf ports: - containerPort: 52773 name: smp-http volumeMounts: - name: iris-external-sys mountPath: /external - name: cpf-merge mountPath: /external/merge livenessProbe: initialDelaySeconds: 25 periodSeconds: 10 exec: command: - /bin/sh - -c - "iris qlist iris | grep running" volumes: - name: iris-external-sys persistentVolumeClaim: claimName: iris-pvc - name: cpf-merge configMap: name: iris-cpf-merge 策略: 重建Recreate, 副本replicas: 1 告诉 Kubernetes 在任何给定时间都应该保持一个且只能有一个 IRIS pod 实例运行。 这对应于我们的“删除 pod”场景。 livenessProbe 部分确保 IRIS 始终在容器内正常运行并应对“IRIS 下线”情况。 initialDelaySeconds 允许 IRIS 启动有一定的宽限期。 如果 IRIS 需要相当长的时间来启动部署,您可能需要增加该值。 IRIS 的 CPF MERGE配置文件合并 功能允许您在容器启动时修改配置文件 iris.cpf 的内容。 请参见 https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_cpf#RACS_cpf_edit_merge 了解相关内容。 在本示例中,我将使用 Kubernetes Config Map 管理合并文件 https://github.com/antonum/ha-iris-k8s/blob/main/iris-cpf-merge.yaml 的内容。这里我们调整了 IRIS 实例使用的全局缓冲区和 gmheap 值,但是您在 iris.cpf 文件中找到的一切都是可修改的。 您甚至可以使用 CPF Merge 文件中的“PasswordHash”字段更改默认 IRIS 密码。 更多信息请参见:https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=ADOCK#ADOCK_iris_images_password_auth 除了持久卷声明 https://github.com/antonum/ha-iris-k8s/blob/main/iris-pvc.yaml 部署 https://github.com/antonum/ha-iris-k8s/blob/main/iris-deployment.yaml 和采用 CPF Merge 内容的 ConfigMap https://github.com/antonum/ha-iris-k8s/blob/main/iris-cpf-merge.yaml,我们的部署还需要一个将 IRIS 部署暴露给公网的服务:https://github.com/antonum/ha-iris-k8s/blob/main/iris-svc.yaml kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE iris-svc LoadBalancer 10.0.18.169 40.88.123.45 52773:31589/TCP 3d1h kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 10d iris-svc 的外部 IP 可用于通过 http://40.88.123.45:52773/csp/sys/%25CSP.Portal.Home.zen 访问 IRIS 管理门户。 默认密码为“SYS”。 备份/恢复和存储扩展 Longhorn 提供了基于 Web 的 UI 来配置和管理卷。 使用 kubectl 标识 pod、运行 longhorn-ui 组件和建立端口转发: kubectl -n longhorn-system get pods # note the longhorn-ui pod id. kubectl port-forward longhorn-ui-df95bdf85-gpnjv 9000:8000 -n longhorn-system Longhorn UI 将可通过 http://localhost:9000 访问 图 4 Longhorn UI 除了高可用性,大多数 Kubernetes 容器存储解决方案还提供了方便的备份、快照和恢复选项。 细节是特定于实现的,但通常的惯例是备份与 VolumeSnapshot 关联。 对于 Longhorn 来说就是这样。 根据您的 Kubernetes 版本和提供商,您可能还需要安装卷快照工具 https://github.com/kubernetes-csi/external-snapshotter “iris-volume-snapshot.yaml”是此类卷快照的示例。 在使用它之前,您需要在 Longhorn 中配置备份到 S3 存储桶或 NFS 卷。 https://longhorn.io/docs/1.0.1/snapshots-and-backups/backup-and-restore/set-backup-target/ # Take crash-consistent backup of the iris volume kubectl apply -f iris-volume-snapshot.yaml 对于 IRIS,建议在获取备份/快照之前执行外部冻结,之后再解冻。 有关详细信息,请参见:https://docs.intersystems.com/irisforhealthlatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=Backup.General#ExternalFreeze 要增加 IRIS 卷的大小,请调整 IRIS 使用的持久卷声明(文件“iris-pvc.yaml”)中的存储请求。 ... resources: requests: storage: 10Gi #change this value to required 然后,重新应用 pvc 规范。 当卷连接到正在运行的 Pod 时,Longhorn 无法实际应用此更改。 请在部署中暂时将副本数更改为零,以便增加卷大小。 高可用性 – 概述 在文章开头,我们为高可用性设置了一些标准。 下面来说明我们如何通过此架构来实现: 故障域 自动缓解方式 容器/VM 内的 IRIS 实例。 IRIS – 级别故障。 部署运行情况探测将在 IRIS 故障时重启容器 pod/容器故障。 部署重新创建 pod 个别集群节点暂时不可用。 一个典型的例子是可用区下线。 部署在其他节点上重新创建 pod。 Longhorn 使数据在其他节点上可用。 个别集群节点或磁盘的永久性故障。 同上,并且 k8s 集群自动缩放器会将受损节点替换为新节点。 Longhorn 在新节点上重建数据。 Zoombie和其他要考虑的事项 如果您熟悉在 Docker 容器中运行 IRIS,则可能已经使用了“--init”标志。 docker run --rm -p 52773:52773 --init store/intersystems/iris-community:2020.4.0.524.0 此标志的目标是防止形成“僵尸进程”。 在 Kubernetes 中,可以使用“shareProcessNamespace: true”(安全注意事项适用)或在您自己的容器中使用“tini”。 使用 tini 的 Dockerfile 示例: FROM iris-community:2020.4.0.524.0 ... # Add Tini USER root ENV TINI_VERSION v0.19.0 ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini RUN chmod +x /tini USER irisowner ENTRYPOINT ["/tini", "--", "/iris-main"] 从 2021 年开始,InterSystems 提供的所有容器映像都默认包括 tini。 您可以通过调整一些参数来进一步减少“强制耗尽节点/终止节点”场景下的故障切换时间。 Longhorn Pod 删除策略 https://longhorn.io/docs/1.1.0/references/settings/#pod-deletion-policy-when-node-is-down 和 kubernetes 基于 taint 的逐出:https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/#taint-based-evictions 免责声明 作为 InterSystems 员工,我必须在这里说明:本文使用 Longhorn 作为 Kubernetes 分布式块存储的示例。 InterSystems 不会对个别存储解决方案或产品进行验证或发布官方支持声明。 您需要测试和验证是否有任何特定存储解决方案符合您的需求。 分布式存储与节点本地存储相比,性能特征可能有很大差别。 特别是在写入操作方面,数据必须写入到多个位置才会被认为处于持久状态。 请确保测试您的工作负载,并了解您的 CSI 驱动程序具有的特定行为和选项。 基本上,InterSystems 不会验证和/或认可具体的存储解决方案(如 Longhorn),就像我们不会验证单个硬盘品牌或服务器硬件制造商一样。 我个人认为 Longhorn 很容易使用,而且开发团队在项目的 GitHub 页面上响应迅速且乐于助人。https://github.com/longhorn/longhorn 结论 Kubernetes 生态系统在过去几年有了长足的发展,通过使用分布式块存储解决方案,您现在可以构建一个高可用性配置来维持 IRIS 实例、集群节点甚至是可用区故障。 您可以将计算和存储高可用性外包给 Kubernetes 组件,这样与传统 IRIS 镜像相比,系统的配置和维护都大为简化。 同时,此配置可能无法提供与镜像配置相同的 RTO 和存储级别性能。 在本文中,我们使用 Azure AKS 作为托管的 Kubernetes 和 Longhorn 分布式存储系统,构建了一个高可用性 IRIS 配置。 你可以探索多种替代方案,如 AWS EKS、用于托管 K8s 的 Google Kubernetes Engine、StorageOS、Portworx 和 OpenEBS 作为分布式容器存储,甚至 NetApp、PureStorage、Dell EMC 等企业级存储解决方案。 附录 A. 在云中创建 Kubernetes 集群 来自公共云提供商之一的托管 Kubernetes 服务是创建此设置所需的 k8s 集群的简单方法。 Azure 的 AKS 默认配置可以直接用于本文所述的部署。 创建一个新的 3 节点 AKS 集群。 其他所有设置都保持默认。 图 5 创建 AKS 集群 在您的计算机上本地安装 kubectl:https://kubernetes.io/docs/tasks/tools/install-kubectl/ 使用本地 kubectl 注册 AKS 集群 图 6 使用 kubectl 注册 AKS 集群 之后,您可以回到文章开头,并安装 longhorn 和 IRIS 部署。 在 AWS EKS 上安装更复杂一些。 您需要确保您的节点组中的每个实例都已安装 open-iscsi。 sudo yum install iscsi-initiator-utils -y 在 GKE 上安装 Longhorn 需要额外步骤,请参见此处:https://longhorn.io/docs/1.0.1/advanced-resources/os-distro-specific/csi-on-gke/ 附件 B. 分步安装 第 1 步 – Kubernetes 集群和 kubectl 您需要 3 节点 k8s 集群。 附录 A 介绍了如何在 Azure 上获得一个这样的集群。 $ kubectl get nodes NAME STATUS ROLES AGE VERSION aks-agentpool-29845772-vmss000000 Ready agent 10d v1.18.10 aks-agentpool-29845772-vmss000001 Ready agent 10d v1.18.10 aks-agentpool-29845772-vmss000002 Ready agent 10d v1.18.10 第 2 步 – 安装 Longhorn kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml 确保“longhorn-system”命名空间中的所有 pod 都处于运行状态。 这可能需要几分钟。 $ kubectl get pods -n longhorn-system NAME READY STATUS RESTARTS AGE csi-attacher-74db7cf6d9-jgdxq 1/1 Running 0 10d csi-attacher-74db7cf6d9-l99fs 1/1 Running 1 11d ... longhorn-manager-flljf 1/1 Running 2 11d longhorn-manager-x76n2 1/1 Running 1 11d longhorn-ui-df95bdf85-gpnjv 1/1 Running 0 11d 有关详细信息和故障排除,请参见 Longhorn 安装指南 https://longhorn.io/docs/1.1.0/deploy/install/install-with-kubectl 第 3 步 – 克隆 GitHub 仓库 $ git clone https://github.com/antonum/ha-iris-k8s.git $ cd ha-iris-k8s $ ls LICENSE iris-deployment.yaml iris-volume-snapshot.yaml README.md iris-pvc.yaml longhorn-aws-secret.yaml iris-cpf-merge.yaml iris-svc.yaml tldr.yaml 第 4 步 – 逐个部署和验证组件 tldr.yaml 文件将部署所需的所有组件捆绑在一起。 这里我们将逐个进行安装,并单独验证每一个组件的设置。 # If you have previously applied tldr.yaml - delete it. $ kubectl delete -f https://github.com/antonum/ha-iris-k8s/raw/main/tldr.yaml # Create Persistent Volume Claim $ kubectl apply -f iris-pvc.yaml persistentvolumeclaim/iris-pvc created $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE iris-pvc Bound pvc-fbfaf5cf-7a75-4073-862e-09f8fd190e49 10Gi RWO longhorn 10s # Create Config Map $ kubectl apply -f iris-cpf-merge.yaml $ kubectl describe cm iris-cpf-merge Name: iris-cpf-merge Namespace: default Labels: <none> Annotations: <none> Data ==== merge.cpf: ---- [config] globals=0,0,800,0,0,0 gmheap=256000 Events: <none> # create iris deployment $ kubectl apply -f iris-deployment.yaml deployment.apps/iris created $ kubectl get pods NAME READY STATUS RESTARTS AGE iris-65dcfd9f97-v2rwn 0/1 ContainerCreating 0 11s # note the pod name. You’ll use it to connect to the pod in the next command $ kubectl exec -it iris-65dcfd9f97-v2rwn -- bash irisowner@iris-65dcfd9f97-v2rwn:~$ iris session iris Node: iris-65dcfd9f97-v2rwn, Instance: IRIS USER>w $zv IRIS for UNIX (Ubuntu Server LTS for x86-64 Containers) 2020.4 (Build 524U) Thu Oct 22 2020 13:04:25 EDT # h<enter> to exit IRIS shell # exit<enter> to exit pod # access the logs of the IRIS container $ kubectl logs iris-65dcfd9f97-v2rwn ... [INFO] ...started InterSystems IRIS instance IRIS 01/18/21-23:09:11:312 (1173) 0 [Utility.Event] Private webserver started on 52773 01/18/21-23:09:11:312 (1173) 0 [Utility.Event] Processing Shadows section (this system as shadow) 01/18/21-23:09:11:321 (1173) 0 [Utility.Event] Processing Monitor section 01/18/21-23:09:11:381 (1323) 0 [Utility.Event] Starting TASKMGR 01/18/21-23:09:11:392 (1324) 0 [Utility.Event] [SYSTEM MONITOR] System Monitor started in %SYS 01/18/21-23:09:11:399 (1173) 0 [Utility.Event] Shard license: 0 01/18/21-23:09:11:778 (1162) 0 [Database.SparseDBExpansion] Expanding capacity of sparse database /external/iris/mgr/iristemp/ by 10 MB. # create iris service $ kubectl apply -f iris-svc.yaml service/iris-svc created $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE iris-svc LoadBalancer 10.0.214.236 20.62.241.89 52773:30128/TCP 15s 第 5 步 – 访问管理门户 最后使用服务的外部 IP http://20.62.241.89:52773/csp/sys/%25CSP.Portal.Home.zen 连接到 IRIS 的管理门户,用户名 _SYSTEM,密码 SYS。 您第一次登录时将被要求更改密码。
文章
姚 鑫 · 四月 3, 2021

第十五章 使用管理门户SQL接口(二)

# 第十五章 使用管理门户SQL接口(二) # 过滤模式内容 Management Portal SQL界面的左侧允许查看模式(或匹配筛选器模式的多个模式)的内容 1. 通过单击SQL interface页面顶部的Switch选项,指定希望使用的名称空间。 这将显示可用名称空间的列表,可以从中进行选择。 2. 应用筛选器或从模式下拉列表中选择模式。 可以使用Filter字段通过输入搜索模式来筛选列表。 可以在一个模式或多个模式中筛选模式,或筛选表/视图/过程名(项)。 搜索模式由模式名、点(`.`)和项目名组成——每个名称由文字和通配符的某种组合组成。字面值不区分大小写。 通配符是: - 星号(`*`)表示0个或多个任意类型的字符。 - 下划线(`_`)表示任意类型的单个字符。 - 撇号(`'`)倒装前缀,意为“不”(除了)。 - 反斜杠(`\`)转义字符:`\_`表示字面上的下划线字符。 例如,`S*`返回所有以`S S*`开头的模式。 `Person`返回所有以`S. *`开头的模式中的所有Person项。 `Person*`返回所有模式中以`Person开头`的所有项。 可以使用逗号分隔的搜索模式列表来选择满足所列模式(或逻辑)中的任何一种的所有项。 例如,`* .Person * *`。 `Employee*`选择所有模式中的所有Person和Employee项。 若要应用筛选器搜索模式,请单击refresh按钮或按Tab键。 过滤器搜索模式将一直有效,直到显式地更改它。 过滤器字段右侧的`“x”`按钮清除搜索模式。 3. 从schema下拉列表中选择一个模式将覆盖并重置之前的任何筛选器搜索模式,选择单个模式。 指定筛选器搜索模式将覆盖之前的任何模式。 4. 可选地,使用下拉“应用到”列表来指定要列出的项目类别:表、视图、过程、缓存查询,或以上所有。 默认为`All`。 在“应用到”下拉列表中指定的任何类别都受到筛选器或模式的限制。 在“应用到”中没有指定的类别继续在名称空间中列出该类别类型的所有项。 5. 可选地,单击System复选框以包含系统项目(名称以`%`开头的项目)。 默认情况下不包含系统项。 6. 展开类别的列表,列出指定架构或指定筛选器搜索模式的项。 展开列表时,不包含项的任何类别都不会展开。 7. 单击展开列表中的项,在SQL界面的右侧显示其目录详细信息。 如果所选项目是表或过程,则Catalog Details类名信息提供到相应类参考文档的链接。 请注意,筛选器设置是用户自定义的,并保留以供该用户将来使用。 ## Browse选项卡 Browse选项卡提供了一种方便的方式,可以快速查看名称空间中的所有模式,或者名称空间中经过过滤的模式子集。 可以选择Show All Schemas或Show Schemas with Filter,这将应用在管理门户SQL界面左侧指定的过滤器。 通过单击模式名称标题,可以按字母升序或降序列出模式。 每个列出的模式都提供指向其关联表、视图、过程和查询(缓存的查询)列表的链接。 如果模式没有该类型的项,则在该模式列表列中显示一个连字符(而不是命名链接)。 这使能够快速获得关于模式内容的信息。 单击“表”、“视图”、“过程”或“查询”链接将显示有关这些项的基本信息的表。 通过单击表标题,可以按该列的值升序或降序对列表进行排序。 过程表总是包括区段过程,而不管管理门户SQL界面左侧的过程设置如何。 可以使用Catalog Details选项卡获得关于单个表、视图、过程和缓存查询的更多信息。 从Browse选项卡中选择表或视图不会激活该表的`Open Table`链接。 # 目录详情 管理门户提供每个表,视图,过程和缓存查询的目录详细信息。管理门户SQL界面的过滤架构内容(左侧)组件允许您选择单个项目以显示其目录详细信息。 ## 目录表的详细信息 每个表提供以下目录详细信息选项: - 表信息:表类型:表类型:无论是表,全局临时或系统表(仅在选择系统复选框时显示系统表),所有者名称,最后编译的时间戳,外部和读取的布尔值,类名称,范围大小,子表的名称和/或父表(如果相关)和一个或多个引用字段到其他表(如果相关),无论是使用`%storage.persistent`默认存储类,无论是支持位图指标, `ROWID`字段名称,`ROWID`基于(如果相关)的字段列表,以及表是否被分析。如果有一个显式分片键,它会显示分片键字段。 类名是在Intersystems类参考文档中的相应条目的链接。类名是通过删除标点字符,如标识符和类实体名称中所述从表名派生的唯一包。 只有当当前表中的某个字段对另一个表有一个或多个引用时,引用才会出现在表信息中。 这些对其他表的引用作为指向所引用表的表信息的链接列出。 Sharded:如果表是一个分片主表,那么表信息将显示分片本地类和表的名称,并链接到InterSystems类参考文档中相应的条目。 如果该表是一个碎片本地表,表信息将显示碎片主类和表的名称,并链接到InterSystems类参考文档中相应的条目。 只有选中“System”复选框时,才会显示“Shard-local”表。 该选项还为打开表时要加载的行数提供了一个可修改的值。 这将设置打开表中显示的最大行数。 可用范围从1到10,000; 默认值为100。 管理门户将一个超出可用范围的值修正为一个有效值:0修正为100; 一个小数四舍五入到下一个更大的整数; 大于10,000的数字更正为10,000。 - 字段:表中字段的列表,显示字段名,数据类型,列#,必需的,惟一的,排序,隐藏,MaxLen, MaxVal, MinVal,流,容器,xDBC类型,引用,版本列,选择性,离群值选择性,离群值和平均字段大小。 - 映射/索引:为表定义的索引列表,显示:索引名、SQL映射名、列、类型、块计数、映射继承和全局。 索引名称是索引属性名称,然后遵循属性命名约定;从SQL索引名称生成时,将删除SQL索引名称中的标点符号(例如下划线)。 SQL映射名称是索引的SQL名称。生成的SQL映射名称与约束名称相同,并遵循相同的命名约定(下面描述)。列指定为索引指定的字段或逗号分隔的字段列表;它可以指定index collation类型和full schinea.table.field参考,如下例所示:`$$sqlupper({sample.people.name})`。类型可以是以下之一:位图范围,数据/主,索引(标准索引),位图或`bitslice`索引以及唯一的约束。块计数包含计数和该计数的确定:由Class Author(定义)明确地设置,由可调组织(测量)计算,或由类编译器(估计)估计。如果映射继承?是的,map是从超类继承的。全局是包含索引数据的下标全局的名称。索引全局的命名约定在索引全局名称中描述。您可以向ZWRITE提供此全局名称以显示索引数据。 此选项还为每个索引提供重建索引的链接。 - 触发:为表显示的触发器列表显示:触发名称,时间事件,订单,代码。 - 约束:表格的字段列表,显示:约束名称,约束类型和约束数据(括号中列出的字段名称)。约束包括主键,外键和唯一约束。主键是定义,唯一;它仅列出一次。此选项列出约束名称的约束;使用显示组件字段的逗号分隔列表的约束数据列出了一次涉及多个字段的约束。约束类型可以是唯一的主键,隐式主键,外键或隐式外键。 还可以通过调用`Information_schema.constraint_column_usage`来列出约束。此列表按字段名称约束。以下示例返回字段的名称和所有唯一,主键,外键和`Check Constraints`的约束的名称: ```sql SELECT Column_Name,Constraint_Name FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_SCHEMA='Sample' AND TABLE_NAME='Person' ``` ![image](/sites/default/files/inline/images/1_28.png) 如果该表定义为`%PublicraWID`,并且没有定义显式主键,则`RowID`字段列出了具有约束名称`RowidField_As_PKey`的`Contriced`主键的约束类型。 对于显式约束,约束名称是如下生成的: - 字段定义中指定的约束:例如,`fullname varchar(48)`唯一或`fullname varchar(48)`主键。字段的约束名称值是具有语法`tableName_ctype#`的生成值,其中ctype是唯一的,`pkey`或`fkey`,`#`是在表定义中指定的顺序分配给未命名约束的顺序整数。例如,如果`FullName`具有`MyTest`表中的第二个未命名的唯一约束(不包括ID字段),则`FullName`的生成约束名称将是`mytest_unique2`;如果`fullname`是`MyTest`表中指定的主键和第3个未命名约束(不包括ID字段),则`FullName`的生成约束名称将是`MyTest_pKey3`。 - 约束关键字命名约束子句:例如,约束`UFULLNAME`唯一(名字,`LastName`)或约束`Pkname`主键(`FullName`)),约束名称是指定的唯一约束名称。例如,`MyTest`表中的名字和`LastName`每个都将每个约束名称`UfullName`; `fullname`将具有约束名称`pkname`。 - 未命名约束子句:例如,唯一(名字,姓氏)或主键(`FullName`)。约束名称值是具有语法`tableNamectype#`的生成值,其中`ctype`是唯一的,`pkey`或`fkey`,`##`是在表定义中指定的顺序分配给未命名约束的顺序整数。例如,如果`FirstName`和`LastName`具有`MyTest`表中的第2个未命名的唯一约束(不包括ID字段),则`FirstName`和`LastName`的生成约束名称将是`MyTestunique2`;如果`FullName`是`MyTest`表中指定的主要键和第3个未命名的约束(不包括ID字段),则`FullName`的生成约束名称将是`MyTestPKEY3`。 (注意混合大写/小写,没有下划线。) 如果一个字段涉及多个唯一约束,则为每个约束名称单独列出。 - 缓存查询:表的缓存查询列表显示:例程名称,查询文本,创建时间,源,查询类型。 - 表的SQL语句:为此表生成的SQL语句列表。与命名空间的SQL语句相同的信息。 ## 目录的视图详细信息 Management Portal SQL接口还提供视图,过程和缓存查询的目录详细信息: 为每个视图提供以下目录详细信息选项: - 查看信息:所有者名称,最后编译的时间戳。使用“编辑视图”链接并保存更改时,此时间戳更新。 定义为只读,视图是可更新的布尔值:如果仅读取的视图定义,则它们分别设置为1和0。否则,如果查看视图是从单个表定义的,它们被设置为0和1;如果视图由已加入的表定义,则它们设置为0和0。可以使用编辑视图链接更改此选项。 类名是唯一的包。通过删除标点字符,如标识符和类实体名称中所述,从视图名称派生的名称。 如果查看定义包含“使用”选项“子句,则仅列出选项。它可以是本地的或级联。您可以使用编辑视图链接更改此选项。 类类型是视图。它提供了编辑视图链接以编辑视图定义。 查看文本是用于定义视图的`SELECT`语句。可以使用编辑视图链接更改视图定义。 字段列表包括字段名称,数据类型,maxlen参数,maxval参数,minval参数,blob(`%stream.globalcharacter`或`%stream.globalbinary`字段),长度,精度和比例。 - 查看的SQL语句:为此视图生成的SQL语句列表。与命名空间的SQL语句相同的信息。 ## 存储过程的目录详细信息 为每个过程提供以下目录详细信息: - 存储过程信息: 类名是一个唯一的包。通过将类型标识符( `‘func’, ‘meth’, ‘proc’, or ‘query’`)预定到类名(例如,SQL函数`MyProc`变为`FuncMyProc`)并删除标点符号字符,如标识符和类实体名称中所述。类文档是Intersystems类参考中相应条目的链接。过程类型(例如,函数)。方法或查询名称生成的类方法或类查询的名称;此名称在标识符和类实体名称中描述。运行过程链接提供交互方式的选项。 - 存储过程SQL语句:为此存储过程生成的SQL语句列表。与命名空间的SQL语句相同的信息。 ## 缓存查询的目录详细信息 缓存查询提供查询的全文,一个选项来显示查询执行计划,以及交互式执行缓存查询的选项。 # 向导 - 数据导入向导 - 运行向导将数据从文本文件导入Intersystems Iris类。 - 数据导出向导 - 运行向导将数据从Intersystems Iris类导出到文本文件中。 - 数据迁移向导 - 运行向导以从外部源迁移数据,并创建一个Intersystems Iris类定义来存储它。 - 链接表向导 - 运行向导,以链接到外部源中的表或视图,就像它是本机Intersystems Iris数据一样。 - 链接过程向导 - 运行向导,以链接到外部源中的过程。 # 操作 - 创建视图 - 显示一个页面以创建视图。使用此选项的说明提供了本书的“定义和使用视图”章节。 - 打印目录 - 允许打印有关表定义的完整信息。单击打印目录显示打印预览。通过单击此打印预览上的指数,触发器和/或约束,可以从目录打印输出中包含或排除此信息。 - Purege缓存查询 - 提供三种用于清除缓存查询的选项:清除当前命名空间的所有缓存查询,清除指定表的所有缓存查询,或者仅清除所选缓存的查询。 - 调谐表信息 - 对选定的表运行调谐表工具。这计算了每个表列对当前数据的选择性。选择性值1表示定义为唯一(因此具有所有唯一数据值)的列。选择性值为`1.0000%`表示未定义所有当前数据值是唯一值的唯一列。 `1.0000%`的百分比值更大,指示当前数据中该列的重复值的相对数量。通过使用这些选择性值,可以确定要定义的索引以及如何使用这些索引来优化性能。 - 调整架构中的所有表 - 运行调谐表工具,针对所属于当前命名空间中指定架构的所有表。 - 重建表索引 - 重建指定表的所有索引。 - 删除此项目 - 删除(删除)指定的表定义,查看定义,过程或缓存查询。必须具有适当的权限来执行此操作。除非表类定义包括[`DDLOWALLED`],否则否则不能在通过定义持久性类创建的表上使用删除。否则,操作失败了,使用`SQLCode -300`错误,其中包含类`“Schema.TableName”`的`%MSG DDL`。如果相应的持久性类具有子类(派生类),则不能在表格上使用删除;使用`%msg`类`'schema.tableName'`具有派生类`SQLCode -300`错误失败,因此无法通过DDL删除。 如果一个类被定义为链接表,则下降操作也会将链接表放在本地系统上,即使链接的表类未被定义为ddlowed。下降不会删除实际表此链接引用服务器上的引用。 - 导出所有语句 - 将所有SQL语句导出在当前命名空间中。 SQL语句以XML格式导出。可以选择导出到文件,或导出到浏览器显示页面。 - 导入语句 - 将SQL语句从XML文件导入当前命名空间。 # 打开表 如果在管理门户SQL接口的左侧选择表或视图,则会显示该表或视图的目录详细信息。页面顶部的打开表链接也变为活动状态。打开表显示表中的实际数据(或通过视图访问)。数据以显示格式显示。 默认情况下,将显示前100行数据;通过在“目录详细信息”选项卡信息中将表打开时,通过设置要加载的行数来修改此默认值。如果表格中的行数多于此行到加载值,则在数据显示的底部显示越多的数据...指示器。如果表格中的行较少,则要加载值的行数,则在数据显示的底部显示完整的指示符。 一列数据类型`%Stream.globalcharacter`将实际数据(最多100个字符)显示为字符串。超出前100个字符的附加数据由省略号(`...`)表示。 一列数据类型`%Stream.Globalbinary`显示为。 # 工具 System Explorer,SQL,Tools下拉列表提供对以下工具的访问。这些是系统资源管理器,工具,SQL性能工具的相同工具: - SQL运行时统计信息:用户界面生成指定查询的SQL运行时统计信息。 - 索引分析仪:用于收集指定架构的各种类型索引分析的用户界面。 - 替代表演计划:用户界面生成指定查询的备用显示计划。 - 生成报告以将SQL查询性能报告提交给Intersystems WRC(全球响应中心客户支持)。要使用此报告工具,必须先从WRC获取WRC跟踪号码。 - 导入报告以通过文件名导入现有WRC报告。仅用于Intersystems使用。 ![image](/sites/default/files/inline/images/2_17.png)
文章
Michael Lei · 七月 25, 2024

配置数据库的多卷存储

ISC 开发者们,我向你们致敬 👑。 多卷数据库 下面有关多卷数据的解释直接从文档搬过来的: 在InterSystems IRIS的默认配置中,数据库会使用单个 IRIS.DAT 文件保存数据。 你也可以将数据库配置为在当其达到指定大小阈值时自动保存到另外的文件(IRIS–0001.VOL、IRIS–0002.VOL 等等)中。 这些文件可能位于与 IRIS.DAT 相同的目录中和/或一组其他的目录中。 我这里想做的是设置一个较小阈值,并检查在备用目录上保存的多个扩展的数据卷。毫无疑问,这对镜像、性能 以及管理的影响是巨大的。简单来说,,前瞻性的解决方案考虑是否可以注入一个“回调”机制,并在溢出扩展之前即时地配置一个新的云存储卷。。 环境我在 2024.1 (Build 263U) 上​​有一个正在运行的IRIS实例,我的 $ISC_DATA_DIRECTORY 设置为一个 50Gi 的OpenEBS PVC,这是大概一个月前配置的。我向命名空间添加了一个额外的 OpenEBS PVC: #kind: PersistentVolumeClaim #apiVersion: v1 #metadata: # name: jiva-iris-volume-claim #spec: # storageClassName: openebs-jiva-csi-default # accessModes: # - ReadWriteOnce # resources: # requests: # storage: 50Gi #-- kind: PersistentVolumeClaim apiVersion: v1 metadata: name: jiva-iris-volume-claim-mv spec: storageClassName: openebs-jiva-csi-default accessModes: - ReadWriteOnce resources: requests: storage: 50Gi 应用 sween@run1:~$ kubectl apply -f deezwatts-volume.yaml -n rivian persistentvolumeclaim/jiva-iris-volume-claim-mv created 随后,通过初始化容器应用这些设置。 <snips> volumes: - name: task-pv-storage persistentVolumeClaim: claimName: jiva-iris-volume-claim - name: task-pv-storage-mvd persistentVolumeClaim: claimName: jiva-iris-volume-claim-mv <snips> volumeMounts: - name: task-pv-storage mountPath: /data - name: task-pv-storage-mvd mountPath: /data-mvd 现在,我们将另一个磁盘卷设置为 `/data-mvd` 的多卷扩展存储 设置 以下是使用 System Management进行设置 首先在IRIS的实例中创建数据库,并设置创建数据库的一些基本属性。 我们创建了数据库“mvd”,以及主数据库文件保存路径,在点击Next后,向导页面会有一些新的设置内容:New Volume Threshold 设置触发扩展存储的数据库大小,在设置后请注意配置参数下方的提示,这个提示非常重要。否则,你又会看到相关的警告。 输入值为零则禁用新卷的自动创建。 如果不为零,当 IRIS.DAT 大小达到到此阈值时,将创建名为 IRIS-0001.VOL 的新数据库文件。 当新数据库文件再次达到阈值时,将创建 IRIS-0002.VOL文件,依此类推。 对于非零值,建议至少设置为 1 TB,以避免文件数量过多。 每个数据库被限制为最多扩展使用200 个数据库文件。 第二步,挂载新的数据库,在挂载之前无法对其进行其他配置。 现在,我们可以在数据库列表中看到 Volumes 的选项 点进Volumes后,我们可以为扩展使用多个数据库文件的数据库设置备用保存位置。扩展 由于我在这个例子中将数据库阈值设置的相当小,它会生成多个数据库文件。 为了将IRIS.DAT 中的空间耗尽,我新建了一个命名空间使用该数据库: 在该命名空间中,我调用 ZPM,从 openexchange 安装了点东西,并运行。 irisowner@iris-deezwatts-deployment-7b9bfcff8f-dssln:~$ irissession IRIS Node: iris-deezwatts-deployment-7b9bfcff8f-dssln, Instance: IRIS USER>zn "MVD" MVD>zn "%SYS" d ##class(Security.SSLConfigs).Create("z") s r=##class(%Net.HttpRequest).%New(),r.Server="pm.community.intersystems.com",r.SSLConfiguration="z" d r.Get("/packages/zpm/latest/installer"),$system.OBJ.LoadStream(r.HttpResponse.Data,"c") Load started on 06/04/2024 13:43:08 Loading file /data/IRIS/mgr/Temp/z9mu1CvnPnaGbA.xml as xml Imported class: %ZPM.Installer Compiling class %ZPM.Installer Compiling routine %ZPM.Installer.1 Load finished successfully. %SYS>zpm ============================================================================= || Welcome to the Package Manager Shell (ZPM). version 0.7.1 || || Enter q/quit to exit the shell. Enter ?/help to view available commands || || Current registry https://pm.community.intersystems.com || ============================================================================= zpm:%SYS>install "zpm-registry" 在数据库 UI 中查看其属性: pod 中设置的文件夹下的内容: irisowner@iris-deezwatts-deployment-7b9bfcff8f-dssln:/data-mvd$ ls -ltr /data-mv* total 5140 drwxrwxrwx 2 irisowner irisowner 16384 Jun 4 11:56 lost+found -rw-rw---- 1 irisowner irisowner 20 Jun 4 12:11 iris.dbdir -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:25 IRIS-0022.VOL irisowner@iris-deezwatts-deployment-7b9bfcff8f-dssln:/data-mvd$ ls -ltr /data/IRIS/mgr/mvd total 164 drwxrwxrwx 2 irisowner irisowner 4096 Jun 4 11:15 stream -rw-rw---- 1 irisowner irisowner 63 Jun 4 12:01 iris.lck -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0001.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0002.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0003.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0004.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0005.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0006.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0007.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0008.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0009.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0010.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0012.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0015.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0018.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0016.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0019.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0020.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0017.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0014.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0013.VOL -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 12:11 IRIS-0011.VOL -rwxrwxrwx 1 irisowner irisowner 5242880 Jun 4 13:08 IRIS.DAT -rw-rw---- 1 irisowner irisowner 5242880 Jun 4 13:08 IRIS-0021.VOL 看来我有新玩具了!结论这篇帖子很短,可能发到讨论区更合适。不过我得回去工作了,这是我从 @jtrog 那看到新特性。期待未来在社区看到更多使用这项功能的分享和体验。我们峰会上见!
文章
Frank Ma · 三月 2, 2022

如何以自动化方式/编程方式创建一个镜像环境

各位好, 你曾建立过一个镜像环境吗?它是否有一个私有网络、虚拟IP地址和SSL配置? 在做了几次之后,我意识到这是一个漫长的过程,而且需要很多手动操作来生成证书和配置每个IRIS实例。 对于经常要做这件事的人来说,这是一个痛苦的过程。 例如,质量保证团队可能需要为每个新的应用程序版本创建一个新的镜像环境来测试。支持团队可能需要创建一个镜像环境来重现一个复杂的问题。 我们肯定需要工具来快速创建这些镜像环境。 在这篇文章中,我们将用如下环境创建一个镜像样例: - 仲裁机 - 主服务器 - 故障切换备份成员 - 读写报告异步成员 - 节点间日志转移的SSL配置 - 镜像环境中的私有网络 - 虚拟IP地址 - 镜像数据库 ![network-schema](https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/img/net-schema.png) 乍一看,它似乎有点复杂,看起来需要大量的代码,但不要担心。 在OpenExchange上有一些库,可以轻松地执行大多数操作。 本文的目的是提供一个例子,说明如何根据你的需要调整这个过程,但在安全问题上,它不是一个最佳实践指南。 现在,让我们来创建我们的样本。 ### 工具和库 - [PKI-script](https://openexchange.intersystems.com/package/PKI-Script): 公钥基础设施(PKI)是一个与IRIS集成的功能,它允许你生成一个自签名的证书并拥有你的授权服务器。在伟大的[Pete Greskoff的文章](https://community.intersystems.com/post/creating-ssl-enabled-mirror-intersystems-iris-using-public-key-infrastructure-pki)之后,PKI-script的目标是以编程方式执行所有操作,避免在管理门户中进行任何手动操作。 该库包括用于镜像的实用方法。 然而,如果你已经有了证书,你可以用它们来代替PKI-Script。 - [config-api](https://openexchange.intersystems.com/package/Config-API): 这个库将被用来配置IRIS。它从1.1.0版本开始支持镜像配置。我们将不对如何使用这个库进行详细描述。 [这里](https://community.intersystems.com/post/environment-setup-config-api) 已经有一组文章。简而言之,config-api将被用来创建IRIS模板配置文件(JSON格式)并轻松加载。 - [ZPM](https://openexchange.intersystems.com/package/ObjectScript-Package-Manager). - Docker. ### Github 页 你可以在[iris-mirroring-samples repository](https://github.com/lscalese/iris-mirroring-samples/)上找到所有必要的资源文件。 ### 准备你的系统 克隆现有的资源库: ```bash git clone https://github.com/lscalese/iris-mirroring-samples cd iris-mirroring-samples ``` 如果你喜欢从头开始创建一个样本,而不是克隆资源库,只需创建一个带有子目录的新目录: `backup` 和 `config-files`. 下载 [irissession.sh](https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/session.sh) : ``` mkdir -p iris-mirroring-samples/backup iris-mirroring-samples/config-files cd iris-mirroring-samples wget -O session.sh https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/session.sh ``` 为了避免以后出现 "权限拒绝 "的问题,我们需要创建`irisowner`组,`irisowner`用户,并将备份目录的组改为`irisowner`。 ```bash sudo useradd --uid 51773 --user-group irisowner sudo groupmod --gid 51773 irisowner sudo chgrp irisowner ./backup ``` 这个目录将被用作卷,在与其他节点建立第一个镜像成员后共享数据库备份。 ### 获得IRIS许可证 镜像在IRIS社区版中不可用。 如果你还没有有效的IRIS容器许可证,请用你的账户连接到 [全球响应中心(WRC)](https://wrc.intersystems.com)。 点击 "Actions" --> "SW distribtion", 然后点击 "Evaluations" 按钮并选择"Evaluation License"; 填写表单。 把你的许可证文件`iris.key`复制到这个目录。 ###登录Intersystems 容器注册中心(Containers Registry) 为了方便起见,我们使用Intersystems Containers Registry(ICR)来提取docker镜像。如果你不知道你的docker登录名/密码,只要用你的WRC账户连接到[SSO.UI.User.ApplicationTokens.cls](https://login.intersystems.com/login/SSO.UI.User.ApplicationTokens.cls) 就可以检索到你的ICR Token。 ```bash docker login -u="YourWRCLogin" -p="YourICRToken" containers.intersystems.com ``` ### 创建`myappdata`数据库和global 我们现在并没有真正创建`myappdata`数据库,而是准备一个配置,在docker构建时创建它。 为此,我们只是用JSON格式创建一个简单的文件。 config-api库将被用来在IRIS实例中加载它。 为此,我们只是用JSON格式创建一个简单的文件。 config-api库将被用来在IRIS实例中加载它。 创建文件[config-files/simple-config.json](https://github.com/lscalese/iris-mirroring-samples/blob/master/config-files/simple-config.json) ```json { "Defaults":{ "DBDATADIR" : "${MGRDIR}myappdata/", "DBDATANAME" : "MYAPPDATA" }, "SYS.Databases":{ "${DBDATADIR}" : {} }, "Databases":{ "${DBDATANAME}" : { "Directory" : "${DBDATADIR}" } }, "MapGlobals":{ "USER": [{ "Name" : "demo.*", "Database" : "${DBDATANAME}" }] }, "Security.Services" : { "%Service_Mirror" : { /* Enable the mirror service on this instance */ "Enabled" : true } } } ``` 这个配置文件允许你用默认设置创建一个新的数据库,并在USER命名空间做global映射`demo.*`。 关于[config-api](https://openexchange.intersystems.com/package/config-api) 配置文件功能的更多信息请参考相关的[文章](https://community.intersystems.com/post/environment-setup-config-api) 或[github页](https://community.intersystems.com/post/environment-setup-config-api) ### Docker 文件 Docker文件是基于现有的 [docker模板](https://github.com/intersystems-community/objectscript-docker-template)的,但我们需要做一些修改,以创建一个工作目录,安装使用虚拟IP的工具,安装ZPM等等。 我们的IRIS image对每个镜像成员都是一样的。镜像将根据其角色The mirroring will be set up on the container starting with the correct configuration depending on its role (第一成员,故障转移备份成员,或读写报告成员) 在容器上以正确的配置开始设置。 请看下面Dockerfile上的注释: ```Dockerfile ARG IMAGE=containers.intersystems.com/intersystems/iris:2021.1.0.215.0 # 不需要从WRC下载image。它将在构建时从ICR中提取。 FROM $IMAGE USER root # COPY session.sh / COPY iris.key /usr/irissys/mgr/iris.key # /opt/demo 将是我们的工作目录,用于存储我们的配置文件和其他安装文件。 # 安装iputils-arping 以得到一个arping 命令。这在配置虚拟IP时会用到。 # 下载最新ZPM 版本 (ZPM 仅包含在社区版中)。 RUN mkdir /opt/demo && \ chown ${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP} /opt/demo && \ chmod 666 /usr/irissys/mgr/iris.key && \ apt-get update && apt-get install iputils-arping && \ wget -O /opt/demo/zpm.xml https://pm.community.intersystems.com/packages/zpm/latest/installer USER ${ISC_PACKAGE_MGRUSER} WORKDIR /opt/demo # 设置默认镜像(Default Mirror)角色为主控(master) # 它将在运行时被重写在docker-compose文件上(第一个实例的主文件、备份和报告)。 ARG IRIS_MIRROR_ROLE=master # 将config-files目录的内容复制到/opt/demo。 # 目前我们只创建了一个simple-config来设置我们的数据库和global映射。 # 在本文的后面,我们将添加其他配置文件来设置镜像。 ADD config-files . SHELL [ "/session.sh" ] # 安装 ZPM # 用 ZPM 安装config-api 和 pki-script # 用config-api加载simple-config.json文件,用以: # - 创建"myappdata" 数据库, # - 为 "myappdata "数据库中global "demo.*"在命名空间 "USER "中添加一个global映射。 # 基本上,安装ObjectScript应用程序的入口在这里。 # 对于这个例子,我们将加载simple-config.json来创建一个简单的数据库和一个global映射。 RUN \ Do $SYSTEM.OBJ.Load("/opt/demo/zpm.xml", "ck") \ zpm "install config-api" \ zpm "install pki-script" \ Set sc = ##class(Api.Config.Services.Loader).Load("/opt/demo/simple-config.json") # 复制镜像初始化脚本。 COPY init_mirror.sh / # 执行一个启动后的脚本,配置镜像。 # init_mirror.sh的内容将在本文后面描述。 CMD ["-a", "/init_mirror.sh"] ``` ### 制作 IRIS image Docker文件已经准备好了;我们可以制作image了。: ``` docker build --no-cache --tag mirror-demo:latest . ``` 这个image将会运行 将被用于运行主节点、备份节点和报告节点。 ### 准备第一个镜像成员的配置文件 config-api库允许配置一个镜像,所以我们必须为第一个镜像成员创建一个专门的配置文件`config-files/mirror-master.json` 为方便起见,注释直接位于JSON中。你可以下载 [没有注释的mirror-master.json](https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/config-files/mirror-master.json). 所有的IP地址将通过`Docker-compose`文件分配给每个节点。 ```json { "Defaults":{ /* Section contains all variables */ "MirrorName" : "Demo", /* The name of our mirror */ "ArbiterNode" : "172.16.238.10|2188", /* IP Address and port of the arbiter node */ "VirtualAddress" : "172.16.238.100/24", /* Virtual IP Address */ "VirtualAddressInterface" : "eth0", /* Network interface used for the Virtual IP Address. */ "MirrorAddress" : "172.16.220.20", /* IP Address of this node in the private mirror network */ "AgentAddress" : "172.16.238.20", /* IP Address of this node (Agent is installed on the same machine) */ "SystemName" : "master", /* This instance name in the mirror */ "DBDir" : "${MGRDIR}myappdata/", /* Database directory to add to the Demo mirror */ "DBName" : "MYAPPDATA" /* Database name in the mirror */ }, "SYS.MirrorMaster" : { "${MirrorName}" : { "Config" : { "Name" : "${MirrorName}", "SystemName" : "${SystemName}", "UseSSL" : true, "ArbiterNode" : "${ArbiterNode}", "VirtualAddress" : "${VirtualAddress}", "VirtualAddressInterface" : "${VirtualAddressInterface}", "MirrorAddress": "${MirrorAddress}", "AgentAddress": "${AgentAddress}" }, "Databases" : [{ /* List of databases to add to the mirror */ "Directory" : "${DBDir}", "MirrorDBName" : "${DBName}" }], "SSLInfo" : { /* SSL Configuration, certificates are generated by PKI */ "CAFile" : "/usr/irissys/mgr/CAServer/CA_Server.cer", "CertificateFile" : "/usr/irissys/mgr/master_client.cer", "PrivateKeyFile" : "/usr/irissys/mgr/master_client.key", "PrivateKeyPassword" : "", "PrivateKeyType" : "2" } } } } ``` ### 准备故障转移备份成员的配置文件 创建一个配置文件,故障转移备份成员`config-files/mirror-backup.json`. 它看起来像第一个成员。 ```json { "Defaults":{ /* Section contains all variables */ "MirrorName" : "Demo", /* Mirror to join */ "AgentAddress" : "172.16.238.20", /* Agent IP Address of the first mirror member */ "SystemName" : "backup", /* This instance name in the mirror */ "PrimaryInstanceName" : "IRIS", /* IRIS Instance name of the first mirror member */ "VirtualAddressInterface" : "eth0", /* Network interface used for the Virtual IP Address. */ "DBDir" : "${MGRDIR}myappdata/", /* DB in mirror */ "MirrorAddress" : "172.16.220.30" /* IP Address of this node in the private mirror network */ }, "SYS.MirrorFailOver" : { "${MirrorName}" : { "Config": { "Name" : "${MirrorName}", "SystemName" : "${SystemName}", "InstanceName" : "${PrimaryInstanceName}", "AgentAddress" : "${AgentAddress}", "AgentPort" : "2188", "AsyncMember" : false, "AsyncMemberType" : "" }, "Databases" : [{ "Directory" : "${DBDir}" }], "LocalInfo" : { "VirtualAddressInterface" : "${VirtualAddressInterface}", "MirrorAddress": "${MirrorAddress}" }, "SSLInfo" : { "CAFile" : "/usr/irissys/mgr/CA_Server.cer", "CertificateFile" : "/usr/irissys/mgr/backup_client.cer", "PrivateKeyFile" : "/usr/irissys/mgr/backup_client.key", "PrivateKeyPassword" : "", "PrivateKeyType" : "2" } } } } ``` ### 准备读写报告异步成员的配置文件 它与故障转移配置文件非常相似。 不同之处在于`AsyncMember`、`AsyncMemberType`和`MirrorAddress`的值。 创建文件`./config-files/mirror-report.json`: ```json { "Defaults":{ "MirrorName" : "Demo", "AgentAddress" : "172.16.238.20", "SystemName" : "report", "PrimaryInstanceName" : "IRIS", "VirtualAddressInterface" : "eth0", "DBDir" : "${MGRDIR}myappdata/", "MirrorAddress" : "172.16.220.40" }, "SYS.MirrorFailOver" : { "${MirrorName}" : { "Config": { "Name" : "${MirrorName}", "SystemName" : "${SystemName}", "InstanceName" : "${PrimaryInstanceName}", "AgentAddress" : "${AgentAddress}", "AgentPort" : "2188", "AsyncMember" : true, "AsyncMemberType" : "rw" }, "Databases" : [{ "Directory" : "${DBDir}" }], "LocalInfo" : { "VirtualAddressInterface" : "${VirtualAddressInterface}", "MirrorAddress": "${MirrorAddress}" }, "SSLInfo" : { "CAFile" : "/usr/irissys/mgr/CA_Server.cer", "CertificateFile" : "/usr/irissys/mgr/report_client.cer", "PrivateKeyFile" : "/usr/irissys/mgr/report_client.key", "PrivateKeyPassword" : "", "PrivateKeyType" : "2" } } } } ``` ### 配置IRIS节点并生成证书 所有的配置文件都准备好了! 我们的[Dockerfile](https://github.com/lscalese/iris-mirroring-samples/blob/master/Dockerfile)的最后一行是`CMD ["-a", "/init_mirror.sh"]`。 现在我们要写这个脚本来生成证书,并用相关的配置文件来设置每个IRIS节点。 正如你在这个脚本中看到的那样,生成证书的代码非常简单: * `Do ##class(lscalese.pki.Utils).MirrorMaster(,"",,,,"backup,report")` -主控节点。 它配置了PKI服务器,PKI客户端,请求证书;等待验证,获得证书,自动接受验证节点的进一步请求,持续5分钟。自动接受的请求只限于`故障转移备份主机` 和 `报告异步成员主机`。 * `Do ##class(lscalese.pki.Utils).MirrorBackup("${PKISERVER}","")` -备份节点和报告节点。 配置 PKI 客户端,请求证书,等待验证,获得证书。 ```bash #!/bin/bash # 用来测试镜像的数据库 DATABASE=/usr/irissys/mgr/myappdata # 目录包含由主控节点备份的myappdata,以便在其他节点上恢复。 BACKUP_FOLDER=/opt/backup # 主控节点的json config-api格式的镜像配置文件。 MASTER_CONFIG=/opt/demo/mirror-master.json # json config-api格式的镜像配置文件,用于故障转移备份节点。 BACKUP_CONFIG=/opt/demo/mirror-backup.json # 报告异步节点的json config-api格式的镜像配置文件。 REPORT_CONFIG=/opt/demo/mirror-report.json # 镜像名字... MIRROR_NAME=DEMO # 镜像成员清单 MIRROR_MEMBERS=BACKUP,REPORT # PKI服务器主机:端口(PKI服务器安装在主实例上)。 PKISERVER=master:52773 # 在主控节点上操作。 # 在这个实例上配置公钥基础设施服务器,并生成证书以配置使用SSL的镜像。 # 请参见文章https://community.intersystems.com/post/creating-ssl-enabled-mirror-intersystems-iris-using-public-key-infrastructure-pki。 # 和相关的工具https://openexchange.intersystems.com/package/PKI-Script。 # 使用config-api加载镜像配置与/opt/demo/simple-config.json文件。 # 启动一个作业,自动接受其他名为 "备份 (backup)"和 "报告 (report)"的成员加入镜像(避免在门户管理中进行手动验证,最大延迟为600秒)。 master() { rm -rf $BACKUP_FOLDER/IRIS.DAT iris session $ISC_PACKAGE_INSTANCENAME -U %SYS