iTwin 缓存云服务器建立

当前 iTwin.js 原生支持 Azure Blob 云存储,用户通过配置 Azure 提供的 accountaccessKey 密钥启动该功能即可。

如需支持其他云服务(阿里云、亚马逊云、MinIO、腾讯云、华为云等)则根据实际情况进行引用或扩展。如在 iTwin.js v3.4 及以后的版本可使用官方提供的 iTwin/object-storage: Monorepo for object storage abstraction packages. (github.com) 包源进行添加;在 iTwin.js v3.4 以前的版本,则需扩展 CloudStorageService 类,详情查看

iTwin.js 缓存云服务版本变更详见:3.4.0 Change Notes - iTwin.js (itwinjs.org)

1. 缓存云服务建立

1.1 主要流程

云缓存服务主要流程

1.2 主要步骤

1.2.1 获取密钥

获取云服务提供商的存储密钥,例如 account,accessKey。通过提供的密钥,采用对应的 SDK 包实现云服务的连接。

1.2.2 前端配置

  • iTwin.js v3.4 版本前

    在调用 IModelApp.startup() 方法之前,需要指定云服务提供商,例如

    CloudStorageTileCache.getCache().provider = CloudStorageProvider.Azure;
    
  • iTwin.js v3.4 版本后

    const tileAdminProps: TileAdmin.Props = {};
    const serverConfig = {
      // eslint-disable-next-line @typescript-eslint/naming-convention
      FrontendStorage: {
        dependencyName: "name",
      },
    };
    tileAdminProps.tileStorage = new FrontendStorageSetup(
      serverConfig,
      yourServerFrontendStorageBindings
    );
    

1.2.3 后端配置

  • iTwin.js v3.4 版本前

    在调用 IModelHost.startup() 方法时,需配置缓存设置等内容,例如

    const credentials: CloudStorageServiceCredentials = {
      service: "Azure",
      account: "account",
      accessKey: "accessKey",
    };
    CloudStorageTileCache.getCache().provider = CloudStorageProvider.Azure;
    
    hostConfig.compressCachedTiles = true;
    hostConfig.tileCacheCredentials = credentials;
    IModelHost.tileCacheService = new AzureBlobStorage(credentials);
    IModelHost.startup(hostConfig);
    
  • iTwin.js v3.4 版本后

    // Pseudocode
    const container = new Container();
    
    useBindings(ServerStorageDependency);
    useBindings(ClientStorageDependency);
    container.bind<DependenciesConfig>...
    
    const yourServerStorage: ServerStorage = this.conatiner.get(ServerStorage);
    const iModelHost: IModelHostOptions = {
        tileCacheStorage: yourServerStorage,
    }
    

2. 云缓存具体实现示例

2.1 iTwin.js v3.4 版本前 -- 以阿里云为例

2.1.1 获取密钥

阿里云需要获取的信息有:

(1)AccessKey

从 AccessKey 里面需要获取 AccessKey ID 和 AccessKey Secret,具体信息可 点击查看 ,对应 iTwin 关系如下:

iTwin AliCloud
account AccessKey ID
accessKey AccessKey Secret

(2)bucketRegion

指阿里云创建对象存储的位置,如 oss-cn-beijing , 具体检索位置可 点击查看

OSS Region 查看示例

(3)bucket

由于阿里云对象存储(OSS)空间有数量限制,同账号下不超过 100 个,因此建议单独创建一个 bucket 存放 iTwin 缓存数据,bucket 的使用限制可 点击查看

OSS Bucket 限制示例

(4)完整配置示例

{
  "service": "AliCloud",
  "account": "XXXXXXXXXXXXXX",
  "accessKey": "XXXXXXXXXXXXXX",
  "container": "imodel-cache",
  "bucketRegion": "oss-cn-beijing"
}

2.1.2 前端配置

CloudStorageTileCache.getCache().provider = CloudStorageProvider.AliCloud;
await IModelApp.startup(opts);

2.1.3 后端配置

