搜索​​​​

清除过滤器
文章
Jingwei Wang · 十月 9, 2024

在 IRIS 中使用 Foreign Table(外部表)

Foreign Table 的概念 Foreign Table 是 IRIS SQL 中一种特殊类型的表。它不代表 IRIS 本地全局管理的数据,而是投射出来的,由 “外部服务器 ”管理的外部数据。从 SQL 的角度来看,外部表与普通表一样,可以在查询中使用,没有任何特定限制。 在数据编织中可以帮助整合不同的来源的数据,以支持各种应用、分析并提供智能洞察力。 Foreign Table 和 Linked Table 的区别 Linked Table Lined Table 不能读取外部文件 对于 JDBC 和 ODBC 的数据源是分开的 Foreign Table 可以读取外部文件 不局限于同一类型的数据源 Foreign Table 的用例 当某个数据集时由外部应用程序在外部数据库中管理,而你的 IRIS 查询需要在该外部数据集中查找某些内容时,您可以使用某种 ETL 流程将数据加载到 IRIS 中,但如果外部数据经常更新,而你的查询需要访问当前的版本,那么这个过程可能会变得很棘手。可以通过 Foreign Table 将数据投射到 IRIS 并在查询时从外部源检索当前数据来解决这个问题。 当您要使用某些基于文件的大型数据集,您只需要偶尔查询一下,用来建立报告或训练模型。由于文件过大,加载文件可能会很麻烦,而且大型数据会占用昂贵的存储空间,而且一旦文件更新,就必须清除并重新加载。Foreign Table可以保持 SQL 查询对数据的可访问性,而不会增加 IRIS 的存储空间占用,也可以会保持最新数据。 Foreign Table 操作步骤 创建外部服务器 CREATE FOREIGN SERVER Sample.PostgresDB FOREIGN DATA WRAPPER JDBC CONNECTION 'postgresConnection' 创建 Foreign Tables CREATE FOREIGN TABLE Sample.AccountTeam ( TeamID BIGINT, Name VARCHAR(50), CountryCode VARCHAR(10) ) SERVER Sample.PostgresDB TABLE 'Sample.Teams' 查询 SELECT t.Name, COUNT(m.*) FROM Sample.AccountManager m JOIN Sample.AccountTeam t ON m.TeamID = t.TeamID WHERE t.CountryCode = 'UK' AND m.Salary > 100000 GROUP BY t.Name 删除 Foreign Tables DROP FOREIGN TABLE Example.MyForeignTable 删除外部服务器 DROP FOREIGN SERVER Example.PostgresDB CASCADE ( CASCADE 选项用来删除外部服务器和该外部服务器上定义的所有外部表 ) Foreign Table 演示 如果您想获取更详细的对于Foreign Table的演示,您可以参考 InterSystems 2023 峰会上面的演示示例,该示例使用docker,示例代码请参考Github ,安装后访问http://localhost:8888/
文章
Claire Zheng · 十月 10

线上研讨会 | 借助IDFS构建实时数据中枢:从多源整合到智能分析

InterSystems Data Fabric Studio(IDFS)提供了一种新方法,可在安全可控的环境中将正确的数据在正确的时间提供给正确的消费者。10月17日14:00,我们将举办题为“借助IDFS构建实时数据中枢:从多源整合到智能分析”的线上研讨会,欢迎👉点击此处报名参会! IDFS是一个完全由云计算管理的解决方案 ,旨在轻松实施和维护智能数据编织(smart data fabric),将不同的数据连接并转换为单一的统一可操作信息源。这一自助式解决方案使数据分析师、数据管理员和数据工程师能够访问和处理业务利益相关者所需的数据,而无需依赖开发人员。 本次分享将展示如何通过多源数据管道自动化构建(定义数据源连接、字段提取与清洗规则)、业务日历驱动的实时调度(周期自动运行数据任务)以实现多种异构数据系统的无缝融合。 您将在此次分享中了解到以下经典场景: 数据工程师视角:通过可视化 “配方” 工具(Recipes)定义数据转换逻辑,无需编码即可完成从数据到分析表的自动化加载; 分析师实践:基于整合后的标准化数据集,快速构建生产效率 BI 立方体,联动Power BI 生成动态看板; 合规管理:利用系统内置的 “快照调度” 功能,自动生成符合审计要求的历史数据存档,结合层级化权限控制(管理员 - 工程师 - 分析师分工),确保数据安全可追溯。 无论您是数据工程师、架构师,还是AI应用开发者,都能在本次研讨会中获取IDFS实战经验、技术架构设计思路与前沿趋势洞察,IDFS助力您轻松部署以数据为中心、连接数据和应用孤岛的AI应用! 我们期待与您的进一步互动。 1. 留言互动 在会议进行过程中,如果您有任何疑问,或者希望与我们进一步讨论,可以在屏幕上方点击“提问”按钮,提交您的问题,我们会在分享结束后整理问题,并通过邮件向您回复。 2. 有奖调研 参会期间,点击屏幕右上角“有奖调研”完成问卷,将有机会获得定制小礼品。 快来加入我们吧٩(๑>◡<๑)۶ 👉点击查看
文章
姚 鑫 · 四月 14, 2021

第二章 定义和构建索引(二)

# 第二章 定义和构建索引(一) # 定义索引 ### 使用带有索引的Unique、PrimaryKey和IdKey关键字 与典型的SQL一样,InterSystems IRIS支持惟一键和主键的概念。 InterSystems IRIS还能够定义`IdKey`,它是类实例(表中的行)的唯一记录ID。 这些特性是通过`Unique`、`PrimaryKey`和`IdKey`关键字实现的: - `Unique` -在索引的属性列表中列出的属性上定义一个唯一的约束。 也就是说,只有这个属性(字段)的唯一数据值可以被索引。 唯一性是根据属性的排序来确定的。 例如,**如果属性排序是精确的,则字母大小写不同的值是唯一的; 如果属性排序是`SQLUPPER`,则字母大小写不同的值不是唯一的。** 但是,请注意,对于未定义的属性,不会检查索引的惟一性。 根据SQL标准,未定义的属性总是被视为唯一的。 - `PrimaryKey` -在索引的属性列表中列出的属性上定义一个主键约束。 - `IdKey` -定义一个唯一的约束,并指定哪些属性用于定义实例(行)的唯一标识。 `IdKey`总是具有精确的排序规则,即使是数据类型为`string`时也是如此。 这些关键字的语法出现在下面的例子中: ```java Class MyApp.SampleTable Extends %Persistent [DdlAllowed] { Property Prop1 As %String; Property Prop2 As %String; Property Prop3 As %String; Index Prop1IDX on Prop1 [ Unique ]; Index Prop2IDX on Prop2 [ PrimaryKey ]; Index Prop3IDX on Prop3 [ IdKey ]; } ``` **注意:`IdKey`、`PrimaryKey`和`Unique`关键字只在标准索引下有效。 不能将它们与位图或位片索引一起使用。** 同时指定`IdKey`和`PrimaryKey`关键字也是有效的语法,例如: ``` Index IDPKIDX on Prop4 [ IdKey, PrimaryKey ]; ``` 这个语法指定`IDPKIDX`索引既是类(表)的`IdKey`,也是它的主键。 这些关键字的所有其他组合都是多余的。 对于使用这些关键字之一定义的任何索引,都有一个方法允许打开类的实例,其中与索引关联的属性有特定的值; ### 定义SQL搜索索引 可以在表类定义中定义SQL搜索索引,如下所示: ```java Class Sample.TextBooks Extends %Persistent [DdlAllowed] { Property BookName As %String; Property SampleText As %String(MAXLEN=5000); Index NameIDX On BookName [ IdKey ]; Index SQLSrchIDXB On (SampleText) As %iFind.Index.Basic; Index SQLSrchIDXS On (SampleText) As %iFind.Index.Semantic; Index SQLSrchIDXA On (SampleText) As %iFind.Index.Analytic; } ``` ### 用索引存储数据 **可以使用index Data关键字指定一个或多个数据值的副本存储在一个索引中:** ```java Class Sample.Person Extends %Persistent [DdlAllowed] { Property Name As %String; Property SSN As %String(MAXLEN=20); Index NameIDX On Name [Data = Name]; } ``` 在本例中,索引`NameIDX`的下标是各种`Name`值的排序(大写)值。名称的实际值的副本存储在索引中。当通过SQL更改`Sample.Person`表或通过对象更改对应的`Sample.Person`类或其实例时,将维护这些副本。 在经常执行选择性(从许多行中选择一些行)或有序搜索(从许多列中返回一些列)的情况下,在索引中维护数据副本会很有帮助。 例如,考虑以下针对`Sample.Person`表的查询: SQL引擎可以通过读取`NameIDX`而从不读取表的主数据来决定完全满足此请求。 ![image](/sites/default/files/inline/images/1_34.png) **注意:不能使用位图索引存储数据值。** ### 索引null 如果一个索引字段的数据为`NULL`(没有数据存在),相应的索引使用索引`NULL`标记来表示这个值。 **默认情况下,索引空标记值为`-1E14`。** 使用索引空标记可以使空值排序在所有非空值之前。 `%Library.BigInt`数据类型存储小于`-1E14`的小负数。默认情况下,`%BigInt`索引空标记值为`-1E14`,因此与现有`BigInt`索引兼容。如果索引的`%BigInt`数据值可能包括这些极小的负数,则可以使用`INDEXNULLMARKER`属性参数更改特定字段的索引`NULL`标记值,作为特性定义的一部分,如下例所示: ```java Property ExtremeNums As %Library.BigInt(INDEXNULLMARKER = "-1E19"); ``` 还可以在数据类型类定义中更改索引`NULL`标记的默认值。 **此参数属性在IRIS里有,Cache里没有。** ### 索引集合 为属性编制索引时,放在索引中的值是整个已整理属性值。对于集合,可以通过将(`Elements`)或(`Key`)附加到属性名称来定义与集合的元素和键值相对应的索引属性。(元素)和(键)允许指定从单个属性值生成多个值,并对每个子值进行索引。当属性是集合时,`Elements`令牌通过值引用集合的元素,`Key`令牌通过位置引用它们。当元素和键都出现在单个索引定义中时,索引键值包括键和关联的元素值。 例如,假设有一个基于`Sample.Person`类的`FavoriteColors`属性的索引。对此属性集合中的项进行索引的最简单形式是以下任一种: ```java INDEX fcIDX1 ON (FavoriteColors(ELEMENTS)); ``` 或 ```java INDEX fcIDX2 ON (FavoriteColors(KEYS)); ``` 其中,`FavoriteColor(Elements)`是指`FavoriteColors`属性的元素,因为它是一个集合。一般形式是`PropertyName`(元素)或`PropertyName`(键),其中该集合的内容是定义为某个数据类型的列表或数组的属性中包含的一组元素)。 若要索引文本属性,可以创建一个由`PropertyNameBuildValueArray()`方法生成的索引值数组(在下一节中介绍)。与集合本身一样,(Elements)和(Key)语法对索引值数组有效。 如果属性集合被投影为数组,则索引必须遵守以下限制才能被投影到集合表。索引必须包括(键)。索引不能引用集合本身和对象ID值以外的任何属性。如果投影索引还定义了要存储在索引中的数据,则存储的数据属性也必须限制为集合和ID。否则,不会投影索引。此限制适用于投影为数组的集合属性上的索引;不适用于投影为列表的集合上的索引。 与集合的元素或键值对应的索引还可以具有所有标准索引功能,例如将数据与索引一起存储、特定于索引的排序规则等。 InterSystems SQL可以通过指定`FOR SOME%ELEMENT`谓词来使用集合索引。 ### 使用(Elements)和(Key)索引数据类型属性 为了索引数据类型属性,还可以使用`BuildValueArray()`方法创建索引值数组。此方法将属性值解析为键和元素的数组;它通过生成从与其关联的属性的值派生的元素值集合来实现这一点。使用`BuildValueArray()`创建索引值数组时,其结构适合索引。 `BuildValueArray()`方法的名称为`PropertyNameBuildValueArray()`,其签名为: ```java ClassMethod propertynameBuildValueArray(value, ByRef valueArray As %Library.String) As %Status ``` - `BuildValueArray()`方法的名称以组合方法的典型方式派生于属性名。 - 第一个参数是属性值。 - 第二个参数是通过引用传递的数组。 这是一个包含键-元素对的数组,键下标的数组等于元素。 - 该方法返回一`%Status` 值。 这个例子: ```java /// DescriptiveWords是一个以逗号分隔的单词字符串 Property DescriptiveWords As %String; /// 基于描述词的索引 Index dwIDX On DescriptiveWords(ELEMENTS); /// 方法的作用是:演示如何在属性的子值上建立索引。 /// /// (如果DescriptiveWords被定义为一个集合,则不需要此方法。) ClassMethod DescriptiveWordsBuildValueArray( Words As %Library.String = "", ByRef wordArray As %Library.String) As %Status { If Words '= "" { For tPointer = 1:1:$Length(Words,",") { Set tWord = $Piece(Words,",",tPointer) If tWord '= "" { Set wordArray(tPointer) = tWord } } } Else { Set wordArray("TODO") = "Enter keywords for this person" } Quit $$$OK } ``` 在本例中,`dwIDX`索引基于`DescriptiveWords`属性。 `DescriptiveWordsBuildValueArray()`方法接受由`Words`参数指定的值,基于该值创建一个索引值数组,并将其存储在`wordArray`中。 InterSystems IRIS在内部使用`BuildValueArray()`实现; 不调用此方法。 注意:没有必要将任何元素/键值建立在属性值的基础上。 唯一的建议是,每次向该方法传递给定值时,都创建相同的元素和键数组。 为各种实例的描述性词所属性设置值和检查这些值的属性涉及活动(如以下): ```java SAMPLES>SET empsalesoref = ##class(MyApp.Salesperson).%OpenId(3) SAMPLES>SET empsalesoref.DescriptiveWords = "Creative" SAMPLES>WRITE empsalesoref.%Save() 1 SAMPLES>SET empsalesoref = ##class(MyApp.Salesperson).%OpenId(4) SAMPLES>SET empsalesoref.DescriptiveWords = "Logical,Tall" SAMPLES>WRITE empsalesoref.%Save() 1 ``` 这 `sample index`内容,例如: DescriptiveWords(ELEMENTS) | ID | Data ---|---|--- " CREATIVE" |3 |"" " ENTER KEYWORDS FOR THIS PERSON"| 1 |"" " ENTER KEYWORDS FOR THIS PERSON"| 2| "" " LOGICAL" |4| "" " TALL"| 4| "" 注意:此表显示抽象中的索引内容。磁盘上的实际存储形式可能会有所变化。 ### 将数组(元素)上的索引投影到子表 要在嵌入式对象中索引属性,需要在引用该嵌入式对象的持久化类中创建索引。 属性名必须指定表(`%Persistent`类)中的引用字段的名称和嵌入对象(`%SerialObject`)中的属性的名称,如下面的示例所示: ```java Class Sample.Person Extends (%Persistent) [ DdlAllowed ] { Property Name As %String(MAXLEN=50); Property Home As Sample.Address; Index StateInx On Home.State; } ``` 此处`Home`是`Sample.Person`中引用嵌入对象`Sample.Address`的属性,该对象包含`State`属性,如下例所示: ```java Class Sample.Address Extends (%SerialObject) { Property Street As %String; Property City As %String; Property State As %String; Property PostalCode As %String; } ``` **只有与持久类属性引用相关联的嵌入对象的实例中的数据值被索引。不能直接索引`%SerialObject`属性**。`%Library.SerialObject`(以及`%SerialObject`的所有未显式定义`SqlCategory`的子类)的`SqlCategory`为字符串。 还可以使用`SQL CREATE INDEX`语句在嵌入式对象属性上定义索引,如下例所示: ```sql CREATE INDEX StateIdx ON TABLE Sample.Person (Home_State) ``` ### 类中定义的索引注释 当在类定义中使用索引时,需要记住以下几点: - **索引定义仅从主(第一个)超类继承。** - **如果使用Studio添加(或删除)数据库中存储数据的类的索引定义,则必须使用“构建索引”中描述的过程之一来手动填充索引。** ## 使用DDL定义索引 如果你使用DDL语句来定义表,也可以使用以下DDL命令来创建和删除索引: - `CREATE INDEX` - `DROP INDEX` `DDL index`命令执行以下操作: 1. 它们更新在其上添加或删除索引的相应类和表定义。 重新编译修改后的类定义。 2. 它们根据需要在数据库中添加或删除索引数据:`CREATE index`命令使用当前存储在数据库中的数据填充索引。 类似地,`DROP INDEX`命令从数据库中删除索引数据(即实际索引)。
文章
姚 鑫 · 三月 30, 2021

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

