搜索​​​​

清除过滤器
文章
Kelly Huang · 九月 3, 2023

在 Python 上使用IRIS REST API 进行 SQL 迁移

对于即将到来的Python 竞赛,我想制作一个小型演示,介绍如何使用 Python 创建一个简单的 REST 应用程序,该应用程序将使用 IRIS 作为数据库。使用这个工具 FastAPI框架,高性能,易学,快速编码,可用于生产 SQLAlchemy 是 Python SQL 工具包和对象关系映射器,为应用程序开发人员提供 SQL 的全部功能和灵活性 Alembic 是一个轻量级数据库迁移工具,可与 SQLAlchemy Database Toolkit for Python 一起使用。 Uvicorn 是 Python 的 ASGI Web 服务器实现。 准备环境 假设已经安装了Python,至少是3.7版本。创建一个项目文件夹,并在其中创建一个包含以下内容的requirements.txt文件 fastapi== 0.101 .1 alembic== 1.11 .1 uvicorn== 0.22 .0 sqlalchemy== 2.0 .20 sqlalchemy-iris== 0.10 .5 我建议在 python 中使用虚拟环境,让我们创建新环境并激活它 python -m venv env && source env/bin/activate 现在我们可以安装我们的依赖项 pip install -r requirements.txt 快速启动 让我们使用 FastAPI 创建最简单的 REST Api。为此,请创建app/main.py from fastapi import FastAPI app = FastAPI( title= 'TODO Application' , version= '1.0.0' , ) @app.get("/ping") async def pong () : return { "ping" : "pong!" } 此时就足以启动我们的应用程序,并且它应该已经可以工作了。要启动服务器,我们将使用uvicorn -----0----- 我们可以发出ping请求 -----1----- FastAPI 提供 UI,我们可以在其中测试 API。 Docker化环境 要将 IRIS 添加到我们的应用程序中,我们将使用容器。 IRIS 镜像将按原样运行,但我们需要为 python 应用程序构建 Docker 镜像。我们需要Dockerfile FROM python: 3.11 -slim-buster WORKDIR /usr/src/app RUN --mount= type = bind ,src=.,dst=. \ pip install --upgrade pip && \ pip install -r requirements.txt COPY . . ENTRYPOINT [ "/usr/src/app/entrypoint.sh" ] 要在容器内启动应用程序需要一个简单的entrypoint.sh #!/bin/sh # Run SQL Migrations, to make DB Schema up to date alembic upgrade head # Start Python application uvicorn app.main:app \ --workers 1 \ --host 0.0.0.0 \ --port 8000 " $@ " 不要忘记添加执行标志 chmod +x entrypoint.sh 并在docker-compose.yml中与 IRIS 结合。 version: "3" services: iris: image: intersystemsdc/iris-community ports: - 1972 environment: - IRISUSERNAME=demo - IRISPASSWORD=demo healthcheck: test: /irisHealth.sh interval: 5 s app: build: . ports: - 8000 :8000 environment: - DATABASE_URL=iris://demo:demo@iris:1972/USER volumes: - ./:/usr/src/app depends_on: iris: condition: service_healthy command: - --reload 让我们来构建它 docker-compose build 第一个数据模型 现在让我们向应用程序声明对 IRIS 数据库的访问权限,添加文件app/db.py ,这将配置 SQLAlchemy 来访问我们的数据库,该数据库是通过 docker-compose.yml 传递的 URL 定义的。它包含几个处理程序,我们稍后将在应用程序中使用 import os from sqlalchemy import create_engine from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base from sqlalchemy.orm import sessionmaker DATABASE_URL = os.environ.get( "DATABASE_URL" ) if not DATABASE_URL: DATABASE_URL = "iris://demo:demo@localhost:1972/USER" engine = create_engine(DATABASE_URL, echo= True , future= True ) Base: DeclarativeMeta = declarative_base() SessionLocal = sessionmaker(autocommit= False , autoflush= False , bind=engine) def init_db () : engine.connect() def get_session () : session = SessionLocal() yield session 准备好定义我们应用程序中的第一个也是唯一一个模型。创建并编辑文件app/models.py ,它将使用 SQLAlchemy 定义模型,名为Todo ,包含三列: id、title 和 description 。 from sqlalchemy import Column, Integer, String, Text from app.db import Base class Todo (Base) : __tablename__ = 'todo' id = Column(Integer, primary_key= True , index= True ) title = Column(String( 200 ), index= True , nullable= False ) description = Column(Text, nullable= False ) 准备 SQL 迁移 在不断变化的世界中,我们知道我们的应用程序将来会得到改进,我们的表结构不是最终的,我们可以添加更多的表、列、索引等。在这种情况下,最好的方案是使用一些 SQL 迁移工具,它们有助于根据我们应用程序的版本升级数据库中的实际结构,并且使用这些工具,也有助于降级它,以防出现问题。虽然在这个项目中我们使用 Python 和 SQLAlchemy,但 SQLAlchemy 的作者提供了他的名为Alembic的工具,我们将在这里使用它。 我们需要使用我们的应用程序启动 IRIS 和容器,此时我们需要 bash,以便能够运行命令 -----2----- 运行命令alembic init app/migrations -----3----- 这准备好了 alembic 配置,我们需要修复它以满足我们的应用程序需求。为此,请编辑app/migrations/env.py文件。这只是文件的开头,应该更新,重点更新sqlalchemy.url和target_metadata 。下面的内容保持不变 import os import urllib.parse from logging.config import fileConfig from sqlalchemy import engine_from_config from sqlalchemy import pool from alembic import context # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config DATABASE_URL = os.environ.get( "DATABASE_URL" ) decoded_uri = urllib.parse.unquote(DATABASE_URL) config.set_main_option( "sqlalchemy.url" , decoded_uri) # Interpret the config file for Python logging. # This line sets up loggers basically. if config.config_file_name is not None : fileConfig(config.config_file_name) # add your model's MetaData object here # for 'autogenerate' support from app.models import Base target_metadata = Base.metadata # target_metadata = None 我们已经有了一个模型,现在我们需要使用命令alembic revision --autogenerate创建一个迁移 -----4----- 让我们看看生成的迁移 上面说它找到了一个新的表todo,以及所有索引,并生成了一个文件,我们可以看一下这个文件,不需要编辑它,但是我们可以看到,它包含了升级和降级的功能。 """empty message Revision ID: 1e4d3b4d51ca Revises: Create Date: 2023-08-22 07:08:01.586330 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '1e4d3b4d51ca' down_revision = None branch_labels = None depends_on = None def upgrade () -> None : # ### commands auto generated by Alembic - please adjust! ### op.create_table( 'todo' , sa.Column( 'id' , sa.Integer(), nullable= False ), sa.Column( 'title' , sa.String(length= 200 ), nullable= False ), sa.Column( 'description' , sa.Text(), nullable= False ), sa.PrimaryKeyConstraint( 'id' ) ) op.create_index(op.f( 'ix_todo_id' ), 'todo' , [ 'id' ], unique= False ) op.create_index(op.f( 'ix_todo_title' ), 'todo' , [ 'title' ], unique= False ) # ### end Alembic commands ### def downgrade () -> None : # ### commands auto generated by Alembic - please adjust! ### op.drop_index(op.f( 'ix_todo_title' ), table_name= 'todo' ) op.drop_index(op.f( 'ix_todo_id' ), table_name= 'todo' ) op.drop_table( 'todo' ) # ### end Alembic commands ### 现在是时候将其应用到数据库了,使用命令alembic Upgrade head ,其中 head 是升级到最新版本的关键字 -----5----- 降级 如果在升级应用过程中我们发现我们必须返回,我们可以降级数据库,例如1最后的修订版,将是head-1 -----6----- 并完全降级回空状态,使用关键字base 随时检查当前状态,如果缺少某些迁移将给出信息 -----7----- 使数据可访问 所以,我们现在可以回到 REST,我们需要它启动并运行,从当前容器退出并正常运行应用程序服务,uvicorn 有一个标志 --reload,所以,它将检查 python 中的更改文件,并在我们更改它们时重新启动 -----8----- FastAPI 使用 pydantic 项目来声明数据模式,我们也需要它,让我们创建app/schemas.py ,与 models.py 中的列相同,但采用简单的 Python 形式 from pydantic import BaseModel class TodoCreate (BaseModel) : title: str description: str class Todo (TodoCreate) : id: int class Config : from_attributes = True 在app/crud.py中声明增删改查操作,我们在其中使用 SQLAlchemy ORM 处理数据库 from sqlalchemy.orm import Session from . import models, schemas def get_todos (db: Session, skip: int = 0 , limit: int = 100 ) : return db.query(models.Todo).offset(skip).limit(limit).all() def create_todo (db: Session, todo: schemas.TodoCreate) : db_todo = models.Todo(**todo.dict()) db.add(db_todo) db.commit() db.refresh(db_todo) return db_todo 最后,我们可以更新app/main.py ,并添加读取和创建待办事项的路由 from fastapi import FastAPI, Depends from .db import init_db, get_session from . import crud, schemas app = FastAPI( title= 'TODO Application' , version= '1.0.0' , ) @app.on_event("startup") def on_startup () : init_db() @app.get("/ping") async def pong () : return { "ping" : "pong!" } @app.get("/todo", response_model=list[schemas.Todo]) async def read_todos (skip: int = 0 , limit: int = 100 , session=Depends (get_session) ) : todos = crud.get_todos(session, skip=skip, limit=limit) return todos @app.post("/todo", response_model=schemas.Todo) async def create_todo (todo: schemas.TodoCreate, session=Depends (get_session) ) : return crud.create_todo(db=session, todo=todo) 文档页面已相应更新,现在我们可以使用它了 试一试 添加新待办事项 并检查我们那里有什么 让我们在 IRIS 中检查一下 -----9----- 希望您在创建 REST 时享受使用 Python 和 FastAPI 的轻松体验。该项目的源代码可在 github 上找到:https://github.com/caretdev/fastapi-iris-demo 原贴作者:@ Dmitry Maslennikov
文章
Kelly Huang · 七月 12, 2023

当 GPT 与 FHIR 碰撞出火花:利用Open API 的规范力量

FHIR 通过提供标准化数据模型来构建医疗保健应用程序并促进不同医疗保健系统之间的数据交换,彻底改变了医疗保健行业。由于 FHIR 标准基于现代 API 驱动的方法,因此移动和 Web 开发人员更容易使用它。然而,与 FHIR API 交互仍然具有挑战性,尤其是在使用自然语言查询数据时。 隆重推出FHIR - AI 和 OpenAPI 链应用程序,该解决方案允许用户使用自然语言查询与 FHIR API 进行交互。该应用程序使用OpenAI 、 LangChain和Streamlit构建,简化了查询 FHIR API 的过程并使其更加用户友好。 FHIR OpenAPI 规范是什么? OpenAPI 规范(以前称为 Swagger,目前是OpenAPI Initiative的一部分)已成为软件开发领域的重要工具,使开发人员能够更有效地设计、记录 API 并与 API 交互。 OpenAPI 规范定义了一种标准的机器可读格式来描述 RESTful API,提供了一种清晰一致的方式来理解其功能并有效地使用它们。 在医疗保健领域,FHIR 成为数据交换和互操作性的领先标准。为了增强FHIR的互操作能力, HL7正式记录了FHIR OpenAPI规范,使开发人员能够将FHIR资源和操作无缝集成到他们的软件解决方案中。 FHIR OpenAPI 规范的优点: 标准化 API 描述:OpenAPI 规范提供 FHIR 资源、操作和交互的全面且标准化的描述。开发人员可以轻松了解基于 FHIR 的 API 的结构和功能,从而更轻松地构建集成并与医疗保健系统交互。 促进互操作性:促进开发人员之间的协作,推动 FHIR 标准和最佳实践的采用。该规范提供了一种通用语言和框架,用于讨论基于 FHIR 的集成和实现,促进开发人员之间的协作。 增强的文档和测试:交互式文档和测试套件,以便更好地理解和验证。开发人员可以创建详细的API文档,使其他开发人员更容易理解和使用基于FHIR的API。基于规范的测试套件可以对API集成进行全面的测试和验证,确保医疗数据交换的可靠性和准确性。 改进的开发人员体验:自动生成客户端库和 SDK 以实现无缝集成。这简化了集成过程,并减少了将 FHIR 功能合并到应用程序中所需的时间和精力 FHIR、OpenAI 和 OpenAPI Chain 如何协同工作? FHIR - AI 和 OpenAPI Chain应用程序利用 LangChain 来加载和解析 OpenAPI 规范( OpenAPI Chain )。之后,根据这些规范,通过 OpenAI 给出的提示链旨在理解自然语言查询并将其转换为适当的 FHIR API 请求。用户可以用简单的语言提出问题,应用程序将与所选的 FHIR API 交互以检索相关信息。 例如,用户可能会问:“患者 John Doe (ID 111) 的最新血压读数是多少?”然后,应用程序会将此查询转换为 FHIR API 请求,获取所需的数据,并以易于理解的格式将其呈现给用户。 FHIR - AI 和 OpenAPI 链的优势 用户友好的交互:通过允许用户使用自然语言查询与 FHIR API 交互,该应用程序使非技术用户可以更轻松地访问和分析医疗保健数据。 提高效率:该应用程序简化了查询 FHIR API 的过程,减少了获取相关信息所需的时间和精力。此外,它还有可能减少从应用程序中查找任何特定信息的点击次数(花费的时间)。 可定制:FHIR 标准简化了从任何 FHIR 服务器检索一致数据的过程,从而可以轻松定制。它可以轻松配置为与任何 FHIR API 无缝集成,为不同的医疗保健数据需求提供灵活且适应性强的解决方案。 FHIR 入门 - AI 和 OpenAPI 链 要开始使用 FHIR - AI 和 OpenAPI Chain 应用程序,请按照以下步骤操作: 从OpenAI Platform获取 OpenAI API 密钥。 获取 FHIR 服务器 API 端点。您可以使用自己的示例 FHIR 服务器(需要未经身份验证的访问),也可以按照InterSystems IRIS FHIR 学习平台中给出的说明创建临时示例服务器。 在线试用该应用程序或使用提供的说明在本地进行设置。 通过集成人工智能和自然语言处理功能,FHIR - AI 和 OpenAPI Chain 应用程序提供了一种与 FHIR API 交互的更直观的方式,使所有技术背景的用户都更容易访问和分析医疗数据。 如果您发现我们的应用程序很有前途,请在大奖赛中投票! 如果您能想到使用此实现的任何潜在应用程序,请随时在讨论线程中分享它们。 @Ikram Shah 致敬原创作者~
文章
姚 鑫 · 六月 4, 2023

第二十四章 开发Productions - ObjectScript Productions - 定义业务服务

