apeescape2.com
  • メイン
  • Kpiと分析
  • データサイエンスとデータベース
  • 財務プロセス
  • 人とチーム
技術

JavaScriptでの複雑なオブジェクトのシリアル化

ウェブサイトのパフォーマンスとデータキャッシュ

最近のWebサイトは通常、データベースやサードパーティのAPIなど、さまざまな場所からデータを取得します。たとえば、ユーザーを認証する場合、Webサイトはデータベースからユーザーレコードを検索し、API呼び出しを介して一部の外部サービスからのデータでそれを装飾する場合があります。データベースクエリのディスクアクセスやAPI呼び出しのインターネットラウンドトリップなど、これらのデータソースへの高額な呼び出しを最小限に抑えることは、高速で応答性の高いサイトを維持するために不可欠です。データキャッシングは、これを実現するために使用される一般的な最適化手法です。

プロセスは、作業データをメモリに保存します。 Webサーバーが単一のプロセス(Node.js / Expressなど)で実行されている場合、このデータは、同じプロセスで実行されているメモリキャッシュを使用して簡単にキャッシュできます。ただし、負荷分散されたWebサーバーは複数のプロセスにまたがっており、単一のプロセスで作業している場合でも、サーバーの再起動時にキャッシュを保持したい場合があります。これには、Redisなどのアウトプロセスキャッシュソリューションが必要です。つまり、データを何らかの方法でシリアル化し、キャッシュから読み取るときに逆シリアル化する必要があります。

シリアル化と逆シリアル化は、C#などの静的に型指定された言語で実現するのは比較的簡単です。ただし、JavaScriptの動的な性質により、問題は少し複雑になります。 ECMAScript 6(ES6)でクラスが導入されましたが、これらのクラス(およびそのタイプ)のフィールドは、初期化されるまで定義されません(クラスがインスタンス化されるときではない場合があります)。また、フィールドと関数の戻り値の型は定義されません。スキーマではまったく。さらに、クラスの構造は実行時に簡単に変更できます。フィールドの追加や削除、タイプの変更などが可能です。これはC#のリフレクションを使用して可能ですが、リフレクションはその言語の「ダークアート」を表します。開発者はそれが機能を壊すことを期待しています。



数年前、ApeeScapeコアチームで働いていたときに、この問題が発生しました。チーム向けにアジャイルダッシュボードを構築していましたが、これは高速である必要がありました。そうでなければ、開発者や製品の所有者はそれを使用しません。作業追跡システム、プロジェクト管理ツール、データベースなど、さまざまなソースからデータを取得しました。このサイトはNode.js / Expressで構築されており、これらのデータソースへの呼び出しを最小限に抑えるためのメモリキャッシュがありました。ただし、迅速で反復的な開発プロセスでは、1日に数回デプロイ(したがって再起動)し、キャッシュを無効にして、その利点の多くを失いました。

明らかな解決策は、次のようなアウトプロセスキャッシュでした。 Redis 。しかし、いくつかの調査の結果、JavaScript用の適切なシリアル化ライブラリが存在しないことがわかりました。組み込みのJSON.stringify / JSON.parseメソッドは、オブジェクトタイプのデータを返し、元のクラスのプロトタイプの関数をすべて失います。つまり、逆シリアル化されたオブジェクトをアプリケーション内で単に「インプレース」で使用することはできません。そのため、代替設計で機能するにはかなりのリファクタリングが必要になります。

ライブラリの要件

JavaScriptで任意のデータのシリアル化と逆シリアル化をサポートし、逆シリアル化された表現と元のデータを交換可能に使用できるようにするには、次のプロパティを持つシリアル化ライブラリが必要でした。

  • デシリアライズされた表現は、元のオブジェクトと同じプロトタイプ(関数、ゲッター、セッター)を持っている必要があります。
  • ライブラリは、ネストされたオブジェクトのプロトタイプが正しく設定された、ネストされた複雑さのタイプ(配列とマップを含む)をサポートする必要があります。
  • 同じオブジェクトを複数回シリアル化および逆シリアル化できる必要があります。プロセスはべき等である必要があります。
  • シリアル化形式は、TCPを介して簡単に送信でき、Redisまたは同様のサービスを使用して保存できる必要があります。
  • クラスをシリアライズ可能としてマークするには、最小限のコード変更が必要です。
  • ライブラリルーチンは高速である必要があります。
  • 理想的には、ある種のマッピング/バージョン管理を通じて、クラスの古いバージョンの逆シリアル化をサポートする何らかの方法があるはずです。