# 第十四章 使用SQL Shell界面(一) # 执行SQL的其他方式 可以使用`$SYSTEM.SQL.Execute()` 方法从Terminal命令行执行一行SQL代码,而无需调用SQL Shell。以下示例显示如何在终端提示下使用此方法: ```sql DHC-APP>SET result=$SYSTEM.SQL.Execute("SELECT TOP 5 name,dob,ssn FROM Sample.Person") DHC-APP>DO result.%Display() Name DOB SSN yaoxin 54536 111-11-1117 xiaoli 111-11-1111 姚鑫 63189 111-11-1112 姚鑫 63189 111-11-1113 姚鑫 50066 111-11-1114 5 Rows(s) Affected ``` 如果SQL语句包含错误,则`Execute()`方法成功完成;否则,该方法无效。 `%Display()`方法返回错误信息,例如: ```java USER>DO result.%Display() [SQLCODE: :] [%msg: < Field 'GAME' not found in the applicable tables^ SELECT TOP ? game ,>] 0 Rows Affected USER> ``` `Execute()`方法还提供了可选的`SelectMode`、`Dialect`和`ObjectSelectMode`参数。 InterSystems IRIS支持许多其他编写和执行SQL代码的方法 这些包括: - 嵌入式SQL:嵌入ObjectScript代码中的SQL代码。 - 动态SQL:使用`%SQL`。 从ObjectScript代码中执行SQL语句的语句类方法。 - 管理门户SQL接口:使用Execute Query接口从InterSystems IRIS管理门户执行动态SQL。 # 调用SQL Shell 可以使用`$SYSTEM.SQL.Shell()`方法在终端提示符中调用SQL Shell,如下所示: ```java DO $SYSTEM.SQL.Shell() ``` 或者,可以使用%SQL作为一个实例调用SQL Shell。 Shell类,如下所示: ```java DO ##class(%SQL.Shell).%Go("IRIS") ``` 或 ```java SET sqlsh=##class(%SQL.Shell).%New() DO sqlsh.%Go("IRIS") ``` 无论如何调用,SQL Shell都会返回SQL Shell提示符,如下所示: ```java [SQL]termprompt>> ``` 其中[SQL]是指在SQL Shell中,termprompt是配置的终端提示符,`>>`是指SQL命令行。 默认情况下,SQL Shell提示符如下所示:`[SQL]nsp>>`,其中`“nsp”`是当前命名空间的名称。 ```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>> > 1>> 1>>SELECT TOP 5 name,dob,ssn FROM Sample.Person 2>> 2>>q DHC-APP>>q ``` 在这个提示符下,可以使用以下任一Shell模式: - 单行模式:在提示符下键入一行SQL代码。 结束SQL语句,按`“Enter”`。 默认情况下,这将准备并执行SQL代码(这称为立即执行模式)。 对于查询,结果集显示在终端屏幕上。 对于其他SQL语句,将在终端屏幕上显示SQLCODE和行数值。 - 多行模式:在提示符下按Enter。这使进入多行模式。可以键入多行SQL代码,每个新行提示均指示行号。 (空行不会增加行号。)要结束多行SQL语句,请键入GO并按Enter。默认情况下,这既准备并执行SQL代码。对于查询,结果集显示在终端屏幕上。对于其他SQL语句,SQLCODE和行计数值显示在终端屏幕上。 多行模式提供以下命令,可以在多行提示符下键入以下命令,然后按Enter:`L`或`LIST`列出到目前为止输入的所有SQL代码。 `C`或`CLEAR`删除到目前为止输入的所有SQL代码。 `C n`或`CLEAR n`(其中`n`是行号整数)以删除特定的SQL代码行。 `G`或`GO`准备和执行SQL代码,然后返回单行模式。 `Q`或`QUIT`删除到目前为止输入的所有SQL代码并返回单行模式。这些命令不区分大小写。发出命令不会增加下一个多行提示的行号。打`?`在多行提示符处列出了这些多行命令。 为了准备一条SQL语句,SQL Shell首先验证该语句,包括确认指定的表存在于当前名称空间中,并且指定的字段存在于表中。如果不是,它将显示适当的SQLCODE。 如果该语句有效,并且具有适当的特权,则SQL Shell会回显SQL语句,并为其分配一个序号。无论您是否更改名称空间和/或退出并重新进入SQL Shell,这些数字在终端会话期间都将按顺序分配。这些分配的语句编号允许重新调用以前的SQL语句,如下所述。 也可以使用`DO Shell^%apiSQL`.在终端提示下调用SQL Shell。 要列出所有可用的SQL Shell命令,请输入`?`。在SQL提示下。 要终止SQL Shell会话并返回到`Terminal`提示符,请在SQL提示符下输入`Q`或`QUIT`命令或`E`命令。 SQL Shell命令不区分大小写。 以下是使用默认参数设置的示例SQL Shell会话: ```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>>SELECT TOP 5 Name,Home_State FROM Sample.Person ORDER BY Home_State 1. SELECT TOP 5 Name,Home_State FROM Sample.Person ORDER BY Home_State Name Home_State xiaoli 姚鑫 姚鑫 姚鑫 姚鑫 5 Rows(s) Affected statement prepare time(s)/globals/lines/disk: 0.0523s/45502/270281/2ms execute time(s)/globals/lines/disk: 0.0004s/225/2915/0ms --------------------------------------------------------------------------- DHC-APP>>q ``` 以下是使用默认参数设置的多行SQL Shell会话: ```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>> > 1>>SELECT TOP 5 2>>Name,Home_State 3>>FROM Sample.Person 4>>ORDER BY Home_State 5>>GO 2. SELECT TOP 5 Name,Home_State FROM Sample.Person ORDER BY Home_State Name Home_State xiaoli 姚鑫 姚鑫 姚鑫 姚鑫 5 Rows(s) Affected statement prepare time(s)/globals/lines/disk: 0.0002s/18/1168/0ms execute time(s)/globals/lines/disk: 0.0003s/225/2886/0ms --------------------------------------------------------------------------- ``` ## GO命令 SQL Shell `GO`命令执行最新的SQL语句。在单行模式下,`GO`重新执行最近执行的SQL语句。在多行模式下,`GO`命令用于执行多行SQL语句并退出多行模式。单行模式下的后续`GO`将重新执行先前的多行SQL语句。 ## 输入参数 SQL Shell支持使用`“?”`输入参数的使用SQL语句中的字符。每次执行SQL语句时,系统都会提示指定这些输入参数的值。必须以与`“?”`相同的顺序指定这些值字符出现在SQL语句中:第一个提示为第一个`“?”`提供一个值,第二个提示为第二个`“?”`提供一个值,依此类推。 输入参数的数量没有限制。可以使用输入参数将值提供给`TOP`子句,`WHERE`子句,并将表达式提供给`SELECT`列表。不能使用输入参数将列名提供给`SELECT`列表。 可以将主机变量指定为输入参数值。在输入参数提示下,指定一个以冒号(:)开头的值。该值可以是公共变量,ObjectScript特殊变量,数字文字或表达式。然后,SQL Shell会提示“这是文字(Y / N)吗?”。在此提示下指定N(否)(或仅按Enter)意味着将输入值解析为主机变量。例如,`:myval`将被解析为局部变量myval的值; `:^ myval`将被解析为全局变量`^myval`的值; `:$HOROLOG`将被解析为`$HOROLOG`特殊变量的值; `:3`将被解析为数字3; `:10-3`将被解析为数字`7`。在此提示符下指定Y(是)表示将输入值(包括冒号)作为文字提供给输入参数。 ## 执行ObjectScript命令 在SQL Shell中,可能希望发出一个ObjectScript命令。例如,通过使用`SET $NAMESPACE`命令将InterSystems IRIS命名空间更改为包含要引用的SQL表或存储过程的命名空间。可以使用SQL Shell!命令或OBJ命令以发出由一个或多个ObjectScript命令组成的ObjectScript命令行。 (OBJ是OBJECTSCRIPT的缩写。)!,OBJ和OBJECTSCRIPT命令是同义词。以下示例显示了这些命令的用法: ```java %SYS>DO $SYSTEM.SQL.Shell() SQL Command Line Shell ---------------------------------------------- The command prefix is currently set to: . Enter q to quit, ? for help. [SQL]%SYS>>! SET oldns=$NAMESPACE SET $NAMESPACE="USER" WRITE "changed the namespace" changed the namespace [SQL]USER>>OBJ SET $NAMESPACE=oldns WRITE "reverted to old namespace" reverted to old namespace [SQL]%SYS>> ``` OBJ命令之后的其余命令行被视为ObjectScript代码。 !之间不需要空格。和ObjectScript命令行。可以在SQL Shell单行模式或SQL Shell多行模式下指定OBJ命令。以下示例在USER名称空间中定义的表上执行SELECT查询: ```java %SYS>DO $SYSTEM.SQL.Shell() SQL Command Line Shell ---------------------------------------------- The command prefix is currently set to: . Enter q to quit, ? for help. [SQL]%SYS>> > 1>>OBJ SET $NAMESPACE="USER" 1>>SELECT TOP 5 Name,Home_State 2>>FROM Sample.Person 3>>GO /* SQL query results */ [SQL]USER>> ``` 请注意,OBJ语句不会增加SQL行数。 在SQL Shell多行模式下,在返回行时将执行OBJ命令,但是直到指定`GO`才发出SQL语句。因此,以下示例在功能上与先前的示例相同: ```java %SYS>DO $SYSTEM.SQL.Shell() SQL Command Line Shell ---------------------------------------------- The command prefix is currently set to: . Enter q to quit, ? for help. [SQL]%SYS>> > 1>>SELECT TOP 5 Name,Home_State 2>>FROM Sample.Person 3>>OBJ SET $NAMESPACE="USER" WRITE "changed namespace" changed namespace 3>>GO /* SQL query results */ [SQL]USER>> ``` 以下示例使用OBJ命令定义主机变量: ```java USER>DO $SYSTEM.SQL.Shell() SQL Command Line Shell ---------------------------------------------- The command prefix is currently set to: . Enter q to quit, ? for help. [SQL]USER>> > 1>>SELECT TOP :n Name,Home_State 2>>FROM Sample.Person 3>>OBJ SET n=5 3>>GO ``` ## 浏览命名空间 SQL Shell支持BROWSE命令,该命令显示在当前名称空间中定义或可从其访问的架构,表和视图。该显示包括多个级别的提示。要返回上一个提示级别,请在提示时按`Return`键。名称区分大小写。 1. 在SQL Shell提示符下键入BROWSE,以列出当前名称空间中的架构。 2. 在“架构:”提示下,按名称或编号选择一个架构。这将列出架构中的表和视图。 3. 在“表/视图:”提示下,按名称或编号选择一个表(T)或视图(V)。这将显示表信息,然后显示选项列表。 4. 在“选项:”提示下,按编号选择一个选项。可以使用此选项列出为表定义的字段或映射。 指定选项1(按名称表示的字段)或选项2(按数字表示的字段)以显示“Field:”提示。指定选项3(地图)以显示“Map:”提示。 5. 在`“Field:”`提示下,按数字或名称选择一个字段,或指定*以列出所有字段。这列出了详细的字段信息。 在`“Map:”`提示下,按数字或名称选择地图,或指定`*`列出所有Map。这列出了详细的Map信息。 ## CALL 命令 可以使用SQL Shell发出SQL `CALL`语句来调用SQL存储过程,如以下示例所示: ```java DHC-APP>>CALL Sample.PersonSets('G','NY') 3. CALL Sample.PersonSets('G','NY') Dumping result #1 Name DOB Spouse Gallant,Thelma Q. 45767 94 Gibbs,Mark S. 37331 13 Goldman,Will H. 59069 10 Gomez,Mo Q. 55626 55 Gore,Alfred M. 42991 13 Gore,Fred X. 32391 6 Grabscheid,Jocelyn B. 59676 79 7 Rows(s) Affected Dumping result #2 Name Age Home_City Home_State Chadbourne,Danielle G. 34 Pueblo NY Eastman,Clint G. 4 Miami NY Pape,Linda M. 71 Vail NY Peterson,Janice N. 49 Islip NY Schaefer,Jocelyn V. 93 Zanesville NY 5 Rows(s) Affected statement prepare time(s)/globals/lines/disk: 0.0043s/2153/15795/0ms execute time(s)/globals/lines/disk: 0.0015s/315/7829/0ms --------------------------------------------------------------------------- ``` 如果指定的存储过程在当前名称空间中不存在,则SQL Shell会发出`SQLCODE -428`错误。 如果指定的输入参数多于存储过程中定义的参数,则SQL Shell会发出`SQLCODE -370`错误。可以使用文字(`“字符串”`),主机变量(`:var`)和输入参数(`?`)的任意组合为存储过程指定参数值。 可以在`CALL`语句中使用主机变量,如以下示例所示: ```java [SQL]USER>>OBJ SET a="G",b="NY" [SQL]USER>>CALL Sample.PersonSets(:a,:b) ``` - 可以在`CALL`语句中使用输入参数(`“?”`字符),如以下示例所示: ```java [SQL]USER>>CALL Sample.PersonSets(?,?) ``` 当执行`CALL`语句时,SQL Shell会提示为每个输入参数提供一个值。 ## 执行SQL脚本文件 SQL Shell `RUN`命令执行一个SQL脚本文件。脚本文件的类型由`DIALECT`设置确定。 `DIALECT`的默认值为IRIS(InterSystems SQL)。
文章
Qiao Peng · 三月 17, 2024