# 第二十四章 开发Productions - ObjectScript Productions - 定义业务服务 本页介绍如何定义业务服务类。 提示: `IRIS® `提供使用特定入站适配器的专用业务服务类,其中之一可能适合需要。如果是这样,则不需要编程。有关部分列表,请参阅 `Introducing Interoperability Productions` 中的连接选项。 # 介绍 业务服务负责接受来自外部应用程序的请求到 `IRIS`。下图显示了它是如何工作的: 请注意,此图仅显示数据的输入流,而不是可选响应。 业务服务负责以下活动: - 等待特定的外部事件(例如来自应用程序的通知、收到 `TCP` 消息等)。 - 读取、解析和验证伴随此类事件的数据, - 如果需要,返回对外部应用程序的确认,表明已收到事件。 - 创建请求消息的实例并将其转发到适当的业务流程或业务操作以进行处理。 业务服务的目的通常是接收数据输入。在大多数情况下,业务服务有一个与之关联的入站适配器。但是,在某些情况下不需要适配器,因为应用程序能够将请求消息发送到服务中,或者因为业务服务已被编写为处理特定类型的外部调用,例如来自复合应用程序的调用。这种类型的业务服务称为无适配器业务服务。 当业务服务具有入站适配器时,它处于数据拉取(而不是推送)模式。在这种模式下,业务服务会定期轮询适配器,看它是否有数据。同时,如果适配器随时遇到输入数据,它会调用业务服务来处理输入。 当业务服务没有适配器时,它不会拉取数据。相反,客户端应用程序调用业务服务并告诉它处理输入(这是一种数据推送模式)。 # 关键原则 首先,务必阅读 `Programming in InterSystems IRIS`。 在业务服务中,可以访问关联适配器的属性和方法,这些适配器作为业务服务的 Adapter 属性提供。这意味着可以更改适配器的默认行为;这样做可能合适也可能不合适。记住封装原则很有用。封装的思想是适配器类应该负责技术特定的逻辑,而业务服务类应该负责生产特定的逻辑。 如果发现有必要在业务服务类中大量或频繁地改变适配器类的行为,那么创建适配器类的自定义子类可能更合适。请参阅不太常见的任务。 这个原则也适用于商业运作。 # 定义业务服务类 要创建一个业务服务类,定义一个类如下: - 类必须在(或子类)中扩展 `Ens.BusinessService`。 - 在类中,`ADAPTER` 参数必须等于此业务服务要使用的适配器类的名称。 提示:如果只是希望业务服务定期唤醒和运行而不关心 `IRIS` 外部的事件,请使用适配器类 `Ens.InboundAdapter`。 - 类必须实现 `OnProcessInput()` 方法,如实现 `OnProcessInput()` 方法中所述。 - 类可以添加或删除设置。请参阅添加和删除设置。 - 类可以实现任何或所有启动和拆卸方法。请参阅覆盖启动和停止行为。 - 类可以包含完成自身内部工作的方法。 有关业务服务类的示例,请参阅适配器指南。 # 实施 `OnProcessInput()` 方法 在业务服务类中, `OnProcessInput()` 方法可以具有以下通用签名: ```java Method OnProcessInput(pInput As %RegisteredObject, pOutput As %RegisteredObject) As %Status ``` 这里的`pInput`是适配器要发送给这个业务服务的输入对象,`pOutput`是输出对象。 首先查看选择的适配器类。 建议编辑 `OnProcessInput()` 方法签名以使用适配器所需的特定输入参数。 `OnProcessInput()` 方法应该执行以下部分或全部操作: 1. 可选地设置业务服务类的属性(在任何适当的时间)。最受关注的业务服务属性是 `%WaitForNextCallInterval`。它的值控制 `IRIS` 调用适配器的 `OnTask()` 方法的频率。 有关其他属性,请参阅 `Ens.BusinessService`的类参考。 2. 如有必要,验证输入对象。 3. 检查输入对象并决定如何使用它。 4. 创建请求消息类的实例,这将是业务服务发送的消息。 5. 对于请求消息,使用输入对象中的值适当地设置其属性。 6. 确定要将请求消息发送到哪里。当发送消息时,将需要在生产中使用业务主机的配置名称。 7. 将请求消息发送到生产(业务流程或业务操作)中的目的地。请参阅下一节。 8. 确保设置输出参数 (`pOutput`)。通常,将其设置为等于您收到的响应消息。此步骤是必需的。 9. 返回适当的状态。此步骤是必需的。
文章
Lele Yang · 六月 8, 2023

没有虚拟 IP 地址的数据库镜像

++ 更新:2018 年 8 月 1 日 使用内置于 Caché 数据库镜像的 InterSystems 虚拟 IP (VIP) 地址有一定的局限性。特别是,它只能在镜像成员驻留在同一网络子网时使用。当使用多个数据中心时,由于增加了网络复杂性( 此处有更详细的讨论),网络子网通常不会“延伸”到物理数据中心之外。出于类似的原因,当数据库托管在云端时,虚拟 IP 通常无法使用。 负载均衡器(物理或虚拟)等网络流量管理设备可用于实现相同级别的透明度,为客户端应用程序或设备提供单一地址。网络流量管理器自动将客户端重定向到当前镜像主服务器的真实 IP 地址。自动化旨在满足灾难后 HA 故障转移和 DR 升级的需求。 网络流量管理器的集成 当今市场上有许多支持网络流量重定向的选项。这些中的每一个都支持类似甚至多种方法来根据应用程序要求控制网络流量。为了简化这些方法,我们考虑了三个类别:数据库服务器调用 API、网络设备轮询或两者的组合。 下一节将概述这些方法中的每一个,并就如何将这些方法与 InterSystems 产品集成提供指导。在所有情况下,仲裁器都用于在镜像成员无法直接通信时提供安全的故障转移决策。可以在此处找到有关仲裁器的详细信息。 出于本文的目的,示例图将描述 3 个镜像成员:主机、备份和 DR 异步。但是,我们知道您的配置可能比这更多或更少。 选项 1:网络设备轮询(推荐) 在这种方法中,网络负载均衡设备使用其内置的轮询机制与两个镜像成员通信以确定主镜像成员。 使用 2017.1 中可用的 CSP 网关的mirror_status.cxw页面的轮询方法可以用作 ELB 健康监视器中对添加到 ELB 服务器池的每个镜像成员的轮询方法。只有主镜像会响应“SUCCESS”,从而将网络流量仅定向到活动的主镜像成员。 此方法不需要向 ^ZMIRROR 添加任何逻辑。请注意,大多数负载均衡网络设备对运行状态检查的频率都有限制。通常,最高频率不少于 5 秒,这通常可以接受以支持大多数正常运行时间服务级别协议。 对以下资源的 HTTP 请求将测试本地缓存配置的镜像成员状态。 /csp/bin/mirror_status.cxw 对于所有其他情况,这些镜像状态请求的路径应该使用与请求真实 CSP 页面所用的相同的层次机制解析到适当的缓存服务器和名称空间。 示例:测试 /csp/user/ 路径中应用程序配置服务的镜像状态: /csp/user/mirror_status.cxw 注意:调用镜像状态检查不会消耗 CSP 许可证。 根据目标实例是否是活动主机,网关将返回以下 CSP 响应之一: ** 成功(是主镜像成员) =============================== HTTP/1.1 200 OK Content-Type: text/plain Connection: close Content-Length: 7 SUCCESS ** 失败(不是主镜像成员) =============================== HTTP/1.1 503 Service Unavailable Content-Type: text/plain Connection: close Content-Length: 6 FAILED ** 失败(Caché服务器不支持Mirror_Status.cxw请求) =============================== HTTP/1.1 500 Internal Server Error Content-Type: text/plain Connection: close Content-Length: 6 FAILED 考虑下图作为轮询的示例。 同步故障转移镜像成员之间自动发生故障转移: 下图演示了将 DR 异步镜像成员提升到负载均衡池中,这通常假设同一个负载均衡网络设备正在为所有镜像成员提供服务(地理分割方案将在本文后面介绍)。根据标准 DR 程序,灾难恢复成员的提升涉及人为决策,然后是数据库级别的简单管理操作。但是,一旦采取该操作,就不需要对网络设备执行任何管理操作:它会自动发现新的主要设备。 选项 2:数据库服务器调用 API 在这种方法中,使用了网络流量管理设备,它有一个用故障转移镜像成员和潜在的 DR 异步镜像成员定义的服务器池。 当镜像成员成为主镜像成员时,向网络设备发出 API 调用以调整优先级或权重,以立即指示网络设备将网络流量定向到新的主镜像成员。 相同的模型适用于在主镜像成员和备份镜像成员都不可用的情况下提升 DR 异步镜像成员。 此 API 在 ^ZMIRROR 代码中定义为过程调用的一部分: $$CheckBecomePrimaryOK^ZMIRROR() 在此过程调用中,插入可用于相应网络设备的任何 API 逻辑和方法,例如 REST API、命令行界面等。与虚拟 IP 一样,这是网络配置的突然更改,不涉及任何应用程序逻辑以通知连接到故障主镜像成员的现有客户端正在发生故障转移。根据故障的性质,这些连接可能由于应用程序超时或错误、新主实例强制旧主实例关闭或客户端使用的TCP 保持活动计时器过期造成的故障本身而关闭。 因此,用户可能必须重新连接并登录。您的应用程序的行为将决定此行为。 选项 3:地理分散部署 在具有多个数据中心和可能地理分散的部署(例如具有多个可用性区域和地理区域的云部署)的配置中,需要使用基于 DNS 的负载均衡和本地负载均衡在一个简单且易于支持的模型中考虑地理重定向实践。 通过这种组合模型,引入了与 DNS 服务配合使用的附加网络设备,如 Amazon Route 53、F5 Global Traffic Manager、Citrix NetScaler Global Server Load Balancing 或 Cisco Global Site Selector,在每个数据中心、可用性区域或云地理区域与网络负载均衡器相结合。 在此模型中,前面提到的轮询(推荐)或 API 方法在本地用于操作任何镜像成员(故障转移或 DR 异步)的位置。这用于向地理/全球网络设备报告它是否可以将流量定向到任一数据中心。同样在此配置中,本地网络流量管理设备将其自己的 VIP 提供给地理/全球网络设备。 在正常稳定状态下,活动主镜像成员向本地网络设备报告它是主镜像成员并提供“启动”状态。此“启动”状态被转发到地理/全球设备以调整和维护 DNS 记录,以将所有请求转发到此活动的主镜像成员。 在同一数据中心内的故障转移场景中(备份同步镜像成员成为主镜像成员),API 或轮询方法与本地负载均衡器一起使用,现在重定向到同一数据中心内的新主镜像成员。由于新的主镜像成员处于活动状态,因此本地负载均衡器仍以“启动”状态响应,因此未对地理/全局设备进行任何更改。 出于本示例的目的,API 方法在下图中用于本地集成到网络设备。 在使用 API 或轮询方法到不同数据中心(备用数据中心中的同步镜像或 DR 异步镜像成员)的故障转移场景中,新提升的主镜像成员开始向本地网络设备报告为主要成员。 在故障转移期间,曾经包含主镜像成员的数据中心现在不再从本地负载均衡器向地理/全球报告“Up”。地理/全球设备不会将流量定向到该本地设备。备用数据中心的本地设备将向地理/全球设备报告“Up”,并将调用 DNS 记录更新以现在定向到备用数据中心的本地负载均衡器提供的虚拟 IP。 选项 4:多层和地理分散的部署 为了使解决方案更进一步,引入了一个单独的 Web 服务器层,既可以作为私有 WAN 的内部,也可以通过 Internet 访问。此选项可能是大型企业应用程序的典型部署模型。 以下示例显示了使用多个网络设备安全隔离和支持 Web 和数据库层的示例配置。在此模型中,使用了两个地理位置分散的位置,其中一个位置被视为“主要”位置,另一个位置纯粹是数据库层的“灾难恢复”位置。数据库层灾难恢复位置将在主要位置因任何原因停止服务的情况下使用。此外,此示例中的 Web 层将显示为双活,这意味着用户将根据各种规则(例如最低延迟、最低连接数、IP 地址范围或您认为合适的其他路由规则)定向到任一位置。 如上例所示,如果在同一位置发生故障转移,则会发生自动故障转移,并且本地网络设备现在指向新的主机。用户仍然连接到任一位置的 Web 服务器, Web 服务器及其关联的 CSP 网关继续指向位置 A。 在下一个示例中,考虑在位置 A 发生的整个故障转移或中断,其中主要和备份故障转移镜像成员都无法使用。然后,DR 异步镜像成员将被手动提升为主要和备份故障转移镜像成员。在升级后,新指定的主镜像成员将允许位置 B 的负载均衡设备使用前面讨论的 API 方法(轮询方法也是一个选项)报告“Up”。由于本地负载均衡器现在报告“启动”,基于 DNS 的设备将识别这一点并将流量从位置 A 重定向到现在的位置 B 以用于数据库服务器服务。 结论 在没有虚拟 IP 的情况下设计镜像故障转移有许多可能的排列。这些选项可应用于最简单的高可用性场景或具有多层的多地理区域部署,包括故障转移和 DR 异步镜像成员,以获得高可用性和容灾解决方案,旨在为您的应用程序维持最高水平的运营弹性. 希望本文提供了一些关于成功部署具有故障转移的数据库镜像的可能的不同组合和用例的见解,这些组合和用例适合您的应用程序和可用性要求。
文章
Michael Lei · 六月 18, 2023

医疗行业的未来--数据与人的融合

在数字化时代,数据的重要性无可置疑。数据作为新型生产要素,不仅在宏观政策层面得到党和政府的大力推动,也是医院高质量发展的关键和改变医疗行业的驱动力。随着医疗信息化的迅猛发展,我们正迈向一个数据随处可及、人人可用易用的医疗信息化时代。这一时代将数据与人的需求相结合,致力于让数据能“主动”找到需要他们的医护人员和患者,每一个行业从业者,都应致力于为医护人员和患者提供简单易用的软件解决方案,减少工作量,提高效率,推动医疗行业的进步。 数据与人的融合是实现医疗行业数字化转型的核心。当然,医疗数据的收集、存储和管理对于提供高质量的医疗服务至关重要。然而,仅仅有大量的数据并不足够,我们需要将数据与人的需求紧密结合起来。这意味着我们应该让更多的数据关联起来,并且能服务于更多的人群,让患者能够随时随地访问他们的电子病历,让医生和科研人员也能及时有效地获取病人在医院围墙内外进行治疗和健康管理的数据,并且以直观易懂的方式呈现给医护人员和患者,使他们能够快速、准确地获取所需的信息。数据的融合还包括将不同来源的数据整合起来,为医护人员提供全面、完整的视图,同时基于医疗诊断的规则,不管是通过CDSS的形式,还是通过ChatBot(聊天机器人),帮助他们做出更好的决策。 实现数据和人的融合要按照人的需求投放数据。数字化转型的重要目标是为医护人员和患者提供所需的数据,以支持决策和治疗过程。这意味着我们应该了解用户的需求,将数据按照他们的角色、职责和关注点进行分类和投放。医生可能需要即时的患者数据、病历历史和最新的医学研究,而患者可能需要查看自己的健康记录、预约医生和接收个性化的健康建议。通过根据人的需求进行数据投放,新型软件可以提供个性化的服务和支持,形成千人千面,为每个用户提供有价值的信息。 简单易用是实现数字化转型成功的另一个关键。医护人员和患者使用的软件解决方案应该简单易用,不需要复杂的培训和技术知识。界面应该简单、直观、友好,操作流程简化和优化,以确保用户能够快速上手并高效地使用软件。简单易用的软件不仅能够减少用户的学习曲线和工作负担,还能提高用户满意度和工作效率。(比如Apple的医疗软件Apple Health,通过FHIR 技术,通过一个app能够连接数千家医院的病历数据,让患者可以通过一个app实现多家医院的互联网服务和数据整合) 无论是数字化转型还是高质量发展,软件为人服务始终是医疗信息化的核心宗旨。我们应该将软件看作是为人服务的工具,旨在帮助医护人员提供更好的医疗服务,提升患者的体验和健康结果。软件应该以用户体验为中心,并不断优化和改进,不断进行供给侧改革,以满足不断变化和不同人群的需求,而不是增加负担。 最后,数据会在安全可靠的前提下进行传递和流通。在互联网发展的早期时代,由于无法可依,野蛮生长,数据的滥用、隐私保护等存在很大问题。但随着《数据安全法》等法律法规的发布,相信未来的医疗行业数据一定会在更加安全、可靠、合规的前提下进行有序流动。 在未来的医疗信息化发展中,数据与人的关系将变得更加密不可分。通过数据的融合、按需投放、简单易用、安全可靠和以人为本的新一代软件,我们可以实现数据随处可及、人人可用易用的医疗信息化目标。这将为医护人员和患者提供更好的工作环境和医疗体验,推动整个医疗行业向前迈进。InterSystems公司作为创新性的数据平台解决方案供应商,我们始终致力于助力合作伙伴开发创新的解决方案,与合作伙伴一起共同实现这一愿景,改善医疗服务的质量和效率,提高患者体验的获得感的同时帮助医院降本增效,实现高质量发展。
文章
姚 鑫 · 六月 14, 2023

