分散ロックには Redis と Zookeeper のどちらを使用すればよいでしょうか?

分散ロックには Redis と Zookeeper のどちらを使用すればよいでしょうか?

分散ロックを使用する理由は何ですか?この問題について議論する前に、ビジネス シナリオを見てみましょう。

[[270723]]

画像はPexelsより

分散ロックを使用する理由は何ですか?

システム A は電子商取引システムであり、現在は 1 台のマシンに導入されています。システムにはユーザーが注文するためのインターフェースがありますが、注文する前に、ユーザーは在庫をチェックして、十分な在庫があるかどうかを確認する必要があります。

システムにはある程度の並行性があるため、商品の在庫は事前に Redis に保存され、ユーザーが注文すると Redis の在庫が更新されます。

この時点でのシステム アーキテクチャは次のとおりです。

しかし、これによって問題が発生します。ある瞬間に、Redis 内の特定の商品の在庫が 1 である場合です。

このとき、2 つのリクエストが同時に到着します。リクエストの 1 つは上図のステップ 3 まで実行され、データベース インベントリを 0 に更新しますが、ステップ 4 はまだ実行されていません。

もう一方のリクエストはステップ 2 に到達し、在庫がまだ 1 であることがわかったので、ステップ 3 に進みます。結果として、2 つのアイテムが販売されましたが、在庫は 1 つのアイテムのみになります。

これは明らかに間違っています!これは在庫の過剰販売による典型的な問題です。この時点で、解決策を考えるのは簡単です。ロックを使用してステップ 2、3、4 をロックし、これらのステップが実行された後に別のスレッドがステップ 2 を実行できるようにします。

上図の通り、ステップ 2 を実行する際に、Java が提供する Synchronized または ReentrantLock を使用してロックし、ステップ 4 が完了したらロックを解除します。

このように、ステップ 2、3、および 4 は「ロック」され、複数のスレッド間でのみ連続して実行できるようになります。

しかし、良い時代は長くは続かなかった。システム全体の同時実行性が急上昇し、1 台のマシンでは処理できなくなりました。次に、以下に示すようにマシンを追加する必要があります。

マシンを追加すると、システムは上記のようになります。すごいですね! 2 人のユーザーのリクエストが同時に到着したが、異なるマシンに届いたと仮定すると、これらの 2 つのリクエストを同時に実行できますか? それとも、在庫の過剰販売の問題が発生しますか?

なぜ?上図の 2 つの A システムは 2 つの異なる JVM で実行されるため、追加されるロックはそれぞれの JVM 内のスレッドに対してのみ有効であり、他の JVM 内のスレッドに対しては無効です。

したがって、ここでの問題は、2 台のマシンによって追加されたロックが同じロックではない (2 つのロックが異なる JVM にある) ため、Java によって提供されるネイティブ ロック メカニズムがマルチマシン展開シナリオで失敗するという点です。

では、2 台のマシンに追加されたロックが同じロックであることを確認すれば、問題は解決するのではないでしょうか。この時点で、分散ロックが登場する時が来ました。

分散ロックの考え方は、システム全体でロックを取得するためのグローバルで一意の「もの」を提供し、各システムがロックを必要とするときにこの「もの」にロックを要求し、異なるシステムが同じロックを取得したと想定できるようにすることです。

この「もの」としては、Redis、Zookeeper、またはデータベースが考えられます。テキストの説明はあまり直感的ではありませんので、次の図を見てみましょう。

上記の分析から、在庫過剰のシナリオでは、分散展開システムで Java ネイティブ ロック メカニズムを使用するとスレッドの安全性が保証されないため、分散ロック ソリューションを使用する必要があることがわかります。

では、分散ロックをどのように実装するのでしょうか?読み続けてください!

Redis ベースの分散ロックの実装

上記の分析は、分散ロックが使用される理由を説明しています。ここでは、分散ロックが実装された場合にどのように対処するかについて詳しく説明します。

①一般的な解決策は、分散ロックにRedisを使用することです

