手首のフリックで古紙メガトン

こんにちは この記事では、プラットフォームでの印刷の仕組みについて説明します



ちょっとした歴史



理解するために、この関数の履歴について説明することが役立ちます。 最初は、「正面」と呼ばれるものの決定。 スクリュースプーラーが使用され、印刷は標準的な手順で実行されました。 実際、このようなソリューションは、クライアントアプリケーションからではなく、アプリケーションサーバーから印刷を実装する必要が生じる瞬間まで、長続きしませんでした。

念のため、最初の記事の図を示します。







ご覧のとおり、アプリケーションサーバーはデータセンターにありました。 また、オフィスは単一のネットワークに接続されていました。 アプリケーションサーバーから印刷を実装すると、いくつかの問題が発生しました。

  1. 通信チャネルの負荷は非常に大きくなっています。 一部のオフィスでは最大100%
  2. アプリケーションサーバーがフリーズし始めました。
  3. 時々、印刷のために送られた紙が印刷されないことがありました。
  4. システムのパフォーマンスが低下しました(ユーザーは操作が完了するまで数分待たなければならないという不満を言い始めました)
  5. 紛らわしい印刷注文


調査自体は、誰もがおそらく推測したように、チャネルの高負荷の理由は、レンダリングされた印刷フォームを直接プリンターに転送することであることをすぐに示しました。

理由2をいじくり回す必要がありましたが、人々もプリンタードライバーを書くことが判明しました。 高負荷でハングし、アプリケーションサーバー全体をドラッグしたのは、それら(ドライバー)でした。 再緊張させるだけで緊張状態から抜け出すことができました。

理由3および5は、Windows印刷スプーラーで見つかりました。

問題4は、セッションの期待を注意深く調べることで解決されました。 理由は表面上はあるように見えますが、それから私たちは若かったです...アプリケーションサーバーからの印刷により、プリンターへのアクセス時間が大幅に増加したことが判明しました。 この間、トランザクションは他のサーバーコールが遭遇したシステムオブジェクトをブロックし続け、楽しい時間を提供してくれました。 もちろん、トランザクションを印刷する前に修正することもできますが、これは私たちの観点からは間違っています。 謝罪しますが、これについてはトランザクション管理に関する別の記事で説明しています。

知り合いの話から、このような問題は私たちだけのものではないことがわかっています。

次に、図に示されていることをプリントサーバーとして実行することにしました。



フォームの印刷



余談をして、印刷内容を正確に伝える必要があります。

印刷の場合、プラットフォームは「印刷フォーム」などを実装します。 申し訳ありませんが、元の名前を見つけることができませんでした。 印刷フォームは、レポートブック用のテンプレートであり、開発者が指定したアルゴリズムに基づいてデータプレートを準備するスクリプトです。 はい、これを単一のリクエストとして定式化できない場合があります。 データベース内のテーブル形式ではなく、「タイプ」DataTableのオブジェクト形式で明確にする必要があります。 引用符で囲む-テーブルの格納に独自のクラスを使用するため、メモリ集約度が低く、よりコンパクトにシリアル化できます。

詳細について話す場合、レポートはDevExpress XtraReportsから使用されます。

スクリプトとレポートテンプレートの両方は、メインクライアントアプリケーションのGUIを介して実行時に編集できます。

スクリプトは常にアプリケーションサーバーで実行されることを思い出してください。 貿易組織が印刷する書類の大部分は、あらゆる種類の請求書、請求書、運送状、領収書、倉庫のセットシートなどです。 同時に、テンプレートは変更されず、データのみが変更されます!

したがって、最初にしたことは、レンダリングされたフォームをレンダリングしてプリンターに送信するタスクをプリントサーバーに送信することでした。 アプリケーションサーバーは、データを準備してプリントサーバーに送信するだけです。 トラフィックの節約-ドキュメントあたり(平均)2MBから40kbまで、プリントサーバーはテンプレートをローカルにキャッシュします。



デバイスと機能



プリントサーバーの機能を一貫して向上させ、これらの問題を取り除くことができました。



それで、今あるもの。



同期および非同期印刷。


原則として、印刷版をプリンターに時間保証付きで配達する必要はありません(つまり、今すぐ印刷します)。 一方、ドキュメントを印刷用に送信するスクリプトは既にデータベースに何らかの変更を加えており、別のトランザクションがこれらのロックに遭遇する可能性が高いです。 この場合、できるだけ早く変更をコミットする必要があります。 このために、アプリケーションサーバーは印刷ジョブを受け入れて非同期的に処理できます。 別のトランザクションで、データ準備スクリプトを実行し、適切なプリントサーバーに送信します。 同期印刷は、データ準備スクリプトが現在のトランザクションで実行されることを意味します。

説明図:







アプリケーションサーバー内の個別のトランザクション(およびスレッド)で動作するPrintBuilderおよびPrintSenderプロシージャは、着信ジョブのキューを処理します。 技術的には、これらは無限ループを実行するアプリケーションサーバー内のスレッドです。 これらのストリームの量は、負荷に応じて管理者が構成します。 PrintBuilderは、非同期で受信したジョブを処理し、印刷フォームのデータを準備するためのスクリプトを実行し、次のターンでそれらをPrintSenderに渡します。 同期された印刷ジョブは同じキューに分類されます。

次に、PrintSenderはジョブをキューから削除し、プリントサーバーに転送します。

ご覧のとおり、このアプローチでは、トランザクション1のブロック時間が大幅に短縮され、ブロック時間が短いほど、サーバーが処理できるトランザクションの数が多くなります。



バッチ印刷


事前に決められた順序でドキュメントのプリンターでの印刷を保証します。 他のタスクに関係なく、バッチ内のすべてのドキュメントは密接に印刷されます。 ドライバーのためのドキュメントの印刷を実装するときに、私たちにとって機会が必要でした(実際のビジネスの実践におけるアプリケーションの )。 これは、ルーティングシート(すべての住所と地図をリストした文書)をプリンターに印刷してから、注文ごとに会計文書を印刷する必要がある場合のタスクです。 ドライバーが100を超え、Windowsスプーラーが混乱し始めるまで、すべてが正常でした。 他の誰かが同じプリンターで他の文書を印刷しようとすると、状況は悪化しました。 何らかの理由で、これらのドキュメントはドライバーのドキュメントの中に時々登場しました。 私はスプーラーを放棄し、自分で作らなければなりませんでした。



配送保証


印刷用に送信された文書は、プリンターに送信されることが保証されます。 プリンターが使用できない場合、またはプリントサーバーのあるオフィスが使用できない場合、システムは接続が復元されるのを待って、SMSと電子メールを管理者に送信します。



プリントサーバーの動作図を示す図を次に示します。







この図では、PrintWorkerは接続された各プリンターに対して実行される個別のプロセスです。 プリンタードライバーがフリーズすると、プロセスは応答を停止し、タイムアウト後、プリントサーバーはプロセスを強制終了し、再起動します。



印刷会計


副作用として、誰が、いつ、何を、どれだけ印刷したか、ユーザーがプリンターに直接アクセスできなくなったことが考慮されます。これにより、印刷論文の用紙消費量が大幅に削減されました。 これがクールなものかどうかはあなた次第です。



プリンターの仮想化


システムから印刷できるプリンターはシステム設定にリストされており、プリントサーバーにのみ接続されています。 これにより、管理者の作業が大幅に簡素化されます。アプリケーションサーバーのクラスター内の各ノードにプリンターを接続する必要がなく、各クライアントマシンにドライバーをインストールし、他のシャーマニズムに従事します。 さらに、システムにプリンターをリストすると、一般ユーザーがプリンターを選択するためのUIを簡単に実装できます。 たとえば、倉庫で用紙を印刷するプリンタ。







したがって、各プリンターには独自の識別子があり、プリンターが変更されると保存され、印刷用のコードは次のようになります。







この例では、従業員が指定したプリンターで、指定されたドキュメントの印刷可能物が印刷されます。



RDBMSのキュー



彼らはキューについて話すと約束した。 言っています。

ご覧のとおり、印刷サブシステムでは、キューメカニズムが積極的に使用されています。 幸いなことに、Oracle Databaseには、特別な別個のサービスに頼ることなく、データベース内でそれらを整理する機能があります。 それらは設計を使用して非常に簡単に実装されます
select .. for update skip locked
      
      



。 このようなクエリを実行すると、他のトランザクションによってブロックされていない行のみが返されます。 rownumと一緒に使用できないという事実、つまり すべての行のみを取得できます。 ただし、回避策があります-上記のすべては、クライアント(データベースに関して)アプリケーションからの要求を実行する場合にのみ当てはまります! そのため、プロシージャを作成できます。







この手順の主なことは、カーソルを宣言し、1つのレコードを取り出すことです。 したがって、不必要なロックなしで、非常に便利に、さまざまなキューの並列処理を編成できます。



たとえば、同じ方法で、400,000個の商品の価格を再計算するタスクが解決されています。



なぜOracle Advanced Queuingを使用しなかったのかという疑問を予想して、次のように答えます。



  1. Managed ODP.NETを使用していますが、AQはまだサポートされていません。
  2. キューを使用すると、データを構造化された形式で保存し、これらの同じキューを管理するための組み込みメカニズムを実装できます。





おわりに



プラットフォームでの印刷サブシステムの実装をほぼ明らかにし、アプリケーションの問題を解決するためのアイデアを思いつくことができたと思います。 次に、トランザクション管理と関連する問題のトピックを拡大してみます。



All Articles