第五十七章 镜像中断程序 - 在手动故障转移之前确定备份是否处于活动状态

# 第五十七章 镜像中断程序 - 在手动故障转移之前确定备份是否处于活动状态 ## 在手动故障转移之前确定备份是否处于活动状态 假设有两个名为 `IRIS A` 和`IRIS B` 的故障转移成员。如果 `^MIRROR` 例程确认备份 (`IRIS B`) 在与主 (`IRIS A`) 丢失联系时处于活动状态,因此具有最新的来自 `IRIS A` 的日志数据,可以使用单个过程手动进行故障转移。当连接因主要故障而丢失时,不会造成数据丢失的风险。但是,当发生多个故障时,活动备份可能没有来自主服务器的所有最新日志数据,因为主服务器在连接丢失后继续运行了一段时间。 使用以下过程确定备份是否处于活动状态: 1. 确认 `IRIS` 实例 `IRIS A` 上的 `ISCAgent` 实际上已关闭(并确保它们在整个手动故障转移过程中保持关闭状态)。 2. 在 `IRIS B` 上,在终端的 `%SYS` 命名空间中运行 `^MIRROR` 例程(请参阅使用 `^MIRROR` 例程)。 3. 在主菜单中选择镜像管理,显示如下子菜单: ```java 1) Add mirrored database(s) 2) Remove mirrored database(s) 3) Activate or Catchup mirrored database(s) 4) Change No Failover State 5) Try to make this the primary 6) Connect to Mirror 7) Stop mirroring on this member 8) Modify Database Size Field(s) 9) Force this node to become the primary 10) Promote Async DR member to Failover member 11) Demote Backup member to Async DR member 12) Mark an inactive database as caught up 13) Manage mirror dejournaling on async member (disabled) 14) Pause dejournaling for database(s) ``` 4. 选择 `Force this node to become the primary` 选项。如果在联系丢失时备份处于活动状态,则会显示如下消息: ```java This instance was an active backup member the last time it was connected so if the primary has not done any work since that time, this instance can take over without having to rebuild the mirror when the primary reconnects. If the primary has done any work beyond this point (file #98), C:\InterSystems\MyIRIS\mgr\journal\MIRROR-GFS-20180815.009 then the consequence of forcing this instance to become the primary is that some operations may be lost and the other mirror member may need to be rebuilt from a backup of this node before it can join as a backup node again. Do you want to continue? ``` 如果有权访问主要文件的日志文件,则可以在继续之前确认引用的文件是最新的。 如果在与主服务器失去联系时备份未处于活动状态,则会显示如下消息: ```java Warning, this action can result in forcing this node to become the primary when it does not have all of the journal data which has been generated in the mirror. The consequence of this is that some operations may be lost and the other mirror member may need to be rebuilt from a backup of this node before it can join as a backup node again. Do you want to continue? ``` ## 手动故障转移到活动备份 如果 `^MIRROR` 例程的 `Force this node to become the primary` 选项确认备份在失去与主节点的连接时处于活动状态,请完成手动故障转移过程,如下所示: 1. 在要继续吗?提示继续该过程。 `Force this node to become the primary` 选项等待 `60` 秒以使镜像成员成为主要节点。如果操作未在 `60` 秒内成功完成,`^MIRROR` 报告操作可能未成功并指示您检查消息日志以确定操作是失败还是仍在进行中。 2. 一旦 `^MIRROR` 例程确认备份已成为主要备份,请在可以这样做时重新启动 `IRIS A`。当 `IRIS` 实例重新启动时, `IRIS A` 作为备份加入镜像。 ## 备份不活动时手动故障转移 即使 `^MIRROR` 例程未确认备份 ( `IRIS B`) 在与主 ( `IRIS A`) 失去连接时处于活动状态,仍然可以使用以下过程继续手动故障转移过程,但是如果这样做,会有数据丢失的风险。如本程序所述,可以在手动故障转移之前将最新的镜像日志文件从 `IRIS A`(如果有权访问)复制到 `IRIS` B,从而最大限度地降低这种风险。 1. 如果有权访问主服务器的镜像日志文件,请将最新的文件复制到 `IRIS B`,从 `IRIS B` 上的最新日志文件开始,然后包括来自 `IRIS A` 的任何后续文件。例如,如果 `MIRROR-MIRRORA-20180220.001`是 `IRIS B` 上的最新文件,复制 `MIRROR-MIRRORA-20180220.001` 和 `IRIS A` 上的任何更新文件。检查文件的权限和所有权,并在必要时更改它们以匹配现有日志文件。 2. 如果接受数据丢失的风险,请在提示时输入 `y` 以确认要继续;备份成为主要的。 `Force this node to become the primary` 选项等待 `60` 秒以使镜像成员成为主要节点。如果操作未在 `60` 秒内成功完成,`^MIRROR` 报告操作可能未成功并指示您检查消息日志以确定操作是失败还是仍在进行中。 3. 一旦 `^MIRROR` 例程确认备份已成为主要备份,请在可以这样做时重新启动 `IRIS A`。 - 如果 `IRIS A` 在 `IRIS` 实例重新启动时加入镜像作为备份,则不需要进一步的步骤。任何在故障成员上但不在当前主成员上的日志数据都已被丢弃。 - 如果在 `IRIS` 实例重新启动时 `IRIS A` 无法加入镜像,如重建镜像成员中描述的引用不一致数据的消息日志消息所示 `IRIS A` 上的最新数据库更改晚于最新的日志数据当 `IRIS B` 被迫成为主服务器时,它会出现在 `IRIS B` 上。要解决此问题,请按照该部分中的描述重建 `IRIS A`。
文章
Hao Ma · 五月 17, 2023

IRIS, Caché监控指导 - 指标监控(1)