分散ロックに Redis を使用するアイデアは、おおよそ次のようになります。Redis にロックが追加されたことを示す値を設定し、ロックが解除されたらキーを削除します。

具体的なコードは次のとおりです。

  1. // ロックを取得する
  2. //キーが存在しない場合は NX は成功を意味し、キーが存在する場合はfalseが返されます。PX は有効期限を指定できます。
  3. anyLockの一意の値NX PX 30000を設定する
  4.  
  5.  
  6. // ロックを解除する: Lua スクリプトを実行する
  7. // ロックの解放には2つの命令が必要であり、これらはアトミックではない
  8. // Redis の Lua スクリプト サポート機能が必要です。 RedisはLuaスクリプトをアトミックに実行する
  9. redis.call( "get" ,KEYS[1]) == ARGV[1]の場合 
  10. redis.call( "del" ,KEYS[1])を返す
  11. それ以外 
  12. 0を返す
  13. 終わり 

このアプローチにはいくつかの重要なポイントがあります。

  • 必ず SET キー値 NX PX ミリ秒 コマンドを使用してください。そうでない場合は、まず値を設定し、次に有効期限を設定します。これはアトミック操作ではないため、有効期限を設定する前にクラッシュが発生し、デッドロックが発生する可能性があります (キー *** が存在します)
  • 値は一意である必要があります。これは、ロック解除時にキーを削除する前に、値がロックされたものと一致していることを確認するためです。

これにより、A がロックを取得し、有効期限が 30 秒であるという状況を回避できます。 35秒後にロックは自動的に解除されます。 A はロックを解除しますが、この時点で B がロックを取得している可能性があります。クライアント A は B のロックを削除できません。

クライアントが分散ロックを実装する方法を考慮するだけでなく、Redis の展開も考慮する必要があります。

Redis には 3 つのデプロイメント方法があります。

  • スタンドアロンモード
  • マスタースレーブ+センチネル選出モード
  • Redis クラスターモード

分散ロックに Redis を使用する場合の欠点は、単一マシンのデプロイメント モードを採用すると、Redis に障害が発生する限り、単一ポイントの問題が発生することです。ロックしても機能しません。

マスタースレーブモードでは、ロック中に 1 つのノードのみがロックされます。 Sentinel によって高可用性が実現されていても、マスター ノードに障害が発生し、マスター スレーブの切り替えが発生すると、ロックが失われる可能性があります。

上記の考慮に基づいて、Redis の作者もこの問題を考慮し、RedLock アルゴリズムを提案しました。

このアルゴリズムの意味は、おおよそ次のようになります。Redis のデプロイメント モードが Redis Cluster であり、マスター ノードが合計 5 つあると仮定します。

次の手順に従ってロックを取得します。

  • 現在のタイムスタンプをミリ秒単位で取得します。
  • 各マスターノードで順番にロックを作成し、有効期限を短い時間(通常は数十ミリ秒)に設定します。
  • 大多数のノードでロックを確立するようにしてください。たとえば、5 つのノードには 3 つのノード (n/2 +1) が必要です。
  • クライアントはロックを確立するための時間を計算します。ロックを確立する時間がタイムアウト期間より短い場合、ロックは正常に確立されます。
  • ロックの確立に失敗した場合、ロックは順番に削除されます。
  • 他の誰かが分散ロックを確立している限り、ロックを取得するためにポーリングを継続する必要があります。

ただし、このアルゴリズムはまだ議論の余地があり、多くの問題がある可能性があり、ロック プロセスが正しいという保証はありません。

②別の方法:レディソン

さらに、Redis クライアントのネイティブ API に基づいて Redis 分散ロックを実装するには、オープン ソース フレームワーク Redission を使用することもできます。

Redisson は、分散ロックのサポートも提供するエンタープライズ レベルのオープン ソース Redis クライアントです。私も皆さんにこれを使うことを強くお勧めします。なぜでしょうか?