生成式大语言模型和检索增强生成

近来生成式大语言模型掀起了革命性的AI浪潮。生成式大语言模型是什么原理?我们怎么在业务中利用它? 一. 大语言模型的工作原理 生成式大语言模型是生成式人工智能底层的机器学习模型,是一种用于自然语言处理的深度学习模型。 人工智能、机器学习与大语言模型的关系如下图: 1.1 为什么我们称之为大语言模型? 大语言模型的“大”体现在多个方面: 首先,模型尺寸巨大,尤其是它的参数数量。例如GPT3有1750亿的参数; 其次,大语言模型是在巨大的算力基础上,基于海量语料进行训练的。例如Meta的Llama 2 的训练数据达到了两万亿个词(token); 再次,大语言模型是为解决通用问题,而非特定问题构建的。 1.2 大语言模型是怎么训练的? 大语言模型是事先训练好的模型。 训练时,大语言模型基于各种语料 - 人类知识库(例如Wikipedia)、公共数据集、网络爬虫数据,让模型进行“填空”练习,并经过人工编辑和“校对” 训练出来的,需要成千上万的GPU建立集群进行训练。根据Meta的信息,其Llama 2 的训练数据达到了两万亿个token,上下文长度为4096,对话上也是使用100万人类标记的数据微调。 运行时,训练产生的大语言模型可以在小的多的硬件上运行。 1.3 大语言模型的机器学习算法 冰冻三尺,非一日之寒;滴水穿石,非一日之功。生成式大语言模型能够落地经历了相当漫长的技术积累与进步。 大语言模型使用的机器学习算法是优化过的神经网络(Neural Network)。 神经网络发明于上世纪40-50年代,本质上是一个曲线拟合算法,通过拟合多个、多层的Softplus(曲线)、ReLU(Rectified Linear Unit 折线)、Sigmoid(对数线),实现对任意曲线的拟合。 “神经网络”名字听起来很高大上,但并不是脑科学的产物。因为发明时,觉得算法中每个节点像神经元、每个连线像神经触突,因此称为神经网络。 它很早就应用于自动控制领域。后来发展出多种神经网络算法,例如用于图像识别的卷积神经网络(CNN)、很早就用于语言学习的递归神经网络(RNN)… 在大语言模型成熟前,自然语言处理进化出过众多的技术,例如词袋、词汇矢量化、基于递归神经网络的模型、超长短期记忆网络(LSTM)… 但都在能力和算力上有众多缺陷,无法用于有实用价值的内容生成领域。 虽然它们不能实现实用化的内容生成,但为内容生成式大语言模型落地打下了基础,也是我们了解大语言模型前必须了解的预备知识。 1.3.1 分词(Tokenization) 词汇是语言模型分析的最小语义单位,所以第一步要把语句拆分成词汇(token)。分词并不简单,例如中文语句的分词就无法通过空格区分。所以用于大语言模型的分词算法也是基于海量语料训练出来的。 而基于大语言模型的内容生成,就是基于当前的所有token,预测下一个token,从而产生完整的内容。 1.3.2 词汇和语句的矢量化 机器学习算法基本只能处理数字,无法处理文本、声音、图像等非数字内容。所以要处理语言,需要对语句进行矢量化的表达,将其转换为数字。 拿我们常玩的一个游戏做解释:一个人在头脑里想象一个事物,让另一个人猜。另一个人可以问任何问题,但第一个人只能回答是和否。例如问:是动物吗?答:是;问:是哺乳动物吗?答:不是。问:有脚吗?答:是。 这个游戏的过程就是用不同维度来验证和归类一个事物,最终可以让这个事物在不同的维度上得以表达,即这个事物在一个高维度矢量空间上可以得到一个定位(矢量),同时相近的概念在矢量空间互相接近。 大语言模型通过大规模语料训练用神经网络将每个词汇在一个高维度空间矢量化,得到表达矢量的数组,将词汇矢量化到如下示意的矢量空间中: 这里的矢量化出来的是密集矢量,即每个维度上都不是0,且维度数固定,从而用更少的字节中存在更多的信息,因此在计算上的利用成本更低。相较于稀疏矢量的例子,例如书籍的归类:科学、言情、教育、音乐… ,词汇和语句的矢量结果密集度高的多,因此是密集矢量。 而语句矢量化在词汇矢量化的基础上,要将词汇在句子中的顺序信息加入,从而将“小明追老虎”和“老虎追小明”这两个词汇完全相同但语义完全不同的句子在矢量化输出上能够加以区分。 1.3.3 基于大语言的矢量化模型 将词汇和语句矢量化,是迈向我们如今看到的生成式大语言模型的第二步。 不同的语言矢量化模型生成的密集矢量维度数是不一样的,越高的维度数的密集矢量需要越大的计算资源和越大的内存消耗。下面是一些常见的矢量化语言模型和它们的维度数: 模型 维度数量 BERT (Bidirectional Encoder Representations from Transformers) 768或1024 GPT (Generative Pre-trained Transformer) 768或1600 Word2Vec 300 USE (Universal Sentence Encoder) 512 MiniLM 384 1.3.4 矢量相似度查询 词汇和句子矢量化后,怎么找到相似的词汇和句子? 对两个矢量进行相似度查询,就是计算两个矢量间的“距离”。有很多算法,如下图中所示的这些常见算法。 在大规模、高维度矢量数据库中查找近义词,如果采用与矢量记录逐一计算相似度的方法,将需要巨大的计算量,其效率并不能满足实用的性能需求。 而实际需求并不需要精确的相似度,因此出现了近似近邻算法(Approximate Nearest Neighbors - ANN)解决效率问题。ANN有多种算法,例如Annoy (Approximate Nearest Neighbors Oh Yeah)、 HNSW (Hierarchical Navigable Small World)。 下图是Annoy算法的示意图: 在矢量数据集中随机找2个矢量,计算出一个矢量平面到2个矢量的距离相同,从而将矢量数据集分割成2个空间;然后再在每个空间里重复上面的过程,直到分割后的空间里矢量数量与目标相似度矢量数量一致(例如我们希望得到返回矢量数量为10个以内的相似度结果集,那么如果空间内的矢量数小于等于10,就停止上述过程);从而我们得到一个决策树,今后可以用这个决策树进行矢量相似度查询,显然会快很多。 因为Annoy是基于最初的随机选择的2个矢量开始决策树构建的,如果这2个矢量本身就是高度相似的,那这2个矢量永远不会被一个矢量相似度查询要求同时命中,从而带来显著的误差。怎么办?可以随机多选几组初始矢量,从而形成多棵决策树的决策森林,提高ANN的精度。 可见ANN是大规模矢量检索查询的核心。 1.3.5 生成式大语言模型 递归神经网络(recurrent neural network - RNN)很早就应用到自然语言处理领域,之后出现了RNN改进模型LSTM (Long short-term memory),它们按顺序处理输入语句的词汇,并行能力不足,而且越高阶的神经网络需要的算力越高,达不到实用化的性能需求。 在2017年Google一个小团队(Transformer八子)发表了一篇论文 - Attention Is All You Need, 阐述了一类特殊的神经网络 – 基于注意力(Attention)机制的Transformer。它的注意力机制根据输入数据的长度执行固定步骤的计算,并且对输入数据的词汇(token)是并行计算的,它奠定了实用的生成式大语言模型的基础。这个团队的成员后大多离开了Google,并创立或加入了目前市场上几个主要生成式大语言模型。 在Transformer并行处理能力和越来越强大的GPU并行算力加持下,生成式大语言模型终是水到渠成,可以说是大力出奇迹! 当然Transformer模型具备多个特殊能力支撑内容生成能力。下面这张图解释了Transformer模型的4个核心特性:词汇矢量化(Word Embedding)、词汇在语句中位置的矢量化叠加(Positional Encoding)、自我注意力(Self-Attention)和残值连接(Residual Connections)。可见它其实构建在前面出现的技术基础之上。 借助这个新神经网络模型思路,众多大厂发布了自己的生成式大语言模型,如下面列出的这些著名的大语言模型。它们的宣传中常常强调其百亿级、甚至千亿级的参数: 模型 厂商 参数 GPT OpenAI (Microsoft) 1750亿 Bard/Gemini Google 18亿,32.5亿 PaLM2 Google 3400亿 Llama 2 Meta 70亿,130亿,700亿 Claude 2/3 Anthropic (Amazon) 未披露 Stable Beluga Stability AI 70亿,130亿,700亿 Coral Cohere 未披露 Falcon Technology Innovation Institute 13亿,75亿,400亿,1800亿 MPT Mosaic 70亿,300亿 往往参数规模越大,其生成的内容越精确和越富有创造力。那么这些参数指什么?无论是什么样的大语言模型,它们底层都是神经网络,这些参数主要就是指神经网络中的权重和偏差。 二. 大语言模型应用中的问题和检索增强生成 从机制上,生成式大语言模型并不神秘。虽然它展现出了强大的理解能力甚至“创造力”,但它有以下几个问题: 它的知识来自于训练语料,并不知道所有知识。例如GPT-4 截止训练数据的时间是2022年1月份,对于后来的世界一无所知,更不可能知道您的机构中的未开放数据。 它是基于通用数据训练的,对于特定领域往往训练不足。 它的内容生成机制是使用神经网络逐词预测出回答中的下一个词从而构成完整的语句。因此它本质上不会拒绝回答任何问题,虽然人类限制它回答诸如如何制作病毒类的问题。结合它的“无知”和“创造力”,对不知道的问题,它也能一本正经地胡说八道,这就是生成式大语言模型的“幻觉”。 生成式大语言模型的“幻觉”在目前的应用中非常常见。例如我问了Bing Copilot一个关于“什么是InterSystems IRIS互联互通套件?”的问题,它不懂但没有拒绝回答,而且回答地相当“幻觉”: 如果想在我们自己的业务中直接应用生成式大语言模型,让它提供患者教育,或者回复患者的预约查询、亦或回答患者关于他/她自己的用药注意事项?显然不靠谱。 是不是可以用我们自己的数据进行训练?一来很多大模型都不是开放的,无法自己训练;二来相信大家都没有训练大语言模型的昂贵算力。 怎么解决这个问题? 大语言模型其实有三次“训练”机会: 预先训练就是大语言模型厂商通过海量语料进行的训练,我们干不了; 调优训练需要基于开放的大语言模型,算力成本也不低; 所以我们可以通过“提示”,让生成式大语言模型给我们想要的答案。 我又试了一次让Bing Copilot回答“什么是InterSystems IRIS互联互通套件?”,不过这次,我给了它提示,让它先读读关于InterSystems IRIS互联互通套件介绍的网页。这次它回答得相当到位: 也就是通过合适的提问,把本地数据提示给生成式大语言模型,从而让它可以准确回答而不会产生幻觉。 检索增强生成基于问题先在本地数据检索,将相关结果提示给生成式大语言模型,从而获得靠谱的回答,这就是检索增强生成(Retrieval Augmented Generation – RAG)。 这里的本地数据检索,是基于大语言的矢量相似度检索。所以,需要借助矢量数据库,对本地的数据矢量化保存、并提供基于问题的矢量相似度查询,从而基于问题给出最匹配的本地数据。 这里是完整的检索增强生成流程示意图,分为2个过程: 1. 基于本地数据建立矢量知识库的过程 预先建立知识库, 将本地文档切分成文本段 使用矢量化语言模型对数据矢量化 将矢量保存到矢量知识库 2. 借助本地矢量知识库和外部大语言模型回答问题的过程 使用矢量化模型将问题矢量化 在矢量数据库中检索与问题相关的矢量记录 将匹配的数据(知识)作为上下文组织到完整的问题与提示中,向大语言模型提问。例如提示模版是:请仅使用以下上下文回答问题 从大语言模型得到回答 由此可见,检索增强生成至少需要以下3个技术组件: 矢量数据库 – 用于本地数据的矢量化保存和矢量化查询 矢量化语言模型 – 用于将本地数据和问题矢量化 内容生成语言模型 – 用于基于问题和上下文生成自然语言回答 矢量化语言模型、内容生成语言模型都有很多选择,根据需要可以选择能部署到本地的模型、也可以选择厂商提供的云服务。 而矢量数据库是保存本地知识数据的矢量化版本的,市面上常见的是一些nonSQL的专用数据库,也就是说需要将本地数据迁移到矢量数据库,并专门学习其数据操作的API。 可以预见,生成式大语言模型的能力将迅速进化,但本地的知识和数据并不会以如此快的速度发生变化。因此RAG将本地的知识和数据通过矢量化与生成式大语言模型集成,借助其不断提升的强大能力又无需被任何一个模型绑架,将是一个合理的解决方案。 三. InterSystems IRIS的内容生成架构 InterSystems IRIS是应用在众多行业的通用数据平台,并在2024版本中加入了对矢量存储和查询的支持,无需将IRIS中已经保存的本地知识数据迁移到别的矢量数据库中,从而消除数据迁移时间差、额外部署矢量数据库的运维成本,同时降低敏感数据泄露风险、确保遵循特定行业中对数据迁移监管的要求。而InterSystems IRIS作为一个具有互操作能力的数据平台,可以轻松集成大语言模型,并建立和管理检索增强生成的pipeline,降低RAG的技术实现复杂度。 3.1 IRIS的矢量存储和矢量查询 IRIS提供矢量数据类型,它被完全集成在IRIS多模型的架构中,尤其使用SQL就可以完整使用矢量存储和查询。 例如要创建含有矢量类型字段vec的表: CREATE TABLE t (txt VARCHAR(1000), vec VECTOR(INT, 200)); 向矢量字段vec中插入数据: INSERT INTO t VALUES (‘…’, TO_VECTOR(‘1,2,3,…’, INT)); 这里的矢量数据是需要通过调用矢量化模型产生的。 基于矢量相似度查询最接近的10条记录: SELECT TOP 10 * FROM FROM ( SELECT t.*, VECTOR_DOT_PRODUCT(vec, TO_VECTOR(…)) AS similarity FROM t ) ORDER BY similarity DESC; 3.2 IRIS的矢量索引 IRIS进一步提供了更易使用的矢量索引:无需创建矢量字段,直接在现有数据表上就可以创建声明式的矢量索引,并自动调用集成的矢量化模型,从而使用SQL就可以免代码方式进行开发。 创建矢量索引 – 通过索引对title、author和article这3个字段组合进行矢量化: CREATE INDEX Vec ON MyNews(Title, Author, Article) AS VectorIndex(MODEL=‘BERT’); 执行矢量查询 – 查询与条件最近似的3条记录: SELECT TOP 3 * FROM MyNews WHERE Category = ‘NYT’ ORDER BY MyTable_VecSim(%ID, ‘climate change’); 3.3 基于IRIS构建完整的RAG方案 基于最新发布的InterSystems IRIS 2024.1,和部署到本地的矢量化模型(all-MiniLM-L12-v2)、内容生成模型(llama2),我构建了一个RAG原型: 这里IRIS实例即是保存本地数据的数据平台,也是本地数据的矢量化数据库,从而避免了数据的跨平台迁移。而全SQL的数据操作能力,让构建在自己数据上的检索增强生成方案能快速落地。 现在就把生成式大语言模型集成到您自己的业务中吧!注:本文中的部分图片来自StatQuest、medium、wikipedia和weaviate。
文章
Michael Lei · 六月 23, 2021

