Netty を使用して高性能な分散サービス フレームワークを作成する方法は?

Netty を使用して高性能な分散サービス フレームワークを作成する方法は?

[[407305]]

1. Nettyとは何ですか?それは何ができるのでしょうか?

Netty は、高性能ネットワーク アプリケーションの作成に特化した成熟した IO フレームワークです。

基盤となる Java IO API を直接使用する場合と比較して、Netty に基づく複雑なネットワーク アプリケーションを構築するためにネットワークの専門家である必要はありません。

業界におけるネットワーク通信に関連する一般的なミドルウェアのほとんどは、Netty に基づいてネットワーク層を実装しています。

2. 分散サービスフレームワークを設計する

1 アーキテクチャ

2 リモートコールプロセス

サーバー(サービスプロバイダー)を起動し、登録センターにサービスを公開します。
クライアント (サービス コンシューマー) を起動し、登録センターに移動して、目的のサービスにサブスクライブします。
クライアントは、登録センターからプッシュされたサービス アドレス リストを受信します。
発信者が呼び出しを開始すると、プロキシはサービス アドレス リストからアドレスを選択し、要求情報 <group、providerName、version>、methodName、args[] などの情報をバイト配列にシリアル化して、ネットワーク経由でそのアドレスに送信します。
サーバーは、リクエスト情報を受信して​​デシリアライズし、<group、providerName、version> に従ってローカル サービス ディクショナリ内の対応する providerObject を検索し、<methodName、args[]> に従ってリフレクションを通じて指定されたメソッドを呼び出し、メソッドの戻り値をバイト配列にシリアル化してクライアントに返します。
クライアントは応答情報を受信し、それを Java オブジェクトに逆シリアル化します。その後、プロキシによってメソッド呼び出し元に返されます。
上記のプロセスはメソッド呼び出し元に対して透過的であり、すべてがローカル呼び出しのように見えます。

3 リモートコールクライアント図

重要な概念: RPC トリプレット <ID、リクエスト、レスポンス>。

PS: netty4.x のスレッド モデルの場合、グローバル Map の代わりに IO Thread(worker) —> Map<InvokeId, Future> を使用すると、スレッドの競合をより適切に回避できます。

4. リモートコールサーバーの図

5 リモートコールトランスポート層図

6 トランスポート層プロトコルスタックの設計

プロトコル ヘッダー

合意

1) メタデータ: <グループ、プロバイダー名、バージョン>

2) メソッド名

3)parameterTypes[]は本当に必要ですか?

(a) 問題は何ですか?

デシリアライズ時に ClassLoader.loadClass() でロック競合が発生する可能性があります。
プロトコル本体のストリーム サイズ。
ジェネリック呼び出しには、より多くのパラメータ タイプがあります。
(b) それは解決できるでしょうか?

Java メソッドの静的ディスパッチ ルールについては、JLS <Java 言語仕様> $15.12.2.5 最も具体的なメソッドの選択の章を参照してください。
(c) 引数[]

(d) その他: traceId、appName…

3. いくつかの機能と優れた実践とパフォーマンスの圧縮

1 クライアントプロキシオブジェクトを作成する

1) プロキシは何をするのですか?

クラスタフォールトトレランス -> 負荷分散 -> ネットワーク
2) プロキシを作成する方法は何ですか?

jdk プロキシ/javassist/cglib/asm/bytebuddy
3) 注意:

リモート呼び出しを回避するために、toString、equals、hashCode などのメソッドをインターセプトする必要があることに注意してください。
4) おすすめ (bytebuddy):

2 エレガントな同期/非同期呼び出し

上にスクロールして「リモートコールクライアントダイアグラム」を見てください。
フェイルオーバーをどのように処理するかを下に見て、将来をどのように得るかを考えてみましょう。

3 ユニキャスト/マルチキャスト

メッセージディスパッチャ
フューチャーグループ

4 一般化された呼び出し