Caché, IRIS在系统产生了最严重的问题时会产生错误信息并通知客户,但这并不足够。一是客户需要更多更灵活的通知消息,二是客户通常会有第3方的监控系统,因此得到Cache, IRIS的监控指标是必须的。 在所有的指标中,用户最关心的是以下几类: - 硬件资源的使用,CPU, 内存, IO性能 - 数据库使用的硬盘的占用 - Cache, IRIS Journal的硬盘占有 - Mirror的状态 - License的使用情况 - Caché的性能指标 除此之外,第3方监控系统还需要获得Caché的一些系统信息,比如版本,instance名字等等。 ## 指标的获得 有以下几个获得指标的方法 ### 1. 系统仪表板及其Web服务 Caché的系统仪表板显示的数据包括:系统性能;系统运行状态 (运行时间,上一次备份,数据库,Journal状况等; 事务和进程情况;软件许可使用情况;任务,ECP等,还有就是错误和警告的数量。 系统仪表板包含4个子面板:Global和Routine统计数据;ECP统计数据;磁盘和缓冲器统计数据;系统资源统计数据。(IRIS和Caché稍有不同,缺少了“磁盘和缓冲器统计数据”) 。它们分别对应主仪表板中的相应模块,给出了更详细的数据。 ![image](/sites/default/files/inline/images/image-20230517111905809.png) Monitoring Web Service默认随系统的启动而开启,远程用户或者第3方系统可以访问该服务得到所以的系统监控指标,也就是系统仪表板上的所有数据。用户还可以使用该服务订阅Caché的警告消息。访问地址是:http://localhost:57772/csp/sys/SYS.WSMon.Service.cls, 或者IRIS : http://localhost:52773/csp/sys/SYS.WSMon.Service.cls *Monitoring Web Service的WSDL* ![image](/sites/default/files/inline/images/image-20230517131516506.png) 调用服务方法的结果示例: GetDashboard() ```xml 204604.00 50183591134 2396232998 264876546 4040613709 60130 585372 77743.51 OK 0 OK 0 OK OK 10d 4h 38m Normal Normal Normal 18304233 Normal Normal 39 1 0 0 256 HealthShare 2018.1.1, Enterprise:256, Concurrent User, Platform Independent, Multi, DeepSee, DSV Reporting, NLP 2 4 1 2 ``` ### 2. REST API 这是IRIS的新特性,参考文档在[这里](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_rest)。 下面是我的一个测试环境的指标列表: ``` iris_cpu_usage 0 iris_csp_activity{id="127.0.0.1:52773"} 44 iris_csp_actual_connections{id="127.0.0.1:52773"} 7 iris_csp_gateway_latency{id="127.0.0.1:52773"} .772 iris_csp_in_use_connections{id="127.0.0.1:52773"} 1 iris_csp_private_connections{id="127.0.0.1:52773"} 0 iris_csp_sessions 1 iris_cache_efficiency 57.260 iris_db_expansion_size_mb{id="DEMO"} 0 iris_db_expansion_size_mb{id="ENSLIB"} 0 iris_db_expansion_size_mb{id="HCC"} 0 iris_db_expansion_size_mb{id="HSCUSTOM"} 0 iris_db_expansion_size_mb{id="HSLIB"} 0 iris_db_expansion_size_mb{id="HSSYS"} 0 iris_db_expansion_size_mb{id="IRISAUDIT"} 0 iris_db_expansion_size_mb{id="IRISLOCALDATA"} 0 iris_db_expansion_size_mb{id="IRISSYS"} 0 iris_db_expansion_size_mb{id="IRISTEMP"} 0 iris_db_expansion_size_mb{id="MOCKSYS"} 0 iris_db_expansion_size_mb{id="OEESP"} 0 iris_db_expansion_size_mb{id="SMART"} 0 iris_db_expansion_size_mb{id="USER"} 0 iris_db_free_space{id="DEMO"} 10 iris_db_free_space{id="ENSLIB"} 17 iris_db_free_space{id="HCC"} 11 iris_db_free_space{id="HSCUSTOM"} 9.5 iris_db_free_space{id="HSLIB"} 131 iris_db_free_space{id="HSSYS"} 9 iris_db_free_space{id="IRISAUDIT"} 8.7 iris_db_free_space{id="IRISLOCALDATA"} 19 iris_db_free_space{id="IRISSYS"} 9.3 iris_db_free_space{id="IRISTEMP"} 161 iris_db_free_space{id="MOCKSYS"} 8.7 iris_db_free_space{id="OEESP"} 6.9 iris_db_free_space{id="SMART"} 16 iris_db_free_space{id="USER"} 4.9 iris_db_latency{id="DEMO"} 0.566 iris_db_latency{id="ENSLIB"} 0.143 iris_db_latency{id="HCC"} 0.191 iris_db_latency{id="HSCUSTOM"} 0.163 iris_db_latency{id="HSLIB"} 0.153 iris_db_latency{id="HSSYS"} 0.143 iris_db_latency{id="IRISAUDIT"} 0.133 iris_db_latency{id="IRISSYS"} 0.200 iris_db_latency{id="IRISTEMP"} 0.145 iris_db_latency{id="MOCKSYS"} 0.157 iris_db_latency{id="OEESP"} 0.565 iris_db_latency{id="SMART"} 1.113 iris_db_latency{id="USER"} 0.236 iris_db_max_size_mb{id="DEMO"} 0 iris_db_max_size_mb{id="ENSLIB"} 0 iris_db_max_size_mb{id="HCC"} 0 iris_db_max_size_mb{id="HSCUSTOM"} 0 iris_db_max_size_mb{id="HSLIB"} 0 iris_db_max_size_mb{id="HSSYS"} 0 iris_db_max_size_mb{id="IRISAUDIT"} 0 iris_db_max_size_mb{id="IRISLOCALDATA"} 0 iris_db_max_size_mb{id="IRISSYS"} 0 iris_db_max_size_mb{id="IRISTEMP"} 0 iris_db_max_size_mb{id="MOCKSYS"} 0 iris_db_max_size_mb{id="OEESP"} 0 iris_db_max_size_mb{id="SMART"} 0 iris_db_max_size_mb{id="USER"} 0 iris_db_size_mb{id="HCC",dir="/usr/irissys/mgr/HCC/"} 365 iris_db_size_mb{id="DEMO",dir="/external/demo/"} 229 iris_db_size_mb{id="USER",dir="/usr/irissys/mgr/user/"} 11 iris_db_size_mb{id="HSLIB",dir="/usr/irissys/mgr/hslib/"} 1219 iris_db_size_mb{id="HSSYS",dir="/usr/irissys/mgr/hssys/"} 21 iris_db_size_mb{id="OEESP",dir="/external/oeesp/"} 102 iris_db_size_mb{id="SMART",dir="/external/smart/"} 162 iris_db_size_mb{id="ENSLIB",dir="/usr/irissys/mgr/enslib/"} 209 iris_db_size_mb{id="IRISSYS",dir="/usr/irissys/mgr/"} 127 iris_db_size_mb{id="MOCKSYS",dir="/usr/irissys/mgr/MOCKSYS/"} 11 iris_db_size_mb{id="HSCUSTOM",dir="/usr/irissys/mgr/HSCUSTOM/"} 21 iris_db_size_mb{id="IRISTEMP",dir="/usr/irissys/mgr/iristemp/"} 162 iris_db_size_mb{id="IRISAUDIT",dir="/usr/irissys/mgr/irisaudit/"} 11 iris_db_size_mb{id="IRISLOCALDATA",dir="/usr/irissys/mgr/irislocaldata/"} 21 iris_directory_space{id="HCC",dir="/usr/irissys/mgr/HCC/"} 17142 iris_directory_space{id="DEMO",dir="/external/demo/"} 34188 iris_directory_space{id="USER",dir="/usr/irissys/mgr/user/"} 17142 iris_directory_space{id="HSLIB",dir="/usr/irissys/mgr/hslib/"} 17142 iris_directory_space{id="HSSYS",dir="/usr/irissys/mgr/hssys/"} 17142 iris_directory_space{id="OEESP",dir="/external/oeesp/"} 34188 iris_directory_space{id="SMART",dir="/external/smart/"} 34188 iris_directory_space{id="ENSLIB",dir="/usr/irissys/mgr/enslib/"} 17142 iris_directory_space{id="IRISSYS",dir="/usr/irissys/mgr/"} 17142 iris_directory_space{id="MOCKSYS",dir="/usr/irissys/mgr/MOCKSYS/"} 17142 iris_directory_space{id="HSCUSTOM",dir="/usr/irissys/mgr/HSCUSTOM/"} 17142 iris_directory_space{id="IRISTEMP",dir="/usr/irissys/mgr/iristemp/"} 17142 iris_directory_space{id="IRISAUDIT",dir="/usr/irissys/mgr/irisaudit/"} 17142 iris_disk_percent_full{id="HCC",dir="/usr/irissys/mgr/HCC/"} 83.61 iris_disk_percent_full{id="DEMO",dir="/external/demo/"} 92.83 iris_disk_percent_full{id="USER",dir="/usr/irissys/mgr/user/"} 83.61 iris_disk_percent_full{id="HSLIB",dir="/usr/irissys/mgr/hslib/"} 83.61 iris_disk_percent_full{id="HSSYS",dir="/usr/irissys/mgr/hssys/"} 83.61 iris_disk_percent_full{id="OEESP",dir="/external/oeesp/"} 92.83 iris_disk_percent_full{id="SMART",dir="/external/smart/"} 92.83 iris_disk_percent_full{id="ENSLIB",dir="/usr/irissys/mgr/enslib/"} 83.61 iris_disk_percent_full{id="IRISSYS",dir="/usr/irissys/mgr/"} 83.61 iris_disk_percent_full{id="MOCKSYS",dir="/usr/irissys/mgr/MOCKSYS/"} 83.61 iris_disk_percent_full{id="HSCUSTOM",dir="/usr/irissys/mgr/HSCUSTOM/"} 83.61 iris_disk_percent_full{id="IRISTEMP",dir="/usr/irissys/mgr/iristemp/"} 83.61 iris_disk_percent_full{id="IRISAUDIT",dir="/usr/irissys/mgr/irisaudit/"} 83.61 iris_ecp_conn 0 iris_ecp_conn_max 2 iris_ecp_connections 0 iris_ecp_latency 0 iris_ecps_conn 0 iris_ecps_conn_max 1 iris_glo_a_seize_per_sec 0 iris_glo_n_seize_per_sec 0 iris_glo_ref_per_sec 0 iris_glo_ref_rem_per_sec 0 iris_glo_seize_per_sec 0 iris_glo_update_per_sec 0 iris_glo_update_rem_per_sec 0 iris_jrn_block_per_sec 0 iris_jrn_entry_per_sec 0 iris_jrn_free_space{id="WIJ",dir="default"} 11802.25 iris_jrn_free_space{id="primary",dir="/usr/irissys/mgr/journal/"} 11802.25 iris_jrn_free_space{id="secondary",dir="/usr/irissys/mgr/journal/"} 11802.25 iris_jrn_size{id="WIJ"} 237 iris_jrn_size{id="primary"} 1 iris_jrn_size{id="secondary"} 0 iris_license_available 4 iris_license_consumed 1 iris_license_percent_used 20 iris_log_reads_per_sec 0 iris_obj_a_seize_per_sec 0 iris_obj_del_per_sec 0 iris_obj_hit_per_sec 0 iris_obj_load_per_sec 0 iris_obj_miss_per_sec 0 iris_obj_new_per_sec 0 iris_obj_seize_per_sec 0 iris_page_space_percent_used 30 iris_phys_mem_percent_used 61 iris_phys_reads_per_sec 0 iris_phys_writes_per_sec 0 iris_process_count 30 iris_rtn_a_seize_per_sec 0 iris_rtn_call_local_per_sec 0 iris_rtn_call_miss_per_sec 0 iris_rtn_call_remote_per_sec 0 iris_rtn_load_per_sec 0 iris_rtn_load_rem_per_sec 0 iris_rtn_seize_per_sec 0 iris_sam_get_db_sensors_seconds .021465 iris_sam_get_interop_sensors_seconds .000165 iris_sam_get_jrn_sensors_seconds .002873 iris_sam_get_sql_sensors_seconds .000258 iris_sam_get_wqm_sensors_seconds .000125 iris_smh_available{id="Classes_Instantiated"} 47360 iris_smh_available{id="DB_Name_&_Directory"} 64326 iris_smh_available{id="Global_Mapping"} 60384 iris_smh_available{id="Lock_Table"} 192816 iris_smh_available{id="Routine_Buffer_In_Use_Table"} 61952 iris_smh_available{id="Security_System"} 0 iris_smh_available{id="Semaphores_objects"} 61440 iris_smh_available{id="TTY_Hash_Table"} 32760 iris_smh_percent_full{id="Classes_Instantiated"} 98 iris_smh_percent_full{id="DB_Name_&_Directory"} 2 iris_smh_percent_full{id="Global_Mapping"} 69 iris_smh_percent_full{id="Lock_Table"} 2 iris_smh_percent_full{id="Routine_Buffer_In_Use_Table"} 5 iris_smh_percent_full{id="Semaphores_objects"} 6 iris_smh_percent_full{id="TTY_Hash_Table"} 50 iris_smh_total 3801088 iris_smh_total_percent_full 6 iris_smh_used{id="Classes_Instantiated"} 1984256 iris_smh_used{id="DB_Name_&_Directory"} 1210 iris_smh_used{id="Global_Mapping"} 136224 iris_smh_used{id="Lock_Table"} 3792 iris_smh_used{id="Routine_Buffer_In_Use_Table"} 3584 iris_smh_used{id="Security_System"} 65536 iris_smh_used{id="Semaphores_objects"} 4096 iris_smh_used{id="TTY_Hash_Table"} 32776 iris_sql_queries_avg_runtime{id="%SYS"} .000057375 iris_sql_queries_avg_runtime{id="all"} .000057375 iris_sql_queries_avg_runtime_std_dev{id="%SYS"} .00000000001060414620168293732 iris_sql_queries_avg_runtime_std_dev{id="all"} .00000000001060414620168293732 iris_sql_queries_per_second{id="%SYS"} .1333333333333333333 iris_sql_queries_per_second{id="all"} .1333333333333333333 iris_system_alerts 3 iris_system_alerts_log 3 iris_system_alerts_new 1 iris_system_state 2 iris_trans_open_count 0 iris_trans_open_secs 0 iris_trans_open_secs_max 0 iris_wd_buffer_redirty 2 iris_wd_buffer_write 2 iris_wd_cycle_time 6 iris_wd_proc_in_global 0 iris_wd_size_write 16 iris_wd_sleep 9969 iris_wd_temp_queue 51 iris_wd_temp_write 0 iris_wdwij_time 3 iris_wd_write_time 2 iris_wij_writes_per_sec 0 iris_wqm_active_worker_jobs{id="SYS"} 0 iris_wqm_commands_per_sec{id="SYS"} 103 iris_wqm_globals_per_sec{id="SYS"} 4 iris_wqm_max_active_worker_jobs{id="SYS"} 0 iris_wqm_max_work_queue_depth{id="SYS"} 0 iris_wqm_waiting_worker_jobs{id="SYS"} 1 ``` ### 3. 通过SNMP 在Caceh' 和IRIS的安装文件夹的SNMP子目录下, 你可以找到.mib文件, 分别是ISC-cache.mib或者ISC-IRIS.mib。如果是ensemble或者health connect, 还会有isc-ensemble.mib。 通过SNMP GET, 您可以使用第3方工具的SNMP客户端获得Caché和IRIS的全部指标。 ### 4. 历史监视器(History Monitor) 如果用户没有专用的监控系统,那么使用历史监视器可以是一个很好的选择。 历史监视器把数据库的读写和系统使用情况的历史数据存在一个表里, 用户可以通过SQL访问。 它的统计的内容为: - CPU Usage - 数据库的大小和Journal的大小 - Global Reference and Updates - 物理读写(Physical reads and writes) - License使用率 这是一个轻量级的监控工具,几乎不对生产环境增加有意义的开销。它收集数据的表的尺寸也很小, 因此SQL查询的速度很快。 (即使收集小时级别的统计数据,每年的硬盘占用也只有130MB左右)。所有它经常被用于建立一个系统性能的基准线,便于日后的性能问题分析,以及容量规划 (capacity planning)。 **启动Caché History Monitor操作** ``` %SYS>do ^%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? 5 1) Set Sample Interval 2) Manage Monitor Classes 3) Change Default Notification Method 4) Manage Email Options 5) Manage Alerts 6) Exit Option? 2 1) Activate/Deactivate Monitor Class 2) List Monitor Classes 3) Register Monitor System Classes 4) Remove/Purge Monitor Class 5) Set Class Sample Interval 6) Debug Monitor Classes 7) Exit Option? 1 Class? %Monitor.System.HistoryMemory Activate class? Yes => yes 1) Activate/Deactivate Monitor Class 2) List Monitor Classes 3) Register Monitor System Classes 4) Remove/Purge Monitor Class 5) Set Class Sample Interval 6) Debug Monitor Classes 7) Exit Option? 1 Class? %Monitor.System.HistoryPerf Activate class? Yes => yes 1) Activate/Deactivate Monitor Class 2) List Monitor Classes 3) Register Monitor System Classes 4) Remove/Purge Monitor Class 5) Set Class Sample Interval 6) Debug Monitor Classes 7) Exit Option? 1 Class? %Monitor.System.HistorySys Activate class? Yes => yes 1) Activate/Deactivate Monitor Class 2) List Monitor Classes 3) Register Monitor System Classes 4) Remove/Purge Monitor Class 5) Set Class Sample Interval 6) Debug Monitor Classes 7) Exit Option? 1 Class? %Monitor.System.HistoryUser Activate class? Yes => yes 1) Activate/Deactivate Monitor Class 2) List Monitor Classes 3) Register Monitor System Classes 4) Remove/Purge Monitor Class 5) Set Class Sample Interval 6) Debug Monitor Classes 7) Exit Option? 2 Class Active SampleInterval ----- ------ -------------- %Monitor.System.HistoryMemory Y default %Monitor.System.HistoryPerf Y default %Monitor.System.HistorySys Y default %Monitor.System.HistoryUser Y default %Monitor.System.AuditCount N default %Monitor.System.AuditEvents N default %Monitor.System.Clients N default %Monitor.System.Diskspace N default %Monitor.System.Freespace N default %Monitor.System.Globals N default %Monitor.System.Journals N default %Monitor.System.License N default %Monitor.System.LockTable N default %Monitor.System.Processes N default %Monitor.System.Routines N default %Monitor.System.Servers N default %Monitor.System.SystemMetrics N default %Monitor.System.CSPGateway N default MyMetric.Freespace N default ``` 注意: 激活后需要重新启动System Monitor。统计数据默认保留60天, 如果希望保留更长的时间, 下面的命令设置为保留一年: `%SYS>do ##class(SYS.History.Hourly).SetPurge(365)` **读取History Monitor数据** 历史监视器把数据存在SQL表里, 表明和其中的字段请参考[在线文档: History Monitor](https://docs.intersystems.com/iris20231/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_historymon) !!!注意: SYS_History.PerfData给出采样间隔内的测量值,而daily, hourly的值为计算出的每秒的平均值。 这里给出使用历史监视器数据获得数据的2个例子 1. 读取平均CPU占用 ```sql SELECT Substr(DS.Daily,1,5) as DateH, (100-DS.Sys_CPUIdle) as AvgCPUBusy FROM SYS_History.Daily_SYS DS WHERE element_key='Avg' ORDER BY DateH ``` 2. 读取每天的CPU占用;9am-12am的CPU占用,global reference and update ```sql SELECT Substr(DS.Daily,1,5) Day, (100-DS.Sys_CPUIdle) as Daily_Avg_CPU, Round(AVG(100-H1.Sys_CPUIdle),2) Morning_Avg_CPU , DP.Perf_GloRef, DP.Perf_GloUpdate FROM SYS_History.Daily_Perf DP, SYS_History.Daily_SYS DS, SYS_History.Hourly_Sys H1 WHERE DP.Daily=DS.Daily and DP.element_key='Avg' and DS.element_key='Avg' and H1.element_key='Avg'and substr(DS.Daily,1,5)=Substr(H1.Hourly,1,5) and Substr(H1.Hourly,8,12) in (32400,36000,39600) GROUP BY DS.daily ``` ### 其他 除了上面的4个方式,Caché和IRIS还提供了另外的采集指标的方法,比如说执行routine, 存储过程,查看其他SQL表等等, 后面的文章中会对不同类型的指标做更近一步的介绍。
文章
姚 鑫 · 六月 17, 2023

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

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

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

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

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

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

FHIR生态

