JAVA jps jstat の利用と応用

1) jps コマンドで、今動いてるJAVAのプロセスを確認

2) jstat で Javaのメモリ使用状況を確認

3) メモリ使用状況のログ追加と、警告メール配信

[ ログテーブルのスキーマ ]


sqlite> .schema log_jstat
CREATE TABLE log_jstat (
tm timestamp not null,
pid integer not null,
pnm text not null,
S0C real not null, 
S1C real not null,
S0U real not null,
S1U real not null,

EC real not null,
EU real not null,
OC real not null,
OU real not null,
ttlCapM integer not null default 0, 
ttlUseM integer not null default 0,
ttlRT   integer not null default 0);
CREATE INDEX log_jstat_tm on log_jstat (tm);
CREATE TRIGGER log_jstat_ai after insert on log_jstat for each row
begin
update log_jstat
set
ttlCapM = cast((S0C + S1C + EC + OC) / 1024 as int),
ttlUseM = cast((S0U + S1U + EU + OU) / 1024 as int),
ttlRT = cast((S0U + S1U + EU + OU) / (S0C + S1C + EC + OC) * 100 as int)
where
tm > current_date || ' 00:00:00';
end;
sqlite> 

[ Shellコードサンプル ]


#! /bin/bash

# --------------------const ------------------------ #

TMP_OUT_FILENAME_JPS=/home/sqlite/sql/jps.txt
TMP_OUT_FILENAME_JSTL=/home/sqlite/sql/jstl.txt

SQL_OUT_FILENAME=/home/sqlite/sql/tmp_jstl_add.sql
DBNAME=/home/sqlite/logs
INSERT_DML="insert into log_jstat (tm, pid, pnm, S0C,S1C,S0U, S1U, EC, EU, OC, OU) values (datetime('now','localtime'), "
echo $INSERT_DML

LAST_URATIO_SQL="SELECT mount, uratio FROM log_df ORDER BY tm DESC LIMIT 0, 3;"
LAST_URATIO_SQL_FILENAME=/home/sqlite/sql/lastgcratio.sql

GC_OUT_TEXTFILENAME=/home/ApacheDoc/APIStatus/tmpgcres.txt

LAST_URATIO_RES_FILENAME=/home/sqlite/sql/lastdf.txt
LAST_URATIO_RES_FILENAME2=/home/sqlite/sql/lastdf2.txt

WARN_TEMPLATE_FILENAME=/home/ssmtptxt/dfwarn.txt
WARN_TMP_FILENAME=/home/ssmtptxt/tmp_dfwarn.txt
WARN_TMP_FILENAME2=/home/ssmtptxt/tmp_dfwarn2.txt

MAX_ALLOWED=89

# ---------------------------------------------------- #

fmtnow=`date +%Y-%m-%d' '%H:%M:%S -d today`
echo "------------------- "$fmtnow" -------------------"

/usr/bin/jps | grep Bootstrap > ${TMP_OUT_FILENAME_JPS}
cat ${TMP_OUT_FILENAME_JPS}

#exit 0

# Write temporaly SQL file ##

echo -n > ${SQL_OUT_FILENAME}

pid=""
pnm=""
jstatres=""
inssql=""
cat ${TMP_OUT_FILENAME_JPS} | while read line
do
  pid=`echo ${line} | gawk '{print $1}'`
  pnm=`echo ${line} | gawk '{print $2}' | sed -e 's/^/"/' -e 's/$/"/'`
  echo ${pid} ${pnm} 

  jstatres=`jstat -gc ${pid} | grep -v S0C | gawk '{print $1","$2","$3","$4","$5","$6","$7","$8}'`
  #echo ${jstatres} 
  inssql=${INSERT_DML}${pid}","${pnm}","${jstatres}");"


  echo ${inssql} >> ${SQL_OUT_FILENAME}
done

cat ${SQL_OUT_FILENAME}
#exit 0


# Write sqlite database #
/usr/bin/sqlite3 ${DBNAME} < ${SQL_OUT_FILENAME}