上で述べたことを思い出してください。 Redis を通じて値を設定する独自のコードを記述する場合は、次のコマンドで設定します。

  1. anyLockの一意の値NX PX 30000を設定する

ここで設定されるタイムアウトは 30 秒です。 30 秒以内にビジネス ロジックを完了しないと、キーの有効期限が切れ、他のスレッドがロックを取得する可能性があります。

この場合、最初のスレッドがビジネス ロジックの実行をまだ完了していない状態で 2 番目のスレッドが実行されると、スレッドの安全性の問題が発生します。

したがって、この有効期限も追加で管理する必要がありますが、これは非常に面倒です。Redisson がこれをどのように実装するかを見てみましょう。

まずはRedissionを使う喜びを感じてください。

  1. 設定 config = new Config();
  2. config.useClusterServers()
  3. .addNodeAddress( "redis://192.168.31.101:7001" )
  4. .addNodeAddress( "redis://192.168.31.101:7002" )
  5. .addNodeAddress( "redis://192.168.31.101:7003" )
  6. .addNodeAddress( "redis://192.168.31.102:7001" )
  7. .addNodeAddress( "redis://192.168.31.102:7002" )
  8. .addNodeAddress( "redis://192.168.31.102:7003" );
  9.  
  10. RedissonClient redisson = Redisson.create (config);
  11.  
  12.  
  13. RLock ロック = redisson.getLock( "anyLock" );
  14. ロック。ロック();
  15. ロックを解除します。

それはとても簡単です。分散ロックを完了するには、API の Lock と Unlock を使用するだけです。それは多くの詳細を考慮するのに役立ちます:

  • すべての Redisson 命令は Lua スクリプトを通じて実行され、Redis は Lua スクリプトのアトミック実行をサポートします。
  • Redisson は、キーのデフォルトの有効期限を 30 秒に設定します。クライアントが 30 秒以上ロックを保持するとどうなりますか?
  • Redisson には、番犬を意味する Watchdog という概念があります。ロックを取得した後、10 秒ごとにキーのタイムアウトを 30 秒に設定できるようになります。

この方法では、ロックが常に保持されていても、キーの有効期限が切れて他のスレッドがロックを取得するという問題は発生しません。

  • Redisson の「ウォッチドッグ」ロジックにより、デッドロックが発生しないことが保証されます。 (マシンがクラッシュするとウォッチドッグはなくなります。このとき、キーの有効期限は延長されません。30秒後に自動的に期限切れになり、他のスレッドがロックを取得できるようになります)