(1)扩展 CloudStorageService 主要实现的方法有:

  • obtainContainerUrl
 public obtainContainerUrl(
 id: CloudStorageContainerDescriptor,
 expiry: Date,
 // eslint-disable-next-line @typescript-eslint/no-unused-vars
 _clientIp ?: string,
): CloudStorageContainerUrl {
 const policy: OSS.SignatureUrlOptions = {
     expires: 1800, //default expire time 30 min
 };

 const url: CloudStorageContainerUrl = {
     descriptor: this.makeDescriptor(id),
     valid: 0,
     expires: expiry.getTime(),
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     url: this._client.signatureUrl(id.name + '/' + id.resource!, policy),
     bound: true,
 };

 return url;
}
  • upload
 public async upload(
 container: string,
 name: string,
 data: Uint8Array,
 options ? : CloudStorageUploadOptions,
): Promise < string > {
 try {
     await this._client.getBucketInfo(this._client.options.bucket);
 } catch (error) {
     // in case of empty bucket info
     if (!this._client.options.bucket) {
         this._client.options.bucket = 'imodel-cache';
     }
     await this._client.putBucket(this._client.options.bucket);
     await this._client.putBucketCORS(this._client.options.bucket, [{
         allowedOrigin: '*',
         allowedMethod: ['GET', 'POST', 'PUT', 'HEAD'],
         allowedHeader: '*',
     }, ]);
 }
 this._client.useBucket(this._client.options.bucket);

 const dataStream = new PassThrough();
 dataStream.end(data);

 let source: Readable;

 const putOptions = {
     mime: options && options.type ? options.type : 'application/octet-stream',
     headers: {
         'Cache-Control': options && options.cacheControl ?
             options.cacheControl :
             'private, max-age=31536000, immutable',
     },
 }
 as OSS.PutStreamOptions;

 if (options && options.contentEncoding === 'gzip') {
     (putOptions.headers as any)['Content-Encoding'] = options.contentEncoding;
     const compressor = zlib.createGzip();
     source = dataStream.pipe(compressor);
 } else {
     source = dataStream;
 }
 await this._client.putStream(container + '/' + name, source, putOptions);
 return '';
}

完整实现代码可查看 iPC repo 的 dev 分支,路径为 backend > src > client > AliCloudStorageService.ts 。

2.1.4 效果展示

(1) iTwin Viewer

在 iTwin 请求加载模型的过程中,通过 Network 面板查看到 Viewer 正在请求阿里云服务的缓存数据。

Network 查看云缓存服务器请求

(2) 阿里云 OSS 控制面板

在阿里云控制台中,通过查看 Bucket 列表,可发现该 iModel Cache 存放的文件。

阿里云对象存储文件查看


2.2 iTwin.js v3.4 版本后

iTwin.js 提供开源的对象存储服务,代码仓库地址:https://github.com/iTwin/object-storage 。 该服务提供对象存储的核心服务,以及基于核心服务(object-storage-core)扩展的 Azure, MinIO, OSS, S3 的存储服务。如需其他云服务存储服务,可基于核心服务进行自行扩展。

使用前说明

  • 包源 官方已提供核心(core)、Azure, MinIO, OSS, S3 的 npm 包,根据项目可以自行进展安装。如使用阿里云的 OSS 存储,运行:
  npm install @itwin/object-storage-oss
  • 版本

    推荐使用与 iTwin.js 引用版本一致的对象存储服务(可在 @itwin/core-common 包中寻找所依赖的 @itwin/object-storage-core 版本)。如 iTwin.js v3.6 采用的是 v1.4 版本的对象存储服务,则在安装包源时需指定该包源版本:

    npm install npm @itwin/object-storage-oss@1.4.0
    
  • 使用方式

    推荐采用依赖注入(Dependency Injection)的方式进行调用,即使用 Iversify。使用方式可参考:object-storage/samples/src/client-file-download/with-inversify at main · iTwin/object-storage (github.com)

2.2.1 微软 Azure Blob 存储

