月: 2025年5月

  • OpenPDF シミュレーション機能が便利

    先日、下のように表を出力するPDF を作成した。

    単純に思えるかもしれないが、以下の様な要望と技術的問題があった。

    • 行の高さを固定せず文字量やデータ量に応じて自動拡張したい
    • 文字量とデータ件数は動的変化する
    • 行の途中で改行しない
    • 各ページの上部に固定ヘッダーを表示したい
    • その上で表のタイトル行も毎ページ表示したい

    これらを満たすためには、表に1行ずつ追加した場合の高さを取得できる必要がある。
    無償のPDF ライブラリを探したところ、OpenPDF でできることが分かった。

    ColumnText にgo(simulate) という機能があり、こちらを使うと描画する前にコンテンツの高さを取得することができる。
    Javadoc

    コードの例

    // 新規PDF ドキュメント作成(マージンとファイル名は適当)
    Document document = new Document(PageSize.A4, 36, 36, 36, 36);
    PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("sample.pdf"));
    document.open();
    PdfContentByte cb = writer.getDirectContent();
    // シミュレーション用のcolumNText を作成
    ColumnText columnText = new ColumnText(cb);
    // 左下(0,0) 右上(1000,1000) のシミュレーション領域を作成
    float baseHeight = 1000;
    columnText.setSimpleColumn(0, 0, 1000, baseHeight);
    // デフォルトフォントでは日本語が表示されないのでNotoSans を指定
    BaseFont baseFont = BaseFont.createFont("NotoSansJP-Regular.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
    Font font = new Font(baseFont, 18);
    PdfPTable table = new PdfPTable(1);
    PdfPCell cell = new PdfPCell(new Phrase("改行込み\n長い長い\nテキスト", font));
    // 計算を簡略化するためセルの余白にゼロを指定
    cell.setPadding(0);
    table.addCell(cell);
    columnText.addElement(table);
    // シミュレーションモードで実行
    columnText.go(true);
    // table 描画後のyLine を取得
    float yLine = columnText.getYLine();
    // 領域の高さからyLine を引いた数値が高さになる
    float height = baseHeight - yLine;
    System.out.println(height); // 54.0 (フォント18pt x 3行 + セルの余白0)

    build.gradle

    implementation("com.github.librepdf:openpdf:2.0.5")

    columnText.go(true) が重要な点である。これを使うと描画せずに高さを取得できる。表の高さがページの余白の高さに収まるか判定すれば要件を満たせる。

    余談だが、フォント(NotoSans)はこちらを使用した。

    【広告】

  • 性能試験と同時接続数

    性能試験でネックになるのがcpu使用率というのはイメージしやすいと思う。私が先日行った性能試験では、CPU 使用率が上昇する前に、同時接続数がネックになってしまった。この反省から注意すべき点を残しておきたい。

    構成は下図であった。

    (1) apache http server 同時接続数

    同時リクエスト受付の上限数が決められている。初期値はosやバージョンで異なるらしいが私の例では150だった。

    (2) spring boot 同時接続数

    デフォルトのtomcat をweb サーバとして使う場合、max thread の初期値が200である。apache より大きくしても意味はない。

    (3) spring boot データベース接続プール数

    デフォルトのHikari CP を使う場合、初期値は10。ディスクアクセスが多い場合はもっとあげても良さそうである。

    (4) postgreSQL 同時接続数

    もしPostgreSQLを自前でインストールした場合、初期値は100らしい。私の例ではaws rds を使っていたので1700だった。(インスタンスタイプ毎に異なる)spring boot の台数と (3)のプール数を掛け合わせた数が収まるように調整する必要がある。

    以上4点が問題となった。

    cpu負荷をかける前に、これらを確認したら良かった。具体的には、spring boot 上にスリープするだけのコントローラを作成して、そこに対して想定のリクエストを投げる事で確認できた。私の場合はcpu 負荷のかかる処理を最初から大量に投げたため切り分けで時間を要してしまった。

    【広告】

  • nginx on docker の運用

    さくらVPSを1台レンタルし、その上で複数のサービスを運用している。http リクエストをnginx で受けて、背後のサービスへリバースプロキシで連携する構成である。

    nginx とサービスA-C がコンテナとなっている。

    この構成でしばらく運用するには主に以下の作業が必要となる。

    • nginx 静的コンテンツ入れ替え
    • nginx バージョンアップ
    • nginx ログ参照
    • サービス増加
    • SSL 証明書更新

    これらをなるべく簡単に行うために、次の点を工夫した。

    1. docker volume によるファイル管理
    2. Makefile を使ったコマンドの簡略化

    1. docker volume によるファイル管理

    volume を使用することで、コンテナから見えるファイルをホストOS 上で操作できるようになる。ホストOSからコンテナへのファイル授受は多少手間なので活用したい。
    対象としたいファイルは以下。

    • nginx 設定ファイル
    • nginx インクルードファイル
    • nginx ログ
    • ドキュメントルート
    • SSL 証明書

    docker-compose で起動するためのyml ファイルはこのようになる。

    compose.yml
    
    services:
      nginx:
        # nginx を最新化するためlatest を指定
        image: nginx:latest
    
        # OS起動時は常に立ち上げておく
        restart: always
    
    
        # タイムゾーンを東京にしておく
        environment:
          - TZ=Asia/Tokyo
    
        # コンテナと共有するフォルダ
        volumes:
          # nginx 設定ファイル
          - ./nginx/nginx.conf:/etc/nginx/nginx.conf
          # nginx インクルードファイル
          - ./nginx/conf.d:/etc/nginx/conf.d
          # nginx ログ
          - /opt/nginx/log:/var/log/nginx
          # SSL証明書など秘密ファイル
          - /opt/secret:/opt/secret
          # ドキュメントルート
          - /opt/nginx/html:/opt/nginx/html
        # ホストモードにすることで
        network_mode: host

    2. Makefile を使ったコマンドの簡略化

    よく使うコマンドは簡単に叩けるようにしておきたい。次のMakefile を作っておく。

    # make の仕様で第一引数と同じ名前のフォルダが存在すると動かなくなる
    # .PHONY: を登録しておくことで単純なコマンド実行とみなされる
    .PHONY: nginx
    
    # 再起動
    restart:
    	@make down
    	@make up
    # 起動
    up:
    	docker-compose up -d
    # 停止
    down:
    	docker-compose down
    # nginx のシェル取得
    nginx-bash:
    	docker-compose exec nginx bash
    # nginx 設定ファイル文法チェック
    test:
    	docker-compose exec nginx nginx -t
    # nginx 設定再読み込み
    reload:
    	docker-compose exec nginx nginx -s reload
    # プロセス一覧
    ps:
    	docker-compose ps
    # ログ(コンテナの標準出力)
    log:
    	docker-compose logs --follow
    # コンテナイメージ削除(down 後に実施)
    prune:
    	docker system prune -af
    # ヘルプ(引数一覧)
    help:
    	echo "usage: up,down,nginx-bash,test,reload,ps,log,prune,help"
    

    使い方は引数に渡すだけだ。参考までにいくつかの実行結果を記載しておく。

    プロセス一覧

    $ make ps 
    docker-compose ps
    NAME          IMAGE                            COMMAND                  SERVICE   CREATED          STATUS          PORTS
    prd-nginx-1   docker.io/library/nginx:latest   "nginx -g daemon off;"   nginx     20 minutes ago   Up 20 minutes   
    
    ↑ 20分前にnginx が起動している

    nginx 設定ファイル文法チェック

    $ make test
    docker-compose exec nginx nginx -t
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful

    nginx 設定再読み込み

    $ make reload
    docker-compose exec nginx nginx -s reload

    これらを組み合わせることで運用に必要な作業を簡易に行うことができる。

    nginx のバージョンアップは、make down make prune make up にて行える。

    サービス増加やSSL 証明書更新後は、make test を行い、success が確認できたらmake reload を行う。make test を行う前に再起動を行うと、もし設定に誤りがあったら起動できなくなる。

    ICOOON-MONN 様のアイコンを使わせていただきました。ありがとうございます。

    https://icooon-mono.com

    【広告】

  • ブログを始める

    日々のシステム開発を通して発見したことを公開することで、誰かの役に立てればと思いブログを始めます。

    【広告】