プロジェクトの背景
このプロジェクトは銀行が所有するプロジェクトで、マルチテナント型のデータ照会プラットフォームです。多分、多くの人はこのコンセプトをよく理解していないと思いますが、心配しないでください。
プロジェクト概要
まず、システム全体はDubboの分散システムアーキテクチャに基づいており、データストレージはデータウェアハウスに統一的に格納されています。データウェアハウスには、MySQL、HDFS、HBSE、Hive、Impala、Spark、ElasticSearchなど様々なストレージ方式が用意されています。そして、業務側にデータアクセス操作をさせると、当然ながら非常に面倒です。そこで、業務システムとデータウェアハウスの間に、データクエリシステムを構築する - これがこの記事の主人公です。
システムアーキテクチャ
プロジェクトのソースコード
ここでは、プロジェクトのソースコードの一部をお見せします。もちろん、お見せするソースコードは、プロジェクトのビジネスに関連したものではなく、機能的なものです。
4つのダイアグラムを見れば、システムのコーディングのレベルが大体わかるはずです。以下では、それぞれのソースコード図のストーリーを1つずつ紐解いていきます。
ソースコード 1:
ソースコード1は、機能デバッグ中に見つけたバグです。ロジックはとてもシンプルで、2つのIDが等しいかどうかを比較するものです。しかし、なぜこのようなバグが発生するのでしょうか?それは単純で、ラッパークラスのキャッシュが原因です!Integer型とLong型は1バイトのキャッシュ、つまり、-128~127のキャッシュを持ちます。バリデーション・コードは以下の通りです:
package demo;
public class IntegerCacheDemo {
public static void main(String[] args) {
compare(1,1);
compare(127,127);
compare(128,128);
compareWithEquals(1,1);
compareWithEquals(127,127);
compareWithEquals(128,128);
}
/**
* 誤った梱包クラスの比較
* @param a
* @param b
*/
public static void compare(Integer a, Integer b){
System.out.println(a == b ? a + " == " + b:a + " != " + b);
}
/**
* 正しい包装クラスの比較
* @param a
* @param b
*/
public static void compareWithEquals(Integer a, Integer b){
System.out.println(a.equals(b) ? a + " == " + b:a + " != " + b);
}
}
テスト結果:テスト結果は前述を裏付けるものでした。
ご存知のように、==比較はアドレスの直接比較であり、キャッシュのため、ラッパークラスのキャッシュが指すのは同じオブジェクトであるため、すべての==判定はtrueを返し、キャッシュのリターンを超えたとき、ラッパークラスのオブジェクトは、新たに作成されたアドレスであるため、==判定の使用はfalseを返し、等号判定は等号メソッドの書き換えを使用して、IntegerのIntegerのequalsメソッドは次のとおりです:
public boolean equals(Object obj) {
//タイプが同じかどうかを する
もし {
//もし同じなら、値が同じかどうかを判断する。.valueはint型の値を格納する。 == 与
//Integer比較は自動解凍のトリガーとなる。 int == int 判断
return this.value == (Integer)obj;
} else {
return false;
}
}
IntegerCacheのソースコードをもう一度見てみましょう。赤で囲まれた部分に注目してください。 Long、Short、Byte などの他のラッパークラスにも対応するキャッシュがあり、それらはすべてバイト値の範囲です。
ソースコード 2:
なお、ソース2は本稼働の翌日にオンライン事故を起こしたものです。
- 事業内容
ビジネスサイドは、クエリ・インターフェイスを通してクエリ・プラットフォームを呼び出し、クエリ・プラットフォームはZookepperを通してHbaseにアクセスし、データを取得して返します。
- 問題スクリーニング
エラーログを見ると、クエリ失敗のリクエストが多く、たまにクエリ成功があり、失敗の数は直線的に増えています。このときは、経験的に接続に問題があると判断しました。案の定、zookeeperのログを見ると、接続数が上限を超えていることがわかりましたが、フィードバック業務はオンラインのみで、利用人数は10人。すると、コードにバグがあると判断できます。
- 問題解決
まず第一に、本番稼働のためのコード修正は必要なプロセスであり、短期間での解決には適していません。 そして、zookepperの最大接続数は100で設定されています。まず、最大接続数を600に調整し、それから修正すべきコードのバグを探します。
つまり、図2のtry-cache内のコードで、createConnection(conf)メソッドを2回呼び出し、一方のコネクションは呼び出し元に返されますが、もう一方のコネクションはコネクションが作成された後返されません。返されたコネクションは使用後に正しく閉じられますが、コネクションが返されないのは、呼び出し元が存在しないため、手動で解放することができず、自動的に解放されるタイムアウトを待つしかありません。このコードを削除してください。
4.最適化アップグレード
前の段落の太字にご注目!なぜ太字にしたかって? 他に何かあるに違いありません、ハハハハハ~~~!図2には3つのコード・スニペットがありますが、全プロセスが「コンフィギュレーションの取得」→「ユーティリティ・オブジェクトの作成」→「接続の作成」→「クエリ」→「接続のクローズ」であることがわかります。
OMG! OMG! OMG! 21世紀の1920年代に、まだこのようなコードを見ることができるという事実に驚嘆せざるを得ません。 2つ目は、utilオブジェクトを実際に新しく作成しなければならないことです。 コネクションプールを使わないことの欠点は、言うまでもなくリソースの無駄遣いです。1台の同時実行マシン上のTCP接続数を見てみましょう。一番上にヒントがあるのがわかりますか? 同時実行性は特に高くなく、20スレッド*200サイクルです。 ご想像の通り、本番環境であれば、同時並行性が少しでも上がれば、そのマシンは真っ先に故障します。
さて、全体の続きです。最適化のアイデア: 1、コンフィギュレーションとツールクラスの分離、ツールオブジェクトを作成するのではなく、コンフィギュレーションオブジェクトを作成すること 2、コネクションプールを使ってコネクションを管理すること、HbaseのJavaクライアントはコネクションプールを提供しています。最適化後、TCP接続は基本的に安定しました。最適化コードはここには掲載しません。
ソース3+ソース4:
ソースコード3は比較的簡単ですが、最も基本的なJDBCは、接続操作を取得し、同じ問題は、全体の操作は、接続を作成することですクエリ、接続を閉じ、接続プールを使用していませんでした。しかし、これはHbaseの操作とは異なる、システム内のHbaseのデータソースは1つだけですが、JDBCのデータソースは非常に多く、MySQLを含む、Hiveは、Impalaは、接続するJDBCを使用しており、各データベースは、データソースです。このように、システム内のデータソースは1つではなく、非常に多くなります。
そして、ソースコード4は、私はより良いコードだと思います , トークン・プールのメカニズムを通じて、単一のサーバーへのデータベース接続の最大数を制限する , このアイデアは、高同時性でも使用することができます . フローを制限するメカニズムに関連して、彼はサーバーの安定性を最大化します。
ここでの最適化のアイデアは、1つのデータソースには1つのコネクション・プールを、複数のデータソースには複数のコネクション・プールを用意し、複数のコネクション・プールをキャッシュすることで、アクセス要求があったときに、まず要求に基づいて対応するコネクション・プールを検索し、コネクション・プールからコネクションを取得し直すというものです。これにより、頻繁にコネクションが生成される問題が解決されます。この方法は一時的にシステムの並行性を向上させますが、サーバーのローカルリソースをより多く消費します。オープンソースのミドルウェアであるMyCatなど、他の解決策もあります。
これを読んでいるということは、あなたが我慢強すぎる証拠ですよ!