5 シリアル化/デシリアル化

プロトコル ヘッダーはシリアライザー タイプをマークし、複数のタイプをサポートします。

6. スケーラビリティ

Java SPI:

java.util.ServiceLoader
META-INF/サービス/com.xxx.Xxx

7 サービスレベルスレッドプール分離

電話を切りたいなら、まずあなたが切った方がいいですよ。私を落ち込ませないでください。

責任連鎖パターンの 8 つのインターセプター

ここから始めるには拡張が多すぎます。

9 指標

10 リンクトラッキング

オープントレーシング

11 登録センター

12 フロー制御(アプリケーションレベル/サービスレベル)

サードパーティのフロー制御ミドルウェアに簡単に接続できるように拡張できる必要があります。

13 プロバイダーのスレッド プールがいっぱいになった場合はどうすればよいですか?

14 ソフトロードバランシング

1) 重み付きランダム(バイナリ検索、トラバーサルなし)

2) 加重ラウンドロビントレーニング(最大公約数)

3) 最小負荷

4) 一貫性のあるハッシュ(ステートフルサービスシナリオ)

5) その他

注: 予熱ロジックが必要です。

15 クラスタのフォールトトレランス

1) 失敗を早くする

2) フェイルオーバー

非同期呼び出しをどのように処理しますか?

悪い

より良い

3) フェイルセーフ

4) フェイルバック

5) フォーク

6) その他

16 パフォーマンスを絞り出す方法(信頼するのではなく、テストする)

1) ASMは、サーバー側のリフレクション呼び出しを置き換えるためにFastMethodAccessorを記述します。

2) シリアル化/デシリアル化

IO スレッドを占有しないように、ビジネス スレッドでシリアル化/デシリアル化します。

シリアル化/デシリアル化には、ごくわずかな数の IO スレッド タイム スライスが消費されます。
デシリアライゼーションにはクラスのロードが含まれることが多く、loadClass では深刻なロック競合が発生します (これは JMC を通じて確認できます)。
効率的なシリアル化/デシリアル化フレームワークを選択します。

kryo/protobuf/protostuff/hessian/fastjson/… など。
選択は最初のステップにすぎません。シリアル化フレームワークがうまく機能しない場合は、拡張して最適化します。

従来のシリアル化/デシリアル化 + 書き込み/読み取りネットワーク プロセス: Java オブジェクト --> byte[] --> オフヒープ メモリ / オフヒープ メモリ --> byte[] --> Java オブジェクト。
最適化: byte[] リンクを省略し、オフヒープ メモリを直接読み書きします。これには、対応するシリアル化フレームワークを拡張する必要があります。
文字列のエンコード/デコードの最適化。
Varint の最適化: 複数の writeByte 操作が writeShort/writeInt/writeLong に統合されます。
Protostuff の最適化の例: UnsafeNioBufInput はオフヒープ メモリを直接読み取り、UnsafeNioBufOutput はオフヒープ メモリを直接書き込みます。
3) CPUにバインドされたIOスレッド

4) 同期ブロッキング呼び出しのクライアントがボトルネックになる可能性が高い、クライアント コルーチン:

Java レベルでは選択肢があまり多くなく、どれもまだ完璧ではありません。

5) Netty ネイティブトランスポートと PooledByteBufAllocator:

GC による変動を軽減します。
6) IO スレッドをできるだけ早く解放して、必要な処理を実行し、スレッドのコンテキスト切り替えを最小限に抑えます。

なぜNettyなのか?

1 BIO 対 NIO

2 JavaネイティブNIO APIの導入から廃止まで

高い複雑性

API は複雑で理解しにくいため、使い始めるのが困難です。
袋がベタベタ・半袋になる問題は非常に厄介です。
強力な並行/非同期プログラミング スキルが必要であり、そうでないと効率的で安定した実装を作成することは困難です。
安定性が悪く、深い穴が多い