以下にその実装コードの一部を示します。

  1. // ロックロジック
  2. プライベート <T> RFuture<Long> tryAcquireAsync(long リースタイム、TimeUnit ユニット、final long スレッド ID) {
  3. リースタイムが -1 の場合
  4. tryLockInnerAsync(leaseTime、ユニット、スレッドId、RedisCommands.EVAL_LONG)を返します
  5. }
  6. // lua スクリプトを呼び出して、いくつかのキーと有効期限を設定します
  7. RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
  8. ttlRemainingFuture.addListener(新しいFutureListener<Long>() {
  9. @オーバーライド
  10. パブリックvoid operationComplete(Future<Long> future) は例外をスローします {
  11. (!future.isSuccess())の場合{
  12. 戻る;
  13. }
  14.  
  15. Long ttlRemaining = future.getNow();
  16. // ロックを取得しました
  17. 残り時間 == null の場合
  18. // ウォッチドッグロジック
  19. スケジュール有効期限更新(スレッドID)
  20. }
  21. }
  22. });
  23. ttlRemainingFutureを返します
  24. }
  25.  
  26.  
  27. <T> RFuture<T> tryLockInnerAsync(long リースタイム、TimeUnit ユニット、long スレッド ID、RedisStrictCommand<T> コマンド) {
  28. 内部ロックリース時間 = unit.toMillis(リース時間);
  29.  
  30. commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, コマンド,を返します
  31. 「(redis.call('exists', KEYS[1]) == 0) の場合」 +
  32. "redis.call('hset', KEYS[1], ARGV[2], 1); " +
  33. "redis.call('pexpire', KEYS[1], ARGV[1]); " +
  34. "nil を返す; " +
  35. 「終了;」 +
  36. 「(redis.call('hexists', KEYS[1], ARGV[2]) == 1) の場合」 +
  37. "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
  38. "redis.call('pexpire', KEYS[1], ARGV[1]); " +
  39. "nil を返す; " +
  40. 「終了;」 +
  41. 「redis.call('pttl', KEYS[1]);を返します。」
  42. コレクション。<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
  43. }
  44.  
  45.  
  46.  
  47. // ウォッチドッグは最終的にこれを呼び出します
  48. プライベート void スケジュール更新 (最終ロング スレッド ID) {
  49. 有効期限更新マップにキーが含まれている場合(getEntryName()) {
  50. 戻る;
  51. }
  52.  
  53. // このタスクは10秒の遅延で実行されます
  54. タイムアウトタスク = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
  55. @オーバーライド
  56. パブリックvoid run(Timeout timeout) 例外をスローします {
  57.  
  58. // この操作により、キーの有効期限が 30 秒にリセットされます
  59. RFuture<ブール値> future = renewExpirationAsync(threadId);
  60.  
  61. future.addListener(新しいFutureListener<Boolean>() {
  62. @オーバーライド
  63. パブリックvoid operationComplete(Future<Boolean> future) は例外をスローします {
  64. 有効期限更新マップを削除します。
  65. (!future.isSuccess())の場合{
  66. log.error( "ロック " + getName() + " の有効期限を更新できません" , future.cause());
  67. 戻る;
  68. }
  69.  
  70. (future.getNow())の場合{
  71. // スケジュールを再設定する
  72. // このメソッドを再帰的に呼び出すことで、有効期限が繰り返し延長されます
  73. スケジュール有効期限更新(スレッドID)
  74. }
  75. }
  76. });
  77. }
  78.  
  79. }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
  80.  
  81. if (expirationRenewalMap.putIfAbsent(getEntryName(), new ExpirationEntry(threadId, task)) != null ) {
  82. タスクをキャンセルします。
  83. }
  84. }

さらに、Redisson は Redlock アルゴリズムのサポートも提供しており、その使い方も非常に簡単です。

  1. RedissonClient redisson = Redisson.create ( config);
  2. RLock lock1 = redisson.getFairLock( "lock1" );
  3. RLock lock2 = redisson.getFairLock( "lock2" );
  4. RLock lock3 = redisson.getFairLock( "lock3" );
  5. RedissonRedLock マルチロック = 新しい RedissonRedLock(lock1、lock2、lock3);
  6. マルチロック。ロック();
  7. マルチロック。ロック解除();

概要: このセクションでは、Redis を分散ロックとして使用する具体的な実装計画とその制限事項を分析し、すべての人に使用を推奨する Redis クライアント フレームワーク Redisson を紹介します。独自のコードを書くよりも、多くの細かい点に注意を払う必要が少なくなります。

Zookeeper に基づく分散ロックの実装

一般的な分散ロック実装ソリューションとしては、Redis を使用するほか、Zookeeper を使用して分散ロックを実装することもできます。

Zookeeper (以下、ZK) を使用して分散ロックを実装する仕組みを紹介する前に、まずは ZK とは何かを簡単に紹介します。ZK は、構成管理、分散コラボレーション、命名を提供する集中型サービスです。

ZK モデルは次のとおりです。ZK には、ファイル システムと同様に、Znode と呼ばれる一連のノードが含まれており、各 Znode はディレクトリを表します。

Znode にはいくつかの特徴があります:

  • 順序付けられたノード: 現在親ノード /lock が存在する場合、この親ノードの下に子ノードを作成できます。 ZK はオプションの順序付き機能を提供します。

