redis アプリケーション編 ---- スパイク、サインイン、セッション共有
I. セコンド
フロントエンドのための商品のスパイクは塩ああ何を追加するには、ああリンクをアンチシェイクを行うことができ、バックエンドのためにしばしば直面しなければならないデータベースの永続層は、実際には、解決策は、それが上に保持しない聖歌、右保持していないことであることができます。
スパイク前は、ユーザーが商品詳細ページを何度も更新していたため、ページリクエストが大量に発生していました。そこで、スパイクの商品詳細ページと、通常の商品詳細ページを分離する必要があります。スパイク商品詳細ページでは、サーバーによる動的な判定が必要なスパイクボタン以外の要素は静的で、その他の静的データはブラウザやCDNでキャッシュできるように工夫しています。こうすることで、スパイク前にページを更新することで発生するトラフィックのうち、サーバー側に入るのはごく一部にとどまります。
CDNは、トラフィックの遮断の最初のレベルであり、トラフィックの遮断の第二レベルは、我々はRedisの読み取り/書き込み分離をサポートするために使用します。この段階では、主にデータを読んで、読み書き分離Redisは完全に需要をサポートできる、最大60万+ qpsをサポートすることができます。最初のステップは、データ制御モジュールを介して読み書きRedisに事前にスパイクアイテムをキャッシュし、次のようにスパイクの開始マーカーを設定します。
"goodsId_count": 100 // total
"goodsId_start": 0 //start marker
"goodsId_access": 0 // number of orders accepted
- スパイクが始まる前に、サービスクラスタはgoodsId_Startを0と読み、そのままnot startedを返す。
- データ制御モジュールは、goodsId_start を 1 に変更し、スパイクの開始を示す。
- サービスクラスタは開始マーカービットをキャッシュし、リクエストの受け付けを開始し、RedisのgoodsId_accessに残りの商品数を(goodsId_count - goodsId_access)としてログを記録します。
- 受け付けた注文の数がgoodsId_countに達すると、すべてのリクエストはブロックされ続け、残り個数は0になる。
発注に正常に参加した後、下位のサービスレベルに入り、注文情報の確認と在庫の控除を開始する。データベースへの直接アクセスを避けるため、在庫控除にはマスター・スレーブ版のRedisを使用することができます。Redisを使用して在庫クエリを最適化し、失敗した秒数のリクエストを事前にインターセプトすることで、システム全体のスループットを大幅に向上させることができます。
ハッシュの中に総数と発注数を格納することができるストレージ設計になっています。
"goodsId" : {
"Total": 100
"Booked": 0
}
金額が引き落とされると、サーバーはRedisにリクエストすることで発注資格を得ますが、これは以下のluaスクリプトで実装されています。Redisはシングルスレッドなので、luaは複数のコマンドに対してアトミック性を担保しています。
local n = tonumber(ARGV[1])
if not n or n == 0 then
return 0
end
local vals = redis.call("HMGET", KEYS[1], "Total", "Booked");
local total = tonumber(vals[1])
local blocked = tonumber(vals[2])
if not total or not blocked then
return 0
end
if blocked + n <= total then
redis.call("HINCRBY", KEYS[1], "Booked", n)
return n;
end
return 0
Redisをメッセージキューとして使用し、受注に成功した注文を非同期にインバウンド永続化する。
II. チェックイン
毎日のチェックイン情報を永続層に書き込むと、データ量が非常に大きくなるだけでなく、統計処理も非常に不便になる。Redisのビットマップを使えば、小さなレコードを保存し、素早くクエリすることが可能です。
- ビットマップは実際のデータ型ではなく、String型に定義されたバイト指向の操作の集合体である。文字列はバイナリセーフのブロックであるため、最大長は512Mで、2^32個の別バイトに設定するのが最適です。
- ビットマップは、2種類のビット操作に分けられる。1. 文字列のビットを1または0に設定したり、ビットの値を取得したりするような、一定時間ごとの単一ビット操作 2. ビットの集合に対する操作で、与えられたビットの範囲内で1にセットされたビットの数を数えるもの(人口統計学など)。
- ビットマップの最大のメリットは、データを格納する際の容量を大幅に削減できることです。例えば、あるプロジェクトでユーザーを識別するために自己増殖型IDを使用すると、40億人のユーザーに関する情報(例えば、ユーザーが新しい通知を受け取りたいかどうか、1と0で識別)を記録するのに、わずか512Mのメモリーしか使用できません。
SETBITキーオフセット値
キーの値(文字列)のビット値をオフセットで設定またはクリアします。その位置のビットは、値(0か1しかない)によって決定されるように、セットまたはクリアされる。キーが存在しない場合、新しい文字列値が作成され、その文字列はオフセットにビット値を持つのに十分な大きさであることが確認される。パラメータoffsetは0以上、2^32未満である必要があります(ビットマップサイズは512MBに制限されます)。キーに対応する文字列のサイズが大きくなると、ビット値の新しい部分が0に設定される。
GETBITキーオフセット
offset で指定されたキーに対応する文字列のビット値を返す。offsetが範囲外の場合、文字列は0ビットで埋め尽くされた連続した空間であるとみなされる。キーが存在しない場合は空文字列と見なされるため、offsetは常に範囲外であり、値も0ビットで満たされた連続した空間であると見なされます。
BITCOUNT キー [開始 終了].
戻り値:1に設定されているビットの数。startとendパラメータはGETRANGEコマンドと同様に設定され、どちらも負の値を使うことができます:例えば、-1は最後のビット、-2は最後から2番目のビット、といった具合です。存在しないキーは空文字列として扱われ、存在しないキーに対するBITCOUNT演算は0になります。
例えば、2021年7月18日にログインしたユーザーは0の位置に1、翌日もログインしたユーザーは1の位置に1というように、1週間にログインした累積日数をカウントする場合、bitcountを使用することでユーザーのビットマップを生成することができます。
iii. シェアードセッション
マルチインスタンス展開の場合、複数のインスタンス間でログイン情報を同期させることが必要ですが、redisを使用する速度を考えると、ログイン後にユーザー用のuuidをトークンとして生成し、このuuidと対応するユーザーの情報をredisに格納するのが良い方法です。次のユーザーがサービスにアクセスした後、このuuidを持っている限り、このuuidを使ってredisからユーザー情報を取得し、そのユーザーが誰で、どのような権限を持っているのかを知ることができるようにします。
uuidをキーにした場合の問題は、ユーザーの権限が変更された後に対応するキーを見つける方法がないこと、そしてもちろん、オフラインでユーザーの強制を実現する方法がないことである。この問題については、我々はMySQLを使用することができます、我々は、ユーザーが10トークンを保持することができますが、トークンが生成され、正常に設定されたとき、それは対応するトークンがユーザーを介して見つけることができるようにMySQLに書き込まれ、同時に、我々はあまりにも多くのトークンがあることを確認したら、私たちも有効期限が長い間使用されていないトークンを設定でき、また、聞いておく必要があります さらに、一度キー有効期限のレコードは自動的に削除する必要があります有効期限の切れた鍵の通知を聞くために必要があります。コード
@Configuration
public class AddAuthInterceptorConfig implements WebMvcConfigurer {
@Autowired private AuthorizationInterceptor authorizationInterceptor;
@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(authorizationInterceptor);
}
}
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
@Autowired private AuthManager authManager;
@Override
public boolean preHandle(
final HttpServletRequest request, final HttpServletResponse response, final Object handler)
throws Exception {
if (handler instanceof HandlerMethod) {
Auth auth = ((HandlerMethod) handler).getMethodAnnotation(Auth.class);
if (auth ! = null && !authManager.validate(auth, request.getHeader(TOKEN))) {
throw new AuthException("token is invalid, please log in again!");
}
}
return true;
}
}
@Component
public class AuthManager {
@Autowired private TokenManager tokenManager;
public UserInfo getUserInfo() {
String token = getRequest().getHeader(TOKEN);
return tokenManager.getByToken(token);
}
private HttpServletRequest getRequest() {
return ((ServletRequestAttributes) requireNonNull(RequestContextHolder.getRequestAttributes()))
. getRequest();
}
public boolean validate(final Auth auth, final String token) {
return ofNullable(tokenManager.getByToken(token))
.map(user -> user.passAuth(auth)) // determine if the user's permissions can access this interface
.orElse(false);
}
}
@Component
public class RedisTokenManager implements TokenManager {
@Autowired private RedisValueOps
関連
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Redisクラスタのマスターノードとスレーブノードを縮小する詳細チュートリアル
-
redis の RedissonLock が待ちロックを実装する方法
-
Redisによる分散ロック(setnx, getset, incr)の実装とタイムアウトの扱い方
-
Redisの重複排除の3つの手法のまとめ
-
redisでluaスクリプトを使用するためのチュートリアル
-
redis クラスタの実装は同じプレフィックスを持つキーをクリーンアップします。
-
Redisのkeysコマンドの遅さ
-
マイクロサービス領域におけるredisの貢献度を説明する
-
RedisClusterが16,384個のスロットを持つ設計になっている理由
-
RedisTemplateでRedisを操作する、この記事で十分です(a)