# Output last res by sqlite (uratio updated by trigger) #
/usr/bin/sqlite3 $DBNAME < ${LAST_URATIO_SQL_FILENAME} > ${GC_OUT_TEXTFILENAME}

exit 0

echo "--- each uratio ---"
#cat ${LAST_URATIO_RES_FILENAME}
#cat ${LAST_URATIO_RES_FILENAME} | sed -e 's/|/;/g'  > ${LAST_URATIO_RES_FILENAME2}
#exit 0

# Loop and send a mail #
tmpmp=""
tmprt=0
wrntxt=""
wrncnt=0
while read line
do
  #echo ${line}

  tmpmp=`echo ${line} | gawk -F"|"  '{print $1}'`
  tmprt=`echo ${line} | gawk  -F"|" '{print $2}'`
  echo ${tmpmp}":"${tmprt}

  if [ ${tmprt} -gt ${MAX_ALLOWED} ]; then
    wrntxt=${wrntxt}${tmpmp}":"${tmprt}"%zzzzz"
    #wrntxt=`echo -e ${wrntxt}${tmpmp}" : "${tmprt}"%  \n"`
    #wrntxt=`echo ""${wrntxt}`
    let wrncnt++
  fi

done < ${LAST_URATIO_RES_FILENAME}

# send a mail by ssmtp #
if [ $[wrncnt] -gt 0 ]; then
  cp ${WARN_TEMPLATE_FILENAME} ${WARN_TMP_FILENAME}
  echo ${wrntxt} >> ${WARN_TMP_FILENAME}
  cat ${WARN_TMP_FILENAME} | sed 's/zzzzz/\n/g' > ${WARN_TMP_FILENAME2}

  echo "" >> ${WARN_TMP_FILENAME}
  echo "Issued Time : "`date`  >> ${WARN_TMP_FILENAME}

  /usr/sbin/ssmtp -t < ${WARN_TMP_FILENAME2}
fi

echo "------------------------------------------------"



4) cronにセットしてスケジュール実行


## Intervaled jstat log add ##
1,21,41 * * * * root /home/sh/jstatcheck.sh >> /home/Log/last_jstatlogadd.log 2>&1

5) メモリ使用状況ログをページで表示

POI が激遅くTomcat が停止した時の改修

レコード数、列数の多いPOIでのエクセル出力は、どうしても遅くなりがちですが、Javaのヒープ上限を超えて、エラーで出力不能となったことがあり、直しました。

[ コードサンプル ]
org.apache.poi.xssf 以下に替えて、org.apache.poi.ss 以下のAPIをインポート


import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.ss.usermodel.WorkbookFactory;

テンプレート読込


  /**
   * テンプレート読込
   *
   * @throws IOException ファイル読込みエラー
   */
  public void readTemplate() throws IOException {

    try {
      InputStream fi = new FileInputStream(new File(exTmplFName));
      this.wb = WorkbookFactory.create(fi);
    }
    catch (Exception e) {
      e.printStackTrace();
    }

  }

エクセルオブジェクト



    private Workbook wb = null;

    Sheet sheet = null;
    Cell cell = null;

    sheet = this.wb.getSheetAt(0);
    Row row = sheet.getRow(1);

MyBatis 大量連続更新時のパフォーマンス向上策

 

 

 

sqlSessionFactory.openSession の引数に ExecutorType.BATCH を入れてます。

[ コードサンプル ]


  /**
   * MyBatisセッションファクトリー
   */
  protected SqlSessionFactory sqlSessionFactory = null;

  /**
   * マルチ選択用ファイル分類一時テーブル削除追加
   */
  protected void delinsFileTmpTable() {

    try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
      // -- ファイル分類一時テーブル更新 -- //
      // 削除 //
      session.delete("delTmpFileList", this);
      // 追加 //
      for (String nmf : this.fileextList) {
        this.nmFile = nmf;
        session.insert("insATmpFileList", this);
      }
      session.commit();
    }
    catch (Exception e) {
      e.printStackTrace();
    }

  }

 

Tomcat 管理ツールは probe 一択

Tomcat9系 でも使えてます。

よく使う機能

1) アプリケーション単位での停止、起動 (Tomcat再起動しなくても一瞬でできる)