たとえば、子ノード「/lock/node-」を作成し、順序を指定すると、ZK は子ノードを生成するときに、現在の子ノードの数に応じて整数のシリアル番号を自動的に追加します。

つまり、最初に作成された子ノードの場合、生成される子ノードは /lock/node-0000000000、次のノードは /lock/node-0000000001 というようになります。

  • 一時ノード: クライアントは一時ノードを作成できます。セッションが終了するかタイムアウトすると、ZK は自動的にノードを削除します。
  • イベント監視: データを読み取るときに、ノードのイベント監視を設定することもできます。ノードデータまたは構造が変更されると、ZK はクライアントに通知します。

現在、ZK には次の 4 つのイベントがあります。

  • ノードの作成
  • ノードの削除
  • ノードデータの変更
  • サブノードの変更

ZK の上記の特性に基づいて、ZK を使用して分散ロックを実装するためのソリューションを簡単に考え出すことができます。

  • ZK の一時ノードと順序付きノードを使用して、各スレッドは /lock/ ディレクトリなどの ZK に一時的な順序付きノードを作成することによってロックを取得します。
  • ノードが正常に作成された後、/lock ディレクトリ内のすべての一時ノードが取得され、現在のスレッドによって作成されたノードがすべてのノードの中で最小のシーケンス番号を持つノードであるかどうかが判断されます。
  • 現在のスレッドによって作成されたノードが、すべてのノードの中で最小のシーケンス番号を持つノードである場合、ロックが正常に取得されたとみなされます。
  • 現在のスレッドによって作成されたノードが、すべてのノードの中で最小のシーケンス番号を持つノードでない場合は、前のノード シーケンス番号を持つノードにイベント リスナーが追加されます。

たとえば、現在のスレッドによって取得されたノード番号が /lock/003 で、すべてのノードのリストが [/lock/001、/lock/002、/lock/003] の場合、イベント リスナーは /lock/002 ノードに追加されます。

ロックが解除されると、次のシーケンス番号を持つノードが起動され、ステップ 3 が再実行され、自身のノード シーケンス番号が最小であるかどうかが判断されます。

例えば、/lock/001 が解放され、/lock/002 が時間を検出すると、ノードセットは [/lock/002, /lock/003] になります。すると、/lock/002 が最小のシーケンス番号を持つノードとなり、ロックを取得します。

全体のプロセスは次のとおりです。

これが具体的な実装アイデアです。コードの書き方については、比較的複雑なのでここでは掲載しません。

キュレーター紹介

Curator は、分散ロックの実装も提供するオープンソースの ZK クライアントです。使い方も比較的簡単です。

  1. InterProcessMutex interProcessMutex = 新しい InterProcessMutex(client, "/anyLock" );
  2. インタープロセスミューテックスを取得します。
  3. プロセスミューテックスを解放します。

分散ロックを実装するためのコア ソース コードは次のとおりです。

  1. private boolean internalLockLoop(long startMillis, Long millisToWait, String ourPath) は例外をスローします
  2. {
  3. ブール値 haveTheLock = false ;
  4. ブール型doDelete = false ;
  5. 試す {
  6. (取り消し可能なget()!= null )の場合{
  7. client.getData().usingWatcher(revocableWatcher).forPath(ourPath);
  8. }
  9.  
  10. ( (client.getState() == CuratorFrameworkState.STARTED) && !haveTheLock ) の間 {
  11. // 現在のすべてのノードのソートされたセットを取得します
  12. リスト<String> children = getSortedChildren();
  13. // 現在のノードの名前を取得します
  14. 文字列sequenceNodeName = ourPath。部分文字列(basePath.length() + 1); //スラッシュを含めるには+1
  15. // 現在のノードが最小のノードであるかどうかを判定する
  16. 述語結果 predicateResults = driver.getsTheLock(client, children, sequenceNodeName, maxLeases);
  17. if (述語結果.getsTheLock()) {
  18. // ロックを取得する
  19. ロックを有効にする = true ;
  20. }それ以外{
  21. // ロックが取得されていない場合は、現在のノードの前のノードのリスナーを登録します
  22. 文字列 previousSequencePath = basePath + "/" + predicateResults.getPathToWatch();
  23. 同期(これ){
  24. 統計 stat = client.checkExists().usingWatcher(watcher).forPath(previousSequencePath);
  25. (統計値がnullの場合
  26. if ( millisToWait != null ) {
  27. millisToWait -= (System.currentTimeMillis() - startMillis);
  28. startMillis = System.currentTimeMillis();
  29. (ミリ秒待機時間<= 0)の場合{
  30. 削除する = true ; // タイムアウト-ノード削除します
  31. 壊す;
  32. }
  33. 待機(ミリ秒単位の待機)。
  34. }それ以外{
  35. 待って();
  36. }
  37. }
  38. }
  39. //そうでない場合は削除されている可能性があります(つまり、ロックが解除されています)。再度取得試みる
  40. }
  41. }
  42. }
  43. キャッチ(例外e){
  44. 削除する = true ;
  45. eを投げる;
  46. } ついに{
  47. if (doDelete) {
  48. パスを削除します。
  49. }
  50. }
  51. haveTheLock を返します
  52. }

