フロントエンドにマルチnodeサービスを導入

5314 ワード

ドキュメントの目的:
複数のサーバにフロントエンドコードを配備できることを実現する.
システムせっけい
フロントエンドnodeバックエンド(java)  
Nodeレイヤは、(1)サービス側レンダリング、(2)restApiインタフェース処理
テクノロジーアーキテクチャ
react + redux + node + express
改変前の検証ロジック  
 tokenチェックの採用 
バックエンド(java側)はtokenとユーザ情報をdata形式でフロントエンド(node層)に送り、フロントエンド(node層)はsessionに保存され、restApiインタフェースではsessionからこのtokenを取得してreq.headerに保存し、sessionからユーザ名、企業であるかどうかなどのフィールドを特定の必要なインタフェースに置くユーザ情報はセッションに保存されます.セッションはexpress-session、すなわちnodeサーバに保存されます

解決策(passに落とされた)
  • セッション方式がまだ使用されている場合.ソリューション:ip_hash法で解決する.
  • という案は、いくつかの点背のユーザーを放棄した.ユーザaがログインすると、ユーザ情報がサーバ1に保存される.このときサーバ1が停止しました.ユーザaは要求を送信し、tokenを取得できず、送信されたのは空またはundefinedであると考えられる.するとバックエンドは403に戻り、期限切れやタイムアウトなどしてログインを飛び出します.
  • このシーンが納得できれば原則として可能です.テスト環境にも異常は発見されなかったが、オンライン上にユーザーのフィードバックシリアル番号があり、影響が大きすぎると感じ、すぐにロールバックし、問題は再現されなかった
  • .
  • それから今の問題は、データベースの操作記録を見て、n(少なくとも>10)の多くのユーザーの操作はすべて問題がなくて、1人のユーザーが彼のシリアル番号をフィードバックしましたが、私たちの開発とテストはすべて問題を再現することができなくて、それからロールバックで終わりました.
  • いったいなぜシリアルナンバーになったのか、病気なく終わったのか......  

  • シリアル番号の問題がある場合、sessionのnodeレイヤでのキャッシュによる可能性が高いと推測されます.
    今、この需要を2回します.前回のバージョンを再オンラインにしますか、それとも考えを変えますか.ドキドキ・・・
    (PS:一番怖いのは需要が難しすぎて開発できないのではなく、まったく再現できない問題で、手がつけられないのです.
    質問を出す
    やり直す以上、雨露を均一につけ、すべてのユーザーの世話をしなければならない.どうすればいいですか.
    複数のサーバを配備し、ユーザー情報をnodeサーバに保存できない.
    理由は簡単で、1台のサーバが停止し、オンラインになった場合、ユーザaのログイン情報が停止されたサーバに保存された場合、そのユーザのログインに異常が発生し、例えばログインを終了するなどである.
    では、どこに保存すればいいのでしょうか.
    シナリオは、Redisなどのデータベースとフロントエンドの2つです.
    Redisは捨てられた
    Redisは高性能key-valueデータベースその利点の1つは、マルチプロセスで共有でき、バックアップとリカバリメカニズムが完備されていることです.に似合うようです
    しかし、ユーザ情報の処理にRedisを使用するだけでは「不器用」すぎるさらにtokenの検証はバックエンド(javaエンド)で維持され、フロントエンドは要求を送信する際にユーザ情報(tokenを含む)を持っているだけで、データベース依存を追加する必要はない.
    3と4の2点を統合したので、この案は放棄された.
    フロントエンドに保存されたスキーマ
    store(実際にはredux-store)に保存するクッキーに保存する.  
    以下、この2つの案について、この問題を解決する考え方を詳しく紹介します.
    シナリオの検討
     
    シナリオ1:store(破棄)
    ログインに成功すると、ユーザー情報はstoreに保存されます.各restApiインタフェースは、storeからのユーザ情報を取得する.
    1台のサーバで正常に動作する複数のサーバの場合、apiインタフェースごとにnodeレイヤでstoreからユーザ情報を取得できないことが判明し、undefined として印刷される.
    これも理解できます.結局storeはフロントエンドであり、node層はサーバ側に属し、単一のサーバは、キャッシュによるものと推測することができる(検証されていない、推測のみ) シナリオ2:storeストレージ方式の継続(廃棄)
    次に、フロントエンドのstoreでユーザ情報を取得し、インタフェースに追加することを考慮する.
    1つの理由:username、email、iscompanyなどのフィールドのrestApiインタフェースを単独で変更することに関連する.
    2つの理由:tokenの生成ルールは、ipを使用します.つまりipは必ず送らなければならないので、各インタフェースはipというパラメータを送信しなければならず、ユーザーの習慣に合わない.機能が実現しても.
    特に第2の原因はを排除した.
     シナリオ3:クッキー(廃棄)
  • は、ユーザ情報とtokenをクッキーで保存することを考慮する. 
  • ユーザログインに成功すると、フロントエンド(node)はバックエンドから戻るユーザ情報(tokenを含む)を受け取り、クッキーに
    const cookieAge = 1 * 60 * 60 * 1000;
        res.setHeader('Set-Cookie',
          [
            `TOKEN=${userInfo.token}`,
            `NAME=${Number(username)}`,
            `EMAIL=${userInfo.email}`,
            `ISCOMPANY=${userInfo.company}`,
          ],
          'Secure',
          `Max-Age=${cookieAge}`
        );
  • 保存する.
  • 新しい問題がまた来ました.同じブラウザがクッキーを共有しているからです.
  • つまり、2つのアカウントaとbにログインすると、クッキーはbになるので、aアカウントはbに更新しなければなりません.そうしないと、アカウント間のデータが混乱しやすくなります.
  • ソリューションは、ユーザ動作actionとコード実行reducerの間に、現在のクッキーと現在のページstoreのtokenが一致しているかどうかをキャプチャし、一致していない場合、ユーザ情報
    //      api
    export default function configureStore(preloadedState) {
      return createStore(
        rootReducer,
        preloadedState,
        applyMiddleware(thunk, api(httpClient), auth)
      )
    }
    
    //    api.js。       ,        
    if (Cookies.get('TOKEN')) {
          next(UpdateUserInfo({
            token: Cookies.get('TOKEN'),
            name: Cookies.get('USERNAME'),
            email: Cookies.get('EMAIL'),
            iscompany: Cookies.get('ISCOMPANY'),
          }))
       }
          UpdateUserInfo   action,    store     
  • を更新するミドルウェアを追加することである.
  • でもまた問題が...このプロジェクトは単一ページアプリケーションであり、すべての機能は非同期要求である.あるボタンをクリックすると、そのボタンに関する内容のみが更新され、他の内容は
  • は更新されないという意味です.
  • は混乱をもたらしやすい.例えばaアカウントの下にbアカウントの情報が表示されると、ユーザー
  • を誤導する.
  • はまた解決策を考えなければならない.aページを操作する時、cookieとstoreのtokenが一致しないことを発見しない限り、すべてのインタフェース情報(現実的ではなく、コード習慣に合わない)を更新する.そして、すべてが更新されれば、ページを更新することと変わらない.
  • で、ページが強制的に更新されます.それは、aページの操作時に
  • を強制的にリフレッシュすることに関する.
  • 当時、aページの強制リフレッシュを操作していると感じていたが、ユーザー体験があまりよくなく、未登録ページ
  • にジャンプすることを考慮できるかどうか.
     シナリオ4:クッキーの継続
    未登録ページにジャンプすると、解決策は、ミドルウェアを追加すると、どのユーザーの行為も、まずミドルウェアを通過し、クッキーとstoreのtokenが一致しているかどうかを判断し、一致しない場合は未登録ページにジャンプします.
    先ほど述べたように、同じブラウザで2つのアカウントにログインする問題について、私が理解している同じブラウザ、tokenは異なり、ジャンプするのは正常です.
    ただし、同じブラウザで同じアカウントを複数回ログインし、ユーザー名が同じでtokenが異なる.ログインからも飛び出します.もちろん、この場所はユーザー名が一致し、tokenが一致しないと判断し、ログインを終了するように変更することもできます.ここで私が使っている前者.
    //      updateUser
    export default function configureStore(preloadedState) {
      return createStore(
        rootReducer,
        preloadedState,
        applyMiddleware(thunk, updateUser(preloadedState), api(httpClient),  auth)
      )
    }
    // updateUser.js
    import Cookies from 'js-cookie';
    import { loginPage } from '../../middlewares/links';
    export default function updateUser(preloadedState) {
      return () => next => (action) => {
        if (preloadedState &&
          preloadedState.auth &&
          Cookies.get('TOKEN') !== preloadedState.auth.user.token) {
          window.location.href = loginPage;
        } else {
          return next(action);
        }
      };
    }
    以上の内容は、いずれも個人的な経験です.不適切またはより良い提案があれば、コメントを歓迎します.