2) データソースの確認と接続チェック

3) 接続セッションの確認、切断

4) 配備後のJSPの全コンパイル

5) ヒープメモリ使用量のチェックとガベージコレクションの実行

CentOS7系 6系より便利なところ

基本的に設定ファイルをいじる方式から、コマンドで設定を変更する方式に変わってきてる。

1)  systemd で起動、終了、自動起動、ステータスがわかりやすい。自作サービスも作りやすい

2) firewalld でファイアウォールの設定がやりやすい

3) nmtui, nmcui のネットワーク設定が便利

詳しくはこちらのリク: https://qiita.com/sion_cojp/items/115e1671fcbc8f214aee 

https://liginc.co.jp/343687

 

HttpFox を使い続ける

FireFoxの定番プラグインのHttpFoxを長年使ってましたが、Ver60以降、Quantamになってから、使えなくなったので、WaterFoxをNetBeansのデフォルトブラウザにして使い続けてます。

レスポンスボディ、Postパラメータのチェックとか、JSON, AJaxを使ってると頻繁に行う必要あり、開発ツールに比べ、シンプルで見やすいです。

JAVA 本番環境リリース時変更をなくす (プロキシサーバー)

開発環境がプロキシなし, 実行環境がプロキシありの場合、そのままでは動かないので、違う設定する必要ありますが、最小限に済ます方法です。
プロキシの設定は、httpclient, seleniumのconnect, jsoupのconnect, curlのシェル実行とかで多用するので、どこに居ても開発がはかどります。

1) web.xmlに使うか使わないかを設定


   <!-- Use proxy or not for curl command -->
  <context-param>
    <param-name>IsUseProxy</param-name> <param-value>0</param-value>
  </context-param>

2) 判別と、使う場合のcurlコマンドに使う引数を、静的設定用クラスに定義


  /**
   * プロキシ利用フラグ
   */
  public static boolean isUseProxy = false;
  /**
   * curl用プロキシ引数
   */
  public static final String ARG_PROXY_CURL = "--proxy http://10.70.1.80:8080";

3) JSPでの利用例


  // クレジットバランス用 //
  ServletContext cntxt = getServletContext();
  String crblcURL = cntxt.getInitParameter("CreditBalanceURL");
 
  crblc.setIsUseProxy(DBini.isUseProxy);

  crblc.setUrl(crblcURL);
  String crrest = crblc.getCreditBalance();
  pageContext.setAttribute("crrest", crrest);

        // == ツールバー == //
        var htmlds = 'From ' + "${dtpcsumsel}" +
                '<span style="margin-left:12px;">' + '${crrest}' + '</span>';
        $('#t_pcsum').prepend(htmlds); // 後でリフレッシュボタンを付けてるのでprepend

  /**
   * curlでクレジットバランス取得
   *
   * @return 文字列
   */
  public String getCreditBalance() {

    int res = 0;
    String proxy = this.isUseProxy ? DBini.ARG_PROXY_CURL: "";

    // コマンド文字列作成 //
    String curcmd = "curl -k --connect-timeout 5 " + proxy + " " + url;
    System.out.println(curcmd);

    String restxt = "";
    try {

      Runtime runtime = Runtime.getRuntime();
      Process p = runtime.exec(curcmd);
      InputStream is = p.getInputStream();

      // レスポンス取得 //
      int nread;
      byte[] rbuf = new byte[500];
      while ((nread = is.read(rbuf)) > 0) {
      }
      restxt = new String(rbuf, "US-ASCII");

    }
    catch (Exception e) {
      e.printStackTrace();
    }
    System.out.println(restxt);

    String blctxt = "";
    if (restxt != null) {
      int crblcstpos = restxt.indexOf("CREDIT_BALANCE=\"");
      int lastchgpos = restxt.indexOf("\" LAST_CHARGED");
      if (crblcstpos != -1 && lastchgpos != -1) {
        blctxt = restxt.substring(crblcstpos + 16, lastchgpos);
      }
      if (blctxt.indexOf("Connection timed out") > -1) {
        blctxt = "TimedOut";
      }
      if (blctxt.equals("")) {
        blctxt = "No Response";
      }
      return blctxt;
    }
    // エラー //
    else {     
      return "Error";
    }
  }