実装

このギャップを埋めるために、私は書くことにしました Tanagra.js 、JavaScript用の汎用シリアル化ライブラリ。ライブラリの名前は、私のお気に入りのエピソードの1つへの参照です。 スタートレック:次世代 、ここでの乗組員 企業 言語が理解できない不思議なエイリアンとのコミュニケーションを学ぶ必要があります。このシリアル化ライブラリは、このような問題を回避するために一般的なデータ形式をサポートしています。

Tanagra.js はシンプルで軽量になるように設計されており、現在Node.js(ブラウザでテストされていませんが、理論的には機能するはずです)とES6クラス(マップを含む)をサポートしています。メインの実装はJSONをサポートし、実験的なバージョンはGoogle ProtocolBuffersをサポートします。ライブラリには標準のJavaScript(現在ES6とNode.jsでテスト済み)のみが必要であり、実験的な機能に依存していません。 バベル トランスパイル、または TypeScript 。

c ++。oファイル

シリアル化可能なクラスは、クラスがエクスポートされるときにメソッド呼び出しでそのようにマークされます。

module.exports = serializable(Foo, myUniqueSerialisationKey)

メソッドはを返します プロキシ クラスに、コンストラクターをインターセプトし、一意の識別子を挿入します。 (指定されていない場合、これはデフォルトでクラス名になります。)このキーは残りのデータとともにシリアル化され、クラスはそれを静的フィールドとして公開します。クラスにネストされた型(つまり、シリアル化が必要な型を持つメンバー)が含まれている場合、それらはメソッド呼び出しでも指定されます。

module.exports = serializable(Foo, [Bar, Baz], myUniqueSerialisationKey)

(以前のバージョンのクラスのネストされた型も同様の方法で指定できるため、たとえば、Foo1をシリアル化すると、Foo2に逆シリアル化できます。)

シリアル化中に、ライブラリはクラスへのキーのグローバルマップを再帰的に構築し、逆シリアル化中にこれを使用します。 (キーは残りのデータとともにシリアル化されることに注意してください。)「トップレベル」クラスのタイプを知るために、ライブラリでは、これを逆シリアル化呼び出しで指定する必要があります。

const foo = decodeEntity(serializedFoo, Foo)

実験的な自動マッピングライブラリは、モジュールツリーをウォークし、クラス名からマッピングを生成しますが、これは一意の名前のクラスに対してのみ機能します。

プロジェクトのレイアウト

プロジェクトはいくつかのモジュールに分かれています。

  • タナグラコア -クラスを次のようにマークする機能など、さまざまなシリアル化形式に必要な共通機能 シリアル化可能
  • tanagra-json -データをJSON形式にシリアル化します
  • tanagra-protobuf -データをGoogleprotobuffers形式にシリアル化します(実験的)
  • tanagra-protobuf-redis-cache -シリアル化されたprotobufを格納するためのヘルパーライブラリ Redis
  • tanagra-自動マッパー -モジュールツリーをウォークインします Node.js クラスのマップを作成します。つまり、ユーザーは逆シリアル化するタイプを指定する必要がありません(実験的)。

ライブラリは米国のスペルを使用していることに注意してください。

使用例

次の例では、シリアル化可能なクラスを宣言し、tanagra-jsonモジュールを使用してシリアル化/逆シリアル化します。