最低限度的监控和警报解决方案

InterSystems 数据平台包括了用于系统监视和警报的实用程序及工具,但对于不熟悉构建于 InterSystems 数据平台(又名 Caché)的解决方案的系统管理员来说,他们需要知道从何处下手以及需要配置什么。 本指南以在线文档和开发者社区帖子为参考,介绍了实现最低限度的监视和警报解决方案的途径,以及如何启用和配置以下组件: 1. **Caché Monitor:**扫描控制台日志并发送电子邮件警报。 2. **System Monitor:**监视系统状态和资源,根据固定参数生成通知(警报和警告),同时跟踪整体系统运行状况。 3. **Health Monitor:**对主要的系统和用户定义指标进行采样,并将它们与用户可配置的参数和既定的标准值进行比较,在样本超过适用或学习的阈值时生成通知。 4. **History Monitor:**维护性能指标和系统使用情况指标的历史数据库。 5. **pButtons:**每天按计划收集操作系统指标和 Caché 指标。 请记住,本指南是最低配置,所包含的工具灵活且可扩展,因此可在需要时提供更多功能。 本指南跳过了文档,让您直接上手。 您需要更深入地研究文档才能充分利用监视工具,同时,请考虑将本指南作为启动和运行的速查表。 ---- # 1. Caché Monitor 控制台日志 `(install-directory/mgr/cconsole.log)` 必须被监视,可以通过第三方工具扫描日志文件,或者像我们这样使用附带的 Caché Monitor 实用工具将警报发送到一个电子邮件地址。 控制台日志是其他监视工具(包括 Caché System Monitor 和 Caché Health Monitor)用于编写警报和通知的中央存储库。 > 至少将 Caché Monitor 配置为向一个电子邮件发送警报。 使用 ^MONMGR 实用工具管理 Caché Monitor。 ## Caché Monitor 基本设置 Caché Monitor 扫描控制台日志,并根据可配置的消息严重级别生成通知。 通知通过电子邮件发送到您配置的收件人列表。 默认扫描周期为每 10 秒一次,但可以更改。 > 提示:将 Caché Monitor 警报严重级别配置为 1(警告、严重和致命条目)。 如果您发现收到的警报太多,可以降回到警报级别 2(严重和致命条目)。 当 60 秒内有一系列条目来自一个给定进程时,只会针对第一个条目生成通知,然后暂停一小时。 因此,当出现问题时,必须进行调查。 因为没有新消息并不表示事件已过去。 此规则的例外是 [Caché Monitor 错误和陷阱](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_monitor#GCM_monitor_errors)中列出的控制台日志条目,会针对其中所有条目生成通知。 [有关完整的配置详细信息,请参见 ^MONMGR 的在线文档。](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_monitor#GCM_monitor_errors) ## Caché Monitor 速查表 启用 Caché Monitor 不需要很多操作。 确保 Monitor 已启动,然后设置电子邮件选项。 %SYS>d ^MONMGR 1) Start/Stop/Update MONITOR 2) Manage MONITOR Options 3) Exit Option? **1** 1) Update MONITOR 2) Halt MONITOR 3) Start MONITOR 4) Reset Alerts 5) Exit Option? **3** Starting MONITOR... MONITOR started 1) Update MONITOR 2) Halt MONITOR 3) Start MONITOR 4) Reset Alerts 5) Exit Option? **** 设置警报严重级别。 1) Start/Stop/Update MONITOR 2) Manage MONITOR Options 3) Exit Option? **2** 1) Set Monitor Interval 2) Set Alert Level 3) Manage Email Options 4) Exit Option? **2** Alert on Severity (1=warning,2=severe,3=fatal)? 2 => **1** 设置电子邮件选项,您可能需要联系 IT 部门以获取电子邮件服务器的地址。 任何有效的电子邮件地址都应该可用。 1) Set Monitor Interval 2) Set Alert Level 3) Manage Email Options 4) Exit Option? **3** 1) Enable/Disable Email 2) Set Sender 3) Set Server 4) Manage Recipients 5) Set Authentication 6) Test Email 7) Exit Option? 确保在设置后测试电子邮件(选项 6)。 ## Caché Monitor 示例 Caché System Monitor 针对高 CPU 利用率生成了一个严重级别为 2 的条目,该条目已发送到 cconsole.log: 03/07/18-11:44:50:578 (4410) 2 [SYSTEM MONITOR] CPUusage Alert: CPUusage = 92, 95, 98 (Max value is 85). 同时也向 Caché Monitor 电子邮件收件人发送了一封电子邮件,其内容与 console.log 相同,主题行为: [CACHE SEVERE ERROR yourserver.name.com:instancename] [SYSTEM MONITOR] CPUusage Alert: CPUusage = 92, 95, 98 (Max value is 85). ## Caché Monitor 更多提示 在开发者社区的另一篇文章的评论中,Aric West 提示可以在发件人电子邮件中嵌入更多信息,例如,不要只设置一个有效的电子邮件,而是将发件人或收件人设置为:"某个名字" \ ---- # Caché System Monitor 工具 Caché System Monitor 是一系列监视工具的汇总,通过 `^%SYSMONMGR` 实用工具进行配置。 如简介中所述,为实现最低限度的监视解决方案,我们将配置: - System Monitor - Health Monitor - History Monitor 顺带说一下,是的,名称 _System Monitor_ 很令人烦恼地重载了其父名称 _Caché System Monitor_。 Caché System Monitor 和 Health Monitor 通知和警报会发送到控制台日志,使 Caché Monitor(在上一节中设置)可以在事件发生时生成电子邮件。 [有关 Caché System Monitor 的所有深入详细信息,请参见在线文档。](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_healthmon) ---- **警告说明:**您还将在 ^%SYSMONMGR 菜单中看到 **Application Monitor**。 本指南中不会配置 Application Monitor。 本指南中介绍的工具和实用程序对系统性能的影响可以忽略不计,然而 Application Monitor 有一些类是此规则的例外。 [有关详细信息,请参见 ^PERFMON 实用工具的文档](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_perfmon)。 如果您使用 Application Monitor,则必须先在非生产系统上测试,因为运行任何时长的 ^PERFMON 都可能对性能产生重大影响。 ---- # 2. System Monitor 根据文档:“System Monitor 对重要的系统状态和资源使用指标(例如 ECP 连接的状态和使用中的锁表的百分比)进行采样,并根据固定状态和阈值生成通知(警报、警告和‘状态正常’消息)”。 [文档中有 System Monitor 状态和资源指标](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_healthmon_sysmon_alerts)的列表。 例如:日志空间(日志目录中的可用空间): - 少于 250 MB = _警告_ - 少于 50 MB = _警报_ - 警告/警报后大于 250 MB = _正常_ ## System Monitor 基本设置 System Monitor 警报和警告会写入控制台日志,因此请确保 Caché Monitor 设置为发送电子邮件警报(上一节)。 使用 ^SYSMONMGR 实用工具管理 System Monitor。 默认情况下,当实例运行时,System Monitor 也一直运行;可以使用 ^%SYSMONMGR 将其停止,但是当实例下次启动时,它会自动再次启动。 默认情况下,System Monitor 具有以下可以更改的设置: - 每 30 秒获取一次传感器指标。 - 仅将警报、警告和消息写入 System Monitor 日志。 System Monitor 还维护单一整体系统运行状况状态,当运行 `ccontrol list` 等命令时可以查询或使用该状态: - 绿色(正常) - 黄色(警告) - 红色(警报) ### System Monitor 速查表 对于最低限度的监视解决方案,实际上无需任何操作,因为当实例运行时,它也一直运行。 ---- # 3. Health Monitor 根据文档:“Caché Health Monitor 通过在特定期间对一系列关键指标的值进行采样,并将这些值与配置的指标参数和这些期间的既定标准值进行比较,来监视正在运行的 Caché 实例;如果采样值过高,Health Monitor 将生成警报或警告。 例如,根据配置的 CPU 使用率最大值或者在星期一上午 9:00 至 11:30 期间采集的标准 CPU 使用率样本,如果 Health Monitor 在星期一上午 10:15 采样的 CPU 使用率过高,Health Monitor 将生成通知。” [Health Monitor 对 41 个系统传感器进行采样,文档中给出了列表和默认值。](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_healthmon#GCM_healthmon_overview_api_sensors) Health Monitor 警报(严重级别 2)和警告(严重级别 1)将写入控制台日志。 Health Monitor 生成: - 警报,如果在一个期间内传感器的三次连续读数大于传感器最大值。 - 警告,如果在一个期间内传感器的五次连续读数大于传感器警告值。 对于为最大值或警告值设置了条目的传感器,即使 Health Monitor 本身未启用,也会立即生成警报。 例如,为 CPU 配置的最大值为 85,警告值为 75,所以当连续 5 次的 CPU 利用率测量值超过 75% 时,会将以下通知发送到控制台日志: 1 [SYSTEM MONITOR] CPUusage Warning: CPUusage = 83 ( Warnvalue is 75). 其他传感器需要收集足够长时间的指标才能创建_图表_。 需要图表来评估指标的平均值,从而得出标准偏差 (sigma),以便在值超出正常范围时发出警报。 在一个_期间_内收集指标。 有 63 个标准期间,一个期间示例是星期一上午 9:00 至 11:30。 期间可以更改。 ## Health Monitor 基本设置 > Health Monitor 不会自动启动,需要使用 ^%SYSMONMGR 中的设置来启用它。 默认情况下,Health Monitor 在 Caché 启动后等待 10 分钟,以便系统达到正常运行状态,如果需要,可以更改此时间。 Caché Health Monitor 传感器对象带有默认值。 例如,如上文所述,_CPUPct_(系统 CPU 使用率百分比)的默认值为:基础值 50,最大值 90,警告值 80。 您可能更保守一些,并想要更改这些值,使用 ^%SYSMONMGR 可以更改这些值,例如,最大值 85,警告值 70。 现在,当 CPU 使用率达到 99% 时,我们会看到: 2 [SYSTEM MONITOR] CPUusage Alert: CPUusage = 99, 99, 99 (Max value is 85). ## Health Monitor 速查表 该速查表相当长,显示在总结后面。 ---- # 4. History Monitor [David Loveluck 在社区上有一个非常好的帖子。](https://community.intersystems.com/post/apm-using-cach%C3%A9-history-monitor)按照该帖中的说明启动 History Monitor,开始收集和评估指标。 ---- # 5. pButtons pButtons 实用工具可根据其创建的日志文件生成可读的 HTML 性能报告,其中包含操作系统和 Caché 指标。 pButtons 输出的性能指标可以被提取、绘图和评估。 例如,一天中 CPU 利用率或 Caché 数据库访问量的图表。 每天 24 小时运行 pButtons 收集是一种简单但至关重要的方法,用于收集指标以进行故障排除。 pButtons 对于趋势分析也非常有用。 以下社区文章详细介绍了 pButtons 以及如何将其计划为每天运行:[InterSystems 数据平台和性能 – 第 1 部分](https://community.intersystems.com/post/intersystems-data-platforms-and-performance-%E2%80%93-part-1) > 如文章中所述,30 秒收集间隔对于趋势分析和 24 小时报告是合适的。 还有说明用于确保您即使没有运行最新版的 Caché,也能拥有最新版的 pButtons:[InterSystems 数据平台和性能 – 如何更新 pButtons](https://community.intersystems.com/post/intersystems-data-platforms-and-performance-%E2%80%93-how-update-pbuttons)。 虽然 pButtons 主要是一个支持工具,但您可以通过快速绘制所收集的指标来获得宝贵的系统使用情况洞察:[Yape - 另一个 pButtons 提取程序(自动创建图表)](https://community.intersystems.com/post/yape-yet-another-pbuttons-extractor-and-automatically-create-charts) ---- # 总结 本帖仅触及了监控选项的皮毛,例如 Health Monitor 将以默认设置工作,但随着时间的推移,您将希望探索可自定义的选项来满足您的应用程序配置文件。 ## 接下来的方向? 如我们所见,我们将 System Monitor 和 Health Monitor 实用工具配置为将警报发送到作为中央报告位置的 cconsole.log。 我们使用了 Caché Monitor 将这些警报发送到电子邮件。 有一些第三方工具可以抓取日志并消耗非结构化的日志数据,您可能已经在组织中使用这些工具,而且也没有理由不使用它们。 如今,我看到许多客户在 VMware 上进行虚拟化。 如果您使用 vSphere,请考虑使用 Log Insight 来监视控制台日志。 在撰写本帖时(2018 年 3 月),对于您拥有的每个 vCenter Server 6.0 实例,您都有资格获取一个免费的 vRealize Log Insight for vCenter 25 OSI 许可证。 Log insight 是一个读取非结构化数据的工具,用于日志管理和分析 — 例如,您可以将其与 cconsole.log 配合使用 — 如果您对此感兴趣,请联系 VMware 以了解更多信息。 同时,我正计划在将来的帖子中展示如何将 Log Insight 与 cconsole.log 配合使用。 ---- 如果您在收集指标,您仍然需要研究它们,并了解他们的含义,我将继续发帖来展示如何解释所呈现的信息,特别是性能指标。 ---- # 应用程序性能监视 David Loveluck 在社区中有一系列关于应用程序性能监视的帖子,请在社区中搜索 APM,或者[从这里开始](https://community.intersystems.com/post/what-apm)。 ---- ## 附录:Health Monitor 速查表 本速查表展示了我们在第 3 节看到的 Health Monitor 的启动过程,并完成了传感器阈值的编辑。 首先让我们启动 Health Monitor。 %SYS>**d ^%SYSMONMGR** 1) Start/Stop System Monitor 2) Set System Monitor Options 3) Configure System Monitor Classes 4) View System Monitor State 5) Manage Application Monitor 6) Manage Health Monitor 7) View System Data 8) Exit Option? **6** 1) Enable/Disable Health Monitor 2) View Alerts Records 3) Configure Health Monitor Classes 4) Set Health Monitor Options 5) Exit Option? **1** Enable Health Monitor? No => **yes** Health Monitor is Enabled. Stop and restart System Monitor to run Health Monitor 如我们从消息中所见,导航回到第一个菜单或再次启动 ^%SYSMONMGR 来停止 System Monitor 并再次启动它,以完成这一过程。 %SYS>**d ^%SYSMONMGR** 1) Start/Stop System Monitor 2) Set System Monitor Options etc... 我们将在这里继续,以编辑 CPUPct 阈值为例。 %SYS>**d ^%SYSMONMGR** 1) Start/Stop System Monitor 2) Set System Monitor Options 3) Configure System Monitor Classes 4) View System Monitor State 5) Manage Application Monitor 6) Manage Health Monitor 7) View System Data 8) Exit Option? **6** 1) Enable/Disable Health Monitor 2) View Alerts Records 3) Configure Health Monitor Classes 4) Set Health Monitor Options 5) Exit Option? **3** 1) Activate/Deactivate Rules 2) Configure Periods 3) Configure Charts 4) Edit Sensor Objects 5) Reset Defaults 6) Exit Option? **4** 先看一下所有传感器; 1) List Sensor Objects 2) Edit Sensor Object 3) Exit Option? **1** Sensor Base Max Max M Warn Warn M -- ---- --- ----- ---- ------ CPUPct 50 80 0 70 0 CPUusage 50 85 0 75 0 CSPActivity 100 0 2 0 1.6 : : : WDWIJTime 60 0 2 0 1.6 WDWriteSize 1024 0 2 0 1.6 1) List Sensor Objects 2) Edit Sensor Object 3) Exit Option? **2** Cannot configure while System Monitor is running. 1) List Sensor Objects 2) Edit Sensor Object 3) Exit 噢,我们需要先返回并禁用 Health Monitor 和 System Monitor。 1) Enable/Disable Health Monitor 2) View Alerts Records 3) Configure Health Monitor Classes 4) Set Health Monitor Options 5) Exit Option? **1** Disable Health Monitor? No => **yes** Health Monitor is Disabled. Stop and restart System Monitor to halt Health Monitor 1) Enable/Disable Health Monitor 2) View Alerts Records 3) Configure Health Monitor Classes 4) Set Health Monitor Options 5) Exit Option?**** 1) Start/Stop System Monitor 2) Set System Monitor Options 3) Configure System Monitor Classes 4) View System Monitor State 5) Manage Application Monitor 6) Manage Health Monitor 7) View System Data 8) Exit Option? **1** 1) Start System Monitor 2) Stop System Monitor 3) Exit Option? **2** Stopping System Monitor... System Monitor stopped 好了,Health Monitor 和 System Monitor 已停止。 现在导航回到 Health Monitor 并编辑一个传感器对象。 1) Start System Monitor 2) Stop System Monitor 3) Exit Option?**** 1) Start/Stop System Monitor 2) Set System Monitor Options 3) Configure System Monitor Classes 4) View System Monitor State 5) Manage Application Monitor 6) Manage Health Monitor 7) View System Data 8) Exit Option? **6** 1) Enable/Disable Health Monitor 2) View Alerts Records 3) Configure Health Monitor Classes 4) Set Health Monitor Options 5) Exit Option? **3** 1) Activate/Deactivate Rules 2) Configure Periods 3) Configure Charts 4) Edit Sensor Objects 5) Reset Defaults 6) Exit Option? **4** 1) List Sensor Objects 2) Edit Sensor Object 3) Exit Option? **2** 输入传感器名称(如果您知道的话),否则输入“?”以显示列表。 传感器? **?** Num Sensor Threshold 1) CPUPct 2) CPUusage : : : 46) WDWIJTime 47) WDWriteSize Sensor? **1** CPUPct Base? 50 =>**** Enter either an Alert Value or a Multiplier Alert Value? 80 => **85** Setting Max Multiplier and Warn Multiplier to 0. Enter a Warn Value Warn Value? 70 => **75** Sensor object CPUPct updated. Base 50 MaxMult 0 AlertValue 85 WarnMult 0 WarnValue 75 1) List Sensor Objects 2) Edit Sensor Object 3) Exit 现在返回并启用 Health Monitor 和启动 System Monitor。 Option?**** 1) Activate/Deactivate Rules 2) Configure Periods 3) Configure Charts 4) Edit Sensor Objects 5) Reset Defaults 6) Exit Option?**** 1) Enable/Disable Health Monitor 2) View Alerts Records 3) Configure Health Monitor Classes 4) Set Health Monitor Options 5) Exit Option? **1** Enable Health Monitor? No => **yes** Health Monitor is Enabled. Stop and restart System Monitor to run Health Monitor 1) Enable/Disable Health Monitor 2) View Alerts Records 3) Configure Health Monitor Classes 4) Set Health Monitor Options 5) Exit Option?**** 1) Start/Stop System Monitor 2) Set System Monitor Options 3) Configure System Monitor Classes 4) View System Monitor State 5) Manage Application Monitor 6) Manage Health Monitor 7) View System Data 8) Exit Option? **1** 1) Start System Monitor 2) Stop System Monitor 3) Exit Option? **1** Starting System Monitor... System Monitor started 好了,工作已完成! ----
文章
姚 鑫 · 七月 16, 2021

