ロックの基本概念からRedis分散ロックの実装まで

ロックの基本概念からRedis分散ロックの実装まで

最近、分散トランザクション、分散フレームワーク、ZooKeeper、SpringCloud など、分散に関する問題が広く言及されるようになりました。この記事では、まずロックの概念を確認し、次に分散ロックを紹介し、Redis で分散ロックを実装する方法について説明します。

1. ロックの基礎知識

まず、私たちの仕事と研究におけるロックの概念を見直してみましょう。

なぜ最初にロックについて説明し、次に分散ロックについて説明しなければならないのでしょうか?

ロックの役割は、共有リソースへのマルチスレッド アクセスによって発生するスレッド セーフティの問題を解決することであることは誰もが知っています。しかし、日常生活で鍵はあまり使われません。ロックの概念や基本的な使用法についてよくわかっていない友人もいるかもしれませんので、まずロックについて説明し、次に分散ロックについて詳しく説明します。

[[286783]]

チケット販売の小さな例を見てみましょう。たとえば、全員が dota2 ti9 のチケットを入手しようとした場合、チケットがロックされていないとどのような問題が発生するでしょうか?この時点でコードは次のようになります。

  1. パッケージスレッド;
  2. java.util.concurrent.TimeUnitをインポートします
  3.  
  4. 公共 クラスチケット{
  5.  
  6. /**
  7. * 初期在庫
  8. * */  
  9. 整数ticketNum = 8 ;
  10.  
  11. 公共  void減らす( int num){
  12. // 在庫が十分かどうかを判定する 
  13. if ((チケット番号 - 番号) >= 0 ){
  14. 試す{
  15. TimeUnit.MILLISECONDS.sleep( 200 );
  16. }キャッチ(InterruptedException e){
  17. e.printStackTrace();
  18. }
  19. チケット番号 -= 番号;
  20. System.out.println(Thread.currentThread().getName() + "販売に成功しました"  
  21. + num + "張、残り" + ticketNum + "張チケット" );
  22. }それ以外{
  23. System.err.println(Thread.currentThread().getName() + "販売されていません"  
  24. + num + "張、残り" + ticketNum + "張チケット" );
  25. }
  26. }
  27.  
  28. 公共 静的  void main(String[] args)InterruptedExceptionをスローします{
  29. チケットticket = new Ticket();
  30. //チケットを取得するために 10 個のスレッドを開始します。論理的に考えると、チケットを入手できない人が 2 人いるはずです。  
  31. ( int i = 0 ; i < 10 ; i++) {
  32. 新しいスレッド(() -> ticket.reduce( 1 )、 "user" + (i + 1 )).start();
  33. }
  34. スレッドをスリープ状態にします(1000L)。
  35. }
  36.  
  37. }

コード分​​析: ここには 8 つの ti9 チケットがあり、同時にチケットを取得するために 10 個のスレッド (つまり、10 人のユーザーをシミュレート) が設定されています。グラブが成功した場合は成功と表示され、失敗した場合は失敗と表示されます。当然ながら、8 人がゲームを奪取することに成功し、2 人が失敗したはずです。実行結果を見てみましょう:

実行結果は期待と一致しないことがわかりました。 10 人全員がチケットを購入したため、スレッド セーフティの問題が発生したことになります。それで、何が原因だったのでしょうか?

理由は、複数のスレッド間に時間差があるためです。

図に示すように、残っているチケットは 1 つだけですが、両方のスレッドによって読み取られた残りのチケットは 1 です。これは、スレッド A が在庫を変更する前に、スレッド B がチケットを正常に取得したことを意味します。

  1. どうすれば解決できるでしょうか?同期キーワードを追加すればうまくいくことは誰もが知っていると思います1 つのスレッドが Reduce メソッドを実行すると、他のスレッドは待機キューでブロックされるため、複数のスレッド間で共有変数の競合は発生しません。
  2. 例えば
  3. たとえば、ジムに行って運動するとき、多くの人が同時に 1 台のマシンを使用したり、1 台のトレッドミルで走ったりすると、大きな問題が発生し、全員が互いに争うことになります。ジムのドアに鍵を付ければ、鍵を持っている人だけがジムに入って運動することができ、他の人はドアの外で待つことになるので、フィットネス機器の取り合いを避けることができます。コードは次のとおりです。
  4. 公共  同期した  void減らす( int num){
  5. // 在庫が十分かどうかを判定する 
  6. if ((チケット番号 - 番号) >= 0 ){
  7. 試す{
  8. TimeUnit.MILLISECONDS.sleep( 200 );
  9. }キャッチ(InterruptedException e){
  10. e.printStackTrace();
  11. }
  12. チケット番号 -= 番号;
  13. System.out.println(Thread.currentThread().getName() + "販売に成功しました"   
  14. + num + "张,REST" + ticketNum + "张票" );
  15. }それ以外{
  16. System.err.println(Thread.currentThread().getName() + "販売されていません"   
  17. + num + "張、残り" + ticketNum + "張チケット" );
  18. }
  19. }

