アポロクライアントGraphSQLサブスクリプションでWebSocketsを使用する初心者ガイド


GraphQL カスタマイズ可能なAPIの書き込みと1つのエンドポイントに複数のサービスを結合するための素晴らしいオプションになり始めた.
単一のエンドポイントを持っているが、モデルの異なる組み合わせを得ることの全体の考えは驚くべきです.そして、異なるフロントエンド/モバイル開発者とデザイナーで大規模なマルチプラットフォームアプリケーションで働いている会社に特に驚きます.

WebSocketは何ですか?


典型的に、WebSocketは永続的な接続を介してブラウザとサーバの間でデータを交換する方法を提供するプロトコルです.
これは非常には、継続的なデータ交換を必要とするアプリケーションに便利です-オンラインゲーム、リアルタイムシステム、取引エンジンなど.
新しいWebSocket接続を開く方法はws 通常のURLのプロトコル.例えば-
通常URLhttp://someurl.comWebSquket URL :ws://someurl.com

WSSはどうですか?


HTTPSがどんなHTTPリクエストよりも安全であるように!wss はHTTPSと同じように暗号化されたプロトコルですが、WebSockets用です.
WSSは暗号化されるだけでなく、さらに信頼性が高い.それでws:// データは暗号化されません.古いプロキシサーバはWebSocketについて知りません.
一方、wss:// TLSの上のWebSocket(HTTPSと同じHTTPがTLSの上にある)、輸送セキュリティ層は送付者でデータを暗号化して、レシーバーで解読します.それで、データパケットはプロキシを通して暗号化されて渡されます.彼らは中に何があるかを見ることができないし、それらを通して.

何がGraphSQLサブスクリプションです!


クエリのように、サブスクリプションはデータを取得する方法です.クエリとは異なり、サブスクリプションはWebSocket経由でGraphSQLサーバーへのアクティブな接続を維持します.これにより、サーバーが時間をかけてサブスクリプションの結果に更新をプッシュすることができます.
クライアントはリアルタイムリクエストに対する更新を通知するのに非常に便利です.バックエンドのデータが任意の時点で変更された場合、クライアントはすぐにアクティブなサーバー接続を通じて通知されます.


アポロクライアントはどこに入りますか


アポロクライアントは、両方のローカルとリモートのデータをグラフィカルに管理することができますJavaScriptのための包括的な州管理ライブラリです.これは、フェッチ、キャッシュ、およびアプリケーションのデータを変更するすべての中に自動的にUIを更新するために使用することができます.
コア@apollo/client ライブラリは、組み込みの統合を提供し、大きなアポロコミュニティは、他の人気のビュー層の統合を維持します.したがって、フロントエンドのGraphSQLデータを管理するための開発者に便利です.

加入者の定義


クライアント側とクライアント側の両方を定義する必要があります.しかし、このブログでは、サーバー側の加入者構成がプロジェクト要件に基づいて変更される可能性があるので、クライアント側での購読者の取り扱いについて特に話をしています.
あなたはGolang、ノードJSなどを使用して加入者サーバーを設定することができますが、クライアント側の処理は、ほとんど別のツール、ライブラリやフレームワークのために同じままになります.
このブログでは、オープンソースプロジェクトと呼ばれるアポロを使ってクライアント側の購読をどのように扱うかについて話し合うつもりですLitmusChaos .

Litmus 雲固有のカオス工学を行うツールセットです.リトマスは、開発者とSresは、アプリケーションの展開の弱点を見つけるためにKubernetesのカオスを調整するツールを提供します.リトマスは、最初にステージング環境で、最終的に生産、バグ、脆弱性を見つけるためにカオス実験を実行するために使用することができます.
プロジェクトはAとして活発な発展を遂げているSandbox project with CNCF .

加入者定義


以来Litmus 我々は多くのカオス実験とワークフローデータに対処するプロジェクトです、我々は我々がワークフローイベントを聞いて、Analyticsグラフ、ワークフロー表現データをプロットするのに必要である重要なパラメータの数を検索するように我々の加入者をセットアップしました.
const WORKFLOW_EVENTS = gql`
  subscription workflowEvents($projectID: String!) {
    workflowEventListener(project_id: $projectID) {
      workflow_id
      workflow_name
      workflow_run_id
      execution_data
      project_id
      cluster_name
      last_updated
    }
  }
`;
アポロクライアントがworkflowEvents サブスクリプション、我々のGraphSQLサーバーへの接続を確立し、応答データをリッスンします.クエリとは異なり、サーバーがすぐにプロセスを処理し、応答を返すという期待はありません.その代わりに、特定のイベントがあなたのバックエンドで発生するとき、我々のサーバーはクライアントにデータをプッシュするだけです(我々のケースでは、新しいワークフローは予定されています/実行されます).

加入者の実行