第五章 使用文件

# 第五章 使用文件 # 使用文件 `%Library.File`类提供了几个类方法,允许对文件执行各种操作。 ## 复制文件 若要复制文件,请使用`CopyFile()`方法,该方法返回一个布尔值来指示成功或失败。 此方法采用四个参数: 1. from 从—指定源文件的名称。 2. to至—指定目标文件的名称。 3. pDeleteBeforeCopy —指定在执行复制之前是否删除目标文件(如果存在)。默认值为0。 4. return 返回—输出参数。如果为负,则包含操作系统返回的错误代码,以防方法失败 下面的第一个示例将目录`e:\temp`中的文件`old.txt`复制到`new.txt`。第二个示例将相同的文件复制到默认目录中的`new.txt`。 ```java DHC-APP>write ##class(%File).CopyFile("e:\temp\old.txt", "e:\temp\new.txt", 0, .return) 1 DHC-APP>write ##class(%File).CopyFile("e:\temp\old.txt", "new.txt", 0, .return) 1 ``` 最后一个示例失败,Windows错误代码为2,或“找不到文件” ```java DHC-APP>write ##class(%File).CopyFile("foo.txt", "new.txt", 0, .return) 0 DHC-APP>w return -2 ``` ## 删除文件 要删除文件,请使用`delete()`方法,该方法成功时返回1,失败时返回0。这个方法需要两个参数。第一个参数是要删除的文件的名称。第二个参数是输出参数。如果为负,它包含操作系统返回的错误代码,以防方法失败。 在下面的第一个示例中,方法成功了。第二个示例失败,出现Windows错误代码2或“找不到文件”。 ```java DHC-APP>write ##class(%File).Delete("e:\temp\myfile.txt", .return) 1 DHC-APP>write ##class(%File).Delete("e:\temp\myfile.txt", .return) 0 DHC-APP>w return -2 ``` 要在删除文件时匹配通配符,请使用`ComplexDelete()`方法。第一个参数指定要删除的文件的名称。第二个参数是输出参数。如果为负,它包含操作系统返回的错误代码,以防方法失败。 下面的示例删除所有带有。`e:\temp`目录中的out扩展名。 ```java DHC-APP>write ##class(%File).ComplexDelete("e:\temp\*.out", .return) 1 ``` ## 截断文件 要截断文件,请使用`truncate()`方法,该方法成功时返回1,失败时返回0。这个方法需要两个参数。第一个参数是要截断的文件的名称。第二个参数是输出参数。如果为负,它包含操作系统返回的错误代码,以防方法失败。 如果截断现有文件,方法会从文件中删除内容,但不会从文件系统中删除内容。如果截断不存在的文件,方法会创建一个新的空文件。 在下面的第一个示例中,方法成功了。第二个示例失败,Windows错误代码为5,或“访问被拒绝” ```java USER>write ##class(%File).Truncate("e:\temp\myfile.txt", .return) 1 USER>write ##class(%File).Truncate("e:\no access.txt", .return) 0 USER>write return -5 ``` ## 重命名文件 若要重命名文件,请使用`rename()`方法,该方法成功时返回1,失败时返回0。这个方法需要三个参数。第一个参数是要重命名的文件的名称,第二个参数是新名称。第三个参数是输出参数。如果为负,它包含操作系统返回的错误代码,以防方法失败。 在下面的第一个示例中,方法成功了。第二个示例失败,错误代码为183,或者“当文件已经存在时,无法创建该文件。” ```java DHC-APP>write ##class(%File).Rename("e:\temp\oldname.txt", "e:\temp\newname.txt", .return) 1 DHC-APP>write ##class(%File).Rename("e:\temp\another.txt", "e:\temp\newname.txt", .return) 0 DHC-APP>write return -2 ``` 使用此方法时,请小心指定路径,因为以下示例会将`e:\temp\oldname.txt`移动到默认目录,然后将其重命名为`newname.txt`。 ```java DHC-APP>write ##class(%File).Rename("e:\temp\oldname.txt", "newname.txt", .return) 1 ``` ## 比较文件 若要比较两个文件,请使用`Compare()`方法,如果两个文件相同,则返回布尔值1,否则返回0。该方法没有用于返回系统错误代码的输出参数。 在下面的第一个示例中,两个文件是相同的,方法返回1。在第二个示例中,两个文件不同,因此方法返回0。 ```java DHC-APP>write ##class(%File).Compare("e:\temp\old.txt", "e:\temp\new.txt") 1 DHC-APP>write ##class(%File).Compare("e:\temp\old.txt", "e:\temp\another.txt") 0 ``` 如果一个或两个文件都不存在,如下例所示,则该方法也返回0。 ```java DHC-APP>write ##class(%File).Compare("foo.txt", "bar.txt") 0 DHC-APP>write ##class(%File).Exists("foo.txt") 0 ``` ## 生成临时文件 要生成临时文件,请使用`TempFilename()`方法,该方法返回临时文件的名称。这个方法需要三个参数。第一个参数是临时文件所需的文件扩展名。第二个是生成临时文件的目录。如果未提供,该方法将在操作系统提供的临时目录中生成文件。第三个参数是输出参数。如果为负,它包含操作系统返回的错误代码,以防方法失败。 Windows示例: ```java USER>write ##class(%File).TempFilename("txt") C:\WINDOWS\TEMP\GATqk8a6.txt USER>write ##class(%File).TempFilename("txt","C:\temp") C:\temp\WpSwuLlA.txt ``` Unix示例: ```java USER>write ##class(%File).TempFilename("", "", .return) /tmp/filsfHGzc USER>write ##class(%File).TempFilename("tmp", "/InterSystems/temp", .return) /InterSystems/temp/file0tnuh.tmp USER>write ##class(%File).TempFilename("", "/tmp1", .return) USER>write return -2 ``` 在上面的第三个示例中,目录不存在,该方法失败,系统错误代码为2,或“没有这样的文件或目录。”
文章
姚 鑫 · 五月 27, 2021

第八章 处理收到的电子邮件