デバッグは難しく、再現するのが非常に難しい奇妙なバグに遭遇することもあります。デバッグ中に泣くことはよくあることです。
Linux で EPollArrayWrapper.epollWait が直接返され、空のポーリングと 100% の CPU 使用率につながるバグはまだ解決されていません。 Netty は、セレクターを再構築することで、この問題を回避するのに役立ちます。

NIOコード実装のいくつかの欠点

1) Selector.selectedKeys() は大量のゴミを生成します

Netty は sun.nio.ch.SelectorImpl の実装を変更し、selectedKeys を格納するために HashSet の代わりに double 配列を使用しました。

HashSet (イテレータ、ラッパー オブジェクトなど) と比較すると、生成されるガベージが少なくなります (GC に役立ちます)。
わずかなパフォーマンスの向上(1〜2%)。
Nio のコードはどこでも同期されています (直接バッファの割り当てや Selector.wakeup() など)。

直接バッファを割り当てるために、Netty の pooledBytebuf には、ロック競合を効果的に削減できる事前 TLAB (スレッドローカル割り当てバッファ) があります。
ウェイクアップ コールが多すぎると、深刻なロック競合と高いオーバーヘッドが発生します (オーバーヘッドが高くなる理由: 選択スレッドの外部で選択スレッドと通信するために、Linux プラットフォームでは 1 組のパイプが使用されますが、Windows ではパイプ ハンドルを fd_set に入れることができないため、2 つの TCP 接続を使用してのみシミュレートできます)。ウェイクアップ コールが少なすぎると、選択中に不要なブロックが発生しやすくなります (混乱する場合は、対応する最適化ロジックを備えた Netty を使用してください)。
Netty Native Transport にはロックがはるかに少なくなっています。
2) fdToKeyマッピング

EPollSelectorImpl#fdToKey は、接続されているすべての fd (記述子) と SelectionKey (HashMap) のマッピングを維持します。
各ワーカー スレッドにはセレクターがあります。つまり、各ワーカーには fdToKey があり、これらの fdToKey はすべての接続をほぼ均等に分割します。
1 台のマシンが数十万の接続を保持し、HashMap がデフォルト サイズの 16 から段階的に再ハッシュされるシナリオを想像してください...
3) セレクタはLinuxプラットフォーム上のEpoll LTによって実装されています

Netty Native Transport は Epoll ET をサポートしています。
4) ダイレクトバッファは実際にはGCによって管理されている

DirectByteBuffer.cleaner は、直接メモリを解放する役割を果たす仮想参照です。 DirectByteBuffer は単なるシェルです。この殻が新世代の年齢制限を生き延びて、最終的に旧世代に昇格するとしたら悲しいことです...
十分な直接メモリを適用できない場合は、GC が明示的にトリガーされます (Bits.reserveMemory() -> { System.gc() })。まず、GC はプロセス全体を中断するだけでなく、コードも 100 ミリ秒間スリープします。起動してもまだ動作しないことが判明した場合、OOM が発生します。
さらに悪いことに、どこかの <XX 最適化ガイド> の誹謗中傷に耳を傾けて -XX:+DisableExplicitGC パラメータを設定すると、悲劇はひっそりと起こります...
Netty の UnpooledUnsafeNoCleanerDirectByteBuf はクリーナーを削除し、Netty フレームワークは参照カウントを維持してリアルタイムでクリーナーを解放します。

ネッティの素顔

1 Nettyにおけるいくつかの重要な概念とその関係

イベントループ

セレクター。
タスク キュー (mpsc_queue: 複数のプロデューサー、単一のコンシューマー、ロックフリー)。
遅延タスク キュー (delay_queue: バイナリ ヒープ構造を持つ優先度キュー、複雑度は O(log n))。
EventLoop はスレッドにバインドされており、パイプライン内のスレッドの競合を直接回避します。

ボス: mainReactor の役割、ワーカー: subReactor の役割