const serializable = require('tanagra-core').serializable class Foo { constructor(bar, baz1, baz2, fooBar1, fooBar2) { this.someNumber = 123 this.someString = 'hello, world!' this.bar = bar // a complex object with a prototype this.bazArray = [baz1, baz2] this.fooBarMap = new Map([ ['a', fooBar1], ['b', fooBar2] ]) } } // Mark class `Foo` as serializable and containing sub-types `Bar`, `Baz` and `FooBar` module.exports = serializable(Foo, [Bar, Baz, FooBar]) ... const json = require('tanagra-json') json.init() // or: // require('tanagra-protobuf') // await json.init() const foo = new Foo(bar, baz) const encoded = json.encodeEntity(foo) ... const decoded = json.decodeEntity(encoded, Foo)

パフォーマンス

2つのシリアライザーのパフォーマンスを比較しました( JSON シリアライザーと実験 protobufs シリアライザー)コントロール(ネイティブJSON.parseおよびJSON.stringify)を使用します。それぞれで合計10回の試行を行いました。

2017年にこれをテストしました Dell XPS15 Ubuntu 17.10を実行している、32Gbメモリを搭載したラップトップ。

次のネストされたオブジェクトをシリアル化しました。

foo: { 'string': 'Hello foo', 'number': 123123, 'bars': [ { 'string': 'Complex Bar 1', 'date': '2019-01-09T18:22:25.663Z', 'baz': { 'string': 'Simple Baz', 'number': 456456, 'map': Map { 'a' => 1, 'b' => 2, 'c' => 2 } } }, { 'string': 'Complex Bar 2', 'date': '2019-01-09T18:22:25.663Z', 'baz': { 'string': 'Simple Baz', 'number': 456456, 'map': Map { 'a' => 1, 'b' => 2, 'c' => 2 } } } ], 'bazs': Map { 'baz1' => Baz { string: 'baz1', number: 111, map: Map { 'a' => 1, 'b' => 2, 'c' => 2 } }, 'baz2' => Baz { string: 'baz2', number: 222, map: Map { 'a' => 1, 'b' => 2, 'c' => 2 } }, 'baz3' => Baz { string: 'baz3', number: 333, map: Map { 'a' => 1, 'b' => 2, 'c' => 2 } } }, }

書き込みパフォーマンス

シリアル化方式 アベニュー株式会社最初の試行(ms) StDev。株式会社最初の試行(ms) アベニュー例最初の試行(ms) StDev。例最初の試行(ms)
JSON 0.115 0.0903 0.0879 0.0256
Google Protobufs 2.00 2,748 1.13 0.278
対照群 0.0155 0.00726 0.0139 0.00570

読んだ

シリアル化方式 アベニュー株式会社最初の試行(ms) StDev。株式会社最初の試行(ms) アベニュー例最初の試行(ms) StDev。例最初の試行(ms)
JSON 0.133 0.102 0.104 0.0429
Google Protobufs 2.62 1.12 2.28 0.364
対照群 0.0135 0.00729 0.0115 0.00390

概要

ザ・ JSON シリアライザーは、ネイティブシリアル化よりも約6〜7倍遅くなります。実験 protobufs シリアライザーは、 JSON シリアライザー、またはネイティブシリアル化よりも100倍遅い。

さらに、各シリアライザー内のスキーマ/構造情報の内部キャッシュは、明らかにパフォーマンスに影響を与えます。 JSONシリアライザーの場合、最初の書き込みは平均の約4倍遅くなります。 protobufシリアライザーの場合、9倍遅くなります。したがって、メタデータがすでにキャッシュされているオブジェクトの書き込みは、どちらのライブラリでもはるかに高速です。

読み取りでも同じ効果が観察されました。 JSONライブラリの場合、最初の読み取りは平均の約4倍遅く、protobufライブラリの場合は約2.5倍遅くなります。

最も人気のある出会い系サイト2017

protobufシリアライザーのパフォーマンスの問題は、まだ実験段階にあることを意味します。何らかの理由でフォーマットが必要な場合にのみお勧めします。ただし、形式はJSONよりもはるかに簡潔であり、したがってネットワーク経由での送信に適しているため、時間をかけて投資する価値があります。 Stack Exchangeは、内部キャッシュにこの形式を使用します。

JSONシリアライザーは明らかにパフォーマンスがはるかに優れていますが、ネイティブ実装よりも大幅に低速です。小さなオブジェクトツリーの場合、この違いは重要ではありません(50ミリ秒のリクエストに加えて数ミリ秒でもサイトのパフォーマンスが損なわれることはありません)が、これは非常に大きなオブジェクトツリーの問題になる可能性があり、私の開発の優先事項の1つです。

ロードマップ

ライブラリはまだベータ段階です。 JSONシリアライザーは、十分にテストされており、安定しています。今後数か月のロードマップは次のとおりです。

  • 両方のシリアライザーのパフォーマンスが向上
  • ES6より前のJavaScriptのサポートの改善
  • ES-Nextデコレータのサポート

複雑なネストされたオブジェクトデータのシリアル化と元の型への逆シリアル化をサポートするJavaScriptライブラリは他にありません。ライブラリのメリットを享受できる機能を実装している場合は、ライブラリを試してみて、フィードバックに連絡し、貢献を検討してください。

プロジェクトホームページ
GitHubリポジトリ

基本を理解する

オブジェクトはJavaScriptにどのように保存されますか?

通常、オブジェクトは保存されません。それらは必要に応じてインスタンス化され、処理に使用され、不要になったときにメモリから削除されます。他の場所で一時的にデータを使用する必要がある場合は、そのデータを別の構造にシリアル化および逆シリアル化します。

JavaScriptオブジェクトとは何ですか?

オブジェクトは、構造体に対して実行できる操作とともに、構造体をカプセル化するコードの一部です。一般に、これはオブジェクト指向プログラミング言語のオブジェクトと同じです。

データオブジェクトとは何ですか?

データオブジェクトは、データストアからのデータを一時的に含み、アプリケーションで読み取ったり処理したりできるようにするコードです。そのデータをメモリに保持する方法がない限り、オブジェクトがスコープ外になるか、その他の方法で不要になったときに、データは書き戻されるか、保持されません。

なぜシリアル化と逆シリアル化が必要なのですか?

シリアル化により、実行中のプロセスまたは必要に応じて他のプロセスで使用するためにデータを保持できます。データを保存し、他の場所で使用される場合は逆シリアル化します。

構造の力–システムモデルを設計するためのガイド

Uiデザイン

構造の力–システムモデルを設計するためのガイド
デザイナーのための効果的なコミュニケーション戦略

デザイナーのための効果的なコミュニケーション戦略

設計プロセス

人気の投稿
実際のデータを使用したプロトタイピング–フレーマーチュートリアル
実際のデータを使用したプロトタイピング–フレーマーチュートリアル
説得力と動き–モーションデザインの原則へのガイド
説得力と動き–モーションデザインの原則へのガイド
Swiftプログラミングの学習:プライムタイムの準備はできていますか?
Swiftプログラミングの学習:プライムタイムの準備はできていますか?
SaaS価格設定モデル-価格設定戦略の例とベストプラクティス
SaaS価格設定モデル-価格設定戦略の例とベストプラクティス
あなたのためにリモートワークを機能させる方法
あなたのためにリモートワークを機能させる方法
 
カスタムフォントの作成方法:7つのステップと3つのケーススタディ
カスタムフォントの作成方法:7つのステップと3つのケーススタディ
小規模市場でスタートアップ資本を調達するための戦略
小規模市場でスタートアップ資本を調達するための戦略
成長キャンペーンスペシャリスト
成長キャンペーンスペシャリスト
成長する成長:このオープンソースコードを使用して独自のコホート分析を実行します
成長する成長:このオープンソースコードを使用して独自のコホート分析を実行します
UXとデザイン思考の重要性
UXとデザイン思考の重要性
人気の投稿
  • 発生を防ぐことができる電子メールのセキュリティ問題は
  • オンラインデートサービスのリスト
  • サイトのパフォーマンスで重要なレンダリングパスを参照する場合、重要なリソースは次のとおりです。
  • 富裕層ファミリーオフィス
  • 設計仕様書の書き方
  • http://api.streamin.to/pair
  • ノードjsでangularjsを使用する
カテゴリー
バックエンド プロセスとツール 革新 Uxデザイン 収益と成長 リモートの台頭 財務プロセス ライフスタイル 投資家と資金調達 Webフロントエンド

© 2021 | 全著作権所有

apeescape2.com