アポロのような別のフックを提供するuseQuery , useSubscription , etcがGraphSQLサーバーからクエリを実行するとき、私たちの生活をより簡単にします.私たちは以前に定義されたサブスクリプションを実行して、イベントを聞いて、その特定のワークフローを購読した後に、フロントエンドからのどんなデータ変化ででも更新されたフロントエンドを維持します.
クエリがアポロクライアントの結果を返すたびに、その結果はsubscribeToMore 関数.この関数を使用して、クエリの元の結果に更新をプッシュする次のサブスクリプションを実行できます.
const { subscribeToMore, data, error } = useQuery(
    WORKFLOW_DETAILS,
    {
      variables: { projectID: selectedProjectID },
      fetchPolicy: 'cache-and-network',
    }
  );

  // Using subscription to get realtime data

  subscribeToMore({
      document: WORKFLOW_EVENTS,
      variables: { projectID: selectedProjectID },
      updateQuery: (prev, { subscriptionData }) => {
        if (!subscriptionData.data) return prev;
        const modifiedWorkflows = prev.getWorkFlowRuns.slice();

        return { ...prev, getWorkFlowRuns: modifiedWorkflows };
      },
  });

すべてのこのサブスクリプションは非常に最初にデータを照会し、その後、同じワークフローイベント上のすべての更新プログラムのサーバーを聞いて、新しいデータが加入されている場合は、加入者は、新しいものと以前のデータを更新します.

The subscribeToMore function is similar in structure to the fetchMore function that's commonly used for handling pagination. The primary difference is that fetchMore executes a followup query, whereas subscribeToMore executes a subscription.


輸送の設定


サブスクリプションが永続的な接続を維持するため、アポロクライアントがクエリや突然変異を使用する既定のHTTPトランスポートを使用できません.代わりに、アポロクライアントのサブスクリプションは、最も一般的にWebSocketsubscriptions-transport-ws 図書館.
我々は必要subscriptions-transport-ws GraphSQLのWebSocketサーバーとクライアントを処理するので、GraphSQLクエリ、突然変異とサブスクリプションをWebSocketの後でフードの後ろに容易にします@apollo/client ライブラリを基本的にすべてのアポロ関連のタスクをスムーズに処理することができますライブラリは、また、他のコミュニティ駆動ライブラリからフック/機能を試しているときにバージョンの衝突を持つ可能性を排除します.
npm install @apollo/client subscriptions-transport-ws
インポートと初期化WebSocketLink あなたが初期化する同じプロジェクトファイルのオブジェクトApolloClient
import { WebSocketLink } from '@apollo/client/link/ws';

const wsLink = new WebSocketLink({
  uri: `ws:<GraphQL Endpoint>`,
  options: {
    reconnect: true,
    lazy: true
  }
});
当社のユースケースでは、HTTPソケットとWebSocketリクエストの両方を扱うことができます.したがって、クライアントのルートは少し異なります.
import {
  ApolloClient,
  ApolloProvider,
  HttpLink,
  InMemoryCache,
  split,
} from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { WebSocketLink } from '@apollo/client/link/ws';
import { getMainDefinition } from '@apollo/client/utilities';
import * as React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import config from './config'; // Stores the GraphQL Configuration
import App from './containers/app/App';
import configureStore from './redux/configureStore';
import getToken from './utils/getToken';

const { persistor, store } = configureStore();

// HTTP Link
const httpLink = new HttpLink({
  uri: `${config.grahqlEndpoint}/query`,
});

// Adds Authentication Headers on HTTP as well as was requests
const authLink = setContext((_, { headers }) => {
  const token = getToken();
  return {
    headers: {
      ...headers,
      authorization: token,
    },
  };
});

// WebSocket Link
const wsLink = new WebSocketLink({
  uri: `${config.grahqlEndpointSubscription}/query`,
  options: {
    reconnect: true,
    lazy: true,
  },
});

// Send query request based on the type definition
const link = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  authLink.concat(wsLink),
  authLink.concat(httpLink)
);

// Apollo Client
export const client = new ApolloClient({
  link,
  cache: new InMemoryCache(),
});

const ReduxRoot = () => {
  return (
    <ApolloProvider client={client}>
      <Provider store={store}>
        <PersistGate persistor={persistor}>
          <App />
        </PersistGate>
      </Provider>
    </ApolloProvider>
  );
};

export default ReduxRoot;
このロジックを使用して、クエリと突然変異はHTTPを通常通りに使用し、サブスクリプションはWebSocketを使用します.

結論


もちろん、これらの構成を拡張することができますし、独自の何かを作成するために周りを再生します.我々は誰もがコメントを歓迎し、私たちは何を/どのように我々はより多くを達成するために改善することができます知ってみよう!あらゆる提案は有り難いです.

あなたはSRE またはKubernetes 熱狂者?Does Chaos Engineering エキサイトユー?
詳細な議論のためにスラックで我々のコミュニティに加わってください.
我々のスラックに参加するには、次の手順に従ってください!
ステップ1:参加Kubernetes slack
ステップ2:Kubernetesスラックまたは使用に関して、Chithis link Kubernetesスラックに加わった後に

チェックアウトLitmus Chaos GitHub repo あなたのフィードバックを共有するかhttps://github.com/litmuschaos/litmus
提出するpull request 必要な変更を確認した場合.
あなたがこれらから利益を得るかもしれないと思う誰かと、これらの資源を共有するのを忘れないでください.平和に.✌🏼