Boss と Worker は EventLoop のコード ロジックを共有します。 Boss は受け入れイベントを処理し、Worker は読み取り、書き込み、およびその他のイベントを処理します。
ボスは接続 (チャネル) をリッスンして受け入れた後、ラウンドロビン方式でチャネルをワーカーに渡します。ワーカーは、このチャネルの後続の読み取り/書き込みおよびその他の IO イベントを処理する責任を負います。
複数のポートをバインドしない場合、BossEventLoopGroup には 1 つの EventLoop のみを含める必要があり、そのうち 1 つだけを使用できます。複数はダメです。
WorkerEventLoopGroup には通常複数の EventLoop が含まれており、経験値は一般的に CPU コア数 * 2 です (シナリオ テストに基づいて最適な値を見つけるのが最善の方法です)。
チャネルは、ServerChannel と Channel の 2 つのカテゴリに分かれています。 ServerChannel はリスニング ソケット (ServerSocketChannel) に対応し、Channel はネットワーク接続に対応します。

2 Netty4 スレッドモデル

3 チャンネルパイプライン

4 プーリングと再利用

プールバイトバッファアロケータ

jemalloc 論文 (3.x) に基づく
ロックフリーの ThreadLocal キャッシュ: このアプローチはかつて落とし穴を引き起こしました。(Bytebuf) を申請したスレッドと (Bytebuf) を返すスレッドが同じではなかったため、メモリ リークが発生しました。その後、問題を解決するために mpsc_queue が使用されましたが、パフォーマンスが少し犠牲になるという代償がありました。
さまざまなサイズクラス。

リサイクラー

スレッドローカル + スタック。
以前は落とし穴がありました。 (要素) を適用するスレッドと (要素) を返すスレッドが同じではなかったため、メモリ リークが発生しました。
その後、異なるスレッドが要素を返す場合、それらの要素は WeakOrderQueue に配置され、スタックに関連付けられるように改善されました。次回ポップするときにスタックが空の場合は、現在のスタックに関連付けられているすべての weaknessOrderQueues が最初にスキャンされます。
WeakOrderQueue は、それぞれのデフォルト サイズが 16 である複数の配列のリンク リストです。
既存の問題: 新しい世代のオブジェクトを参照する古い世代のオブジェクトが GC に与える影響について考えてみましょう。

5 ネッティネイティブトランスポート

Nio と比較すると、作成されるオブジェクトが少なくなり、GC の圧力も少なくなります。

Linux プラットフォーム向けに最適化されており、次のような特定の機能があります。

SO_REUSEPORT - ポートの再利用 (複数のソケットが同じ IP+ポートをリッスンできるようにし、RPS/RFS と連携してパフォーマンスをさらに向上させます): RPS/RFS は、ソフトウェア レベルでマルチキュー ネットワーク カードをシミュレートし、ネットワーク カードのパケットの送受信の中断が 1 つの CPU コアに集中してパフォーマンスに影響するのを回避するための負荷分散機能を提供するものと漠然と理解できます。
TCP_FASTOPEN - 3 ウェイ ハンドシェイク中にデータを交換する場合にも使用されます。
EDGE_TRIGGERED (Epoll ET のサポートが重要なポイントです)。
Unix ドメイン ソケット (Service Mesh など、同じマシン上のプロセス間通信)。

6 多重化の概要

選択/投票

実装メカニズム自体には制限があります (ポーリングを使用して準備完了イベントを検出し、時間の計算量は O(n) で、肥大化した fd_set を毎回ユーザー空間とカーネル空間の間でコピーする必要があります)。同時接続数が増えるほど、パフォーマンスは低下します。
ファイル記述子の最大数の制限が削除されることを除けば、poll と select の間に大きな違いはありません。
選択とポーリングは両方とも LT モードです。
エポール

コールバック メソッドは、準備完了イベントを検出するために使用されます。時間計算量はO(1)です。各 epoll_wait 呼び出しは、準備完了のファイル記述子のみを返します。
epoll は LT モードと ET モードをサポートします。

