小伙伴問我性能指標監控怎么做,這次我安排上了!!
作者個人研發的在高并發場景下,提供的簡單、穩定、可擴展的延遲消息隊列框架,具有精準的定時任務和延遲隊列處理功能。自開源半年多以來,已成功為十幾家中小型企業提供了精準定時調度方案,經受住了生產環境的考驗。為使更多童鞋受益,現給出開源框架地址:https://github.com/sunshinelyz/mykit-delay
小伙伴的疑問小伙伴:監控怎么做?
我:你指的是?
小伙伴:性能指標。
我:后面會專門寫這些文章。
使用JMX監控Tomcat
關于監控的文章,先寫些什么呢?想來想去,我們先來寫一篇使用JMX監控Tomcat的實戰文章吧。好了,我們直接進入主題。
激活Tomcat的JMX遠程配置
要通過JMX遠程監控Tomcat,首先需要激活Tomcat的JMX遠程配置。
① 修改腳本
先修改Tomcat的啟動腳本,windows下為bin/catalina.bat(linux下為catalina.sh),添加以下內容,8999是jmxremote使用的端口號,第二個false表示不需要鑒權:
- set JMX_REMOTE_CONFIG=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
- set CATALINA_OPTS=%CATALINA_OPTS% %JMX_REMOTE_CONFIG%
要注意以上語句的位置不能太后面,可以加在【if "%OS%" == "Windows_NT" setlocal】一句后的大段的注釋后面。
參考官方說明:
- http://tomcat.apache.org/tomcat-6.0-doc/monitoring.html#Enabling_JMX_Remote
- http://tomcat.apache.org/tomcat-7.0-doc/monitoring.html#Enabling_JMX_Remote
- http://tomcat.apache.org/tomcat-8.0-doc/monitoring.html#Enabling_JMX_Remote
- http://tomcat.apache.org/tomcat-9.0-doc/monitoring.html#Enabling_JMX_Remote
② 鑒權
上面的配置是不需要鑒權的,如果需要鑒權則添加的內容為:
- set JMX_REMOTE_CONFIG=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password -Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access
- set CATALINA_OPTS=%CATALINA_OPTS% %JMX_REMOTE_CONFIG%
③ 復制并修改授權文件
$JAVA_HOME/jre/lib/management下有jmxremote.access和jmxremote.password的模板文件,將兩個文件復制到$CATALINA_BASE/conf目錄下
- 修改$CATALINA_BASE/conf/jmxremote.access 添加內容
- monitorRole readonly
- controlRole readwrite
- 修改$CATALINA_BASE/conf/jmxremote.password 添加內容:
- monitorRole binghe
- controlRole binghe
注意:如果進行了以上步驟導致Tomcat啟動不了,那么很可能是密碼文件的權限問題
需要修改jmxremote.password文件的訪問權限,只有運行Tomcat的用戶才能擁有訪問權限 :
Windows的NTFS文件系統下,選中文件,點右鍵 -->“屬性”-->“安全”--> 點“高級”--> 點“更改權限”--> 去掉“從父項繼承....”--> 彈出窗口中選“刪除”,這樣就刪除了所有訪問權限。再選“添加”--> “高級”--> “立即查找”,選中你的用戶(或用戶組,如果選用戶不行那就選用戶組),例administrator,點“確定",“確定"。來到權限項目窗口,勾選“完全控制”,點“確定”,OK了。
官方的提示
- The password file should be read-only and only accessible by the operating system user Tomcat is running as.
④驗證配置
重新啟動Tomcat,在Windows命令行輸入“netstat -a”查看配置的端口號是否已打開,如果打開,說明上面的配置成功了。
⑤ 使用jconsole測試JMX
運行$JAVA_HOME/bin目錄下的jconsole.exe,打開J2SE監視和管理控制臺,然后建立連接,如果是本地的Tomcat則直接選擇然后點擊連接,如果是遠程的,則進入遠程選項卡,填寫地址、端口號、用戶名、口令即可連接。。Mbean屬性頁中給出了相應的數據,Catalina中是tomcat的,java.lang是jvm的。對于加粗的黑體屬性值,需雙擊一下才可看內容。
代碼獲取監控指標
- 關鍵代碼
- String jmxURL = "service:jmx:rmi:///jndi/rmi://192.168.10.93:8999/jmxrmi";
- JMXServiceURL serviceURL = new JMXServiceURL(jmxURL);
- Map map = new HashMap();
- // 用戶名密碼,在jmxremote.password文件中查看
- String[] credentials = new String[] { "monitorRole", "tomcat" };
- map.put("jmx.remote.credentials", credentials);
- JMXConnector connector = JMXConnectorFactory.connect(serviceURL, map);
- MBeanServerConnection mbsc = connector.getMBeanServerConnection();
- // 端口最好是動態取得
- ObjectName threadObjName = new ObjectName("Catalina:type=ThreadPool,name=http-8080");
- MBeanInfo mbInfo = mbsc.getMBeanInfo(threadObjName);
- // tomcat的線程數對應的屬性值
- String attrName = "currentThreadCount";
- MBeanAttributeInfo[] mbAttributes = mbInfo.getAttributes();
- System.out.println("currentThreadCount:" + mbsc.getAttribute(threadObjName, attrName));
- 完整代碼
- import java.lang.management.MemoryUsage;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Formatter;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.Set;
- import javax.management.MBeanAttributeInfo;
- import javax.management.MBeanInfo;
- import javax.management.MBeanServerConnection;
- import javax.management.ObjectInstance;
- import javax.management.ObjectName;
- import javax.management.openmbean.CompositeDataSupport;
- import javax.management.remote.JMXConnector;
- import javax.management.remote.JMXConnectorFactory;
- import javax.management.remote.JMXServiceURL;
- /**
- * @author binghe
- * @description JMX監控Tomcat代碼實戰
- */
- public class JMXTest {
- public static void main(String[] args) {
- try {
- String jmxURL = "service:jmx:rmi:///jndi/rmi://127.0.0.1:8999/jmxrmi";
- JMXServiceURL serviceURL = new JMXServiceURL(jmxURL);
- Map map = new HashMap();
- String[] credentials = new String[] { "monitorRole", "tomcat" };
- map.put("jmx.remote.credentials", credentials);
- JMXConnector connector = JMXConnectorFactory.connect(serviceURL,map);
- MBeanServerConnection mbsc = connector.getMBeanServerConnection();
- // 端口最好是動態取得
- ObjectName threadObjName = new ObjectName("Catalina:type=ThreadPool,name=http-8080");
- MBeanInfo mbInfo = mbsc.getMBeanInfo(threadObjName);
- String attrName = "currentThreadCount";// tomcat的線程數對應的屬性值
- MBeanAttributeInfo[] mbAttributes = mbInfo.getAttributes();
- System.out.println("currentThreadCount:" + mbsc.getAttribute(threadObjName, attrName));
- // heap
- for (int j = 0; j < mbsc.getDomains().length; j++) {
- System.out.println("###########" + mbsc.getDomains()[j]);
- }
- Set MBeanset = mbsc.queryMBeans(null, null);
- System.out.println("MBeanset.size() : " + MBeanset.size());
- Iterator MBeansetIterator = MBeanset.iterator();
- while (MBeansetIterator.hasNext()) {
- ObjectInstance objectInstance = (ObjectInstance) MBeansetIterator.next();
- ObjectName objectName = objectInstance.getObjectName();
- String canonicalName = objectName.getCanonicalName();
- System.out.println("canonicalName : " + canonicalName);
- if (canonicalName.equals("Catalina:host=localhost,type=Cluster")) {
- // Get details of cluster MBeans
- System.out.println("Cluster MBeans Details:");
- System.out.println("=========================================");
- // getMBeansDetails(canonicalName);
- String canonicalKeyPropList = objectName.getCanonicalKeyPropertyListString();
- }
- }
- // ------------------------- system ----------------------
- ObjectName runtimeObjName = new ObjectName("java.lang:type=Runtime");
- System.out.println("廠商:"+ (String) mbsc.getAttribute(runtimeObjName, "VmVendor"));
- System.out.println("程序:"+ (String) mbsc.getAttribute(runtimeObjName, "VmName"));
- System.out.println("版本:"+ (String) mbsc.getAttribute(runtimeObjName, "VmVersion"));
- Date starttime = new Date((Long) mbsc.getAttribute(runtimeObjName,"StartTime"));
- SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- System.out.println("啟動時間:" + df.format(starttime));
- Long timespan = (Long) mbsc.getAttribute(runtimeObjName, "Uptime");
- System.out.println("連續工作時間:" + JMXTest.formatTimeSpan(timespan));
- // ------------------------ JVM -------------------------
- // 堆使用率
- ObjectName heapObjName = new ObjectName("java.lang:type=Memory");
- MemoryUsage heapMemoryUsage = MemoryUsage.from((CompositeDataSupport) mbsc.getAttribute(heapObjName,"HeapMemoryUsage"));
- long maxMemory = heapMemoryUsage.getMax();// 堆最大
- long commitMemory = heapMemoryUsage.getCommitted();// 堆當前分配
- long usedMemory = heapMemoryUsage.getUsed();
- System.out.println("heap:" + (double) usedMemory * 100 / commitMemory + "%");// 堆使用率
- MemoryUsage nonheapMemoryUsage = MemoryUsage.from((CompositeDataSupport) mbsc.getAttribute(heapObjName,"NonHeapMemoryUsage"));
- long noncommitMemory = nonheapMemoryUsage.getCommitted();
- long nonusedMemory = heapMemoryUsage.getUsed();
- System.out.println("nonheap:" + (double) nonusedMemory * 100 / noncommitMemory + "%");
- ObjectName permObjName = new ObjectName("java.lang:type=MemoryPool,name=Perm Gen");
- MemoryUsage permGenUsage = MemoryUsage.from((CompositeDataSupport) mbsc.getAttribute(permObjName, "Usage"));
- long committed = permGenUsage.getCommitted();// 持久堆大小
- long used = heapMemoryUsage.getUsed();//
- System.out.println("perm gen:" + (double) used * 100 / committed + "%");// 持久堆使用率
- // -------------------- Session ---------------
- ObjectName managerObjName = new ObjectName("Catalina:type=Manager,*");
- Set<ObjectName> s = mbsc.queryNames(managerObjName, null);
- for (ObjectName obj : s) {
- System.out.println("應用名:" + obj.getKeyProperty("path"));
- ObjectName objname = new ObjectName(obj.getCanonicalName());
- System.out.println("最大會話數:" + mbsc.getAttribute(objname, "maxActiveSessions"));
- System.out.println("會話數:" + mbsc.getAttribute(objname, "activeSessions"));
- System.out.println("活動會話數:" + mbsc.getAttribute(objname, "sessionCounter"));
- }
- // ----------------- Thread Pool ----------------
- ObjectName threadpoolObjName = new ObjectName("Catalina:type=ThreadPool,*");
- Set<ObjectName> s2 = mbsc.queryNames(threadpoolObjName, null);
- for (ObjectName obj : s2) {
- System.out.println("端口名:" + obj.getKeyProperty("name"));
- ObjectName objname = new ObjectName(obj.getCanonicalName());
- System.out.println("最大線程數:" + mbsc.getAttribute(objname, "maxThreads"));
- System.out.println("當前線程數:" + mbsc.getAttribute(objname, "currentThreadCount"));
- System.out.println("繁忙線程數:" + mbsc.getAttribute(objname, "currentThreadsBusy"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- public static String formatTimeSpan(long span) {
- long minseconds = span % 1000;
- span = span / 1000;
- long seconds = span % 60;
- span = span / 60;
- long mins = span % 60;
- span = span / 60;
- long hours = span % 24;
- span = span / 24;
- long days = span;
- return (new Formatter()).format("%1$d天 %2$02d:%3$02d:%4$02d.%5$03d", days, hours, mins, seconds, minseconds).toString();
- }
- }
Tomcat9 JVM參數調優
修改配置
- #要添加在tomcat 的bin 下catalina.sh 里添加
- JAVA_OPTS="-Xms1024m -Xmx2048m -Xss2048K -XX:PermSize=128m -XX:MaxPermSize=256m"
參數說明
- -Xms 初始化內存大小,一般設置為和Xmx一致,避免每次垃圾回收后重新分配內存
- -Xmx 最大可用內存
- -Xmn 年輕代大小
- -Xss 設置每個線程棧的大小
- -XX:MetaspaceSize=512M 初始元空間大小,達到該值就會觸發垃圾收集進行類型卸載,同時GC會對該值進行調整:如果釋放了大量的空間,就適當降低該值;如果釋放了很少的空間,那么在不超過MaxMetaspaceSize時,適當提高該值。
- -XX:MaxMetaspaceSize=512M
- -XX:+UseConcMarkSweepGC 并發標記清除(CMS)收集器
- -XX:+CMSClassUnloadingEnabled
- -XX:+HeapDumpOnOutOfMemoryError 表示當JVM發生OOM時,自動生成DUMP文件。
- -XX:HeapDumpPath={目錄}/java_heapdump.hprof。如果不指定文件名,默認為:java__heapDump.hprof。
Tomcat 相關參數優化
連接數,線程數,緩存,修改server.xml
打開被注釋的默認連接池配置。
默認配置如下所示:
- <!--
- <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
- maxThreads="150" minSpareThreads="4"/>
- -->
修改實例:
- <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
- maxThreads="150"
- minSpareThreads="100"
- prestartminSpareThreads="true"
- maxQueueSize="100"/>
參數說明:
- name:線程名稱
- namePrefix:線程前綴
- maxThreads:最大并發連接數,不配置時默認200,一般建議設置500~ 800 ,要根據自己的硬件設施條件和實際業務需求而定。
- minSpareThreads:Tomcat啟動初始化的線程數,默認值25
- prestartminSpareThreads:在tomcat初始化的時候就初始化minSpareThreads的值, 不設置trueminSpareThreads 的值就沒啥效果了 。
- maxQueueSize:最大的等待隊列數,超過則拒絕請求
修改后的配置如下所示:
- <Connector port="8080" protocol="HTTP/1.1"
- connectionTimeout="20000"
- redirectPort="8443"
- executor="tomcatThreadPool"
- enableLookups="false"
- maxIdleTime="60000"
- acceptCount="100"
- maxPostSize="10485760"
- acceptorThreadCount="2"
- disableUploadTimeout="true"
- URIEncoding="utf-8"
- keepAliveTimeout ="6000"
- maxKeppAliveRequests="500"
- />
參數說明:
- port:連接端口。
- protocol:連接器使用的傳輸方式。
- executor:連接器使用的線程池名稱
- enableLookups:禁用DNS 查詢
- maxIdleTime:線程空閑時間,超過該時間后,空閑線程會被銷毀,默認值為6000(1分鐘),單位毫秒。
- acceptCount:指定當所有可以使用的處理請求的線程數都被使用時,可以放到處理隊列中的請求數,超過這個數的請求將不予處理,默認設置 100 。
- maxPostSize:限制 以FORM URL 參數方式的POST請求的內容大小,單位字節,默認是 2097152(2兆),10485760 為 10M。如果要禁用限制,則可以設置為 -1。
- acceptorThreadCount:用于接收連接的線程的數量,默認值是1。一般這個指需要改動的時候是因為該服務器是一個多核CPU,如果是多核 CPU 一般配置為 2。
- disableUploadTimeOut:允許Servlet容器,正在執行使用一個較長的連接超時值,以使Servlet有較長的時間來完成它的執行,默認值為false。
- keepAliveTimeout - 表示在下次請求過來之前,tomcat保持該連接多久。這就是說假如客戶端不斷有請求過來,且未超過過期時間,則該連接將一直保持。
- maxKeepAliveRequests -表示該連接最大支持的請求數。超過該請求數的連接也將被關閉(此時就會返回一個Connection: close頭給客戶端).(maxKeepAliveRequests="1"代表禁用長連接)(1表示禁用,-1表示不限制個數,默認100個。一般設置在100~200之間)
本文轉載自微信公眾號「冰河技術」,可以通過以下二維碼關注。轉載本文請聯系冰河技術公眾號。