清除过滤器
文章
姚 鑫 · 八月 8, 2022
# 第二十一章 源代码文件 REST API 参考(三)
# GetDocNames
此方法返回源代码文件名列表。可选的 `cat` 和 `type` 约束源代码文件的类型。
## URL
`GET http://server:port/api/atelier/v1/namespace/docnames`
`GET http://server:port/api/atelier/v1/namespace/docnames/cat`
`GET http://server:port/api/atelier/v1/namespace/docnames/cat/type`
其中
- `cat` 指定类别代码:`CLS = class`; `RTN = routine`; `CSP = CSP 文件`; `OTH = other`。默认为 `*`。
- `type` 指定源代码文件类型。可以是 `*` 通配符或文件类型。对于 `CLS`,类型必须是 `*`。对于 `RTN`,类型可以是 `mac、int、inc、bas 、mvi 或 mvb`。对于 `CSP`,`type` 可以是 `js` 或 `css` 等文件类型的列表,以逗号分隔。默认为 `*`。
## URL Parameters
- `URL` 参数 `'generated=1'` 指定应包含生成的源代码文件。
- `URL` 参数 `'filter'` 提供了一个可用于匹配名称的 `SQL` 过滤器。
## JSON Messages
以下是返回的内容,源代码文件描述符数组:
```
{
"status": {
"errors": [],
"summary": ""
},
"console": [],
"result": {
"content": [
{
"name": "%Api.DocDB.cls",
"cat": "CLS",
"ts": "2016-08-03 20:01:42.000",
"upd": true,
"db": "IRISLIB",
"gen": false
},
...
{
"name": "EnsProfile.mac",
"cat": "RTN",
"ts": "2003-09-19 13:53:31.000",
"upd": true,
"db": "INVENTORYR",
"gen": false
},
...
{
"name": "xyz.mac",
"cat": "RTN",
"ts": "2016-08-11 15:05:02.167",
"upd": false,
"db": "INVENTORYR",
"gen": false
}
]
}
}
```
## HTTP 返回码
- `HTTP 200` 如果正常。
- `HTTP 500` 如果发生意外错误(详细信息将在状态错误数组中)。
# GetModifiedDocNames
此方法返回自数据库具有指定哈希值以来已修改的源代码文件列表。它以 `JSON` 数组的形式传递数据库键和哈希列表。哈希值用于确定该键定义的数据库中是否有任何更改。通常,首先使用一个空数组调用此 `API` 作为传入的 `JSON` 消息。这将返回命名空间中所有源代码文件的名称以及每个文件的数据库键和哈希。然后可以发布 `dbname` 和 `dbhash` 以发现自上次调用以来在服务器上修改了哪些源代码文件。
发布要检查的源代码文件列表,如以下示例所示:
```
[ { "dbname" : "USER",
"dbhash" : "KWAGbOdnRblPzANaiv1Oiu0BZLI"
}, ... ]
```
## URL
POST http://server:port/api/atelier/v1/namespace/modified/type
其中:
- `type` - 将源代码文件类型指定为 * 或三字母代码、`ls、mac、int、inc、bas 或 mvi`。默认为 `*`。
此调用需要标头 `Content-Type application/json`。
## JSON Messages
以下是返回的内容,源代码文件描述符数组:
```
[ { "dbname" : "USER",
"dbhash" : "Qx1zuNaulq3b_1yR9ahZAfjkc-",
"crhash" : "47763751EC",
"docs": [{
"name": "User.NewClass1.cls",
"ts": "2016-01-04 14:00:04.000",
"gen": false,
"depl": false
}, ... ]
}, ... ]
```
如果在指定的 `dbhash` 之后删除了源代码文件,则在列表中返回该文件,并将时间戳设置为空字符串:
```
"ts": ""
```
如果由于映射而包含数据库并且删除了映射,则 `dbhash` 和 `crhash` 都将返回`“000”`值,并且 `docs` 作为空数组返回。
## HTTP 返回码
- `HTTP 200` 如果正常。
- 如果发布的内容为空或类型不是 `CLS`,则 `HTTP 400`。
- 如果内容类型不是应用程序`/json`,则为 `HTTP 415`。
- `HTTP 500` 如果发生意外错误(详细信息将在状态错误数组中)。
文章
Louis Lu · 九月 21, 2022
我在这里和大家分享下在 Interoperability 的接口开发中,调用Web Service接口的几个超时参数的设置经验。
赶时间的同学可以直接拉到文章最下面看结论就好。
1.实验过程
首先我设计了一个Web service的服务器端,强制在接收到请求后 8s 返回结果。
在客户端我设置了响应超时7s, 重试间隔5s, 故障超时23s,如图:
在客户端、服务器端均设置了SOAP Log 记录接收和发送的内容
set ^ISCSOAP("Log")="io"
set ^ISCSOAP("LogFile")="c:\temp\soapClient.txt"
经过整理日志我画了下面的数据流图
2. 得出下面结论:
*1:重试间隔虽然设置为5s,但是仅当有响应超时错误后才会被触发
*2:虽然服务器端有返回值,但是已经超过了设定的响应超时(7s)时间,则返回值不会被客户端接收
*3:故障超时虽然设置为23s,但是仅当有响应超时错误后才会被触发
3. 结论再验证
为了验证上面结论我修改了Web service的服务器端代码,强制在接收到请求后 23s 返回结果,
并且设置响应超时20s, 重试间隔6s, 故障超时25s,得出上面同样的结论:自动重发或者故障超时错误的触发条件都是收到响应超时错误。
所以我们要特别注意设置响应超时这个参数的值:
如果设置的值过短,特别是小于服务器端返回值的时间,则客户端一定接收不到返回值
如果设置的值过长,那么该进程会一直处于等待结果的状态,其他的请求会被放入到队列中,从而减低处理效率,系统资源造成浪费。
4. 进一步思考
如果熟悉 Production开发的人一定知道,只能在Services 或者Processes中设定消息的发送是以同步或者异步的方式,那么对于Operations我们怎么处理呢?
特别是像遇到这篇文章中的情况,接入的三方系统可能需要比较长的时间才能返回结果,如果 Production 每次都需要一个进程等待结果返回,只有收到返回后才能把该进程释放出来处理另一个消息请求,则必然造成系统资源的浪费。
这种情况我推荐使用消息传递的第三种方式 Deferred sending,也就是通过Servies接收到的内容构建原Operations的返回消息,如下图:
有关使用deferred sending的例子我会在之后的文章中分享。 对于超时,有一种处理方式:如果服务超时了,那把本次请求的消息id返回,并返回超时,下次请求的时候,带入上次得到的请求返回的消息id,服务通过消息id查询该id对应的返回。
文章
jieliang liu · 五月 15
在本文中,我们将使用基于分布式存储的 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接口(二)
# 过滤模式内容
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'
```

如果该表定义为`%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使用。