7 エポルについてもう少し深く理解する

LT 対 ET

コンセプト:

LT:レベルトリガー
ET:エッジトリガー エッジトリガーで読み取れる内容:

バッファが空でない場合、fd のイベント内の対応する読み取り可能ステータスは 1 に設定され、それ以外の場合は 0 に設定されます。
書き込めます:

バッファに書き込み可能なスペースがある場合、fd のイベント内の対応する書き込み可能ステータスは 1 に設定され、それ以外の場合は 0 に設定されます。
図:

3つのepollメソッドの紹介

1) メインコード: linux-2.6.11.12/fs/eventpoll.c

2) int epoll_create(int サイズ)

rb-tree (赤黒木) と ready-list (準備完了リンクリスト) を作成します。

容量要件が不確実で大きくなる可能性がある場合、効率とメモリ使用量のバランスをとる赤黒木 O(logN) が最適な選択肢です。
サイズパラメータはもはや意味を持ちません。初期の epoll 実装はハッシュ テーブルであったため、サイズ パラメータが必要でした。
3) int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

epitem を rb-tree に配置し、カーネル割り込みハンドラに ep_poll_callback を登録します。コールバックがトリガーされると、エピテムを準備リストに追加します。
4) int epoll_wait(int epfd, struct epoll_event * イベント, int maxevents, int タイムアウト)

準備リスト —> イベント[]。

epoll データ構造

epoll_wait ワークフローの概要

比較コード: linux-2.6.11.12/fs/eventpoll.c:

1) epoll_waitはep_pollを呼び出す

rdlist (ready-list) が空 (準備完了ファイルなし) の場合、現在のスレッドは中断され、rdlist が空でなくなるまでスレッドは起動されません。

2) ファイル記述子fdのイベントステータスが変化する

バッファが読み取り不可から読み取り可能へ、または書き込み不可から書き込み可能へ変更され、対応する fd 上のコールバック関数 ep_poll_callback がトリガーされます。

3) ep_poll_callbackがトリガーされる

対応する fd を rdlist の epitem に追加して、rdlist が空にならないようにし、スレッドを起動して、epoll_wait が実行を継続できるようにします。

4) ep_events_transfer関数を実行する

rdlist 内の epitem を txlist にコピーし、rdlist をクリアします。
epoll LT であり、fd.events の状態が変更されていない場合 (たとえば、バッファー内のデータが完全に読み取られていない場合は状態は変更されません)、epitem は rdlist に戻されます。

5) ep_send_events関数を実行する

txlist 内の各 epitem をスキャンし、関連付けられている fd に対応する poll メソッドを呼び出して、新しいイベントを取得します。
取得したイベントと対応する fd をユーザー空間に送信します。

Netty の 8 つのベスト プラクティス

1) ビジネススレッドプールの必要性

ビジネス ロジック、特にブロック時間が長いロジックは、Netty の IO スレッドを占有せず、ビジネス スレッド プールにディスパッチする必要があります。

2) バッファウォーターマークの書き込み

デフォルトの高低ウォーターマーク設定(32K〜64K)に注意し、シーンに応じて適切に調整します(使用方法を考えることができます)。

3) MessageSizeEstimator を書き換えて、実際の最高値と最低値を反映させる

デフォルトの実装ではオブジェクトのサイズを計算できません。メッセージ サイズは書き込み中に outboundHandler が渡される前に計算され、この時点ではオブジェクトは Bytebuf にエンコードされていないため、サイズ計算は間違いなく不正確です (小さすぎます)。

4) EventLoop#ioRatio の設定に注意してください (デフォルト 50)

これは、EventLoop が IO タスクと非 IO タスクを実行する時間比率を制御します。

5) アイドル リンク検出をスケジュールするのは誰ですか?