2023年6月底,世卫组织(WHO)和HL7签署了合作协议,利用HL7 FHIR提供互操作性,来支撑WHO的SMART指南(SMART Guideline)愿景 - 使用数智化的方式推动并加速一致化的健康干预措施建议,让世界上每个人都能立即从临床、公卫和数据使用建议中充分受益。 作为WHO的《2020-2025 年全球数字卫生战略》的一部分,SMART 指南使用 FHIR 、HL7的临床质量语言 (CQL) 和ICD标准以表达 WHO 的各种健康和临床指南,实现数据互操作、决策支持与指标、术语的一致性。这些标准被进一步利用来为各国及其合作伙伴开发一个由软件库、服务和工具组成的支持生态系统,并作为数字公共产品服务全球卫生健康事业。 为什么世卫组织会采用FHIR作为卫生信息互操作的标准在全球推广其一致化的健康干预措施建议?因为FHIR不仅标准成熟适用,而且还具有一个极具生命力的生态。 一个有生命力的标准会吸引生态的构建,而完善的生态将促进标准的成熟和演进。HL7 FHIR作为新一代的卫生信息互操作标准,其生态已经初具规模并蓬勃发展。 HL7 FHIR的知识产权类型 HL7 FHIR的知识产权是CC0,也就是知识共享。任何机构、组织和个人都可以无需向HL7申请而免费使用、扩展FHIR的标准。其知识产权类型配合FHIR标准的丰满程度,极大地鼓励和促进了基于FHIR的生态建设,应该也是WHO采用FHIR的原因之一。 FHIR的标准发布和标准的推广 标准应该是方便可及的 - 不仅有用户可阅读、可理解的文字说明,更需要要可以直接下载让计算机可用、可理解的电子结构化标准。 HL7 FHIR官网详细说明了每个版本、每个FHIR资源的结构与关系、使用范围、用例和示例。在下载页面提供了各种版本的标准、值集、profile和工具的免费下载。 对于用户的扩展、再约束和实施指南,有专门的实施指南注册和发布网站。这里可以免费注册自己的实施指南、也可以访问、查阅和下载别人的实施指南,从而让基于FHIR标准的自定义扩展可以无障碍地被分享、使用、理解,甚至进一步扩展。 下图是发布在注册网站的按用例类型统计的FHIR实施指南: 这众多方向的实施指南也是FHIR横跨交叉领域建立起成熟生态的体现。FHIR有什么快速建立生态的秘诀? 成熟的卫生信息标准要能应对各种行业互操作挑战,FHIR有一个四层机制用于制定标准并用各种互操作挑战来测试、验证和推进FHIR落地: 工作组(workgroups):FHIR有40多个工作组,专注不同的领域的需求,并制定和改进相关FHIR资源和用例标准。例如FHIR基础架构、基因组学、电子健康档案、财务管理、设备... 加速器计划(accelerators):为了推进在主要互操作领域的成熟和落地,FHIR建立加速器计划让每个领域的各个利益相关方参与进来,通过研究各方的需求、凝聚各方的智慧来推动FHIR。如今已经有8个不同领域的加速器计划: 例如Vulcan是专注连接临床研究、转化研究和医疗保健的加速器,它的成员不仅有HL7这样的标准开发组织,还有学会 - 例如约翰霍普金斯医学院,行业协会 - 例如全球医疗数据科学社区PHUSE,政府机构 - 例如FDA,技术厂商 - 例如InterSystems,药厂 - 例如GSK,甚至意见领袖。 课题(projects):FHIR通过课题,研究具体的需求、实现具体的目标,让FHIR扎实、可用。例如Vulcan加速器有以下课题: 课题 目标 Schedule of Activities (SoA) 活动安排 用FHIR表示电子表格中的活动时间表。 使得研究中的每项活动的描述、时间和标识都能保持一致 Real World Data (RWD) 真实世界数据 以标准化的格式从EHR中提取数据,以支持临床研究,特别是向监管机构提交数据 Phenotypic Data 表型数据 为基因组研究和基因组医学提供更多高质量的标准化表型信息 Electronic Product Information (ePI) 电子产品信息 为产品信息(各论)定义一个共同的结构,支持患者对产品数据的跨边界交换 Adverse Events (AE) 不良事件 支持对不良事件的报告和格式进行标准化。 提高相关FHIR资源的成熟度 FHIR to OMOP FHIR与OMOP映射 支持开发FHIR到OMOP的数据传输,以便更好地分析临床数据,用于研究 连接测试马拉松(connectathons):这是一个针对技术厂商的FHIR互操作系列化的一致性认证。每年3次的连接测试马拉松会确定众多的具体互操作用例,厂商选择并参与这些用例,用FHIR进行跨厂商的互操作测试。它不仅是技术厂商验证自己的FHIR互操作一致性的试验场,更是通过测试和反馈来发现标准的问题、确定标准适用性的大型沟通会。 FHIR confluence上公布有历次的连接测试马拉松的用例说明、实施指南、学习资料等详尽的资料。 除了这些手段,HL7还有FHIR认证,建立FHIR标准的智力资源池、确保FHIR在全球的正确采纳。 FHIR标准的适应性 FHIR的适应性核心在于其标准的设计 - 通过profile,在资源模型层面已经考虑到如何让用户进行不破坏标准的扩展和再约束;在标准成熟上,设计了成熟度模型,让标准基于实际使用和反馈逐步成熟。 Profile可以让用户裁剪、扩展FHIR标准,以适用于自己的术语体系和用例场景,实现基于统一标准的千人千面。 在标准的理解与反馈上,FHIR官方沟通提供了开放的交流和反馈的渠道。 FHIR生态的工具 成熟的生态工具是FHIR的一大亮点。这些工具是整个生态贡献的,好的工具得到广泛认同和采纳,既促进了标准的理解与使用、也避免了低水平的重复建设。 1. 标准学习工具: 理解和学习是标准推行的第一要务。除了汗牛充栋的学习材料和视频,FHIR还有不错的学习网站,例如Clinfhir ,最初设计是方便医生理解如何用FHIR构建和解决自己的用例的,但实际上也被广大卫生信息从业者用于理解FHIR标准。 2. 测试数据生成工具: 想学习标准?没有什么比直观的数据更能说明问题了。FHIR生态下有名的Synthea是一个基于马塞诸塞州的患者真实数据经过统计、混淆后的FHIR测试数据生成工具,可以按用户要求生成指定数量的、符合真实数据分布的FHIR资源,会为每个生成的虚拟患者生成一个FHIR boundle文件,并生成对应的医院、医生等FHIR资源。大家可以免费下载Synthea使用它产生测试数据。 另外,国内也广泛使用的MIMIC - 麻省理工贝斯以色列迪康医学中心的有5万多患者真实完整的高质量重症医疗数据集,如今也有了FHIR版本。 3. FHIR服务器: 还没有FHIR服务器,怎么测试FHIR? FHIR生态下有大量的免费沙箱,用户可以选择它们进行标准的学习和测试。例如官网提供的沙箱和各个厂商提供的沙箱。通过各种API工具,例如postman,学习者无需注册即可以了解FHIR标准的方方面面,甚至将自己的测试数据加载进去并测试自己的解决方案。 4. 标准扩展和再约束构建工具: 如何方便、直观地构建自己的术语、扩展和再约束(Profile)和用例?FHIR生态下有众多公司提供的免费工具可用 - 随君取用。例如术语扩展可以用Snapper和FSH、进行小规模profile开发可以用可视化的Forge或Trifolia-on-FHIR、进行大规模的profile和实施指南开发可以用FSH。 5. 标准验证工具: 需要基于profile对FHIR资源进行校验?资源更多了,不仅有FHIR官网提供的FHIR资源校验网页,还有各种开发语言版本的校验工具代码: JAVA C#/DotNet FHIR生态下百花齐放的各种应用架构、应用方向 更令人眼前一亮的是FHIR生态下各种应用架构、应用方向和众多其它生态对FHIR的采纳。 应用开发架构: FHIR提供了标准卫生信息模型和相应的API,为行业应用的快速开发提供了坚实的基础。FHIR生态下最有名的SMART on FHIR,实现即插即用和可复用的应用开发架构。在国际卫生信息互操作标准发展简史中有简要介绍。 SMART on FHIR市场已经有大量的应用可以直接下载部署。 决策支持架构: 决策支持已经是卫生信息数字化转型的核心需求之一。卫生信息化已经建设了各种基于知识库和基于机器学习的决策支持系统,涵盖了临床、业务管理、费用、组学与科研、公卫、健康管理等全部业务,但仍面临众多挑战。 任何知识库系统和决策支持系统面临的一个关键挑战是决策支持的可移植性!如果决策支持厂商都按自己的数据、术语和服务标准构建解决方案,用户在使用多个决策支持产品时,将面临大量数据转换和映射及服务集成带来的非常高的实施成本和潜在决策错误风险。 FHIR通过Clinical Reasoning模块和CDS Hooks分别提供了本地决策支持架构和外部决策支持架构,通过标准化降低成本和风险、提高决策效率和范围。这里是对CDS Hooks的介绍。 其它标准对FHIR的采纳: 相较于之前流行的互操作标准,FHIR在标准化、灵活性、可用性 三方面取得了很好的平衡。FHIR资源模型比大多数的行业通用数据模型(CDM)都简化,方便使用。曾经各自为战的众多标准都发现FHIR无处不在,且FHIR资源和API可以作为自己的数据和访问数据的基石,而融入FHIR生态可以更方便获得数据、获得更多的推广、发挥更大的价值,因此一系列的XX on FHIR项目应运而生 - 或者直接采纳FHIR、或者与FHIR相兼容。除了上面提到的SMART on FHIR,这里简单汇总一下主要的已完成和进行中的on FHIR项目和标准。 1. IHE IHE(Integrating the Healthcare Enterprise)是国际上比较流行且成功的卫生信息交换服务规范。它一直采用流行和稳定的互操作基础标准来开发自己的服务规范,最初使用DICOM + HL7 V2消息,后来用到HL7 V3 和CDA。IHE发现新的FHIR互操作标准有助于应对新的用例、并更好解决老的用例,认为FHIR会成为最流行的互操作基础标准,因此已经发布了很多基于FHIR的IHE服务,尤其是那些和移动业务相关的服务,例如移动患者人口统计查询 (PDQm)。 2. OMOP on FHIR OMOP(Observational Medical Outcomes Partnership)是包括国内在内全球科研人员进行真实世界研究的重要工具,它开发了通用数据模型CDM和分析工具库。 HL7国际和OHDSI宣布合作提供单一的通用数据模型,用于共享临床护理和观察研究信息 - 这就是OMOP on FHIR项目。 OMOP-on-FHIR 是构建在 OMOP CDM 数据库之上的 FHIR 服务器,它提供中间映射层,实现OMOP CDM和FHIR资源之前的双向转换,从而打通两大生态,使临床医生和研究人员能够从多个来源提取数据并以相同的结构进行分析处理与共享交换而不会降低数据质量,可以同时使用两个生态下丰富的应用与工具,利用各自的生态优势。例如OMOP让FHIR生态可以利用其丰富的预测模型,而FHIR让OMOP的研究分析可以集成到临床工作流程中,推动精准医学的落地。 3. FHIR to CDISC Joint Mapping CDISC 是一个标准开发组织,开发了生物制药行业使用的诸多数据标准,常用于提交临床试验数据以进行分析和监管审批。 通过与HL7合作,FHIR to CDISC Joint Mapping实施指南定义了FHIR 与三个特定 CDISC 标准之间的映射: 研究数据列表模型实施指南 (SDTMIG) 3.2 临床数据采集标准协调实施指南 (CDASH) 2.1 实验室1.0.1 通过简化 HL7 FHIR和 CDISC 标准之间的数据转换,消除使用临床信息支持科研的障碍。用途包括: 捕获“真实世界证据”(RWE),让那些不是为临床试验目的采集的数据可以用于研究监管 利用FHIR 的 SMART等技术,直接在临床系统内部捕获试验驱动的数据,而不是建立单独的临床试验管理解决方案 在回顾性研究中更容易利用临床数据 创建病例报告表单 (CRF),链接到使用 FHIR 资源和Profile定义的数据元素 使两个标准社区的专家能够理解彼此的术语,并随着两套规范的不断发展更好地协调它们 4. 通用数据模型协调 Common Data Models Harmonization(CDMH) 在卫生信息领域,有众多的通用数据模型(Common Data Models)服务于不同的或相同的业务领域。虽然都是“通用”数据模型,但数据在彼此之间并不通用。 FHIR的细颗粒度统一语义资源模型可以作为众多通用数据模型间的桥梁。通用数据模型协调(CDMH)目标就是借助FHIR打通各个通用数据模型,让它们的数据可以相互转换。 CDMH 项目由美国FDA 领导,与其他联邦政府机构合作。已发布的通用数据模型协调 (CDMH) FHIR 实施指南 (IG) 将重点放在以患者为中心的结果研究 (PCOR) 和其它目的提取的观察数据的映射和转换为 FHIR 格式。该项目重点关注以下四种通用数据模型 (CDM) 到 FHIR 的映射: 以患者为中心的结果研究网络 (PCORNet) 整合生物学和床边 (Informatics for Integrating Biology & the Bedside - i2b2) 临床试验 (ACT) 信息学,也称为 i2b2/ACT。 观察性医疗结果合作伙伴 (OMOP) 美国食品和药物管理局的哨兵(Sentinel) 5. Arden Syntax on FHIR 和HL7的临床质量语言(Clinical Quality Language - CQL)类似,Arden Syntax 是一种结构化、可执行的医学知识表示和处理语言,将医学知识表达为独立的单元 - 医学逻辑模块(Medical Logical Modules),常用于设计CDS系统,构建临床指南规则和临床决策规则。 新版本 Arden Syntax 3.0 版采用FHIR进行扩展,重新定义了基于FHIR的标准化的数据模型和数据访问方式。作为经过审计、基于共识的迭代 HL7 标准开发流程的一部分,3.0版已成功通过投票。 6. HL7 V2 to FHIR HL7 V2在全球依然有很高的采纳度,但其局限性和FHIR的成熟度都在推动从V2到FHIR的迁移。HL7 V2 to FHIR 项目建立实施指南,将HL7 V2的组件映射到FHIR组件:V2的消息、消息段、数据类型和词汇分别映射到 FHIR 的Bundle、FHIR资源、数据类型和编码系统,并对FHIR进行相应扩展以弥补二者间的差距。 7. C-CDA on FHIR C-CDA是最广泛实施的 HL7 CDA 实施指南之一,涵盖了临床护理的文档范围。CDA 和 FHIR 之间的互操作能力是推动临床文档进化的重要渠道。 C-CDA on FHIR 实施指南 (IG) 定义了一系列 FHIR 配置文件,以表示 C-CDA 中的各种文档类型,并弥补二者设计上的差异。C-CDA on FHIR 利用FHIR使文档标准更为精简。 还有更多的on FHIR项目没有介绍到,例如SNOMED on FHIR、PDMP on FHIR... 同时可以预期还会有越来越多的on FHIR项目会不断涌现。 不仅是这些on FHIR 项目,越来越多的机构发现FHIR的价值,将自己原来的数据模型改为FHIR。例如美国互操作核心数据集USCDI(U.S. Core Data for Interoperability) 起初采用通用临床数据集CCDS作为模型, 如今已经完全采纳FHIR,并且成为美国国家FHIR标准US Core的一部分。FHIR也得到了很多国家采纳作为国家级卫生信息互操作的标准。 大规模数据统计与分析: 一个好的标准应该有助于解决完整的行业需求。FHIR作为行业互操作标准已经超越了传统互操作的能力范围,除了互操作的数据模型、消息、文档、服务和API,FHIR服务器加上FHIR资源仓库为大规模的卫生信息持久化和访问提供了方案。 FHIR的完整蓝图目前尚缺一块拼图 - 基于FHIR的大规模数据统计与分析。 1. 大规模数据检索 FHIR API提供检索类型的API,通过查询参数(Search Parameter)对资源进行检索。 例如: 想要获取所有检验项目为loinc 1234-1,且检验结果小于9.2的Observation资源,可以用这样的查询参数: GET http://fhirsvr.com/Observation? code-value-quantity=loinc|1234-1$lt9.2 除了FHIR Core发布的查询参数,用户还可以扩展自己的查询参数,满足检索需求。 FHIR标准里的FHIR Path为FHIR资源模型提供了类似于XPath的资源路径导航和获取语言,可以方便地筛选、过滤层次化的FHIR数据。 但FHIR查询API和FHIR Path都仅适合于单资源类型的检索,对于需要多类型资源联合分析、汇聚、统计等分析需求无能为力。 2. 大规模的数据统计分析 HL7为临床质量指标与决策支持提出了临床质量语言(Clinical Quality Language - CQL) ,CQL如今基于FHIR,使用FHIR资源模型来构建标准化的指标体系,以支持决策和基于指标的管理。 对于科研数据分析,借助上面介绍的OMOP on FHIR和其它项目,用户可以用自己熟悉的科研工具并利用FHIR数据支持自己的科研工作,本质上是将FHIR数据转换并导入自己的科研工具。 对于通用大规模数据统计分析,虽然FHIR提供了API、FHIR资源数据序列化的JSON、XML可以作为文档进行分析,但市面上的统计分析工具和机器学习工具大都支持SQL,SQL也是最流行的数据统计分析语言。 FHIR的深层次化模型是立体的、对象化的,而SQL是扁平的、表格化的。这个差异让FHIR对主流分析工具和机器学习工具不友好。这对基于FHIR原生的大规模数据分析利用造成了障碍,是FHIR最需要完善的那一块。 FHIR和生态已经创立了很多项目,努力补上这一环。 SQL on FHIR SQL on FHIR项目的思路是为SQL用户提供FHIR的SQL表示层。SQL表示层提供一个机制:让用户根据自己的需要基于FHIR Path定义视图。这里的视图不是SQL视图,而是一个SQL模型的逻辑表达,由一个新的FHIR工件ViewDefinition定义。各个技术厂商负责物理实现它并展现为SQL表。 例如下面的视图定义: { "resourceType": "http://hl7.org/fhir/uv/sql-on-fhir/StructureDefinition/ViewDefinition", "select": [ { "column": [ { "path": "getResourceKey()", "alias": "id" }, { "path": "gender" } ] }, { "column": [ { "path": "given.join(' ')", "alias": "given_name", "description": "A single given name field with all names joined together." }, { "path": "family", "alias": "family_name" } ], "forEach": "name.where(use = 'official').first()" } ], "name": "patient_demographics", "status": "draft", "resource": "Patient" } 它定义一张这样的SQL表: 考虑到FHIR资源模型的复杂,SQL on FHIR目前尚待成熟。当前是版本2,尚未发布,且有很多限制,例如不能在视图里定义跨资源的字段。 技术厂商的FHIR资源SQL实现 除了SQL on FHIR项目,很多技术厂商也在借助自身技术上的优势为FHIR提供SQL访问层。 例如InterSystems IRIS是一个多模型数据平台技术,它可以同时支持对FHIR资源逻辑模型使用对象建模、对FHIR序列化的JSON/XML使用文档建模,并将这些模型投射为SQL模型。InterSystems IRIS正是借助于这个特性,提供一个名为FHIR SQL构建器(FHIR SQL Builder)的工具,用户通过图形化方式拖拽建立需要的SQL模型,而无需拷贝和转换数据。 FHIR生态正展现出蓬勃的生命力,如今已经是百花齐放。FHIR展现的统一行业语义能力和强大的生态,不仅帮助WHO发布数字公共产品服务,也可以赋能卫生信息数字化转型。
文章
Michael Lei · 四月 9