実際、Curator の分散ロック実装の基本原理は、上記の分析と似ています。ここでは、図を使ってその原理を詳しく説明します。

概要: このセクションでは、分散ロックを実装するための ZK のソリューションと ZK のオープンソース クライアントの基本的な使用方法を紹介し、その実装原則を簡単に紹介します。

2つのソリューションの長所と短所の比較

このセクションでは、2 つの分散ロック実装について学習した後、Redis と ZK の実装の長所と短所について説明します。

Redis 分散ロックの場合、次のような欠点があります。

  • ロックを取得する方法は単純かつ粗雑です。ロックの取得に失敗した場合は、ロックの取得を試行し続けるため、パフォーマンスが低下します。
  • さらに、Redis の設計により、データの一貫性が強くないことが判断され、極端な場合には問題が発生する可能性があります。ロック モデルは十分に堅牢ではありません。
  • Redlock アルゴリズムを使用した場合でも、一部の複雑なシナリオでは 100% 問題がないとは保証されません。 Redlock の詳細については、「分散ロックの実行方法」を参照してください。
  • Redis の分散ロックでは、実際には常にロックの取得を試行する必要があり、パフォーマンスが低下します。

しかし一方で、Redis を使用して分散ロックを実装することは多くの企業で非常に一般的であり、ほとんどの場合、いわゆる「非常に複雑なシナリオ」に遭遇することはありません。

したがって、Redis を分散ロックとして使用することも適切なソリューションです。最も重要な点は、Redis はパフォーマンスが高く、高並行性のロック取得および解放操作をサポートできることです。

ZK 分散ロックの場合:

  • ZK は分散調整と強力な一貫性を実現するように設計されています。ロック モデルは堅牢で使いやすく、分散ロックに適しています。
  • ロックを取得できない場合は、常にポーリングする必要はなく、リスナーを追加するだけで済み、パフォーマンスの消費は比較的低くなります。

ただし、ZK にも欠点があります。ロックを頻繁に申請したり解除したりするクライアントが多い場合、ZK クラスターにかかる負荷が比較的高くなります。

まとめ: まとめると、Redis と ZK にはそれぞれ長所と短所があります。これらの問題は、テクノロジーを選択する際の参考要素として使用できます。

いくつかの提案

これまでの分析から、分散ロックを実装するための一般的なソリューションは Redis と ZK の 2 つであり、それぞれに利点があることがわかりました。どのように選択すればよいでしょうか?

個人的には、ZK によって実装されたロックを好みます。Redis には隠れた危険があり、不正確なデータが発生する可能性があるためです。ただし、どのように選択するかは、企業内の具体的なシナリオによって異なります。

企業が ZK クラスターの条件を満たしている場合は、ZK の実装が優先されます。ただし、企業内に Redis クラスターしかない場合、ZK クラスターを構築するための条件はありません。