# 第八章 处理收到的电子邮件 # 处理收到的电子邮件 本节介绍如何处理通过`%Net.POP3`检索到的电子邮件(`%Net.MailMessage`)。 ## Message Basics 检索电子邮件(`%Net.MailMessage`)后,通常首先确定它是哪种类型的邮件以及如何阅读它;也就是说,它是否是多部分邮件以及各部分是否是二进制的。在此步骤中,您可以使用`ContentType`属性。或者,可以使用`IsBinary`、`IsHTML`和`IsMultiPart`属性,它们间接提供与`contentType`相同的信息。 如果消息是多部分消息,则每个部分都是`%Net.MailMessagePart`的一个实例。 ## Message Headers 消息本身和消息的每个部分都有一组标头。 `%Net.MailMessage`和`%Net.MailMessagePart`类提供的属性使可以轻松访问最常用的标头。例如,`%Net.MailMessage`提供收件人、发件人、主题和日期等属性。`Headers`数组属性允许访问任何自定义标题. 此外,如果已通过`%Net.POP3`检索到消息,则可以使用`GetAttribute()`方法。在给定标头名称和属性的情况下,此方法返回该属性的值。 ## Message Contents 了解常规消息结构后,请使用以下技术检索内容: - 对于多部分消息,请使用`Parts`属性,该属性是部分的数组。`Parts.Count()`给出部件的数量。每个部件的键都是一个整数,从1开始。使用`GetAt()`方法检索给定的部件。消息部分是`%Net.MailMessagePart`的实例。 - 对于二进制消息(或消息部分),请使用`BinaryData`属性。 - 对于文本消息(或消息部分),请使用`TextData`属性。 - 如果`IsHTML`为0,则`TextData`属性为普通文本字符串。 - 如果`IsHTML`为1,则`TextData`属性为HTML文本字符串。 请注意,发送邮件的电子邮件客户端确定邮件中的任何包装。邮件服务器无法控制这一点, ## 其他消息信息 `MessageSize`属性表示邮件的总长度(不包括任何附加的电子邮件)。 以下方法提供有关消息的其他信息: ### GetLocalDateTime() 返回检索消息的日期和时间,并转换为`$HOROLOG`格式的本地时间。 ### GetUTCDateTime() 返回检索消息的日期和时间,并以`$HOROLOG`格式转换为UTC。 ### GetUTCSeconds() 返回自1840年12月31日以来检索消息的日期和时间(秒)。 以下类方法也可用于时间/日期转换: ### HToSeconds() 将`$HOROLOG`格式的日期/时间转换为自1840年12月31日以来的秒的类方法。 ### SecondsToH() 将自1840年12月31日以来的秒数转换为`$HOROLOG`格式的日期/时间的类方法。 示例1:`ShowMsgInfo()` ```java ClassMethod ShowMsgInfo(msg as %Net.MailMessage) { Write "Message details *****",! Write "To (count): ", msg.To.Count(),! Write "From: ", msg.From,! Write "Cc (count): ", msg.Cc.Count(),! Write "Bcc (count): ", msg.Bcc.Count(),! Write "Date: ", msg.Date,! Write "Subject: ", msg.Subject,! Write "Sender: ", msg.Sender,! Write "IsMultipart: ", msg.IsMultiPart,! Write "Number of parts: ", msg.Parts.Count(),! Write "Number of headers: ", msg.Headers.Count(),! Write "IsBinary: ", msg.IsBinary,! Write "IsHTML: ", msg.IsHTML,! Write "TextData: ", msg.TextData.Read(),! Write "BinaryData: ", msg.BinaryData.Read(),! } ``` 此方法产生类似于以下内容的输出: ```java Message details ***** To (count): 1 From: "XXX XXX" Cc (count): 0 Bcc (count): 0 Date: Fri, 16 Nov 2007 11:57:46 -0500 Subject: test 5 Sender: IsMultipart: 0 Number of parts: 0 Number of headers: 16 IsBinary: 0 IsHTML: 0 TextData: This is test number 5, which is plain text. BinaryData: ``` 示例2:`ShowMsgPartInfo()` 以下方法写入有关消息一部分的信息: ```java ClassMethod ShowMsgPartInfo(msg as %Net.MailMessage, partno as %Integer) { Set part=msg.Parts.GetAt(partno) Write "Message part details *****",! Write "Message part: ", partno,! Write "IsMultipart: ", part.IsMultiPart,! Write "Number of parts: ", part.Parts.Count(),! Write "Number of headers: ", part.Headers.Count(),! Write "IsBinary: ", part.IsBinary,! Write "IsHTML: ", part.IsHTML,! Write "TextData: ", part.TextData.Read(),! Write "BinaryData: ", part.BinaryData.Read(),! } ``` 这将产生与以下内容类似的输出(给定的消息与前面显示的不同): ```java Message part details ***** Message part: 1 IsMultipart: 0 Number of parts: 0 Number of headers: 2 IsBinary: 0 IsHTML: 0 TextData: 1 test string BinaryData: ``` 示例3:`ShowMsgHeaders()` 下面的方法写入有关消息头的信息;可以编写一个类似的方法,对消息部分执行相同的操作。 ```java ClassMethod ShowMsgHeaders(msg as %Net.MailMessage) { Set headers=msg.Headers Write "Number of headers: ", headers.Count(),! //iterate through the headers Set key="" For { Set value=headers.GetNext(.key) Quit:key="" Write "Header:",key,! Write "Value: ",value,!! } } ``` 这将产生类似于以下内容的输出: ```java Number of headers: 16 Header: content-class Value: urn:content-classes:message Header: content-type Value: multipart/alternative; boundary="----_=_NextPart_001_01C8286D.D9A7F3B1" Header: date Value: Fri, 16 Nov 2007 11:29:24 -0500 Header: from Value: "XXX XXX" Header: message-id Value: Header: mime-version Value: 1.0 ... ``` # 自动编码和字符翻译 电子邮件部分包含有关使用的字符集和使用的内容传输编码(如果有的话)的信息。作为参考,本节介绍如何使用此信息。 ## 外发电子邮件 `%Net.SMTP`检查每个部分的字符集属性,然后应用适当的转换表。 如果未指定给定部件的字符集属性,InterSystems IRIS将使用UTF-8。 `%Net.SMTP`还检查`ContentTransferEncoding`属性。如果此属性为 `"base64"`或`"quoted-printable"`,则在创建消息时,`%Net.SMTP`会根据需要对正文进行编码。(如果内容传输编码为 `"7bit"` 或 `"7bit"`,则不需要编码。) 重要提示:请注意,如果内容为`“Base64”`编码,则不能包含任何`Unicode`字符。如果要发送的内容包括`Unicode`字符,请确保使用`$ZCONVERT`将内容转换为UTF-8。 ## 传入电子邮件 `%Net.POP3`检查每个邮件部分的`Content-Transfer-Encoding`标头,并根据需要对正文进行解码。 然后`%Net.POP3`检查每个邮件部分的`Content-Type`标头。这会影响消息部分的字符集属性,还会控制在InterSystems IRIS中创建消息部分时使用的转换表。
文章
Hao Ma · 一月 15, 2021

IAM实践指南——OAuth 2.0下的API保卫战(第三部分)

在这个由三个部分组成的系列文章中,介绍了如何在OAuth 2.0标准下使用IAM简单地为IRIS中的未经验证的服务添加安全性。 第一部分介绍了一些OAuth 2.0背景知识,以及IRIS和IAM的一些初始定义和配置,以帮助读者理解确保服务安全的整个过程。 第二部分详细讨论和演示了配置IAM所需的步骤——验证传入请求中的访问令牌,并在验证成功时将请求转发到后端。 本系列的最后一部分将讨论和演示IAM生成访问令牌(充当授权服务器)并对其进行验证时所需的配置,以及一些重要的最终考虑事项。 如果您想试用IAM,请联系InterSystems销售代表。 场景2:IAM作为授权服务器和访问令牌验证器 与上个场景不同的是,该场景中将使用一个名为“OAuth 2.0 Authentication”的插件。 如果要在资源所有者密码凭证流中将IAM作为授权服务器使用,客户端应用程序必须对用户名和密码进行身份验证。只有在身份验证成功时,才能发出获取IAM访问令牌的请求。 首先,将其添加到“SampleIRISService”中。正如下面截屏所示,需要填充一些不同的字段来配置此插件。 首先,将“SampleIRISService”的ID粘贴到“service_id”字段中,这样就可以在服务中启用该插件。 在“config.auth_header_name”字段中,需要指定携带授权令牌的头名称。本例中,我保留默认值“authorization”。 “OAuth 2.0 Authentication”插件支持的OAuth 2.0流包括授权码授权(Authorization Code Grant)、客户端凭证(Client Credentials)、隐式授予(Implicit Grant)或资源所有者密码凭证授权(Resource Owner Password Credentials Grant)。我们在本文中使用的是“资源所有者密码凭证”流,故选中“config.enable_password_grant”。 在“config.provision_key”字段中输入要用作配置密钥的字符串。此值用来向IAM请求访问令牌。 本例中,我保留了所有其他字段的默认值。可以在此处查看插件文档中每个字段的完整引用。 下面是插件配置的最终效果: 创建插件后,需要为“ClientApp”客户端创建凭证。 为此,打开左侧菜单上的“Consumers”,然后单击“ClientApp”。接下来,点击“Credentials”标签,然后点击“New OAuth 2.0 Application”按钮。 在下个页面的“name”字段输入名称,以标识应用程序,在“client_id”和“client_secret”字段分别定义客户端ID和客户端密钥,最后在应用程序中输入URL,在“redirect_uri”字段上授权后,用户将被发送到该URL。然后,单击“Create”。 现在,可以发送请求了。 需要发出的第一个请求是获取IAM访问令牌。“OAuth 2.0 Authentication”插件自动创建一个端点,并将“/oauth2/token”路径附加到已经创建的路由上。 注意:必须使用HTTPS协议和IAM的代理端口(默认端口为8443)监听TLS/SSL请求。这是OAuth 2.0规范要求。 因此在本例中,需要向URL发出一个POST请求: https://iamhost:8443/event/oauth2/token 请求主体中应包括以下JSON: { "client_id": "clientid", "client_secret": "clientsecret", "grant_type": "password", "provision_key": "provisionkey", "authenticated_userid": "1" } 如上所示,该JSON包含了在创建“OAuth 2.0 Authentication”插件时定义的值(如“grant_type”和“provision_key”),以及在创建客户端凭证时定义的值(如“client_id”和“client_secret”)。 当提供的用户名和密码成功通过身份验证后,客户端应用程序还应该添加“authenticated_userid”参数值作为已通过身份验证的用户的唯一标志。 该请求及其响应如下: 现在可以请求从上面的响应中获取事件数据(包括“access_token”值),并作为对URL的GET请求中的“bearner token” https://iamhost:8443/event/1 如果访问令牌过期,可以使用收到的刷新令牌和过期的访问令牌一起生成一个新的访问令牌,方法是向用于获取访问令牌的相同端点发出POST请求,但主体略有不同: { "client_id": "clientid", "client_secret": "clientsecret", "grant_type": "refresh_token", "refresh_token": "E50m6Yd9xWy6lybgo3DOvu5ktZTjzkwF" }  该请求及其响应如下: “OAuth 2.0 Authentication”插件的一个有趣很好的特性是能够查看和禁用访问令牌。 若要查看令牌列表,向以下IAM的Admin API终端发送GET请求即可: https://iamhost:8444/{workspace_name}/oauth2_tokens 其中{workspace_name}是IAM工作区的名称。如果启用了RBAC,则需要输入必要的凭证才能调用IAM Admin API。 注意,“credential_id”是在ClientApp客户端内部创建的OAuth应用程序(本例中名为SampleApp)的id,“service_id”是应用此插件的“SampleIRISService”的id。 要想禁用令牌,可以向以下端点发送删除请求 https://iamhost:8444/Sample/oauth2_tokens/{token_id} 其中{token_id}是要禁用的令牌id。 如果尝试使用无效令牌,将包含该无效令牌的GET请求作为Bearer Token 发送到URL,则会收到一条消息,提示令牌无效或已过期: https://iamhost:8443/event/1 最后需要考虑的因素 本文演示了如何将IAM中的OAuth 2.0身份验证添加到IRIS中未经身份验证的服务中。需要牢记的是,IRIS中的服务本身仍是未经身份验证的。因此,如果有人绕过IAM层直接调用IRIS服务端点,则可以在不经过任何身份验证的情况下查看信息。出于该原因,在网络级别设置安全规则以防止不必要的请求绕过IAM层是很重要的。 可以在此处了解更多有关IAM的信息。 如果您想试用IAM,请联系InterSystems销售代表。
文章
Hao Ma · 一月 30, 2021

WebGateway系列(3): 配置IIS连接IRIS