2.2.2 MinIO

  • MinIO Server 配置和参数获取

  • 前端配置

    • 安装依赖包

      npm i @itwin/object-storage-minio@1.4.0
      npm i reflect-metadata
      
    • 启动配置

      import "reflect-metadata";
      import { FrontendMinioS3ClientWrapperFactory } from "@itwin/object-storage-minio/lib/frontend";
      
      const tileAdminProps: TileAdmin.Props = {...};
      const frontendMinIOWrapperFactory = new FrontendMinioS3ClientWrapperFactory();
      tileAdminProps.tileStorage = new MinioFrontendStorage(
          frontendMinIOWrapperFactory
        );
      
  • 后端配置

    • 安装依赖包

         npm i inversify
      
    • MinIO 绑定

      import { Container } from "inversify";
      
      import {
        ClientStorageDependency,
        ServerStorage,
        ServerStorageDependency,
      } from "@itwin/core-common/node_modules/@itwin/object-storage-core";
      import {
        Bindable,
        DependenciesConfig,
        Types as DependencyTypes,
      } from "@itwin/core-backend/node_modules/@itwin/cloud-agnostic-core";
      
      export class MinioServerSetup extends Bindable {
        public container = new Container();
      
        constructor(
          config: DependenciesConfig,
          serverStorageDependency: new () => ServerStorageDependency,
          clientStorageDependency: new () => ClientStorageDependency
        ) {
          super();
      
          this.requireDependency(ServerStorageDependency.dependencyType);
          this.requireDependency(ClientStorageDependency.dependencyType);
      
          this.useBindings(serverStorageDependency);
          this.useBindings(clientStorageDependency);
      
          this.container
            .bind<DependenciesConfig>(DependencyTypes.dependenciesConfig)
            .toConstantValue(config);
      
          this.bindDependencies(this.container);
        }
      
        public getServerStorage(): ServerStorage {
          return this.container.get(ServerStorage);
        }
      }
      
    • 方法引用

      import { MinioServerSetup } from "./MinioServerSetup";
      
       // MinIO config
        const dependencyName = "minio";
        const serverStorageConfig: S3ServerStorageConfig = {
          bucket: "xxx",
          accessKey: "xxx",
          secretKey: "xxx",
          baseUrl: "http://xxxx:9000",
          region: "us-east-1",
          roleArn: "<role-arn>",
          stsBaseUrl: "http://xxxx:9000",
      };
      
        const minioConfig = {
          // eslint-disable-next-line @typescript-eslint/naming-convention
          ServerStorage: {
            dependencyName,
            ...serverStorageConfig,
          },
          // eslint-disable-next-line @typescript-eslint/naming-convention
          ClientStorage: {
            dependencyName,
            bucket: serverStorageConfig.bucket,
          },
          // eslint-disable-next-line @typescript-eslint/naming-convention
          FrontendStorage: {
            dependencyName,
            bucket: serverStorageConfig.bucket,
          },
      };
      
        const minioServer = new MinioServerSetup(
          minioConfig,
          MinioServerStorageBindings,
          MinioClientStorageBindings
      );
      
      // inject to iModelHost
      const iModelHost: IModelHostOptions = {
          ...,
          tileCacheStorage: minioServer.getServerStorage(),
      };
      
    • 调用结果示例

      minio 调用结果示例

  • Minio 特殊字符处理

    由于在 Windows 上 Minio Server 遇到冒号“:”的路径会抛异常,无法按带有冒号的路径存储文件,详情可见 https://github.com/minio/minio/issues/3711 。因此需要对路径生成的方法进行修改,可参考以下“去除冒号”方式处理,需对 iTwin 的前端及后端进行同时修改方可生效。

    • 定位至前端或后端 @itwin/core-common 包源中的 lib/*/TileProps.js 中的 getTileObjectReference 方法
    • 将 treeId 进行修改,替换冒号“:”,如 treeId.replace(":", "")

      去除冒号


3. iTwin.js 非云缓存存储位置

  • 可通过 backend 进行配置,例如

    const hostConfig = new IModelHostConfiguration();
    hostConfig.cacheDir = "D:\\tmp";
    
  • 默认 CacheDir 路径 默认路径

results matching ""

    No results matching ""