Netty4.x は、デフォルトで、O(log N) の複雑度を持つバイナリ ヒープによって実装された優先キューである eventLoop の delayQueue を使用して、IO スレッド スケジューリングを使用します。各ワーカーは独自のリンク監視を処理するため、コンテキストの切り替えが削減されますが、ネットワーク IO 操作とアイドルは相互に影響を及ぼします。
接続の総数が数万以内など少ない場合は、上記の実装で問題ありません。接続数が多い場合は、HashedWheelTimer を使用して IdleStateHandler を実装することをお勧めします。 HashedWheelTimer の複雑さは O(1) であり、ネットワーク IO 操作とアイドルが相互に影響するのを防ぐことができますが、コンテキスト切り替えのオーバーヘッドがあります。

6) ctx.writeAndFlush または channel.writeAndFlush を使用しますか?

ctx.write は次の送信ハンドラーに直接送られます。本来の意図に反してアイドル リンク検出をバイパスしないように注意してください。
channel.write は最後から開始し、パイプライン内のすべての送信ハンドラーを逆方向に処理します。

7) rangeCheck() を避けるために ByteBuf.readByte() をループする代わりに Bytebuf.forEachByte() を使用する

8) 不要なメモリコピーを避けるためにCompositeByteBufを使用する

欠点は、インデックス計算時間の複雑さが高いことです。独自のシナリオに応じて測定してください。

9) int を読み取りたい場合は、Bytebuf.readBytes(buf, 0, 4) ではなく、Bytebuf.readInt() を使用してください。

これにより、メモリのコピーを回避できます (long、short などでも同様)。

10) JDKのDirectByteBufを置き換えるためにUnpooledUnsafeNoCleanerDirectByteBufを設定し、Nettyフレームワークが参照カウントに基づいてオフヒープメモリを解放できるようにします。

io.netty.maxDirectMemory:

< 0: クリーナーを使用せず、Netty は JDK によって設定された最大直接メモリ サイズを直接継承します (JDK の直接メモリ サイズは独立しているため、合計直接メモリ サイズは JDK 構成の 2 倍になります)。
== 0: クリーナーを使用し、Netty は最大直接メモリ サイズを設定しません。
0: クリーナーを使用せず、このパラメータは Netty の最大直接メモリ サイズを直接制限します (JDK の直接メモリ サイズは独立しており、このパラメータによって制限されません)。

11) 最適な接続数

1 つの接続にボトルネックがあると、CPU を効果的に活用できません。接続が多すぎると役に立たなくなります。ベストプラクティスは、独自のシナリオに従ってテストすることです。

12) PooledBytebufを使用する場合は、-Dio.netty.leakDetection.levelパラメータの使用に注意してください。

4 つのレベル: DISABLED、SIMPLE、ADVANCED、PARANOID。
SIMPLE と ADVANCED のサンプリング レートは同じで、1% 未満です (ビット単位の AND 演算マスク == 128 - 1)。
デフォルトはオーバーヘッドが低い SIMPLE レベルです。
リークが発生すると、ログに「LEAK:」と表示されます。時々ログをgrepしてください。 「LEAK: 」が表示されたら、すぐにレベルをADVANCEDに変更して再度実行してください。これにより、リークされたオブジェクトがアクセスされた場所が報告されます。
PARANOID: このレベルは、100% のサンプリングによるテストに推奨されます。

13) Channel.attr() でオブジェクトをチャンネルにアタッチします

ジッパー方式で実装されたスレッドセーフなハッシュテーブルもセグメントロックされており(リンクリストの先頭のみがロックされます)、ハッシュの競合がある場合にのみロックの競合が発生します(ConcurrentHashMapV8 バージョンと同様)。
デフォルトのハッシュ テーブルには 4 つのバケットしかないため、勝手に使用しないでください。

Netty ソースコードから学ぶ 9 つのコーディングのヒント

1) AtomicIntegerFieldUpdater --> 大規模オブジェクト シナリオでの AtomicInteger