IIS在Windows Server里是默认安装,在Windows7和Windows10里面需要用户到"控制面板>程序"里面找到"Turn Windows features on or off"的设置来启动。 本文假设IIS已经在用户的服务器上启动,并且正常工作。 通常情况下,当IRIS安装在Windows系统时,用户会在同一台机器上使用IIS,很少有单独安装一台IIS服务器给远程IRIS提供Web服务器的,当然这样也绝无问题。 有两个软件包可以安装CSP Gateway。一个是IRIS的安装包。在IRIS的安装过程中, 如果有IIS正在工作, 那么安装程序会自动的安装CSP Gateway。 2018年以前的Ensemble或者Cache'的版本的安装过程中会跳出一个询问框,让用户选择是否"安装CSP网关。。。”。而后来的版本大多不做询问而自动为用户做了选择。 如果需要强制安装或者不安装CSP Gateway,那么需要在安装中选"Customer Component"设置。 另一个软件包是单独的CSP Gateway安装包, 可以在InterSystems的WRC或者工程师处得到。这是一个只有10多兆的很小的安装包,它只安装CSP Gateway, 并不安装IRIS实例,适合在单独的IIS硬件服务器上安装CSP Gateway; 或者, 当一个服务器上已有了IRIS, 但后来想添加IIS服务和CSP Gateway,用这个单独的安装包也很方便。 单独的CSP Gateway的安装会在IIS的工作目录“C:\Inetpub"下添加子目录CSPGateway,或者WebGateway, 然后在IIS的default Website上添加CSP Gateway的配置。整个配置相当简单。下面的步骤是在Windows10上单独安装Web Gateway安装包2020.1连接同一台服务器上的IRIS的的过程。 其中后面配置CSP Gateway访问IRIS应用的部分可以参考Apache上配置CSP Gateway的文档。 ## CSP Gateway安装 1. 使用浏览器访问 http://localhost, 显示IIS的欢迎主页, 确认IIS已工作。 2. 打开WebGateway-20201.1.xxxx-win_x64应用程序开始安装。安装时会提示用户输入连接的IRIS Server的IP地址和端口。默认的Application Name用CSP,IP地址端口使用localhost, 51773。安装过程会重启IIS服务,安装结束后用户不用手工重启。 3. 安装后的检查IIS - 检查Web Gateway安装后的文件。 确认在IIS的安装目录(默认为C:\inetput)下安装了CSPGateway子目录, 其中包含若干dll文件。它们是IIS中用到的CSP Gateway的模块,在不同的CSP Gateway版本中这些Dll文件的数量和名字稍有不同。 - 创建了c:/intersystems/WebGateway的文件夹,早些的版本中并不创建这个目录。 - 打开IIS配置界面, 确认在Default Site下安装了CSP application. 在某些版本的Web Gateway安装后, /csp被安装为Virtual Directory, 关于IIS中application和virtual diectory的区别请自行查询, 无论安装成那种类型, 对csp的使用和配置没有区别。使用HealthConnect 2018以前版本的安装包安装的CSP Gateway生成CSP和ensemble两个Application。 4. 查看CSP Gateway登录页面。 登录http://localhost/csp/bin/Systems/Module.cxw。 在主页上会显示Web Gateway的版本, Web Server Type是"Microseof-IIS"。配置文件和日志文件在c:\Inetpub\CSPGateway目录下。 (如果是打开时出现HTTP错误500.19, 你需要重启IIS) 5. 查看连接的IRIS Server。 从左边菜单栏进入Server Access配置界面。其中csp是默认安装的IRIS连接。2018以前的版本可能用的是LOCAL。不管怎么说,使用"Edit Server"打开,可以看到连接的IRIS的端口和设置。 配置UserName"CSPSystem"和Password "SYS",并保存。 在左边菜单栏,使用"Test Server Connection"测试到“csp"的连接,测试成功会显示IRIS的版本。 Test Server Connection Server connection test was successful: csp (localhost:51773) $ZVersion: IRIS for Windows (x86-64) 2020.2 (Build 199U) Tue Apr 28 2020 17:17:56 EDT 6. 访问IRIS维护主页 (可选) 从链接 http://WebServer/csp/sys/Utilhome.csp 访问IRIS维护主页System Management Portal应该可以成功了,但您会发现有部分网页内容(element)无法加载。这是因为在默认的安装中,只将"csp cls zen cxw"这4种类型的请求发送给CSP Gateway, 而被称为Static file的文件,比如.js, .css, .png等等类型的文件并没有被发送给CSP Gateway. 这是另外的一个安全机制,强制客户人工的配置是否需要从Web服务器访问IRIS维护主页。如果答案是NO, 那么访问IRIS维护页面就只能通过PWS,用IRIS服务器的52773的接口。 如果用户认为从Web服务器访问IRIS维护页面是必要的, 需要修改CSPFileTypes配置,把任意类型的请求发送给IRIS。 7. 访问IRIS上的其他Web Application IRIS上其他的Web Application, 比如”/api", ”/test"等等,通常情况下当Web Application被添加后,配置会自动同步到Web Gateway, 用户不用去Web Gateway的页面配置路由。但IIS上必须填写响应的配置,才能把请求从IIS发送到CSP Gateway. 这样操作: - 添加applications。如下图所示, 在IIS的default site下添加了两个新application, test和api. - 为每个applcation配置"Handler Mappings"。**使用右边动作栏中的”Add Module Mapping",而不是另3种动作。** 还要注意不要勾选"Request Restrictions"的"invoke handler only if request is mapped to"选择框,这样在"Handler Mappings"页面看到的Path Type一栏中显示的是"Unspecified", 否则会显示"File"或者其他内容。 - 测试访问一些应用, 比如 http://172.16.58.200/api/mgmnt/v2/ http://172.16.58.200/test/test.webservice1.cls 一般情况下,到目前为止IIS已经能够正常工作,将需要的请求发送给IRIS。如果出现故障或者需要调整CSP gateway的配置,请参考[WebGateway系列_配置Apache连接IRIS] 请求如何在nginx配置呢 @Hao.Ma 马老师给看看? 目前只有在线文档, 回头我们写一个贴上来
文章
Michael Lei · 七月 3, 2023

基于LangChain的IRIS ChatGPT – 释放大语言模型LLM的全部潜力

你好社区在本文中,我将介绍我的应用程序irisChatGPT ,它是基于LangChain Framework构建的。首先,让我们对框架进行一个简单的概述。 全世界都在谈论ChatGPT以及大型语言模型 (LLM) 如何变得如此强大,并且表现超出预期,提供类似人类的对话。这只是将其应用于每个企业和每个领域的开始! 剩下的最重要的问题是如何将这种能力应用于适合企业需求的特定领域数据和特定场景响应行为。 LangChain为这个问题提供了结构化且有效的答案! LangChain 技术可以帮助实现法学硕士的巨大潜力,通过围绕法学硕士提供抽象层并使法学硕士的使用变得简单有效,从而构建令人惊叹的应用程序。 LangChain 是一个框架,可以快速轻松地开发使用大型语言模型(例如 GPT-3)的应用程序。 然而,该框架引入了额外的可能性,例如,轻松使用外部数据源(例如维基百科)来放大模型提供的功能。我相信你们都可能尝试过使用 Chat-GPT,并发现它无法回答特定日期之后发生的事件。在这种情况下,在维基百科上搜索可以帮助 GPT 回答更多问题。 LangChain结构 该框架分为六个模块,每个模块允许您管理与法学硕士互动的不同方面。让我们看看这些模块是什么。 模型:允许您实例化和使用三种不同类型的语言模型,它们是: 大型语言模型 (LLM):这些能够理解自然语言的基础机器学习模型。它们接受输入中的字符串并在输出中生成字符串。 聊天模型:由 LLM 支持的模型,但专门用于与用户聊天。您可以在这里阅读更多内容。 文本嵌入模型:这些模型用于将文本数据投影到几何空间中。这些模型将文本作为输入并返回数字列表,即文本的嵌入。 提示:提示是我们如何与模型交互以尝试从中获取输出。现在知道如何编写有效的提示至关重要。这个框架模块可以让我们更好的管理提示。例如,通过创建我们可以重用的模板。 索引:最好的模型通常是与一些文本数据相结合的模型,以便为模型添加上下文或解释某些内容。这个模块可以帮助我们做到这一点。 链:很多时候,要解决任务,对 LLM 的单个 API 调用是不够的。该模块允许集成其他工具。例如,一个调用可以是一个组合链,其目的是从维基百科获取信息,然后将此信息作为模型的输入。该模块允许连接多个工具以解决复杂的任务。 内存:该模块允许我们在模型调用之间创建持久状态。能够使用记住过去说过的话的模型肯定会改善我们的应用程序。 代理:代理是一个法学硕士,它做出决定,采取行动,观察其所做的事情,并以这种方式继续,直到完成其任务。该模块提供了一组可以使用的代理。 现在让我们更详细地了解一下如何利用不同的模块来实现代码。 Langchain工作原理 步骤1 :用户向LangChain发送问题第2步 :LangChain将此问题发送至Embedding Model步骤3:嵌入模型将文本转换为向量,文本以向量形式存储在数据库中并返回给LangChain步骤4 :LangChain将这些向量发送到向量数据库(有多个向量数据库,我们在我们的应用程序中使用chroma)步骤5:向量数据库返回前 K 个近似最近邻 ( KNN ) 向量第6步:LangChain 将问题与KNN向量一起发送到大型语言模型 (LLM) (我们在应用程序中使用 OpenAI)步骤7:LLM向Langchain返回答案步骤8:Langchain将答案返回给用户 关于申请 irisChatGPT应用程序利用围绕大型语言模型 (LLM) 构建的最热门 Python 框架LangChain的功能。 LangChain 是一个框架,可以快速轻松地开发使用大型语言模型的应用程序。应用程序是在系统间嵌入式 Python功能的帮助下使用 objectscript 构建的。它还包含Streamlit Web 应用程序,这是一个开源 Python 应用程序框架,用于为数据科学和机器学习创建漂亮的 Web 应用程序。 特征 以下是应用程序功能列表以及相关屏幕截图 内置Intersystems ObjectScript 参考ChatGPT 内置InterSystems 大奖赛 2023 ChatGPT 使用 SQLDatabaseChain 回答有关缓存数据库的问题 创建您自己的 chatGPT 模型并与其聊天 OpenAI 聊天GPT 维基百科搜索 使用DuckDuckGo(DDG)通用搜索引擎在互联网上搜索 使用Python REPL LangChain功能生成Python代码 Streamlit Web 应用程序在线演示 谢谢
文章
Jeff Liu · 二月 5, 2021

精华文章置顶--使用VSCode 进行IRIS 开发

VSCode 是目前很流行的一款免费开发工具,IRIS也支持使用其进行连接和开发,相比Studio 只能在windows 环境使用,Vscode 可以跨平台使用。 我们传统的工具Studio 是连接代码服务器的形式,不能便捷的使用目前流行的有本地代码的版本控制工具(如git),但VSCode可以存在本地代码,并且能方便的使用各类存在本地代码的版本控制工具。 以下的内容会帮助大家来配置使用VSCode连接IRIS 进行开发。 VSCode 可以在微软的官网免费下载https://code.visualstudio.com/ 如果安装为英文要切换为中文则可以通过Command Palette 中运行Configure Display Language,install another language, 选择中文,再根据提示安装相应的语言插件。 接下来在扩展商店里搜索intersystems 安装如图所见的3个扩展包 VSCode目前有两种方式可以链接IRIS: 直接连接代码服务器进行编辑 使用客户端方式连接,将代码下载到本地,便于使用有本地代码的版本控制工具 注:两种连接方式的连接端口都为服务器web port 下面具体讲解两种连接方式。 直接连接代码服务器进行编辑 其总体的使用方式和studio相似,直接连接服务器进行代码编辑。在vscode中可以使用全局只读的方式来浏览代码,从而避免误操作修改了代码。 如果会经常使用这个服务器,可以考虑先建立一个工作区,将设置保存在工作区,当然直连服务器的模式下,建立服务器不是必选项。 如果我们正确的安装了之前提到的三个扩展,其中下面扩展会帮助我们连接IRIS代码服务器,并保存在VSCode的全局 settings.json配置文件中: 进入VSCode 设置中找到“扩展”下的Intersystems Server Manager ,进入设置可见安装自带的服务器连接字符串可以依据用户需要进行添加和修改 配置完成可以回到工作区界面进行连接 选择Choose Server and Namespaces, 从右边的服务器列表中选择要连接的服务器,也可以使用“+”来根据提示信息来添加新的服务器。 选择服务器之后会提示输入密码(如果没有在配置中写明登录用户,也会提示输入登录用户)验证成功后可以选择想查看的命名空间,最后按照提示选择是以编辑或者只读方式来查看代码。 在服务器直连的模式下,可以将多个服务器的多个命名空间的代码添加至当前工作区。 目前的连接方式进入之后会频繁的要求登录验证,输入密码,这里实际可以将密码存在VSCode的钥匙链中: 1.打开命令面板 2.输入store 使用下图中的命令则可以为选定的服务器存储密码,但是每个服务器只能保存一个密码,相应的在命令面板中输入clear 会提示清除密码的选项用来删除已存储的密码 2. 使用客户端方式连接 如果使用客户端的连接方式,则必须要在工作区内添加一个文件夹,以及保存此工作区。当添加文件夹到工作区之后侧边栏会多出图示的按钮: 点击按钮选择Choose server and namespaces 此时如果在vscode中已经添加过想要连接的IRIS 代码服务器,这里会直接显示已存储的服务器,设置法与之前提到的直连方式相同。 连接完成后如果想添加多个命名空间到工作区,可以使用右上角的+号进行添加 接下来可以在客户端模式连接导出ObjectScript代码到本地。从而进行源代码的版本控制管理。 在objectscript explorer中选择任意的包,或者类右键选择export,则可以将其导出到目前工作区的文件夹下 代码会被导出到工作区目录的src文件夹下。 客户端模式中使用 GIT 首先打开一个GIT Repo文件夹,如果本来然后按照客户端连接方式连接用来开发的服务器。在任何的包或文件上点击import and compile 可以将版本控制的代码导入开发服务器。 注意这时候只要在本地新建文件并且保存,新的修改就会被同步到开发服务器实例上。 以客户端方式连接时连接多个服务器或多个命名空间的方式: 首先将一个文件夹添加到工作区,并保存工作区,在保存之后的工作区中可以看到如下的工作区配置文件( 此处我使用的默认工作区配置文件名,实际可以自行定义),其中定义了工作区的目录结构: 此时我们在这个工作区内添加一个子目录,在工作区的列表右键单击,选择“将文件夹添加到工作区”: 添加后可以看到,在工作区配置文件中增加了新的目录,为了这个显示层级更加清楚,可以在settings 中使用files.exclude 来过滤是否显示子目录 此时子再点击侧边栏中的 object script explorer,则会让用户选择使用哪一个子目录进行连接: 要使用另一个目录连接一个服务器的命名空间时,将第一个已连接的文件夹下.vscode 拷贝到第二个文件夹: 直接对这个文件进行修改,将其配置成为想连接的服务器以及命名空间 连接配置文件同样支持如下的写法: 此时再回到object script exploer,可见到添加了的服务器。 可见这里服务器的连接和本地代码编译的目标对象只由当前文件夹下.vscode/settings.json控制。 所有的之前提到的配置文件都可以手工编辑,不必一定在vscode中进行添加文件夹和配置连接。 在VSCode中调试 以客户端方式连接时可以进行调试: 在侧边栏选择调试按钮,首次调试应在工作区创建launch.json,并选择ObjectScript Debug 对于Class Method,可以直接在类中点击“Debug this Method” 下边是一个Class Method 实例的实例调试界面 附加到进程调试: 对于需要附加到的进程,在lauch.json中添加”program” 属性,并填入进程号,保存。 program 中也可以填入要debug的routine 比如“##class(Test.test).test()”, “name”可以修改成自定义的名字,之后会在debug运行按钮的下拉菜单显示。 调试的配置可以添加多个,都会显示在debug下拉菜单中 进入调试界面,点击xDebug 旁边的绿色启动按钮开始调试,其他同Class Method 调试方式 关于VSCode几个编辑器小技巧: 在底部栏点击当前连接的服务器会出现提示菜单,从这里可以便捷进入实例管理界面和class reference 在任意的类或者方法上点击右键可以转到定义。如果使用了#dim预定义变量,则可以在变量上使用转到变量声明,而且可以使用转到类型定义类型的定义。 尾声 当前版本不支持xml 代码文件导入!VSCode community中已经有人提到过这个问题,开发者回复之后的版本很有可能会加入 本项目的Github的issue地址,可以在这里提出问题:https://github.com/intersystems-community/vscode-objectscript/issues 支持的产品 :Caché/Ensemble 2016.2 以及更新, 以及所有的基于IRIS 的产品版本. 调试入参类型为%Library.DynamicAbstractObjec的参数如何输入入参呢 对象入参的话,的确直接调试没法从键盘输入,可能还是需要附加到进程或者写一个临时方法来生成这个入参对象,再调用想调试的方法检查断点。 知乎文章:使用VS Code进行Caché数据库开发 好的,谢谢
文章
Hao Ma · 十二月 23, 2024