Open AI 与 IRIS 集成 - 文件管理

人工智能不仅限于通过带有说明的文本生成图像,或通过简单的指示创建叙事。您还可以制作图片的变体,或为已有图片添加特殊背景。此外,您还可以获得音频转录,无论其语言和说话者的语速如何。让我们来分析一下文件管理是如何工作的。 问题描述 在分析 OpenAI 有关需要将文件作为输入值的方法的信息时,必须使用 multipart/form-data 提供参数。 在 IRIS 中,我们知道如何使用 JSON 内容创建对 POST 方法的调用。但在这种情况下,使用带有 Base64 格式文件内容的参数并不实用。 要在多址/表单数据(multipart/form-data)中包含文件内容,必须使用%Net.MIMEPart.类。 要在我们的调用中包含文件,应创建一个与类对象 %Net.MIMEPart 相关联的 Content-Disposition 标头 set content = ##class(%Net.MIMEPart).%New() set contentDisposition = "form-data; name="_$CHAR(34)_"image"_$CHAR(34) set contentDisposition = contentDisposition_"; filename="_$CHAR(34)_fileName_$CHAR(34) do content.SetHeader("Content-Disposition",contentDisposition) 由于我们使用请求类来保留进程的值,因此我们必须将 Base64 内容转换为流,以构成内容的主体。 我们可以使用StreamUtils实用程序将 Base64 转换为流。 注意:"pImage"变量包含文件内容的 Base64 字符串。 Do ##class(HS.Util.StreamUtils).Base64Encode(pImage, .tStream) Set content.Body = tStream 不过,在 2023 年全球峰会上,我有幸从 InterSystems 专家那里学到了一个更好的技巧。他告诉我,这种执行方法比 StreamUtils 更有效,因为 StreamUtils 最后会循环读取字符串并记录到 Stream 中。这个解决方案就像使用 JSON 并将其转换为 Stream 的 Get 一样简单。 set contentfile = {} set contentfile.file = pImage set content.Body = contentfile.%Get("file",,"stream<base64") 在调用中包含了所需的所有参数后,我们就可以创建一个新的 MIMEPart 类来封装部件了。 Set rootMIME = ##class(%Net.MIMEPart).%New() do rootMIME.Parts.Insert(content) set writer = ##class(%Net.MIMEWriter).%New() set tSC = writer.OutputToStream(tHttpRequest.EntityBody) set tSC = writer.WriteMIMEBody(rootMIME) Set tContentType = "multipart/form-data; boundary="_rootMIME.Boundary set tSC = ..Adapter.SendFormDataArray(.tHttpResponse, "POST", tHttpRequest,,,url) 这就是我们如何将文件内容发送到我们在 OpenAI 中需要的方法。 Image files图像文件 图像方法允许您发送图片并进行变化。由于所有插图都必须是 PNG 格式,因此当我们以 Base64 格式指明文件内容时,文件名会随机生成,并带有 PNG 扩展名。下面是一个如何更改照片的示例。 Original Variation 正如你所看到的,程序以自己的方式解释指令。它认为公司的标志是一个圆圈,所以用另一个圆圈代替了它。它还发现办公室有一扇玻璃门,于是用另一扇玻璃门代替,但暂时用砖墙代替。此外,它还修改了衬衫的颜色,并改变了男子手臂的位置。此外,OpenIA 还允许您通过提供一个蒙版来编辑图像,蒙版上有您想要插入提示内容的区域。利用同一幅图像,我应用了一个去掉图像背景的蒙版。 Original Mask 当我要求它把我传送到牙买加海滩时,得到了如下结果: 现在,下次见到亲朋好友时,您就可以炫耀自己的假期了 😊 Image图像 Endpoint: POST https://api.openai.com/v1/images/variations 它允许你对已有的图像进行修改。由于它不需要提示您要如何修改,因此我们必须相信人工智能的品味,它会如何解释这张图片。此外,我们还可以定义大小和返回结果的方式,无论是通过链接还是 Base64 格式的内容。 输入参数如下: image: 必选 在这里,您要提及要转换的图像文件。 n: 可选. 默认为 1 在此区域,您可以决定生成图像的最大数量。(使用 1 到 10 之间的数字)。 size: 可选. 默认 1024x1024 定义图像大小,其数值必需为 “256x256”, “512x512”, 或者 “1024x1024”. response_format: 可选.默认是“url” 这个参数是关于您希望如何返回生成图像的格式。此处的值应为 "url "或 "b64_json"。 Endpoint: POST https://api.openai.com/v1/images/edits 它可以让你修改现有的图片,根据掩码文件,按照提示创建图片。此外,我们还可以指定尺寸和返回结果的方式,无论是通过链接还是 Base64 格式的内容。输入参数如下: image: 必选 如上. mask: 必选 这部分是关于所应用的蒙版图像文件. n: 可选,默认 1 如上 size: 可选,默认 1024x1024 如上 response_format: 可选. 默认是 “url” 如上 Audio files声音文件 OpenAI 管理的不仅仅是图像。我们还可以使用音频文件来获取所提供录音的转录或翻译。这种方法使用 Whisper 模型,可以区分专有名词、品牌和俚语,从而提供正确的转录和翻译。例如,将 "微型机器 "作为一个品牌来谈论,与将 "微型机器 "作为一个普通名词翻译成西班牙语是不一样的。下面的例子是对 80 年代一个著名广告插播的转录: 因此,指示 Whisper 为我们转录音频的结果如下: { "text": "This is the Micromachine Man presenting the most midget miniature motorcade of micromachines. Each one has dramatic details, terrific trim, precision paint jobs, plus incredible micromachine pocket playsets. There's a police station, fire station, restaurant, service station, and more. Perfect pocket portables to take anyplace. And there are many miniature playsets to play with and each one comes with its own special edition micromachine vehicle and fun fantastic features that miraculously move. Raise the boat lift at the airport, marina, man the gun turret at the army base, clean your car at the car wash, raise the toll bridge. And these playsets fit together to form a micromachine world. Micromachine pocket playsets, so tremendously tiny, so perfectly precise, so dazzlingly detailed, you'll want to pocket them all. Micromachines and micromachine pocket playsets sold separately from Galoob. The smaller they are, the better they are." } 多么神奇! 你觉得呢? 之所以能取得上述成果,是因为 Whisper 模型接受了训练。我们可以从 OpenAI 页面提供的下图中看到一些相关信息。 更多信息可以访问 https://openai.com/research/whisper 请记住,告知程序文件名至关重要,因为服务需要知道它正在处理的文件类型(如 WAV、MP3、OGG 等)。由于我们在调用中只包含 Base64 内容,因此还必须指明文件扩展名,以便用随机文本和建议的扩展名创建文件名。例如,St.OpenAi.Msg.Audio.AudioRequest 消息的 "类型 "属性可显示音频的种类: MP3、OGG、WAV、FLAC 等。 Endpoint: https://api.openai.com/v1/audio/transcriptions 通过这种方法,您可以将音频内容转录为有声语言。 输入参数如下: file: 必要 在这里,您可以指定要转录的音频文件(而不是文件名)。它支持以下格式: FLAC、MP3、MP4、MPEG、MPGA、M4A、OGG、WAV 或 WEBM model: 必要 用于转录的模型。目前只有 "whisper-1 "可用 language: 可选. 默认是音频语言. 如果指定的话,根据 ISO-639-1,可以提高准确率和延时. prompt: 可选. 这是一段可选的文字,用于引导模型的风格或延续上一段音频。此处的信息必须与音频语言一致。. response_format. 可选,默认为 “json”. 在这一部分中,您要明确转录输出的格式。请使用以下选项之一: "json"、"text"、"verbose_json"。 temperature: 可选,默认为 0. 采样温度应介于 0 和 1 之间。 0.8 等较高值会使输出更加随机,而 0.2 等较低值则会使输出更加集中和确定。如果设置为 0,模型将使用对数似然自动提高温度,直到达到特定阈值。 本方法的文档请参考 https://platform.openai.com/docs/api-reference/audio/createTranscription<. Endpoint: https://api.openai.com/v1/audio/translations 此方法可将音频内容翻译成英语。输入参数如下: file: 必要 它是您要翻译的音频文件(而不是文件名)。它支持以下格式: FLAC、MP3、MP4、MPEG、MPGA、M4A、OGG、WAV 或 WEBM model: 必要. 如上. prompt: 可选l. 这是一段可选的文字,用于引导模型的风格或延续上一段音频。此处的信息必须使用英语。 response_format. 可选. 默认是 “json”. 在这里,您可以用以下选项之一决定转录输出的格式: "json"、"text"、"verbose_json"。 temperature: 可选. 默认为 0. 如上 更多文档请查阅 https://platform.openai.com/docs/api-reference/audio/createTranscription. 下一步? 由于 OpenAi 在不断发展,下一次迭代将是将文本转换为音频的方法,以及其他一些新功能。如果您喜欢这篇文章,请记得点个 "赞"。
文章
Lilian Huang · 十二月 29, 2023

使用 FHIR 适配器在传统系统上提供 FHIR 服务 - 阅读资源