操作結果:

予想通り、2名がチケットを入手できませんでした。我々の目標は達成されたようだ。

2. ロックパフォーマンスの最適化

2.1 ロック保持時間を短縮する

実際、私たちの日常生活の理解によれば、ジム全体で運動している人が 1 人だけということはあり得ません。したがって、1 台のマシンをロックするだけで済みます。たとえば、1 人がランニングをしているときに、もう 1 人は他のスポーツを行うことができます。

チケットシステムの場合、在庫変更操作のコードをロックするだけで済みます。他のコードは引き続き並行して実行できるため、ロック保持時間が大幅に短縮されます。コードは次のように変更されます。

  1. 公共  voidロックによる削減( int num){
  2. ブールフラグ = false ;
  3. 同期済み(チケット番号) {
  4. if ((チケット番号 - 番号) >= 0 ){
  5. チケット番号 -= 番号;
  6. フラグ = true ;
  7. }
  8. }
  9. if (フラグ){
  10. System.out.println(Thread.currentThread().getName() + "販売に成功しました"  
  11. + num + "张,REST" + ticketNum + "张票" );
  12. }
  13. それ以外{
  14. System.err.println(Thread.currentThread().getName() + "販売されていません"  
  15. + num + "张,REST" + ticketNum + "张票" );
  16. }
  17. チケット番号 == 0の場合{
  18. System.out.println( "所要時間" + (System.currentTimeMillis() - startTime) + "ミリ秒" );
  19. }
  20. }
  21.  
  22. これを行う目的は、CPU リソースを最大限に活用し、コードの実行効率を向上させることです。
  23. ここでは、2 つの方法で時間を出力します。
  24. 公共 同期した  void減らす( int num){
  25. // 在庫が十分かどうかを判定する 
  26. if ((チケット番号 - 番号) >= 0 ){
  27. 試す{
  28. TimeUnit.MILLISECONDS.sleep( 200 );
  29. }キャッチ(InterruptedException e){
  30. e.printStackTrace();
  31. }
  32. チケット番号 -= 番号;
  33. チケット番号 == 0の場合{
  34. System.out.println( "所要時間" + (System.currentTimeMillis() - startTime) + "ミリ秒" );
  35. }
  36. System.out.println(Thread.currentThread().getName() + "販売に成功しました"  
  37. + num + "张,REST" + ticketNum + "张票" );
  38. }それ以外{
  39. System.err.println(Thread.currentThread().getName() + "販売されていません"  
  40. + num + "张,REST" + ticketNum + "张票" );
  41. }
  42. }

確かに、コードの一部だけをロックすると、コードの実行効率が大幅に向上します。

したがって、スレッドの安全性の問題を解決した後、ロック後のコード実行効率も考慮する必要があります。

2.2 ロックの粒度を下げる

たとえば、最近公開された映画には『哨戒班』と『スパイダーマン』の 2 つがあります。支払い購入プロセスをシミュレートし、メソッドを待機させて、CountDownLatch の await メソッドを追加します。実行結果は次のとおりです。

  1. パッケージスレッド;
  2. java.util.concurrent.CountDownLatchをインポートします
  3.  
  4. 公共 クラスムービー {
  5. プライベート 最終的なCountDownLatchラッチ =新しいCountDownLatch( 1 );
  6. //悪魔の哨戒 
  7. プライベート整数 babyTickets = 20 ;
  8.  
  9. //スパイダーマン 
  10. プライベート整数spiderTickets = 100 ;
  11.  
  12. 公共 同期した  void showBabyTickets()は InterruptedExceptionをスローします{
  13. System.out.println( "魔少年哨戒のチケットの残り枚数は: " + babyTickets);
  14. //買う 
  15. ラッチを待機します。
  16. }
  17.  
  18. 公共 同期した  void showSpiderTickets()は InterruptedExceptionをスローします{
  19. System.out.println( "スパイダーマンの残りのチケットの数は: " + spiderTickets);
  20. //買う 
  21. }
  22.  
  23. 公共 静的  void main(String[] args) {
  24. ムービー movie = new Movie();
  25. 新しいスレッド(() -> {
  26. 試す{
  27. 映画.showBabyTickets();
  28. }キャッチ(InterruptedException e){
  29. e.printStackTrace();
  30. }
  31. }, "ユーザーA" ).start();
  32.  
  33. 新しいスレッド(() -> {
  34. 試す{
  35. 映画.showSpiderTickets();
  36. }キャッチ(InterruptedException e){
  37. e.printStackTrace();
  38. }
  39. }, "ユーザーB" ).start();
  40. }
  41.  
  42. }