从TTL值发现网络中的中间人攻击

技术支持团队在不同的项目中发现了类似中间人攻击的情况, 和各位分享一下。 我们的系统一般是安装在内网里,没有恶意的中间人攻击的风险。但是在有些医院发现了这样的情况:IT在网络中安装了某种网络监控或者嗅探的设备, 它会在通信通道中模拟其中一方,或者双方的通信节点, 以截获通信双方的网络流量。通常它不影响双方的通信,但偶尔,它会中断双方的连接, 造成业务的中断。实质上这也是一种中间人攻击的情况,只不过这是用户允许的行为,偶然出现了故障。 我们看看以下的例子: 以下的wireshark抓包截图中, 172.18.1.131和172.18.1.145在正常的通信过程中, 忽然收到了RST消息,造成了TCP连接上的复位。 其中172.18.1.131是intersystems的health connect系统, 它在序号50134的包里面首先发送了RST,因此客户怀疑是不是Health Connect出错,中断了连接,也就把问题提交了InterSystems的技术支持。 我们的技术支持检查了各种内部日志,没有发现任何错误,咨询了InterSystems的网络专家,才发现这是个网络层的错误,也就是说: 这个RST不是Health Connect发送的, 同样, 序号50135的RST消息, 也不一定是172.18.1.145发送的,这是中间网络层的行为。 经过客户的证实,发现客户在网络层安装的监控工具, 不知道什么原因,触动了某种安全机制,它模拟两个系统,分别向对方各发送了一个RST消息。 我们的技术人员之所以发现了疑似中间人攻击的情况, 是基于仔细研究了RST消息中的TTL值。 IP 数据包中的 TTL(Time To Live)是什么: 当 IP 数据包在网络中传输时,TTL 用于防止数据包在网络中无限循环转发。 发送方在创建 IP 数据包时设置一个初始 TTL 值,通常为 64、128 或 255 等。每经过一个路由器,TTL 值就会减 1。当 TTL 减为 0 时,路由器会丢弃该数据包,并向源地址发送一个 ICMP(Internet Control Message Protocol)超时消息。TTL确保网络中的数据包不会无限制地循环,避免网络拥塞。同时,TTL 也可以帮助网络管理员诊断网络问题,例如通过观察 TTL 值的变化来确定数据包经过的路由路径。可以通过 IP 数据包中的 TTL 值来大致判断路由信息,但这种方法并不十分精确,只能提供一些有限的线索。 为了理解上面一段话的意思,我们向不同的目的地发ping包观察一下TTL值: hma@CNMBP23HMA ~ % ping 127.0.0.1 PING 127.0.0.1 (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.132 ms 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.162 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.151 ms ^C --- 127.0.0.1 ping statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 0.132/0.148/0.162/0.012 ms hma@CNMBP23HMA ~ % ping bjsvmse01 PING bjsvmse01.iscinternal.com (172.19.85.68): 56 data bytes 64 bytes from 172.19.85.68: icmp_seq=0 ttl=64 time=10.388 ms 64 bytes from 172.19.85.68: icmp_seq=1 ttl=64 time=6.013 ms 64 bytes from 172.19.85.68: icmp_seq=2 ttl=64 time=4.614 ms ^C --- bjsvmse01.iscinternal.com ping statistics --- 7 packets transmitted, 7 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 3.941/5.778/10.388/2.050 ms hma@CNMBP23HMA ~ % ping www.baidu.com PING www.a.shifen.com (110.242.68.4): 56 data bytes 64 bytes from 110.242.68.4: icmp_seq=0 ttl=52 time=14.167 ms 64 bytes from 110.242.68.4: icmp_seq=1 ttl=52 time=12.411 ms 64 bytes from 110.242.68.4: icmp_seq=2 ttl=52 time=12.560 ms ^C --- www.a.shifen.com ping statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 12.411/13.046/14.167/0.795 ms hma@CNMBP23HMA ~ % ping www.intersystems.com PING e9933.b.akamaiedge.net (104.124.163.63): 56 data bytes 64 bytes from 104.124.163.63: icmp_seq=0 ttl=42 time=173.183 ms 64 bytes from 104.124.163.63: icmp_seq=1 ttl=42 time=170.947 ms 64 bytes from 104.124.163.63: icmp_seq=2 ttl=42 time=169.031 ms 64 bytes from 104.124.163.63: icmp_seq=3 ttl=42 time=171.195 ms 64 bytes from 104.124.163.63: icmp_seq=4 ttl=42 time=168.908 ms 64 bytes from 104.124.163.63: icmp_seq=5 ttl=42 time=169.736 ms 上面我分别发送ICMP到4个地址:127.0.0.1,bjsvmse01, www.baidu.com, www.intersystems.com, 从对端响应消息中显示的TTL值分别是64, 64, 52, 42。 TTL=64是Linux系统的TTL初始值。 在linux系统ping 127.0.0.1时返回的当然就是64。 第二个服务器bjsvmse01返回TTL也是64, 说明它和我的本机在同一个网络,他们中间没有路由器的转接。 第3个第4个ping返回的消息中的TTL分别是52和42, 理解为:从Baidu.com的服务器到我的本机,经过了64-52=12个路由,而从intersystems.com到我的本机经过了64-42=22个路由。因为intersystems.com的服务器在国外,经过更多路由。从ping的结果看, 响应时间也更长,平均响应要100多ms。 有了TTL的知识,我们来看看前面医院里的RST是怎么回事。 首先是RST的包, 可以看到TTL是128。 这就不对了。 128是Windows系统的TTL初始值, 而我们技术人员已经知道了, Health Connect是安装在一个Linux系统上,如果是Health Connect发出的RST,TTL应该是低于64的. 我们去和一个正常工作的从这个服务器发出的TCP消息来比较,比如上面的HTTP消息,下面的截中可以看到TTL是64, 这是对的。本来这两个服务器就在一个网络里。 因此, 结论就是: 抓包中显示为从Health Connect 172.1.18.131发出的RST消息,因为TTL不对, 应该不是真的从Health Connect服务器发出的。 类似的情况出现在不同的客户的不同医院环境, 所以我们认为维护或者支持任何可能应该了解一下这个情况。 如果遇到,请及时和医院的IT工程师了解情况。 有关TCP消息中的TTL值, 再补充几点: 如果源和目的服务器不在一个网络,TTL值不是64和128,也可以用来判断是否是真实的服务器发送的消息。我们有个支持的工单里, 发现了正常工作的TTL是62,而发送了一个错误的TCP消息,TTL是57, 这也是可疑且需要网络工程师确认的。 上面的例子中,从Health Connect对端,也就是172.18.1.145也发出了一个无法理解的RST消息。一个TCP端,收到一个RST消息后,不需要也不应该再回复,所以这个RST和上面Health Connect发出的RST没有关系。检查这个RST, 它的TTL值是128, 而正常通信时从这个服务器发出的消息也是128, 显然这是个Windows系统,所以这样的情况下, 只通过TTL是无法判断消息来源的身份真假的。除非在不同的地方,比如在中间的交换机上和该服务器上分别抓包比较,才可以得到线索。 网络路径中的某些设备(如防火墙、代理服务器等)对数据包进行了特殊处理。这些设备可能会修改数据包的 TTL 值,以确保数据包能够在网络中存活足够长的时间到达目的地. 典型的是当ping 8.8.8.8,也就 是 Google 的公共 DNS 服务器的时候,收到的TTL可能是不同的值,下面的截图中TTL是111, 如果您也测试一下, 您可能发现这个值是150, 170等等, 这时不只是源服务器的操作系统,LINUX/UNIX/Windows的初始值起作用,而是在中间网络设备将这个值做过特殊处理。 hma@CNMBP23HMA ~ % ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: icmp_seq=0 ttl=111 time=50.109 ms 64 bytes from 8.8.8.8: icmp_seq=1 ttl=111 time=45.896 ms ^C --- 8.8.8.8 ping statistics --- 2 packets transmitted, 2 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 45.896/48.002/50.109/2.107 ms hma@CNMBP23HMA ~ % 因此,关于TTL值的局限性, 有这样的说法: 准确性有限: 由于网络中的路由可能会动态变化,TTL 值推断出的路由信息可能并不完全准确。 一些网络设备可能会对 TTL 值进行修改或干扰,导致推断结果不准确。 不完整性: 对于一些复杂的网络环境,如使用 VPN(Virtual Private Network)、代理服务器或网络地址转换(NAT)的情况,TTL 值可能无法准确反映真实的路由路径。
文章
姚 鑫 · 二月 15, 2021

第三十五章 Caché 变量大全 $ZNSPACE 变量

# 第三十五章 Caché 变量大全 $ZNSPACE 变量 包含当前命名空间名称。 # 大纲 ```java $ZNSPACE ``` # 描述 `$ZNSPACE`包含当前命名空间的名称。通过设置`$ZNSPACE`,可以更改当前名称空间。 要获取当前命名空间名称,请执行以下操作: ```java DHC-APP>SET ns=$ZNSPACE DHC-APP>WRITE ns DHC-APP ``` 还可以通过调用`%SYSTEM.SYS`类的`Namespace()`方法来获取当前命名空间的名称,如下所示: ```java DHC-APP>SET ns=$SYSTEM.SYS.NameSpace() DHC-APP>WRITE ns DHC-APP ``` 可以使用`%SYS.Namespace`类的`Existes()`方法测试命名空间是否已定义,如下所示: ```java DHC-APP>WRITE ##class(%SYS.Namespace).Exists("USER") 1 DHC-APP>WRITE ##class(%SYS.Namespace).Exists("LOSER") 0 ``` 对于UNIX®系统,默认命名空间建立为系统配置选项。对于Windows系统,它是使用命令行启动选项设置的。 命名空间名称不区分大小写。InterSystems IRIS始终以全大写字母显示显式名称空间名称,以全小写字母显示隐含的名称空间名称。 要获取指定进程的命名空间名称,请使用`%SYS.ProcessQuery`类的方法,如下例所示: ```java DHC-APP>WRITE ##CLASS(%SYS.ProcessQuery).%OpenId($JOB).NameSpaceGet() DHC-APP ``` # 设置当前命名空间 可以使用`ZNSPACE`命令、`SET $NAMESPACE`、`SET $ZNSPACE`或`%cd`实用程序更改当前名称空间。 - 在终端命令提示符下,`ZNSPACE`命令是更改名称空间的首选方式。`SET $ZNSPACE`在功能上与`ZNSPACE`命令相同。 - 在代码例程中,新建`$NAMESPACE`,然后设置`$NAMESPACE = NAMESPACE`是更改当前名称空间的首选方式。通过使用`new $NAMESPACE`和`SET $NAMESPACE`,可以建立一个名称空间上下文,该上下文在方法结束或发生意外错误时自动恢复到前一个名称空间。 可以使用`SET $ZNSPACE`更改进程的当前命名空间。将新命名空间指定为字符串文字或计算结果为带引号的字符串的变量或表达式。可以指定显式名称空间(`“NAMESPACE”`)或隐式名称空间(`“^SYSTEM^DIR”或“^^DIR”`)。 如果指定当前命名空间,则`SET $ZNSPACE`不执行任何操作,也不返回任何错误。如果指定了一个未定义的名称空间,则`SET $ZNSPACE`会生成一个``错误。 不能`new $ZNSPACE`特殊变量。 # 示例 在以下示例中,如果当前命名空间不是`USER`,则`SET $ZNSPACE`命令会将当前命名空间更改为`USER`。请注意,由于`if`测试,命名空间必须全部用大写字母指定。 ```java /// d ##class(PHA.TEST.SpecialVariables).ZNSPACE() ClassMethod ZNSPACE() { SET ns="USER" IF $ZNSPACE=ns { WRITE !,"命名空间已经 ",$ZNSPACE } ELSEIF 1=##class(%SYS.Namespace).Exists(ns) { WRITE !,"命名空间是 ",$ZNSPACE SET $ZNSPACE=ns WRITE !,"将命名空间设置为 ",$ZNSPACE } ELSE { WRITE !,ns," 不是定义的命名空间" } QUIT } ``` ```java DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZNSPACE() 命名空间是 DHC-APP 将命名空间设置为 USER ``` 此示例要求`UnnownUser`已分配`%DB_IRISSYS`和`%DB_USER`角色。
文章
姚 鑫 · 二月 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 ```