4) Servletでの利用例


    // コマンド //
    String curlcmd = "curl -k " + (DBini.isUseProxy ? DBini.ARG_PROXY_CURL: "")
            + (isSimple ? DBini.vesselPositionURLSimple : DBini.vesselPositionURL) + imo;
    System.out.println(curlcmd);

    // 開始時間取得 //
    long startTime = System.currentTimeMillis();

    VesselPosition vsp = new VesselPosition(DBini.tmNowFmt());

    String restxt = "";
    try {

      Runtime runtime = Runtime.getRuntime();
      Process p = runtime.exec(curlcmd);
      InputStream is = p.getInputStream();

      // レスポンス取得 //
      int nread;
      byte[] rbuf = new byte[2048];
      while ((nread = is.read(rbuf)) > 0) {
      }
      restxt = new String(rbuf, "US-ASCII");

    }
    catch (Exception e) {
      e.printStackTrace();
      vsp.setResmsg(this.FAIL_MESSAFE);
    }

JAVA 本番環境リリース時変更をなくす (ファイルアップロード)

開発環境がWindows, 実行環境がLinuxの場合、そのままでは動かないので、違う設定する必要ありますが、最小限に済ます方法です。

1) web.xmlの記述で、URIを違えた2種類の設定を入れる (アノテーションでも出来ると思うが慣れてないので)


  <!-- ***** Excel Goods master upload **** -->
  <!-- Windows -->
  <servlet>
    <servlet-name>ExcelCheckWin</servlet-name>
    <servlet-class>dnksg.svlt.mstupld.ExcelCheck</servlet-class>
    <multipart-config>
      <max-file-size>-1</max-file-size>
      <max-request-size>-1</max-request-size>
      <file-size-threshold>0</file-size-threshold>
      <location>D:\\#NBTest\\dnksgDocumentTmp</location>
    </multipart-config>
    <init-param>
      <param-name>UploadPath</param-name>
      <param-value>D:\\#NBTest\\dnksgDocumentTmp\\</param-value>
    </init-param>
  </servlet>
  
  <!-- Linux -->
  <servlet>
    <servlet-name>ExcelCheckLin</servlet-name>
    <servlet-class>dnksg.svlt.mstupld.ExcelCheck</servlet-class>
    <multipart-config>
      <max-file-size>-1</max-file-size>
      <max-request-size>-1</max-request-size>
      <file-size-threshold>0</file-size-threshold>
      <location>/home/dnksgDocumentTmp</location>
    </multipart-config>
    <init-param>
      <param-name>UploadPath</param-name>
      <param-value>/home/dnksgDocumentTmp/</param-value>
    </init-param>
  </servlet> 
  
  <!-- Windows -->
  <servlet-mapping>
    <servlet-name>ExcelCheckWin</servlet-name>
    <url-pattern>/ExcelCheckWin</url-pattern>
  </servlet-mapping>
  
  <!-- Linux -->    
  <servlet-mapping>  
    <servlet-name>ExcelCheckLin</servlet-name>
    <url-pattern>/ExcelCheckLin</url-pattern>
  </servlet-mapping>  

2) JSPでファイルセパレータからWindows or Linuxを判別して、pageContextにセット


// Linux or Windows //
  boolean isLin = System.getProperty("file.separator").equals("/");
  pageContext.setAttribute("LW", isLin ? "Lin" : "Win");

3) 2)をJavascriptのconstな変数にセット


    <script type="text/javascript">

      const LW = '${LW}';

4) dropzone.js の 送信先URL末尾で 3)を使う


 // 送信先URL //
  const UPLOAD_URLS = ['ImageUpload', 'FileUploadMulti', 'FileUploadMulti', 'AmazonDocUpload'];
$('#' + itm).dropzone({
      url: UPLOAD_URLS[j] + LW + "?dropzone=1&userid=" + ID_USER + UPLOAD_KIND_PARAMS[j],
[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

Tomcatハンドブック
価格:5170円(税込、送料無料) (2023/1/11時点)