実行結果:

悪魔少年ネザの残り投票数: 20

Nezha チケットの購入時にブロックすると、Spiderman チケットの購入に影響することが判明しました。実際、これら 2 つのムービーは互いに独立しているため、ロックの粒度を下げて、ムービー オブジェクト全体のロックを 2 つのグローバル変数のロックに変更する必要があります。変更されたコードは次のとおりです。

  1. 公共  void showBabyTickets()は InterruptedExceptionをスローします{
  2. 同期済み(babyTickets) {
  3. System.out.println( "魔少年哨戒のチケットの残り枚数は: " + babyTickets);
  4. //買う 
  5. ラッチを待機します。
  6. }
  7. }
  8. 公共  void showSpiderTickets()は InterruptedExceptionをスローします{
  9. 同期済み(spiderTickets) {
  10. System.out.println( "スパイダーマンの残りのチケットの数は: " + spiderTickets);
  11. //買う 
  12. }
  13. }

実行結果:

悪魔少年ネザの残り投票数: 20

スパイダーマンの残り投票数: 100

これで、2 つの映画のチケットを購入しても、お互いに影響がなくなります。これはロックを最適化する 2 番目の方法であり、ロックの粒度を減らします。ちなみに、Java 並行性パッケージの ConcurrentHashMap は、大きなロックを 16 個の小さなロックに変換し、セグメント化されたロックを通じて効率的な並行安全性を実現します。

2.3 ロックの分離

ロック分離は、一般的に読み取り/書き込み分離と呼ばれます。ロックは読み取りロックと書き込みロックに分けられます。読み取りロックはブロックする必要はありませんが、書き込みロックでは同時実行の問題を考慮する必要があります。

3. ロックの種類

  • フェアロック: ReentrantLock
  • 不公平なロック: Synchronized、ReentrantLock、cas
  • 悲観的ロック: 同期
  • 楽観的ロック: cas
  • 排他ロック: Synchronized、ReentrantLock
  • 共有ロック: セマフォ

ここでは、各タイプのロックの概念を一つ一つ説明することはしません。自分で学ぶこともできます。ロックは、バイアスロック、軽量ロック、重量ロックに分類することもできます。

4. Redis 分散ロック

ロックとロックの最適化の基本的な概念を理解した後、分散ロックの概念について詳しく説明します。

上の図は私たちが構築した分散環境を示しています。チケット購入アイテムは 3 つあり、1 つのインベントリに対応します。各システムには複数のスレッドがあります。上記の通り、在庫変更操作はロックされています。これら 6 つのスレッドのスレッド セーフティは保証できますか?

もちろん違います。各チケット購入システムにはそれぞれ独立した JVM プロセスがあるため、同期を追加しても 1 つのシステムのスレッドの安全性は保証されますが、分散スレッドの安全性は保証されません。

したがって、この問題を解決するには、3 つのシステムすべてに共通するミドルウェアが必要です。

ここでは、分散ロックとして Redis を選択します。複数のシステムが Redis に同じキーを設定します。設定は、キーが存在しない場合にのみ成功し、キーはいずれかのシステムの一意の識別子に対応します。システムがリソースへのアクセスを終了すると、キーは削除され、ロックを解除する目的が達成されます。

4.1 分散ロックで注意すべきこと

1) 相互排他性

一度にロックを取得できるのは 1 つのクライアントのみです。

これは理解しやすいですね。すべてのシステムのうち 1 つのシステムだけがロックを保持できます。

2) デッドロック防止