Java では、オブジェクト ヘッダーは 12 バイトです (圧縮ポインターが有効な場合)。 Java オブジェクトは 8 バイトに揃えられているため、オブジェクトの最小サイズは 16 バイトです。 AtomicInteger のサイズは 16 バイト、AtomicLong のサイズは 24 バイトです。
AtomicIntegerFieldUpdater は、静的フィールドとして volatile int を操作します。

2) FastThreadLocal、JDK実装よりも高速

線形検出用のハッシュ テーブル -> インデックスのアトミック自動増分を備えたネイキッド配列ストレージ。

3)IntObjectHashMap/LongObjectHashMap…

整数—> int
Node[] —> 裸の配列

4) リサイクル可能な配列リスト

上記の Recycler に基づいて、新しい ArrayList が頻繁に作成されるシナリオが考えられます。

5) JCTツール

一部の SPSC/MPSC/SPMC/MPMC ロックフリー同時実行キューと NonblockingHashMap は JDK では利用できません (ConcurrentHashMapV6/V8 と比較できます)

<<:  アリババは、NvidiaやGoogleと比較してコンピューティング電力消費を80%削減する1兆パラメータのAIモデルM6をリリースしました。

>>:  複数のクラスター内の Tekton パイプライン

推薦する

マーケターがソーシャルメディアのROIに関する5つの間違いを避ける方法

導入:この記事では、マーケティング担当者がソーシャル メディアの ROI に関して抱くよくある誤解を...

簡潔な分析: 二級都市と三級都市のオンラインメディアが直面する共通のジレンマ

3G や 4G に不満を言い続ける時代に生きている人たちは、ダイヤルアップ インターネット アクセス...

エッジで生活し、エッジ コンピューティング ビジネスを行っていますか?

コンピューティング能力の限界化により、現代ではより多くのビジネスチャンスが生まれています。従来のクラ...

分散オブジェクトストレージOzoneはApache Foundationを卒業し、正式にApacheのトップレベルプロジェクトになりました

Apache Foundation の取締役会が、分散ファイル オブジェクト ストレージ Ozone...

urpad-ヒューストン専用サーバー 50% オフ

urpad買収後の新たな動きです。サーバーレンタル事業に着手しました。多角的な展開も必至です。企業は...

取引方法の合理化によりウェブサイトをより高いレベルに導く

インターネットの拡大に​​伴い、今年のタオバオ11フェスティバルも取引高の記録を更新しました。しかし...

SEO 業務における Python の応用 - データ収集

1. このシリーズの紹介私はずっと、PythonとSEOに関する記事をシリーズで書きたいと思っていま...

ウェブサイトのプロモーションで良い結果を達成する方法:次の点に注意する必要があります

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

テンセントは自社開発事業をクラウドに完全移行したと発表した

テンセントは本日、長年の努力と革新を経て、自社で開発した大規模な社内事業をクラウドに完全に移行したこ...

仮想マシンの移行によって、クラウド コンピューティングの互換性に関するどのような問題が発生しますか?

仮想マシン (VM) をパブリック クラウドに移動すると、多くの互換性の問題が発生する可能性がありま...

Googleの「江南スタイル」時代のGoogle PR大幅アップデート

Google PR 値は、ウェブマスターがフレンドリー リンクを交換するための基準の 1 つであるた...

クラウドネイティブマイクロサービスの実装を加速する方法: Baidu CRM の取り組み

企業と顧客・潜在顧客との関係やさまざまな双方向戦略を管理するシステムとして、CRM(顧客関係管理)が...

Vultrはどうですか? Vultrの東京データセンターの簡単なレビュー

Vultrはどうですか? Vultr Japanはどうですか?何年も経ちましたが、Vultr の日本...

カザフスタンの VPS: pqhosting、月額 3.77 ユーロから、1G メモリ/1 コア/15g SSD/G ポート無制限トラフィック

中央アジアのカザフスタン VPS は市場では珍しいようですね。現在、pq.hosting はカザフス...