実際、Redis を使用して実装することも可能です。さらに、システム設計者は、システムにすでに Redis があるが、外部依存関係を再度導入したくないと考えているため、Redis を使用することができます。これは、システム設計者がアーキテクチャに基づいて考慮する必要があるものです。

Chinese Huperzine: 10 年以上の BAT アーキテクチャ経験、一流インターネット企業のテクニカル ディレクター。数百人のチームを率いて、数億のトラフィックを処理する複数の高同時実行システムを開発しました。長年の研究で蓄積してきた研究論文や経験の要約を文書にまとめましたので、皆様にご紹介したいと思います。 WeChat 公開アカウント: Shishan’s Architecture Notes (ID: shishan100)。

<<:  Kafka アプリケーションを理解するための 2 つの図

>>:  5G 時代のクラウド コンピューティング市場の動向を通信事業者はどのように把握するのでしょうか?

推薦する

Springboot+Dubboを使用して分散マイクロサービスを構築し、プロセス全体にわたって開発に注釈を付ける

インターネットの発展に伴い、ウェブサイトアプリケーションの規模は拡大し続けています。従来の垂直アプリ...

ウェブサイトを構築する前に、クラウドサーバーと仮想ホストの4つの違いを見てみましょう

クラウド サーバーと仮想ホストは名前が非常に似ているため、多くの企業はそれらの違いを理解していません...

VULTR の IP が「不明」とマークされている場合はどうすればよいですか?

多くの人が Vultr の VPS を使用していますが、IP ブロックや「説明できない」問題、そして...

ウェブサイトのプログラムとSEOランキングの関係

なぜこの話題を取り上げようと思ったかというと、今日のグループチャットで、ウェブサイトのプログラムと ...

UCloudウランチャブ自社構築データセンターが中国北部にデュアルセンターを設立するための起工式を開催

7月16日、内モンゴル自治区ウランチャブ市でUCloudウランチャブデータセンターの起工式が開催され...

hosthatch UK VPS はいかがでしょうか?ロンドン VPS レビュー

Hosthatchは英国ロンドンで独自のVPSクラウドサーバー事業を展開しており、安価で費用対効果が...

camohosting 無料 250M 仮想ホスト

250MB のハード ドライブ、月間トラフィック 10G、Web サイト 1 つ、MySQL 5 つ...

ユビキタスギガビットが5G屋内の新トレンドをリード - ファーウェイの5G屋内分散型Massive MIMOが最優秀ソリューションケースを受賞

[中国、北京、2021年9月27日] 2021年中国国際情報通信博覧会において、ファーウェイの5G屋...

App Store有料リストのゲームアプリが罰せられました!

有料リストに詳しい友人は、過去にはトップ 10 が間違いなくゲームアプリケーションだったことを知って...

フォレスター、2020年のクラウドコンピューティングの変化を予測:アリババがグーグルを抜いて3位になる可能性

最近、Forrester はクラウド コンピューティングに関するレポートを発表し、2020 年のクラ...

この記事はKubernetesにおける証明書の動作メカニズムを徹底的に理解するのに役立ちます

この記事はWeChatの公開アカウント「趙華兵」から転載したものです。この記事を転載する場合は、趙華...

Facebookマーケティングは難しいですか?このように操作することでのみ、Cポジションにデビューできます

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っていますFaceb...

NetEase Bafangがサインインサービスを停止した6つの理由

テンセントテクノロジーロイス11月8日総合レポート「サインインは終わった」という叫びは2012年も鳴...

電子商取引企業がコンテンツマーケティングを実施するためのいくつかの新しい方法について簡単に説明します。

ハードなプロモーションと比較すると、コンテンツマーケティングは人々に受け入れられやすく、ユーザーに認...

オンラインマーケティングで売上を獲得するには、まず消費者を獲得する必要がある

消費者は市場の主な消費者として、企業の発展の過去と未来を担っています。昨日の市場では、一部の消費者が...