クライアントがロックを保持したままクラッシュし、ロックを解放しない場合、他のクライアントはロックを取得できず、デッドロックが発生します。したがって、クライアントがロックを解除することを確認する必要があります。

Redis では、デッドロックが発生しないようにロックの有効期限を設定できます。

3) ロックホルダーでロックを解除する

ベルを結んだ人が、それを解く必要があります。システムのロックとロック解除には同じクライアントを使用する必要があります。クライアント A のスレッドによって追加されたロックは、クライアント A のスレッドによってロック解除される必要があります。クライアントは別のクライアントのロックを解除できません。

4) 再入性

クライアントがオブジェクト ロックを取得した後、クライアントはオブジェクトのロックを再度取得できます。

4.2 Redis分散ロックプロセス

Redis 分散ロックの具体的なプロセス:

1) まず、Redis キャッシュのプロパティを使用して、Redis にキーと値のペアを設定します。キーはロックの名前です。すると、クライアントの複数のスレッドがロックを競い合うことになります。競争が成功した場合、値はクライアントの一意の識別子に設定されます。

2) ロックを競合するクライアントは、次の 2 つのことを行う必要があります。

  • ロックの有効時間を設定する目的は、デッドロックを防ぐことです(非常に重要)

有効期間の長さは、ビジネスニーズに基づいて継続的なストレステストを通じて決定する必要があります。

  • ロック所有者がロックを解除できるように、クライアントに一意の ID を割り当てます (非常に重要)

したがって、ここでの値は一意の識別子 (uuid など) に設定されます。

3) 共有リソースにアクセスする

4) ロックを解除します。ロックを解除する方法は2つあります。 1 つ目は、有効期間が経過すると自動的にロックを解除することです。 2 番目は、一意の識別子に基づいて、ロックを解除する権限があるかどうかを最初に判断することです。識別子が正しい場合、ロックは解除されます。

4.3 ロックとロック解除

4.3.1 ロック

1) setnx コマンド ロック

存在しない場合は設定します。Redis コマンド setnx を使用します。 setnx の意味は、ロックが存在しない場合にのみロックが正常に設定されるということです。

2) デッドロックを防ぐためにロックの有効期間を設定します。

ロックには 2 つの手順が必要です。何か問題が起きないか考えてみませんか?

ロック後にクライアントが突然クラッシュしたらどうなりますか?そうすると、ロックは有効期間のないロックとなり、デッドロックが発生する可能性があります。これが起こる可能性は非常に低いですが、問題が発生すると非常に深刻なので、これら 2 つの手順を 1 つにまとめる必要があります。

幸いなことに、Redis 3.0 ではこれら 2 つの命令が 1 つの新しい命令に統合されました。

Jedis の公式ドキュメントのソースコードを見てみましょう。

  1. パブリック文字列セット(文字列キー、文字列値、文字列nxxx、文字列expx、長い時間) {
  2. これは.checkIsInMultiOrPipeline() です。
  3. this .client.set(キー、値、nxxx、expx、時間);
  4. 戻る これは.client.getStatusCodeReply() です。
  5. }

これが私たちが望んでいるものです!

4.3.2 ロック解除

  1. ロックを保持しているかどうかを確認します(一意の識別子を決定します)。
  2. ロックを外します。

ロック解除も 2 段階のプロセスであり、ロック解除の原子性も確保され、2 つの手順が 1 つに結合される必要があります。

これは Redis では実現できず、Lua スクリプトでのみ実現できます。

  1. Redis.call( "get" 、key==argv[ 1 ])の場合
  2. Redis.call( "del" ,key)を返します
  3. それ以外 戻る  0終了

これは、ロックを保持しているかどうかを判定し、ロックを解除する Lua スクリプトです。

Lua スクリプトがアトミックなのはなぜですか? Lua スクリプトは eval() 関数を使用して jedis によって実行されるため、実行されると完全に実行されます。