问题
lin qijun · 九月 13, 2021
1.Caché数据库有没有办法配置然后用sql读取数据库实时变化的数据,类似于mssql那样?我看了可以写类去读取global获取journal的值,但是怎么用sql读呢?
2.不行的话,那用什么方式可以读取到journal日志文件,并输出日志文件的内容?
先谢谢大家了!!! 请参考CDC 系列文章:https://cn.community.intersystems.com/post/cdc%E7%B3%BB%E5%88%97%E4%B9%8B%E4%B8%80-%EF%BC%9A%E4%BD%BF%E7%94%A8dejournal-filter%E5%9C%A8intersystems-iriscach%C3%A9%E4%B8%8A%E9%80%9A%E8%BF%87mirroring%E5%AE%9E%E7%8E%B0cdc%E5%8A%9F%E8%83%BD 这个试过了,配置dejournal没有问题,但是用$$$JRNNEWVAL(Address) 输入偏移量获取不到值的 注意文章中的提示:
1. $$$JRNNEWVAL(Address) 、$$$JRNOLDVAL(Address)`这2个宏定义在%syJrnRecord.inc文件里,因此需要将这个include文件加入ZCustom.MirrorDejournal类定义:Include %syJrnRecord
2.在InterSystems IRIS上修改并编译dejournal过滤器类后,需要重启异步镜像成员的Mirror,以使更改生效。 加了这个定义,并重启了的Mirror的,还是获取不到值的 使用的产品版本是什么?Dejournal filter是在Mirror report类型的异步镜像成员做的还是在shadow上做的?不同给的版本对应不同的处理方式。
同时还是建议将问题提到WRC:support@intersystems.com,他们会给予更详细的错误分析以及解决方案。 是在Mirror report类型的异步镜像成员上做的,版本是Cache for Windows (x86-64) 2016.2 (Build 736_0_16871U) Wed Dec 21 2016 09:38:49 EST版本 可以参考 %SYS.Journal.File.cls 这个类下的代码是如何使用$$$JRNNEWVAL(Address) 、$$$JRNOLDVAL(Address)的。
文章
Frank Ma · 六月 13, 2022
这是一个在InterSystems IRIS中用python和objectscript建立的对比测试。
测试目的是比较在python和objectscript中从BP到BO来回发送一千条请求/消息的速度。
更多信息,请访问 https://github.com/LucasEnard/benchmark-python-objectscript。
**重要提示** : 这里用的是python, graph objectscipt和objectscript从一个BP到一个BO来回发送1000条消息的时间,单位是秒。
字符串信息是由十个字符串变量组成。
对象信息由十个对象变量组成,每个对象都是它自己的int、float、str和List(str)。
| 消息字符串| 1000条消息来回的时间 (秒) |
|------------------------|------------------|
| Python BP | 1.8 |
| BPL | 1.8 |
| ObjectScript | 1.4 |
| 消息对象| 1000条消息来回的时间 (秒) |
|------------------------|------------------|
| Python BP | 3.2 |
| BPL | 2.1 |
| ObjectScript | 1.8 |
行中函数的时间是列中函数的x倍 :
| 消息字符串| Python | BPL | ObjectScript |
|------------------------|------------|------------------------|------------------|
| Python | 1 | 1 | 1.3 |
| BPL | 1 | 1 | 1.3 |
| ObjectScript | 0.76 | 0.76 | 1 |
例如,第一行告诉我们,Python字符串的时间是Objectscript图形字符串函数的1倍,是Objectscript字符串函数的1.3倍。 ( 利用第一个表格,我们可以验证我们的结果 : 1.3 * 1.4 = 1.8 1.3是第一行最后一列表格中的x,1.4s是本节第一个表格中看到的objectscript中的字符串信息的时间,1.8s实际上是python中的字符串信息的时间,我们可以通过寻找本节第一个表格或通过前面所示的微积分找到。)
行中的函数有列中函数X倍的时间:
| Messages objects| Python | BPL | ObjectScript |
|------------------------|------------|------------------------|------------------|
| Python | 1 | 1.5 | 1.8 |
| BPL | 0.66 | 1 | 1.2 |
| ObjectScript | 0.55 | 0.83 | 1 |
问题
Michael Lei · 四月 27, 2022
Hi, 请问如何更改表(有数据)上的主键?谢谢!
答:
如果数据已经存在,那么这是一项必须重视的任务,特别是如果存在继承或父/子关系,因为这将导致你的数据存储方案的改变。
最简单的方法是通过一个中间(临时)表来实现。
创建一个具有相同结构的新类,但有一个新的主键。使用SQL(不是合并命令)将数据从旧的类中移到它里面。删除旧类中的数据/索引,然后改变其中的主键。使用合并命令,将数据从新类移到旧类中。删除带有数据的新类。重建索引(如果有的话)。
几个有用的链接: MERGE
持久性对象和InterSystems IRIS SQL
持久性对象的介绍
如果仍然有问题,最好向WRC寻求帮助。
答:
如果数据已经存在,那么这是一项必须重视的任务,特别是如果存在继承或父/子关系,因为这将导致你的数据存储方案的改变。
最简单的方法是通过一个中间(临时)表来实现。
创建一个具有相同结构的新类,但有一个新的主键。使用SQL(不是合并命令)将数据从旧的类中移到它里面。删除旧类中的数据/索引,然后改变其中的主键。使用合并命令,将数据从新类移到旧类中。删除带有数据的新类。重建索引(如果有的话)。
几个有用的链接: MERGE
文章
Jingwei Wang · 十二月 30, 2021
可以使用内嵌REST API用描述文件生成REST服务
请求消息如下:
POST: http://[YourServer]/api/mgmnt/v2/INTEROP/cmAPI
Body: API 描述文件,例如下面的Json文件Basic Authorization Username: 用户名
Basic Authorization Password: 密码
Content-Type Header: application/json
** 注意**:调用接口前,需要创建相应命名空间,本示例为INTEROP
API 描述文件:
{
"swagger": "2.0",
"info": {
"description": "An API for coffee sales using InterSystems IRIS",
"version": "1.0.0",
"title": "Coffee Maker API",
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
},
"schemes": [
"https"
],
"paths": {
"/coffeemakers": {
"post": {
"description": "Returns all coffeemakers\n",
"operationId": "QueryAll",
"produces": [
"application/json"
],
"parameters": [],
"responses": {
"200": {
"description": "Success"
},
"500": {
"description": "Server error"
}
}
}
},
"/newcoffeemaker": {
"post": {
"description": "Add a new coffeemaker to the store. ID is autogenerated. Other info must be provided in the request body. Name and brand are required fields. Returns new coffeemaker\n",
"operationId": "NewMaker",
"produces": [
"application/json"
],
"parameters": [
{
"in": "body",
"name": "body",
"required": true,
"schema": {
"$ref": "#/definitions/CoffeeMaker"
}
}
],
"responses": {
"200": {
"description": "Success"
},
"400": {
"description": "Invalid message body"
},
"500": {
"description": "Server error"
}
}
}
},
"/coffeemaker": {
"post": {
"description": "Retrieve existing coffeemaker given ID and data. Returns coffeemaker\n",
"operationId": "QueryMaker",
"produces": [
"application/json"
],
"parameters": [
{
"name": "id",
"in": "query",
"description": "CoffeemakerID",
"required": true,
"type": "integer"
},
{
"name": "prod",
"in": "query",
"required": false,
"type": "boolean"
}
],
"responses": {
"200": {
"description": "Success"
},
"404": {
"description": "Coffeemaker not found"
},
"500": {
"description": "Server error"
}
}
},
"put": {
"description": "Update existing coffeemaker given ID and data. Returns updated coffeemaker\n",
"operationId": "EditMaker",
"produces": [
"application/json"
],
"parameters": [
{
"name": "id",
"in": "query",
"description": "CoffeemakerID",
"required": true,
"type": "integer"
},
{
"in": "body",
"name": "body",
"description": "coffeemaker info",
"required": true,
"schema": {
"$ref": "#/definitions/CoffeeMaker"
}
},
{
"name": "prod",
"in": "query",
"required": false,
"type": "boolean"
}
],
"responses": {
"200": {
"description": "Success"
},
"400": {
"description": "Invalid message body"
},
"404": {
"description": "Coffeemaker not found"
},
"500": {
"description": "Server error"
}
}
},
"delete": {
"description": "Delete existing cofffeemaker given ID. Returns deleted coffeemaker\n",
"operationId": "RemoveMaker",
"produces": [
"application/json"
],
"parameters": [
{
"name": "id",
"in": "query",
"description": "CoffeemakerID",
"required": true,
"type": "integer"
},
{
"name": "prod",
"in": "query",
"required": false,
"type": "boolean"
}
],
"responses": {
"200": {
"description": "Success"
},
"404": {
"description": "Coffeemaker not found"
}
}
}
}
},
"definitions": {
"CoffeeMaker": {
"type": "object",
"properties": {
"Name": {
"type": "string"
},
"Brand": {
"type": "string"
},
"Price": {
"type": "number"
},
"NumCups": {
"type": "integer"
},
"Color": {
"type": "string"
},
"Img": {
"type": "string"
}
}
}
}
}
返回消息:
{
"msg": "New application cmAPI created"
}
公告
Michael Lei · 八月 13, 2022
嗨,开发者们。
不要错过这个由InterSystems Healthcare副总裁@Donald.Woodlock主持的动手实践环节:
⏯ Machine Learning 201 - Neural Networks and Image Recognition
观看如何训练机器学习模型来做图像分类。几十年来,机器学习试图解决的最初的经典问题之一是如何在图片中区分狗和猫--这甚至是一个小孩子都能做到的事,但计算机却发现它非常困难。几十年后,这个问题被解决了,并为机器学习现在善于阅读放射学图像、识别人脸、为自动驾驶汽车识别物体类型、从卫星图像识别森林砍伐以及其他各种用例铺平了道路。我们将在这个实践环节中学习如何做到这一点。特别是,我们将解决识别手写数字的问题。我们将陆续建立更复杂的模型来提高这项任务的准确性,包括逻辑回归、前馈神经网络和卷积神经网络。
这是一个2小时的会议,通过虚拟会议的方式现场录制,有一些与会者参加。不要求有机器学习或python的经验,但最好不要讨厌编程。
你将需要一个Kaggle账户(http://www.kaggle.com)来跟上这个视频。该账户需要经过 "手机验证",以便使用Kaggle的GPU功能,这是其中一个练习所需要的。
作为本课的一部分,你将需要的链接是:https://www.kaggle.com/competitions/digit-recognizer笔记本的链接是:http://www.donwoodlock.com/ml201/25Jul2022/index.html
祝您愉快,敬请期待!
文章
Michael Lei · 八月 31, 2022
背景Background
大多数网站都有一个 "Fav.ico "文件,用于设置网页的图标。大多数用户有多个环境,开发、测试和生产环境。通常情况下,你很难一眼就看出你在哪个环境中。如果能直观地通过图标看到你所处的版本和环境,可以提供更好的用户体验。在这个例子中,所有的实例都被命名为 "ENSEMBLE"。注意,这是在2022.1上使用的IRIS FOR HEALTH。
默认图标是 IR
在这篇文章中,我们将把标识改为类似于以下的内容:
图标文件
图标文件安装在你的安装文件夹csp/broker/portal中
创建一个名为Archive的文件夹放在该文件夹中
复制并粘贴ISC_IRIS_icon.ico到这个文件夹,对旧图标进行备份
使用一个图标编辑器。我使用了在线创建和编辑ICO文件| RedKetchup,因为它很容易使用,并且有简单的文本选项。
将.ico文件复制到你的本地文件,并打开它(Icon-> Open)
5. 采取铅笔工具。清除任何旧的字母(提示:改变铅笔大小可以更容易操作)。
6. Click test. Set colour. Play around with the font. DON'T FORGET TO PRESS APPLY
7. 保存图标.
8. 在一个全新安装的文件夹(DRIVELETTER:\InterSystems\HealthConnect\CSP\broker)中替换图标
9. 重新加载管理门户。. 注意你可能需要在chrome/edge上做一个硬刷新页面(通过按shift同时点击重新加载按钮)。
请注意,上述步骤在升级时可能不会生效,保存任何使用的图标,以便你可以再次更换标识。
问题
sun yao · 十二月 27, 2022
如下图,系统表或代码中是否有相关方法可直接解析BP中的swith分支内容,实现接口的自动统计相关功能另:当前版本是否有已封装的页面,方便用户操作查看消息等功能 您好,对于BPL中的成分,由于其在代码中以XML记录,可以通过解析该类中xdata中的xml代码段,基于xml文本提取出switch标签中的内容进行解析可以获得其中的内容,可参考https://docs.intersystems.com/irisforhealth20222/csp/docbook/Doc.View.cls?KEY=GOBJ_xdata
但需要理解的是,基于静态代码分析得到的结果是理论上的分支,不一定能代表实际的业务运行情况,例如在代码有bug导致分支无效的情况等。而所有流经IRIS的消息都被持久化在消息标中,基于消息表通过SQL进行统计更能反映实际的业务运行状况。可参考:https://cn.community.intersystems.com/post/intersystems-%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E4%BA%92%E6%93%8D%E4%BD%9C%E5%8A%9F%E8%83%BD%E8%BF%90%E8%A1%8C%E7%BB%B4%E6%8A%A4%E7%AE%A1%E7%90%86%E5%9F%BA%E7%A1%80-%E4%BA%92%E6%93%8D%E4%BD%9C%E6%B6%88%E6%81%AF%E7%AE%A1%E7%90%86https://cn.community.intersystems.com/post/%E9%9B%86%E6%88%90%E4%BA%A7%E5%93%81%E7%9A%84%E4%B8%9A%E5%8A%A1%E8%A1%8C%E4%B8%BA%E7%9B%91%E6%8E%A7
另外,IRIS所有产品都带有查看消息的web界面,可以从portal中访问,也可以作为一个url被集成到用户自己的应用中去:
文章
Frank Ma · 三月 2, 2022
各位好,
你曾建立过一个镜像环境吗?它是否有一个私有网络、虚拟IP地址和SSL配置?
在做了几次之后,我意识到这是一个漫长的过程,而且需要很多手动操作来生成证书和配置每个IRIS实例。
对于经常要做这件事的人来说,这是一个痛苦的过程。
例如,质量保证团队可能需要为每个新的应用程序版本创建一个新的镜像环境来测试。支持团队可能需要创建一个镜像环境来重现一个复杂的问题。
我们肯定需要工具来快速创建这些镜像环境。
在这篇文章中,我们将用如下环境创建一个镜像样例:
- 仲裁机
- 主服务器
- 故障切换备份成员
- 读写报告异步成员
- 节点间日志转移的SSL配置
- 镜像环境中的私有网络
- 虚拟IP地址
- 镜像数据库

乍一看,它似乎有点复杂,看起来需要大量的代码,但不要担心。
在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
文章
Hao Ma · 五月 31, 2021
# 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,意思是”服务器已接受请求,但尚未处理“. 默认是不选中这个配置。
文章
jieliang liu · 一月 8, 2021
你好!
本文简单介绍一款工具,帮您理解InterSystems产品(从IRIS到Caché、Ensemble以及HealthShare)中的类及其结构。
简言之,它将类或整个包可视化,显示类之间的关系,并向开发人员和团队领导提供各种信息,而无需到 Studio 中检查代码。
如果您正在学习InterSystems产品,经常查看项目,或只对InterSystems技术解决方案中的新内容感兴趣,欢迎阅读ObjectScript类浏览器概述!
InterSystems 产品简介
IRIS(之前称为Caché) 是一个多模型DBMS。您可以使用SQL查询来访问它,也可以通过各种编程语言可用的接口与存储的对象和过程进行交互。但最多的还是使用DBMS原生内置语言--ObjectScript (COS) 开发应用程序。
Caché支持DBMS级别的类。有两种主要的类类型:Persistent(可以存储在数据库中)和 Registered(不存储在数据库中,扮演程序和处理程序的角色)。还有几种特殊的类类型:Serial(可集成到持久类中用于创建复杂数据类型(如:地址)的类),DataType(用于创建用户定义的数据类型)、Index、View 和 Stream。
进入类浏览器
Caché类浏览器是一个工具,它将Caché类的结构可视化为图表,显示类之间的依赖关系和所有相关信息,包括各种类元素的方法代码、查询、xData块、注释、文档和关键字。
功能
类浏览器使用扩展版UML类图进行可视化,因为Caché有一组很重要但不被标准UML支持的附加实体:查询、xData块、方法和属性的大量关键字(如System、ZenMethod、Hidden、ProcedureBlock等)、父子关系和一多关系、类类型等。
Caché 类浏览器(1.14.3版)允许您执行以下操作:
显示包、类图或整个包的层次结构;
编辑图表显示后的外观;
保存类图的当前图像;
保存一个图表的当前外观,并在将来恢复;
按图表或类树中显示的任何关键字搜索;
使用工具提示获得关于类、其属性、方法、参数、查询和xData块的完整信息;
查看方法、查询或xData块的代码;
启用或禁用任何图表元素的显示,包括图形图标。
为了更好理解下文内容,先看下类浏览器的如何对类进行可视化的。举个例子,让我们显示来自“samples”命名空间的“cinema”包:
详细信息和功能概述
左侧边栏包含一个包树。将鼠标指针放在包名称上,然后单击出现在其右侧的按钮以显示整个包。在包树中选择类,将其与其链接的类一起呈现。
类浏览器可以显示类之间的几种依赖关系类型:
1. 继承。以白色实心箭头显示,箭头指向继承的类;
2. 类之间的“关联”或关系。如果其中一个类的字段包含另一个类的类型,图表构建器将把它显示为关联关系;
3. 父子关系和一多关系:维护数据完整性的规则。
如果将鼠标指针指向该关系,则创建该关系的属性将高亮显示:
注意,类浏览器不会更深入,也不会为当前包之外的类绘制依赖关系。它将只显示当前包中的类,如果需要限制类浏览器查找类的深度,请使用“依赖级别”设置:
类本身显示为一个矩形,分为六个部分:
1. 类名:将鼠标指针指向类名,可以了解它是何时创建和修改的,查看注释以及所有分配给这个类的关键字。双击类头将打开其文档;
2. 类参数:所有带类型、关键字和注释的赋值参数。斜体的参数以及任何属性都有工具提示并且可以悬停;
3. 类属性与参数类似;
4. 方法:点击任何方法都可以查看其源代码。COS 语法将被高亮显示;
5. 查询:与方法类似--点击可以查看源代码;
6. xData块:主要包含XML数据的块。点击将显示块中格式化的源代码。
默认每个类都显示有许多图形图标。点击屏幕右上角的“帮助”按钮,了解每个图标的含义。如果您需要默认显示更严格或更包容的 UML 类图,以及任何类的任何部分,可以在设置部分禁用。
如果关系图太大,而且您也不太熟悉,可以使用快速图表搜索功能。包含您输入的关键字的任何部分的类将被高亮显示。要跳转到下一个匹配项,只需按 Enter 键或再次单击搜索按钮:
最后,在完成了图表编辑,去掉所有不必要关系,并将元素安放好,实现期望的外观之后,可以点击左下角的“下载”按钮来保存:
激活固定按钮 时,元素在当前类(或包)组的图表上的位置将被固定。例如,如果您选择A类和B类,然后用固定按钮保存视图,则再次选择A类和B类时将看到完全相同的视图,即使在重新启动浏览器或机器之后也不会变化。但是如果您只选择A类,布局将是默认的。
安装
要安装Caché类浏览器,只需将最新版本xml包导入到任何命名空间中。导入后就能看到名为hostname/ClassExplorer/(末尾的斜杠不能丢)的新web应用程序。
详细安装说明
1. 下载最新版Caché类浏览器压缩包;
2. 提取名为Cache/CacheClassExplorer-vX.X.X.xml的XML文件;
3. 使用以下方法之一将包导入任何命名空间:
1. 只需将XML文件拖到Studio上;
2. 使用系统管理门户:系统资源管理器 -> 类 -> 导入,并指定本地文件路径;
3. 使用terminal命令:
do ##class(%Installer.Installer).InstallFromCommandLine(“Path/Installer.cls.xml”);
4. 读取导入日志--如果顺利安装,就可以通过 http://hostname/ClassExplorer/ 打开 web 应用程序。如果出了问题,请检查以下内容:
1. 是否有足够权限将类导入该命名空间;
2. web应用程序用户是否有足够权限访问不同的命名空间;
3. 如果出现错误404,只需检查是否在 URL 末尾添加了斜杠。
附加截图
[截图 1] DSVRDemo 包,鼠标指针悬停在一个类名上。
[截图 2] DataMining 包,在图表中搜索“TreeInput”关键字。
[截图 3] JavaDemo.JavaListSample 类中的方法代码视图。
[截图 4] 查看 ClassExplorer.Router 类中的 Xdata 块内容。
您可以尝试在标准SAMPLES命名空间中使用类浏览器:演示。这个项目的评测视频。
欢迎任何反馈、建议和意见--提交到这里或GitHub仓库。希望对您有用!
文章
姚 鑫 · 五月 23, 2021
# 第三章 发送HTTP请求
# 发送HTTP请求
创建HTTP请求后,使用以下方法之一发送该请求:
### Delete()
```java
method Delete(location As %String = "",
test As %Integer = 0,
reset As %Boolean = 1) as %Status
```
发出HTTP DELETE请求。
### Get()
```java
method Get(location As %String = "",
test As %Integer = 0,
reset As %Boolean = 1) as %Status
```
发出HTTP GET请求。此方法使Web服务器返回请求的页面。
### Head()
```java
method Head(location As %String,
test As %Integer = 0,
reset As %Boolean = 1) as %Status
```
发出HTTP Head请求。此方法使Web服务器仅返回响应头,而不返回正文。
### Patch()
```java
method Patch(location As %String = "",
test As %Integer = 0,
reset As %Boolean = 1) as %Status
```
发出HTTP修补程序请求。使用此方法可以对现有资源进行部分更改。
### Post()
```java
method Post(location As %String = "",
test As %Integer = 0,
reset As %Boolean = 1) as %Status
```
发出HTTP POST请求。使用此方法可将数据(如表单结果)发送到Web服务器,或上载文件。有关示例,请参阅“发送表单数据”。
### Put()
```java
method Put(location As %String = "",
test As %Integer = 0,
reset As %Boolean = 1) as %Status
```
发出HTTP PUT请求。使用此方法将数据上载到Web服务器。PUT请求并不常见。
### Send()
```java
method Send(type As %String,
location As %String,
test As %Integer = 0,
reset As %Boolean = 1) as %Status
```
将指定类型的HTTP请求发送到服务器。此方法通常由其他方法调用,但如果要使用不同的HTTP谓词,则提供此方法以供使用。此处`type`是指定HTTP谓词(如`“POST”`)的字符串。
在所有情况下:
- 每个方法都返回一个状态,应该检查该状态。
- 如果该方法正确完成,则对此请求的响应将位于`HttpResponse`属性中。
- `Location`参数是要请求的URL,例如:`"/test.html"`。
- `Location`参数可以包含参数,假定这些参数已经URL转义,例如:`"/test.html?PARAM=%25VALUE"`将`PARAM`设置为等于`%VALUE`。
- 使用test参数检查正在发送的是您预期要发送的内容:
- 如果test为1,则该方法不会连接到远程计算机,而是将其本应发送到Web服务器的内容输出到当前设备。
- 如果`test`为`2`,则在发出`HTTP`请求后将响应输出到当前设备。
- 在从服务器读取响应后,每个方法都会自动调用`Reset()`方法,除非`test=1`或`Reset=0`。
`Reset()`方法重置`%Net.HttpRequest`实例,以便它可以发出另一个请求。这比关闭此对象并创建新实例要快得多。这还会将`Location`标头的值移动到`Referer`标头。
```java
Set httprequest=##class(%Net.HttpRequest).%New()
Set httprequest.Server="www.intersystems.com"
Do httprequest.Get("/")
```
# 创建和发送多部分POST请求
要创建和发送多部分`POST`请求,请使用`%Net.MIMEPart`类,本书后面将详细讨论这些类。下面的示例发送包含两个部分的`POST`请求。第一部分包括文件二进制数据,第二部分包括文件名。
```java
ClassMethod CorrectWriteMIMEMessage3(header As %String)
{
// Create root MIMEPart
Set RootMIMEPart=##class(%Net.MIMEPart).%New()
//Create binary subpart and insert file data
Set BinaryMIMEPart=##class(%Net.MIMEPart).%New()
Set contentdisp="form-data; name="_$CHAR(34)_"file"_$CHAR(34)_"; filename="
_$CHAR(34)_"task4059.txt"_$CHAR(34)
Do BinaryMIMEPart.SetHeader("Content-Disposition",contentdisp)
Set stream=##class(%FileBinaryStream).%New()
Set stream.Filename="/home/tabaiba/prueba.txt"
Do stream.LinkToFile("/home/tabaiba/prueba.txt")
Set BinaryMIMEPart.Body=stream
Do BinaryMIMEPart.SetHeader("Content-Type","text/plain")
// Create text subpart
Set TextMIMEPart=##class(%Net.MIMEPart).%New()
Set TextMIMEPart.Body=##class(%GlobalCharacterStream).%New()
Do TextMIMEPart.Body.Write("/home/tabaiba/prueba.txt")
// specify some headers
Set TextMIMEPart.ContentType="text/plain"
Set TextMIMEPart.ContentCharset="us-ascii"
Do TextMIMEPart.SetHeader("Custom-header",header)
// Insert both subparts into the root part
Do RootMIMEPart.Parts.Insert(BinaryMIMEPart)
// create MIME writer; write root MIME message
Set writer=##class(%Net.MIMEWriter).%New()
// Prepare outputting to the HttpRequestStream
Set SentHttpRequest=##class(%Net.HttpRequest).%New()
Set status=writer.OutputToStream(SentHttpRequest.EntityBody)
if $$$ISERR(status) {do $SYSTEM.Status.DisplayError(status) Quit}
// Now write down the content
Set status=writer.WriteMIMEBody(RootMIMEPart)
if $$$ISERR(status) {do $SYSTEM.Status.DisplayError(status) Quit}
Set SentHttpRequest.Server="congrio"
Set SentHttpRequest.Port = 8080
Set ContentType= "multipart/form-data; boundary="_RootMIMEPart.Boundary
Set SentHttpRequest.ContentType=ContentType
set url="alfresco/service/sample/upload.json?"
_"alf_ticket=TICKET_caee62bf36f0ea5bd51194fce161f99092b75f62"
set status=SentHttpRequest.Post(url,0)
if $$$ISERR(status) {do $SYSTEM.Status.DisplayError(status) Quit}
}
```
# 访问HTTP响应
发送`HTTP`请求后,请求的`HttpResponse`属性将更新。此属性是`%Net.HttpResponse`的实例。本节介绍如何使用`Response`对象。它包括以下主题:
## 访问响应的数据
HTTP响应的正文包含在响应的Data属性中。此属性包含流对象(特别是`%GlobalBinaryStream`)。要使用此流,请使用标准流方法:`Write()`、`WriteLine()`、`Read()`、`ReadLine()`、`Rewind()`、`MoveToEnd()`和`Clear()`。还可以使用流的`Size`属性。
请求的`ReadRawMode`属性控制如何读取响应正文。
- 默认情况下,此属性为False,并且InterSystems IRIS假定正文在响应的`HTTP`标头中指定的字符集内(并相应地转换该字符集)。
- 如果此属性为true,InterSystems IRIS将以原始模式读取正文(不执行字符集转换)。
还可以使用`OutputToDevice()`方法,该方法将完整响应写入当前设备。标头的顺序与Web服务器生成的顺序不同。
下面是一个简单的示例,在该示例中,我们将响应流复制到文件并保存:
```java
/// w ##class(PHA.TEST.HTTP).Stream()
ClassMethod Stream()
{
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")
if $$$ISERR(status) {
do $system.OBJ.DisplayError()
} else {
set response=request.HttpResponse
}
Set file=##class(%FileCharacterStream).%New()
set file.Filename="e:/temp/rfc7158.txt"
set status=file.CopyFrom(response.Data)
if $$$ISERR(status) {
do $system.OBJ.DisplayError()
}
do file.%Close()
q ""
}
```
## 按名称获取HTTP标头
`%Net.HttpResponse`类将其`HTTP`标头存储在InterSystems IRIS多维数组中。要访问标头,请使用以下方法:
### GetHeader()
返回给定头的值。
### GetNextHeader()
返回给定标头之后的下一个标头的名称。
这些方法中的每一个都只有一个参数,即HTTP标头的名称字符串。
还可以使用`OutputHeaders()`方法,该方法将HTTP标头写入当前设备(尽管它们的生成顺序不同)。
## 访问有关响应的其他信息
`%Net.HttpResponse` 类提供了存储HTTP响应其他特定部分的属性:
- `StatusLine`存储HTTP状态行,这是响应的第一行。
- `StatusCode`存储HTTP状态码。
- `ReasonPhrase`存储与`StatusCode`对应的人类可读的原因。
- `ContentInfo`存储关于响应体的附加信息。
- `ContentType`存储了`Content-Type:`标头的值。
- `HttpVersion`表示发送响应的web服务器所支持的HTTP版本。