我们继续推出有关可供 HealthShare HealthConnect 和 InterSystems IRIS 用户使用的 FHIR 适配器工具的系列文章。 在前几篇文章中,我们介绍了小型应用程序,并在此基础上建立了我们的工作,并展示了安装 FHIR 适配器后在 IRIS 实例中部署的架构。在今天的文章中,我们将看到一个示例,说明如何执行最常见的 CRUD(创建 - 读取 - 更新 - 删除)操作之一,即读取操作,我们将通过恢复资源来完成此操作。 什么是资源? FHIR 中的一个资源对应一种相关的临床信息,这种信息可以是病人(Patient)、对实验室的请求(ServiceRequest)或诊断(Condition)等。每种资源都定义了组成它的数据类型,以及对数据的限制和与其他类型资源的关系。每个资源都允许对其包含的信息进行扩展,从而满足 FHIR 80% 以外的需求(满足 80% 以上用户的需求)。 在本文的示例中,我们将使用最常见的资源 "Patient"。让我们来看看它的定义: { "resourceType" : "Patient" , // from Resource: id, meta, implicitRules, and language // from DomainResource: text, contained, extension, and modifierExtension "identifier" : [{ Identifier }], // An identifier for this patient "active" : <boolean>, // Whether this patient's record is in active use "name" : [{ HumanName }], // A name associated with the patient "telecom" : [{ ContactPoint }], // A contact detail for the individual "gender" : "<code>" , // male | female | other | unknown "birthDate" : "<date>" , // The date of birth for the individual // deceased[x]: Indicates if the individual is deceased or not. One of these 2 : "deceasedBoolean" : <boolean>, "deceasedDateTime" : "<dateTime>" , "address" : [{ Address }], // An address for the individual "maritalStatus" : { CodeableConcept }, // Marital (civil) status of a patient // multipleBirth[x]: Whether patient is part of a multiple birth. One of these 2 : "multipleBirthBoolean" : <boolean>, "multipleBirthInteger" : <integer>, "photo" : [{ Attachment }], // Image of the patient "contact" : [{ // A contact party (eg guardian, partner, friend) for the patient "relationship" : [{ CodeableConcept }], // The kind of relationship "name" : { HumanName }, // IA name associated with the contact person "telecom" : [{ ContactPoint }], // IA contact detail for the person "address" : { Address }, // I Address for the contact person "gender" : "<code>" , // male | female | other | unknown "organization" : { Reference(Organization) }, // I Organization that is associated with the contact "period" : { Period } // The period during which this contact person or organization is valid to be contacted relating to this patient }], "communication" : [{ // A language which may be used to communicate with the patient about his or her health "language" : { CodeableConcept }, // R! The language which can be used to communicate with the patient about his or her health "preferred" : <boolean> // Language preference indicator }], "generalPractitioner" : [{ Reference(Organization|Practitioner| PractitionerRole) }], // Patient's nominated primary care provider "managingOrganization" : { Reference(Organization) }, // Organization that is the custodian of the patient record "link" : [{ // Link to a Patient or RelatedPerson resource that concerns the same actual individual "other" : { Reference(Patient|RelatedPerson) }, // R! The other patient or related person resource that the link refers to "type" : "<code>" // R! replaced-by | replaces | refer | seealso }] } 正如您所看到的,它几乎涵盖了患者的所有管理信息需求。 从我们的 HIS 中恢复患者信息 如果您还记得之前的文章中我们部署了一个模拟 HIS 系统数据库的 PostgreSQL 数据库,那么让我们看一下我们特定 HIS 中的示例表。 虽然数量不多,但对于我们的例子来说已经足够了。让我们更详细地看看我们的患者表。 这里我们有 3 个示例患者,您可以看到每个患者都有一个唯一的标识符 ( ID ) 以及一系列与卫生组织相关的管理数据。我们的首要目标是为我们的一位患者获取 FHIR 资源。 患者咨询 我们如何从我们的服务器请求患者数据?根据 FHIR 制定的实现规范,我们必须通过 REST 对包含我们服务器地址、资源名称和标识符的 URL 执行 GET。我们必须调用: http://SERVER_PATH/Patient/{id} 在我们的示例中,我们将搜索 Juan López Hurtado,其 id = 1,因此我们必须调用以下 URL: http://localhost:52774/Adapter/r4/Patient/1 为了进行测试,我们将使用 Postman 作为客户端。让我们看看服务器的响应是什么: { "resourceType" : "Patient" , "address" : [ { "city" : "TERUEL" , "line" : [ "CALLE SUSPIROS 39 2ºA" ], "postalCode" : "98345" } ], "birthDate" : "1966-11-23" , "gender" : "M" , "id" : "1" , "identifier" : [ { "type" : { "text" : "ID" }, "value" : "1" }, { "type" : { "text" : "NHC" }, "value" : "588392" }, { "type" : { "text" : "DNI" }, "value" : "12345678X" } ], "name" : [ { "family" : "LÓPEZ HURTADO" , "given" : [ "JUAN" ] } ], "telecom" : [ { "system" : "phone" , "value" : "844324239" }, { "system" : "email" , "value" : "juanitomaravilla@terra.es" } ] } 现在让我们分析一下我们的请求在生产中所采取的路径: 这里我们有路径: 请求到达我们的 BS InteropService。 将请求转发到我们已配置为 BS 目的地的 BP,在该 BP 中将恢复所接收呼叫的患者标识符。 从我们的 BO FromAdapterToHIS 查询到我们的 HIS 数据库。 将患者数据转发到我们的 BP,并将其转换为 FHIR 患者资源。 将响应转发给BS。 让我们看一下我们在 BP ProcessFHIRBP中收到的消息类型: 让我们看一下三个属性,它们对于识别客户端请求的操作类型至关重要: Request.RequestMethod:它告诉我们要执行什么类型的操作。在此示例中,搜索病人将采用 GET 方式。 Request.RequestPath:该属性包含到达服务器的请求路径,该属性将指示我们要处理的资源,在本例中,它将包括恢复资源的特定标识符。 Quick.StreamId: FHIR 适配器会将收到的每条 FHIR 消息转换为流,并为其分配一个标识符,该标识符将保存在此属性中。在本例中,我们不需要它,因为我们执行的是 GET,并没有发送任何 FHIR 对象。 让我们深入分析负责处理的 GLP,继续我们的消息之旅。 流程FHIRBP: 我们在生产中实施了 BPL,它将管理我们从业务服务收到的 FHIR 消息传递。让我们看看它是如何实现的: 让我们看看每个步骤中将执行的操作: 管理 FHIR 对象: 我们将调用负责连接到 HIS 数据库并负责数据库查询的 BO FromAdapterToHIS。 Method ManageFHIR(requestData As HS.FHIRServer.Interop.Request, response As Adapter.Message.FHIRResponse) As %Status { set sc = $$$OK set response = ##class (Adapter.Message.FHIRResponse). %New () if (requestData.Request.RequestPath = "Bundle" ) { If requestData.QuickStreamId '= "" { Set quickStreamIn = ##class (HS.SDA3.QuickStream). %OpenId (requestData.QuickStreamId,, .tSC) set dynamicBundle = ##class ( %DynamicAbstractObject ). %FromJSON (quickStreamIn) set sc = ..GetBundle (dynamicBundle, .response) } } elseif (requestData.Request.RequestPath [ "Patient" ) { if (requestData.Request.RequestMethod = "POST" ) { If requestData.QuickStreamId '= "" { Set quickStreamIn = ##class (HS.SDA3.QuickStream). %OpenId (requestData.QuickStreamId,, .tSC) set dynamicPatient = ##class ( %DynamicAbstractObject ). %FromJSON (quickStreamIn) set sc = ..InsertPatient (dynamicPatient, .response) } } elseif (requestData.Request.RequestMethod = "GET" ) { set patientId = $Piece (requestData.Request.RequestPath, "/" , 2 ) set sc = ..GetPatient (patientId, .response) } } Return sc } 我们的 BO 将检查收到的HS.FHIRServer.Interop.Request类型的消息,在本例中,通过设置 GET 并在与患者资源对应的路径中指示将调用GetPatient方法,我们将在下面看到: Method GetPatient(patientId As %String , Output patient As Adapter.Message.FHIRResponse) As %Status { Set tSC = $$$OK set sql= "SELECT id, name, lastname, phone, address, city, email, nhc, postal_code, birth_date, dni, gender FROM his.patient WHERE id = ?" //perform the Select set tSC = ..Adapter .ExecuteQuery(.resultSet, sql, patientId) If resultSet.Next() { set personResult = { "id" :(resultSet.GetData( 1 )), "name" : (resultSet.GetData( 2 )), "lastname" : (resultSet.GetData( 3 )), "phone" : (resultSet.GetData( 4 )), "address" : (resultSet.GetData( 5 )), "city" : (resultSet.GetData( 6 )), "email" : (resultSet.GetData( 7 )), "nhc" : (resultSet.GetData( 8 )), "postalCode" : (resultSet.GetData( 9 )), "birthDate" : (resultSet.GetData( 10 )), "dni" : (resultSet.GetData( 11 )), "gender" : (resultSet.GetData( 12 )), "type" : ( "Patient" )} } else { set personResult = {} } //create the response message do patient.Resource.Insert(personResult. %ToJSON ()) Return tSC } 正如您所看到的,此方法仅在我们的 HIS 数据库上启动查询并恢复所有患者信息,然后生成一个 DynamicObject,随后将其转换为 String 并存储在Adapter.Message.FHIRResponse类型的变量中。我们已将 Resource 属性定义为字符串列表,以便能够稍后在跟踪中显示响应。您可以直接将其定义为 DynamicObjects,从而节省后续转换。 检查是否捆绑: 根据 BO 的响应,我们检查它是否是 Bundle 类型(我们将在以后的文章中解释)或者它是否只是一个 Resource。 创建动态对象: 我们将 BO 响应转换为 DynamicObject 并将其分配给临时上下文变量 (context.temporalDO)。用于转换的函数如下: ##class ( %DynamicAbstractObject ). %FromJSON (context.FHIRObject.Resource.GetAt( 1 )) FHIR 变换: 使用 DynamicObject 类型的临时变量,我们将其转换为HS.FHIR.DTL.vR4.Model.Resource.Patient类的对象。如果我们想寻找其他类型的资源,我们必须为每种类型定义特定的转换。让我们看看我们的转变: 这种转换使我们能够拥有 BS InteropService 可以解释的对象。我们将结果存储在变量context.PatientResponse中。 将资源分配给 Stream : 我们将FHIR变换中获得的变量context.PatientResponse转换为Stream。 转换为 QuickStream: 我们将必须返回给客户端的所有数据分配给响应变量: set qs= ##class (HS.SDA3.QuickStream). %New () set response.QuickStreamId = qs. %Id () set copyStatus = qs.CopyFrom(context.JSONPayloadStream) set response.Response.ResponseFormatCode= "JSON" set response.Response.Status= 200 set response.ContentType= "application/fhir+json" set response.CharSet = "utf8" 在这种情况下,我们总是返回 200 响应。在生产环境中,我们应该检查是否已正确恢复搜索到的资源,如果没有,请将响应状态从 200 修改为对应“未找到”的 404。正如您在此代码片段中看到的,对象HS.FHIR.DTL.vR4.Model.Resource.Patient转换为 Stream 并存储为HS.SDA3.QuickStream ,将所述对象的标识符添加到QuickStreamID属性,随后我们的 InteropService 服务将以 JSON 形式正确返回结果。 结论: 让我们总结一下我们所做的事情: 我们发送了一个 GET 类型的请求,以搜索具有定义 ID 的患者资源。 BS InteropService已将请求转发至配置的BP。 BP 调用了负责与 HIS 数据库交互的 BO。 已配置的 BO 已从 HIS 数据库检索患者数据。 业务处理程序将结果转换为默认互操作服务创建的 BS 可理解的对象。 BS已收到响应并将其转发给客户端。 如您所见,操作相对简单,如果我们想在服务器中添加更多类型的资源,只需在 BO 中添加对数据库中与要恢复的新资源相对应的表的查询,并在 BP 中将 BO 的结果转换为与之相对应的 HS.FHIR.DTL.vR4.Model.Resource.* 类型的对象。 在下一篇文章中,我们将回顾如何将患者类型的新 FHIR 资源添加到我们的 HIS 数据库中。 感谢大家的关注!
文章
Hao Ma · 五月 26, 2023

IRIS镜像配置(4)_配置后的步骤

题外话:我刚刚翻译了InterSystems专家Bob Binstock的[Caché Mirroring 101:简要指南和常见问题解答](https://cn.community.intersystems.com/post/cach%C3%A9-mirroring-101%EF%BC%9A%E7%AE%80%E8%A6%81%E6%8C%87%E5%8D%97%E5%92%8C%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94)。 尽管题目是Caché Mirror 101, 而且是写于2016年,但因为讲解的都是Mirror的基本原理,所以在大量使用IRIS的今天也完全适用。 前面的3篇文章,包括了配置Mirror的各个方面。如果您照着操作,现在已经有了一个工作的mirror环境,并加入了您的数据库。然而,还没完,这篇我来讨论一下后面的工作,首先的问题是: **Mirror不复制什么** 简单说,Caché/IRIS镜像是**数据库复制(Database Replication)**。在Caché/IRIS里什么是数据库?也就是**Cache.dat和iris.dat**文件。数据库的修改日志,也就是journal,从主机被传送到其他镜像成员。而除此之外的内容,需要维护人员来分别的个个处理, 解决这些内容在各个镜像成员间的拷贝。需要很多的计划和细心。 >系统数据库, 包括IRISSYS, IRISTEMP, IRISLIB等等, 这些Caché/IRIS本身的数据库不应该被加入Mirror,在大多数Caché/IRIS版本里也都设置成不可以加入入MIRROR。 > >例外的HealthCare产品, HSSYS需要做Mirror, HSCustom可以做Mirror, 而HSLIB不可以Mirror 我们可以把问题转换成下面的题目: ## 需要人工在镜像成员中同步的项目 ### 命名空间(namespace)和Mapping 命名空间是应用开发的概念,它使用数据库。命名空间定义了3种映射关系:Package Mapping, Routing Mapping, Global Mapping。这样在一个命名空间可以使用多个数据库的内容。 通常情况下,用户会在主机创建命名空间的同时,创建一个新的带有mirror属性的数据库,然后会在其他mirror成员中手工一个个的创建命名空间,加入镜像的数据库。之后,管理员无需考虑更多的操作。 然而,对命名空间的修改,比如要添加或者删除命名空间的某些mapping,这偶尔会需要,尤其是应用迭代和系统扩容的情况下,那么,管理员/实施人员,必须清楚Mirror无法同步这个修改,您必须手工同步修改到其他机器去。 如果配置的mapping比较多, 我建议使用Manifest来操作。Mainfest是一个xml的文本,用来安装或者修改Caché/IRIS的配置,你可以参考[在线文档: Using a Manifest](https://docs.intersystems.com/iris20231/csp/docbook/DocBook.UI.Page.cls?KEY=GCI_manifest), 或者社区文章[使用Manifest](https://cn.community.intersystems.com/post/%E4%BD%BF%E7%94%A8manifest)。 这里给一个配置mapping的例子: ```xml ``` 如果是资深的Caché维护工程师,懂得如果修改CPF文件并在不重启实例的情况下应用修改后的内容,可以考虑把主机上的CPF中的mapping部分复制粘贴到其他机器。如果您没有这方面的经验,我不建议这种方式。 另外,在IRIS 2022后的版本中有了一个新工具,Configuration Merge。 文档在[这里](https://docs.intersystems.com/iris20231/csp/docbook/Doc.View.cls?KEY=ACMF)。可惜只有最新版的IRIS或者Health Connect 用户有的用。 ### 数据库的修改 数据库的内容会通过Journal从主机同步到其他成员,但修改不会,一般会遇到的是**压缩和截断**。 由于某种错误操作,某个数据库,会扩展到不正常的大,而当错误修正后,用户可能需要对该数据库进行压缩和截断,以释放被错误占用的空闲的磁盘空间。 由于除主机外,其他镜像成员的数据库都是只读的,这个操作的顺序应该是这样: 1. 在主机A执行压缩和截断 2. 切换到备机B, 再次执行压缩和截断。 3. 异步成员DR。 一种方案是吧DR提升到备机。这时当前的备机A会将为灾备,然后再切换DR为主机,再进行压缩和截断。 还有一个选择,就是重新配置DR上的这个数据库,这需要从主机到DR的数据库备份和恢复。 ### IRIS实例的配置 从最常用的内存的配置,Service的配置, **用户,权限,资源**的配置等等。它们都不会被MIRROR同步。如果您在MIRROR主机里做了修改了缩表的大小,或者启动了一个,比如TELNET服务, 您需要人工在其他机器上做相同操作。 像上面的mapping配置一样,这里还是建议使用Manifest人工同步IRIS得修改。注意的是,Mainfest不保证能支持所有的配置。比如在Caché的版本下, 比如您在主机上启动了TELNET服务, Manifest没有相应的标签。这种情况下, 如果您熟悉ObjectScript语言,可以把ObjectScript实现加入执行Manifest的方法,比如说: ```java ClassMethod main(){ //执行Manifest修改命名空间 Set pVars("Namespace")="MYNAMESPACE" $$$ThrowOnError(..ModifyNamespace(.pVars)) //启动IRIS的TELNET服务 set properties("Enabled")=1 // 有効 set sts=##class(Security.Services).Modify("%Service_Telnet",.properties) } ``` 当然,如果您缺乏开发实施的知识,在用户界面上一个个机器的操作是最省心的办法。 问题是,打开一个服务,修改一个配置参数操作都很简单,但是如果要添加大量的用户和权限怎么办? 用Manifest管理是一个办法。但根本上,如果您经常有大量的用户管理的工作,其实使用Kerberos或者LDAP管理用户身份认证和授权的工作, 在有多个镜像成员的情况下,尤其的合适。 关于这部分内容,请参考[在线文档:Authentication and Authorization](https://docs.intersystems.com/iris20231/csp/docbook/DocBook.UI.Page.cls?KEY=PAGE_security_authentication_authorization) ### 定时任务(TASK) 在主机上创建的定时任务, 您需要人工在其他机器上做相同操作。这里有2个步骤: 1. 在主机上创建新任务的时候,要选择”**应如何为镜像运行任务**“。 这是个下拉菜单,选项有*”仅在主镜像成员上运行“,“仅在非主镜像成员上运行“ ,“在任何镜像成员上运行"。* 选择的出发点是:非主镜像成员的数据库是只读的。因此,比如一个Ensemble的镜像配置中, 删除Ensemble消息的定时任务, 一定是”仅在主镜像成员上运行“。 2. 把新的定时任务从主机同步到其他成员。 ​ 如果是一个或者少量几个TASK, 那么手工在其他各个镜像成员上添加是最简单直接的做法。而如果是有很长 的任务列表,尤其在配置Mirror得时候可以需要同步一个长长的列表时, 您可以考虑**从主机导出Task到其 他机器导入**,我只知道使用ObjectScript命令的方法, 使用`%SYS.Task.ExportTask()`和 `%SYS.Task.ImportTasks()`。 文档在[这里](https://docs.intersystems.com/iris20231/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYS.Task)。 ### Web Application 主机上配置的Web Applicaiton 也要同步到其他镜像成员。如果要同步的Web Application比较多,推荐的方式依然是Manifest, 下面是一个例子。 ```xml ``` 麻烦的是不同的版本Caché/IRIS使用的标签上会略有不同,要稍微仔细的查看一下您的版本的文档。 如果您对ZPM, 现在称为IPM熟悉的话, 用ZPM做同步也是个好选择。关于zpm, 您可以参考这个帖子[zpm介绍](https://cn.community.intersystems.com/post/zpm%E4%BB%8B%E7%BB%8D1)。提醒一下的是,程序因为是存在数据库里面的,如果该数据库是被镜像的,您其实不需要用ZPM把程序代码拷贝到其他镜像成员。 ### Gateway 一般用到的有**SQL Gateway**和**External Language Gateway**,它们分别用于连接其他的数据库和使用其他语音的代码包。 SQL Gateway 记录保存在%SYS命名空间的*%Library.sys_SQLConnection*数据表里。简单的方法是使用工具把表记录导入导出。 External Language Gateway(外部语言网关) 新版的IRIS系统内嵌了外部语言服务器,包括%Python Server, %Java Server, %Dotnet Server等。如果您使用的是默认配置,各个镜像成员是一致的,无需操心。如果只是IP端口的修改,手工同步一下也很容易,毕竟工作量有限,只是您需要清楚的记得,这个也是不被Mirror自动同步的。 ### 文件 我把文件分为两类, 一类是“固定文件”,包括一下几个部分, - CSP文件,js文件,css文件,html文件等 - XSLT文件 - 其他语言的程序代码,Java文件,python文件, .Net文件 这类文件上传到主机的时候, 也必须上传到其他镜像成员,这是个简单的操作,别忘了就行。 麻烦的是**流文件**。在ObjectScript里如果使用了%Stream.FileBinary, %Stream.FileCharacter等类,那么数据不是保存到Cache.Dat或者IRIS.data, 而是保存在和.Dat同目录的一个stream的子目录下,而这个目录是不会被镜像同步的。 而且,因为这是实时数据,你也不可能手工的把它拷来拷去。 如果您的应用里用到了文件流,我任务您需要一个文件服务器保证流文件在各个各个镜像成员间的同步。 ### Ensemble Production Consideration 对于Ensemble和Health Connect用户,您需要阅读这部分在线文档: [Production Considerations for Mirroring](https://docs.intersystems.com/iris20223/csp/docbook/DocBook.UI.Page.cls?KEY=GHA_mirror_manage#GHA_mirror_set_ensemble) , 简单总结一下: - 创建的带有ensemble或者Inteoprability的命名空间,数据库要创建为Mirror的数据库。 - **"production是否自动启动“**应该在主机和备机上,甚至DR上都配置为“自动启动”。 在Mirror配置下的Production会先检查这个实例是不是主机,如果不是,“自动启动”的配置也不会生效,这样保证了Production只在主机上运行,而切换后也不需要人工干预。 上面的这些并不是完整的内容,尽管在大多少情况下这些内容差不多够了。如果您想要确保Mirror的主机的工作内容完全同步到了备机和DR, 请仔细阅读在线文档的这一部分:[Mirror Configuration Guidelines](https://docs.intersystems.com/iris20223/csp/docbook/DocBook.UI.Page.cls?KEY=GHA_mirror_set_config#GHA_mirror_set_config_guidelines) 另外,对于各种需要人工同步的内容的操作,还建议阅读[在线文档:Server Migration](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=AMIG#AMIG_migration_external)。 如果是最新的IRIS用户,请参考[在线文档:Deploy Mirrors Using Configuration Merge](https://docs.intersystems.com/iris20223/csp/docbook/DocBook.UI.Page.cls?KEY=GHA_mirror_set_config#GHA_mirror_set_config_auto_merge)
文章
Qiao Peng · 十二月 4, 2023

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

1. 通用RESTful业务服务和业务操作 InterSystems IRIS 提供了一组通用的RESTful 业务服务和业务操作类,用户无需开发自定义的业务服务和业务操作类,就可以直接向外提供RESTful服务和调用外部的RESTful API。 BS EnsLib.REST.GenericService 通用REST业务服务 BS EnsLib.REST.SAMLGenericService 检查SAML令牌的签名和时间戳的REST业务服务 BO EnsLib.REST.GenericOperation 通用REST业务操作 BO EnsLib.REST.GenericOperationInProc 用于透传模式的通用REST业务操作 2. 通用RESTful 消息 通用的RESTful 业务服务和业务操作类使用一个通用的RESTful消息类 - EnsLib.REST.GenericMessage,它是EnsLib.HTTP.GenericMessage的子类,二者数据结构都是 HTTPHeaders 记录http头的数组 Stream 记录http体的数据流 Type 数据流类型,例如是字符流还是二进制流。自动赋值,无需设置 Attributes 记录属性的数组 OriginalFilename 无需使用 OutputFolder 无需使用 OutputFilename 无需使用 因此EnsLib.REST.GenericMessage和EnsLib.HTTP.GenericMessage都可以被通用RESTful业务操作和业务服务所使用。 3. 通用RESTful 业务操作 使用通用的RESTful业务操作,可以连接到任何第三方的RESTful服务器,调用其RESTful API。 3.1 向production中加入通用RESTful业务操作 增加通用RESTful业务操作,只需要在Production配置页面的操作中添加EnsLib.REST.GenericOperation。 建议加入Production时,给业务操作起一个名字,用于代表具体的业务,例如是连接到LIS的RESTful 服务,可以命名为RESTtoLIS(可以考虑的命名规则 - 接口方式+业务系统)。如果未命名,默认会使用类名作为业务操作名。 3.2 配置通用RESTful业务操作 主要的设置项是以下3个: 1. HTTP服务器:目标RESTful服务器的服务器名或IP地址 2. HTTP端口:目标RESTful服务器提供RESTful API的端口号 3. URL:RESTful API的服务端点 启用该业务操作后,既可以访问外部RESTful API了。 3.3 测试通用RESTful业务操作 启用后,加入的通用的RESTful业务操作即可测试了。因为EnsLib.HTTP.GenericMessage的REST消息体是一个流类型的属性,为了测试时方便输入这个数据,我们增加一个业务流程。 1. 创建一个新的业务流程,设置其请求消息为Ens.StringRequest,用于测试时传入REST body数据。并为其上下文增加一个名为DataBody、类型为%Stream.GlobalCharacter(可持久化的字符流类型)的属性: 2. 在业务流程中增加一个代码流程(<code>),将请求消息的字符串数据写入上下文的DataBody字符流: Do context.DataBody.Write(request.StringValue) 注意行首加空格。 3. 然后在业务流程中再加入一个调用流程(<call>),调用上面已经加入production的业务操作,例如RESTtoLIS,并设置请求和响应消息为EnsLib.REST.GenericMessage或EnsLib.HTTP.GenericMessage。 4. 配置RESTtoLIS业务操作的请求消息(Request) 可以直接点击构建请求消息(Request Builder)按钮,使用图形化拖拽建立请求消息: 4.1 将左边上下文context里的DataBody拖拽到callrequest的Stream属性上; 4.2 对callrequest的HTTPHeaders赋值,它是一个元素类型为字符串的数组,代表HTTP请求的头。以下3个HTTP头是必须要填写的: HTTP头属性说明 下标 值 HTTP方法 "httprequest" 例如"POST" HTTP消息体的内容类型 "content-type" 例如"application/json" 客户端希望接收的内容类型 "Accept" 例如"*/*" 这3个数组元素赋值,可以通过在添加操作下拉列表中设置(Set)进行赋值。 5. 将业务流程加入Production,并测试 确保Production的设置是允许调试。在Production配置页面中选中这个业务流程,在右侧的操作标签页中选择测试按钮,并在弹出的测试消息页面里填入测试用的数据,并点击调用测试服务: 然后可以检查测试的消息处理流程,并确认REST消息体和HTTP消息头被正确地传递到目标REST API 4. 通用RESTful 业务服务 使用通用的RESTful业务服务,可以向外发布能处理任何RESTful API调用请求的RESTful服务端。 4.1 将通用RESTful业务服务加入Production 在Production配置页面,点击服务后面的加号。弹出的向导页面,服务类选择EnsLib.REST.GenericService;输入服务名,建议写一个能代表组件功能的名字,例如向HIS系统开放的REST服务,可以起名RESTforHIS;选中立即启用。 RESTful通用业务服务可以通过2种方式向外提供RESTful API服务:第一种通过Web服务器向外提供服务,第二种使用IRIS服务器的特定TCP端口向外提供服务。第二种方式不依赖于独立的Web服务器,但推荐使用Web服务器,从而得到更好的性能和安全性。 这里我们使用Web服务器提供REST服务,因此在业务服务的端口配置中,保持空白。在接受消息的目标名称中,选择接收RESTful API请求的业务流程或业务操作,这里我们测试使用一个空的业务流程。点击应用激活这些设置。 4.2 建立一个向外提供RESTful API的Web应用 向外发布RESTful服务,不仅涉及到服务发布的URL,还涉及到安全。我们通过创建一个专用的Web应用来进行管理和控制。 在IRIS系统管理门户>系统管理>安全>应用程序>Web应用程序 中,点击新建Web应用程序按钮,新建一个Web应用程序,并做以下配置: 1. 名称,填写一个计划发布的服务端点,例如/IRISRESTServer。注意前面的/ 2. NameSpace,选择Production所在的命名空间 3. 选中启用 REST,并设置分派类为EnsLib.REST.GenericService 4. 根据安全需要,配置安全设置部分。这里方便测试起见,允许的身份验证方法选择了未验证(无需验证)。如果是生产环境,或者您在做性能压力测试,都应该选择密码或Kerberos安全的身份验证方式! 注意,请保证同一个命名空间下,仅有一个分派类为EnsLib.REST.GenericService的REST类型的Web应用。 4.3 测试RESTful业务服务 现在就可以测试这个RESTful业务服务了。这个RESTful服务可以响应任何REST API的请求,如何响应则是后续业务流程/业务操作的事。 它的完整的RESTful URL是:[Web服务器地址]:[Web服务器端口]/[Web应用的名称]/[通用REST服务在production中的配置名]/[API名称和参数],例如我在IRIS本机的私有Apache的52773端口上访问上面创建的REST通用业务服务,调用PlaceLabOrder的API (注意,这里我们并没有实现过PlaceLabOrder这个API,但我们依然可以响应,而不会报404错误),那么完整的REST 调用地址是: 127.0.0.1:52773/IRISRESTServer/RESTforHIS/PlaceLabOrder 打开POSTMAN,用POST方法,发起上面REST API的调用: 在IRIS里会得到类似这样的消息追踪结果,如果你没有实现过处理REST API请求的业务流程,会得到一个500错,但依然可以查看IRIS产生的EnsLib.HTTP.GenericMessage消息内容: 这个通用RESTful业务服务会把REST请求转换为EnsLib.HTTP.GenericMessage消息,向目标业务操作/业务流程发送。因此,通过解析它的消息内容,就知道REST API请求的全部信息: 1. Stream里是POST的数据 2. HTTPHeaders 的下标"HttpRequest"是HTTP的方法 3. HTTPHeaders 的下标"URL"是完整的API路径,包括了服务端点(在"CSPApplication"下标下)、REST业务服务名称(在"EnsConfigName"下标下)和API 后续业务流程可以通过这些数据对REST API请求进行响应。 4.4 使用业务流程对REST API调用进行路由 有了通用RESTful业务服务生成的EnsLib.HTTP.GenericMessage消息,我们就可以使用消息路由规则或业务流程对REST API请求进行路由。这里我使用业务流程方法对REST API请求进行路由演示。 构建一个新的业务流程,请求消息和响应消息都是EnsLib.REST.GenericMessage或EnsLib.HTTP.GenericMessage,同时为context增加一个名为ReturnMsg的字符串类型的属性,并设置它默认值为:"{""Code"":-100,""Msg"":""未实现的API""}"。 在业务流程里增加一个<switch>流程,然后在<switch>下增加2个条件分支,分别为: 名称:下达检验医嘱,条件:判断是否http头的URL为PlaceLabOrder,且http头的HttpRequest为POST: (request.HTTPHeaders.GetAt("URL")="/IRISRESTServer/RESTforHIS/PlaceLabOrder") && (request.HTTPHeaders.GetAt("HttpRequest")="POST") 名称:查询检验项目,条件:判断是否http头的URL为GetLabItems,且http头的HttpRequest为GET: (request.HTTPHeaders.GetAt("URL")="/IRISRESTServer/RESTforHIS/GetLabItems") && (request.HTTPHeaders.GetAt("HttpRequest")="GET") 在两个分支里,分别增加<code>, 产生返回的REST消息内容: Set context.ReturnMsg="{""Code"":200,""Msg"":""检验医嘱下达成功""}" Set context.ReturnMsg="{""Code"":200,""Msg"":""查询检验项目成功""}" 最后在<switch>后增加一个<code>,构建响应消息: // 初始化响应消息 set response = ##class(EnsLib.REST.GenericMessage).%New() // 初始化响应消息的流数据 Set response.Stream = ##class(%Stream.GlobalCharacter).%New() // 将REST返回数据写入流 Do response.Stream.Write(context.ReturnMsg) 编译这个业务流程,并将其加入Production。 之后修改通用RESTful业务服务的设置,将接收消息的目标名称改为这个新建的业务流程。 现在再通过POSTMAN测试一下各种API,并查看返回REST响应: 在真实项目中,根据实际情况,将上面<switch>流程分支的<code>替换为API响应业务流程或业务操作即可。 总结:使用通用RESTful业务操作和业务服务,无需创建自定义的RESTful 业务组件类,就可以调用外部RESTful API和向外提供RESTful API服务,降低开发和实施成本,实现低代码开发。 后记:关于EnsLib.REST.GenericService对CORS(跨域资源共享)的支持 CORS是一种基于 HTTP 头的机制,通过允许服务器标示除了它自己以外的其它origin(域、协议和端口)等信息,让浏览器可以访问加载这些资源。所以要让EnsLib.REST.GenericService支持CORS,需要让它的响应消息增加对于CORS支持的HTTP头的信息,这里不详细介绍这些头含义了,大家可以去W3C的网站或者搜索引擎查询具体定义,最简单可以使用以下代码替代上面4.4中的初始化响应消息代码: // 设置HTTP响应的头信息 set tHttpRes=##class(%Net.HttpResponse).%New() set tHttpRes.Headers("Access-Control-Allow-Origin")="*" set tHttpRes.Headers("Access-Control-Allow-Headers")="*" set tHttpRes.Headers("Access-Control-Allow-Methods")="*" // 初始化响应消息 set response = ##class(EnsLib.REST.GenericMessage).%New(,,tHttpRes)