小伙伴問(wèn)我性能指標(biāo)監(jiān)控怎么做,這次我安排上了!!
作者個(gè)人研發(fā)的在高并發(fā)場(chǎng)景下,提供的簡(jiǎn)單、穩(wěn)定、可擴(kuò)展的延遲消息隊(duì)列框架,具有精準(zhǔn)的定時(shí)任務(wù)和延遲隊(duì)列處理功能。自開(kāi)源半年多以來(lái),已成功為十幾家中小型企業(yè)提供了精準(zhǔn)定時(shí)調(diào)度方案,經(jīng)受住了生產(chǎn)環(huán)境的考驗(yàn)。為使更多童鞋受益,現(xiàn)給出開(kāi)源框架地址:https://github.com/sunshinelyz/mykit-delay
小伙伴的疑問(wèn)小伙伴:監(jiān)控怎么做?
我:你指的是?
小伙伴:性能指標(biāo)。
我:后面會(huì)專(zhuān)門(mén)寫(xiě)這些文章。
使用JMX監(jiān)控Tomcat
關(guān)于監(jiān)控的文章,先寫(xiě)些什么呢?想來(lái)想去,我們先來(lái)寫(xiě)一篇使用JMX監(jiān)控Tomcat的實(shí)戰(zhàn)文章吧。好了,我們直接進(jìn)入主題。
激活Tomcat的JMX遠(yuǎn)程配置
要通過(guò)JMX遠(yuǎn)程監(jiān)控Tomcat,首先需要激活Tomcat的JMX遠(yuǎn)程配置。
① 修改腳本
先修改Tomcat的啟動(dòng)腳本,windows下為bin/catalina.bat(linux下為catalina.sh),添加以下內(nèi)容,8999是jmxremote使用的端口號(hào),第二個(gè)false表示不需要鑒權(quán):
- 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%
要注意以上語(yǔ)句的位置不能太后面,可以加在【if "%OS%" == "Windows_NT" setlocal】一句后的大段的注釋后面。
參考官方說(shuō)明:
- 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
② 鑒權(quán)
上面的配置是不需要鑒權(quán)的,如果需要鑒權(quán)則添加的內(nèi)容為:
- 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%
③ 復(fù)制并修改授權(quán)文件
$JAVA_HOME/jre/lib/management下有jmxremote.access和jmxremote.password的模板文件,將兩個(gè)文件復(fù)制到$CATALINA_BASE/conf目錄下
- 修改$CATALINA_BASE/conf/jmxremote.access 添加內(nèi)容
- monitorRole readonly
- controlRole readwrite
- 修改$CATALINA_BASE/conf/jmxremote.password 添加內(nèi)容:
- monitorRole binghe
- controlRole binghe
注意:如果進(jìn)行了以上步驟導(dǎo)致Tomcat啟動(dòng)不了,那么很可能是密碼文件的權(quán)限問(wèn)題
需要修改jmxremote.password文件的訪問(wèn)權(quán)限,只有運(yùn)行Tomcat的用戶(hù)才能擁有訪問(wèn)權(quán)限 :
Windows的NTFS文件系統(tǒng)下,選中文件,點(diǎn)右鍵 -->“屬性”-->“安全”--> 點(diǎn)“高級(jí)”--> 點(diǎn)“更改權(quán)限”--> 去掉“從父項(xiàng)繼承....”--> 彈出窗口中選“刪除”,這樣就刪除了所有訪問(wèn)權(quán)限。再選“添加”--> “高級(jí)”--> “立即查找”,選中你的用戶(hù)(或用戶(hù)組,如果選用戶(hù)不行那就選用戶(hù)組),例administrator,點(diǎn)“確定",“確定"。來(lái)到權(quán)限項(xiàng)目窗口,勾選“完全控制”,點(diǎn)“確定”,OK了。
官方的提示
- The password file should be read-only and only accessible by the operating system user Tomcat is running as.
④驗(yàn)證配置
重新啟動(dòng)Tomcat,在Windows命令行輸入“netstat -a”查看配置的端口號(hào)是否已打開(kāi),如果打開(kāi),說(shuō)明上面的配置成功了。
⑤ 使用jconsole測(cè)試JMX
運(yùn)行$JAVA_HOME/bin目錄下的jconsole.exe,打開(kāi)J2SE監(jiān)視和管理控制臺(tái),然后建立連接,如果是本地的Tomcat則直接選擇然后點(diǎn)擊連接,如果是遠(yuǎn)程的,則進(jìn)入遠(yuǎn)程選項(xiàng)卡,填寫(xiě)地址、端口號(hào)、用戶(hù)名、口令即可連接。。Mbean屬性頁(yè)中給出了相應(yīng)的數(shù)據(jù),Catalina中是tomcat的,java.lang是jvm的。對(duì)于加粗的黑體屬性值,需雙擊一下才可看內(nèi)容。
代碼獲取監(jiān)控指標(biāo)
- 關(guān)鍵代碼
- String jmxURL = "service:jmx:rmi:///jndi/rmi://192.168.10.93:8999/jmxrmi";
- JMXServiceURL serviceURL = new JMXServiceURL(jmxURL);
- Map map = new HashMap();
- // 用戶(hù)名密碼,在jmxremote.password文件中查看
- String[] credentials = new String[] { "monitorRole", "tomcat" };
- map.put("jmx.remote.credentials", credentials);
- JMXConnector connector = JMXConnectorFactory.connect(serviceURL, map);
- MBeanServerConnection mbsc = connector.getMBeanServerConnection();
- // 端口最好是動(dòng)態(tài)取得
- ObjectName threadObjName = new ObjectName("Catalina:type=ThreadPool,name=http-8080");
- MBeanInfo mbInfo = mbsc.getMBeanInfo(threadObjName);
- // tomcat的線程數(shù)對(duì)應(yīng)的屬性值
- 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監(jiān)控Tomcat代碼實(shí)戰(zhàn)
- */
- 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();
- // 端口最好是動(dòng)態(tài)取得
- ObjectName threadObjName = new ObjectName("Catalina:type=ThreadPool,name=http-8080");
- MBeanInfo mbInfo = mbsc.getMBeanInfo(threadObjName);
- String attrName = "currentThreadCount";// tomcat的線程數(shù)對(duì)應(yīng)的屬性值
- 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("啟動(dòng)時(shí)間:" + df.format(starttime));
- Long timespan = (Long) mbsc.getAttribute(runtimeObjName, "Uptime");
- System.out.println("連續(xù)工作時(shí)間:" + 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();// 堆當(dāng)前分配
- 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("應(yīng)用名:" + obj.getKeyProperty("path"));
- ObjectName objname = new ObjectName(obj.getCanonicalName());
- System.out.println("最大會(huì)話數(shù):" + mbsc.getAttribute(objname, "maxActiveSessions"));
- System.out.println("會(huì)話數(shù):" + mbsc.getAttribute(objname, "activeSessions"));
- System.out.println("活動(dòng)會(huì)話數(shù):" + 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("最大線程數(shù):" + mbsc.getAttribute(objname, "maxThreads"));
- System.out.println("當(dāng)前線程數(shù):" + mbsc.getAttribute(objname, "currentThreadCount"));
- System.out.println("繁忙線程數(shù):" + 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參數(shù)調(diào)優(yōu)
修改配置
- #要添加在tomcat 的bin 下catalina.sh 里添加
- JAVA_OPTS="-Xms1024m -Xmx2048m -Xss2048K -XX:PermSize=128m -XX:MaxPermSize=256m"
參數(shù)說(shuō)明
- -Xms 初始化內(nèi)存大小,一般設(shè)置為和Xmx一致,避免每次垃圾回收后重新分配內(nèi)存
- -Xmx 最大可用內(nèi)存
- -Xmn 年輕代大小
- -Xss 設(shè)置每個(gè)線程棧的大小
- -XX:MetaspaceSize=512M 初始元空間大小,達(dá)到該值就會(huì)觸發(fā)垃圾收集進(jìn)行類(lèi)型卸載,同時(shí)GC會(huì)對(duì)該值進(jìn)行調(diào)整:如果釋放了大量的空間,就適當(dāng)降低該值;如果釋放了很少的空間,那么在不超過(guò)MaxMetaspaceSize時(shí),適當(dāng)提高該值。
- -XX:MaxMetaspaceSize=512M
- -XX:+UseConcMarkSweepGC 并發(fā)標(biāo)記清除(CMS)收集器
- -XX:+CMSClassUnloadingEnabled
- -XX:+HeapDumpOnOutOfMemoryError 表示當(dāng)JVM發(fā)生OOM時(shí),自動(dòng)生成DUMP文件。
- -XX:HeapDumpPath={目錄}/java_heapdump.hprof。如果不指定文件名,默認(rèn)為:java__heapDump.hprof。
Tomcat 相關(guān)參數(shù)優(yōu)化
連接數(shù),線程數(shù),緩存,修改server.xml
打開(kāi)被注釋的默認(rèn)連接池配置。
默認(rèn)配置如下所示:
- <!--
- <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
- maxThreads="150" minSpareThreads="4"/>
- -->
修改實(shí)例:
- <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
- maxThreads="150"
- minSpareThreads="100"
- prestartminSpareThreads="true"
- maxQueueSize="100"/>
參數(shù)說(shuō)明:
- name:線程名稱(chēng)
- namePrefix:線程前綴
- maxThreads:最大并發(fā)連接數(shù),不配置時(shí)默認(rèn)200,一般建議設(shè)置500~ 800 ,要根據(jù)自己的硬件設(shè)施條件和實(shí)際業(yè)務(wù)需求而定。
- minSpareThreads:Tomcat啟動(dòng)初始化的線程數(shù),默認(rèn)值25
- prestartminSpareThreads:在tomcat初始化的時(shí)候就初始化minSpareThreads的值, 不設(shè)置trueminSpareThreads 的值就沒(méi)啥效果了 。
- maxQueueSize:最大的等待隊(duì)列數(shù),超過(guò)則拒絕請(qǐng)求
修改后的配置如下所示:
- <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"
- />
參數(shù)說(shuō)明:
- port:連接端口。
- protocol:連接器使用的傳輸方式。
- executor:連接器使用的線程池名稱(chēng)
- enableLookups:禁用DNS 查詢(xún)
- maxIdleTime:線程空閑時(shí)間,超過(guò)該時(shí)間后,空閑線程會(huì)被銷(xiāo)毀,默認(rèn)值為6000(1分鐘),單位毫秒。
- acceptCount:指定當(dāng)所有可以使用的處理請(qǐng)求的線程數(shù)都被使用時(shí),可以放到處理隊(duì)列中的請(qǐng)求數(shù),超過(guò)這個(gè)數(shù)的請(qǐng)求將不予處理,默認(rèn)設(shè)置 100 。
- maxPostSize:限制 以FORM URL 參數(shù)方式的POST請(qǐng)求的內(nèi)容大小,單位字節(jié),默認(rèn)是 2097152(2兆),10485760 為 10M。如果要禁用限制,則可以設(shè)置為 -1。
- acceptorThreadCount:用于接收連接的線程的數(shù)量,默認(rèn)值是1。一般這個(gè)指需要改動(dòng)的時(shí)候是因?yàn)樵摲?wù)器是一個(gè)多核CPU,如果是多核 CPU 一般配置為 2。
- disableUploadTimeOut:允許Servlet容器,正在執(zhí)行使用一個(gè)較長(zhǎng)的連接超時(shí)值,以使Servlet有較長(zhǎng)的時(shí)間來(lái)完成它的執(zhí)行,默認(rèn)值為false。
- keepAliveTimeout - 表示在下次請(qǐng)求過(guò)來(lái)之前,tomcat保持該連接多久。這就是說(shuō)假如客戶(hù)端不斷有請(qǐng)求過(guò)來(lái),且未超過(guò)過(guò)期時(shí)間,則該連接將一直保持。
- maxKeepAliveRequests -表示該連接最大支持的請(qǐng)求數(shù)。超過(guò)該請(qǐng)求數(shù)的連接也將被關(guān)閉(此時(shí)就會(huì)返回一個(gè)Connection: close頭給客戶(hù)端).(maxKeepAliveRequests="1"代表禁用長(zhǎng)連接)(1表示禁用,-1表示不限制個(gè)數(shù),默認(rèn)100個(gè)。一般設(shè)置在100~200之間)
本文轉(zhuǎn)載自微信公眾號(hào)「冰河技術(shù)」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系冰河技術(shù)公眾號(hào)。


