5. Redis分散ロックコードの実装

  1. 公共 クラスRedisDistributedLockはLockを実装します{
  2. //コンテキスト、現在のロックホルダーIDを保存します 
  3. プライベートThreadLocal<String> lockContext =新しいThreadLocal<String>();
  4.  
  5. //デフォルトのロックタイムアウト 
  6. プライベート 長時間= 100 ;
  7.  
  8. //再入可能性 
  9. プライベートスレッド所有者スレッド;
  10.  
  11. パブリックRedisDistributedLock() {
  12. }
  13.  
  14. 公共  voidロック() {
  15. while (!tryLock()){
  16. 試す{
  17. スレッド.スリープ( 100 );
  18. }キャッチ(InterruptedException e){
  19. e.printStackTrace();
  20. }
  21. }
  22. }
  23.  
  24. 公共 ブール型tryLock() {
  25. tryLock(time,TimeUnit.MILLISECONDS)を返します
  26. }
  27.  
  28. 公共 ブール型tryLock(長い時間、TimeUnit単位){
  29. 文字列 id = UUID.randomUUID().toString(); //各ロック保持者には一意のIDが割り当てられます 
  30. スレッド t = Thread.currentThread();
  31. ジェディス jedis = new Jedis( "127.0.0.1" , 6379 );
  32. //ロックが存在しない場合にのみロックし、ロックの有効時間を設定します 
  33. if ( "OK" .equals(jedis.set( "lock" ,id, "NX" , "PX" , unit.toMillis(time)))){
  34. //ロックを保持している人のID  
  35. ロックコンテキストを設定します。 ①
  36. //現在のスレッドを記録する 
  37. 所有者スレッドを設定します。 ②
  38. 戻る 真実;
  39. }それ以外 所有者スレッド == t の場合
  40. //ロックは再入可能であるため、現在のスレッドがすでにロックを保持しているかどうかを判断する必要があります。  
  41. 戻る 真実;
  42. }それ以外{
  43. 戻る 間違い;
  44. }
  45. }
  46.  
  47. プライベート  void setOwnerThread(スレッドt){
  48. この.ownerThread = t;
  49. }
  50.  
  51. 公共  voidロック解除() {
  52. 文字列スクリプト = null ;
  53. 試す{
  54. ジェディス jedis = new Jedis( "127.0.0.1" , 6379 );
  55. スクリプト = inputStream2String(getClass().getResourceAsStream( "/Redis.Lua" ));
  56. lockContext.get() がnull場合
  57. //誰もロックを保持していない 
  58. 戻る;
  59. }
  60. //ロックを削除 ③  
  61. jedis.eval(スクリプト、Arrays.asList( "lock" )、Arrays.asList(lockContext.get()));
  62. ロックコンテキストを削除します。
  63. }キャッチ(例外 e) {
  64. e.printStackTrace();
  65. }
  66. }
  67.  
  68. /**
  69. * InputStreamを文字列に変換する
  70. * @paramは
  71. * @戻る
  72. * @throwsIOException 例外をスローします
  73. */  
  74. パブリックString inputStream2String(InputStream is) はIOExceptionをスローします{
  75. ByteArrayOutputStream baos =新しいByteArrayOutputStream();
  76. 整数i = - 1 ;
  77. ((i = is.read()) != - 1 )の間{
  78. baos.write(i);
  79. }
  80. baos.toString()を返します
  81. }
  82.  
  83. 公共  void lockInterruptibly()InterruptedExceptionをスローします{
  84.  
  85. }
  86.  
  87. public条件 newCondition() {
  88. 戻る ヌル;
  89. }
  90. }
  • コンテキスト グローバル変数を使用して、ロックを保持している人の UUID を記録します。ロックを解除するときは、ロックを解除できるかどうかを判断するために、uuid を Lua スクリプトにパラメータとして渡す必要があります。
  • 分散ロックの再入可能性を実現するには、現在のスレッドを記録する必要があります。現在のスレッドがロックを保持している場合も、ロックは成功したとみなされます。
  • ロック解除中のアトミック性を保証するために、eval 関数を使用して Lua スクリプトを実行します。

6. 分散ロックの比較

6.1 データベースベースの分散ロック

1) 実装

ロックを取得するときにデータを挿入し、ロックを解除するときにデータを削除します。

2) デメリット

  • データベースに障害が発生すると、ビジネス システムは利用できなくなります。
  • 有効期限を設定しないとデッドロックが発生します。

6.2 Zookeeper に基づく分散ロック

1) 実装

ロックすると、指定されたノードのディレクトリに新しいノードが作成され、ロックが解除されるとこの一時ノードは削除されます。ハートビート検出機能があるため、デッドロックが発生せず、より安全です。

2) デメリット

パフォーマンスは平均的で、Redis ほど効率的ではありません。

それで:

  • パフォーマンスの観点から: Redis > zookeeper > データベース
  • 信頼性(セキュリティ)の観点から: zookeeper > Redis > データベース

七。結論

この記事では、ロックの基本概念から始め、複数のスレッドが共有リソースにアクセスするときに発生する可能性のあるスレッド セーフティの問題を提案し、次にロックを追加することでスレッド セーフティの問題を解決します。この方法ではパフォーマンスが低下するため、ロック保持時間の短縮、ロック粒度の低減、ロックの分離という 3 つの方法でロックを最適化する必要があります。

次に、分散ロックの 4 つの特性について説明します。

  • 相互排他性
  • デッドロック防止
  • ロッカーのロックを解除する
  • 再入性

その後、Redis を使用して分散ロックを実装しました。ロックには Redis コマンドが使用され、ロック解除時のアトミック性を保証するために Lua スクリプトが使用されました。

最後に、3 つの分散ロックの利点、欠点、および使用シナリオを比較します。

分散ロックについて新たな理解が得られ、問題解決を検討する際にはパフォーマンスの問題についてさらに深く考えるようになることを願っています。

[この記事は51CTOコラムYiXin Technology Institute、WeChatパブリックアカウント「YiXin Technology Institute(id:CE_TECH)」からのオリジナル記事です]

この著者の他の記事を読むにはここをクリックしてください

<<:  クラウドユーザーがデータベース開発を振り返る 2019年クラウドコンピューティングソフトウェアレビュー

>>:  Java プログラムでよく使用されるメモリ モジュールは何ですか?

推薦する

NAIYUN:中秋節期間限定38%オフ、香港/アメリカ/セラ高防御クラウドサーバー/CN2/9929/4837/専用サーバー/海外電子商取引/TikTok専用サーバー/AIGC/CDN

Naiyun の最新の中秋節と国慶節特別オファーが始まりました: クラウド サーバーの月額支払いは ...

Linode-日本データセンター新プラン月額5ドルVPSレビュー

昨日、Linode は、最低 VPS 価格を 1G メモリで月額 5 ドルに引き下げると発表しました...

クラウド上のフィンテック

世界中で金融テクノロジーのブームが起こっており、新たな「競争相手」の出現により、従来の金融機関は大き...

ハイブリッドクラウドの深い価値:弾力性のある相互接続、セキュリティ、効率性

クラウド コンピューティング業界は 10 年以上前から存在しており、インターネットに代表される爆発的...

ウェブサイト診断 - 新しいウェブサイトの分析

新しいウェブサイトの診断と分析には、一般的に 2 つの状況があります。1 つは新しいウェブサイトの運...

Baidu DirectアカウントはWeChat公式アカウントを破壊できますか?

2014年9月3日、百度世界大会で、百度は長らく計画していた大きな動きであるダイレクトナンバーパブリ...

最適化計画を成功させるために必要な7つの要素を分析する

最適化担当者にとって、自社サイトを最適化する場合でも、サイトの最適化を依頼する場合でも、開始する前に...

「ハイブリッドクラウド産業推進アライアンス」が設立され、ZStackの強みが強化

1月10日、中国情報通信研究院(以下、「CAICT」)が主催し、クラウドコンピューティング標準およ...

新しいメディアアートはブランドマーケティングにとって強力な「薬」である

月収10万元の起業の夢を実現するミニプログラム起業支援プランこのような包括的で完璧なユーザー エクス...

キーワードの主な用途: アンカーテキスト

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

ブラックジューンからセプテンバーまでのウェブマスターの努力

6 月のブラック ストームは、多くのウェブマスターの友人を「悲痛な思い」にさせたに違いありません。当...

業界リーダーがオンラインで対話: 産業のデジタル化はなぜ長期主義に固執する必要があるのか​​?

突然の感染症の発生により、デジタル化は生活、仕事、社会活動の統治にとって「必須の選択肢」であるだけで...

solarvps-simple レビュー/512m メモリ/ロサンゼルス

solarvps を購入しました。無駄のないプローブ: http://65.181.116.152:...

2012 年の電子商取引業界のトップ 10 キーワードのレビュー: 再編と価格戦争

12月24日には、3月にVipshopが赤字で上場、5月にテンセントの電子商取引事業が独立、8月にグ...

推奨: alpha1servers-7.5 ドル/メモリ 1g/ハードディスク 50g/トラフィック 2T

alpha1servers は 2011 年に設立され、その VPS の価格は常にかなり手頃です。K...