python代码测试jvm虚拟机

本文分享了在Java应用环境中使用JMX进行监控的具体实践,包括如何通过Python脚本收集JVM性能指标,如线程、内存、垃圾回收等数据,并探讨了多线程与串行处理的性能对比,以及在生产环境中推荐的监控策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题外话:工作了许多年,基本在java圈里做运维,稍微了解一点jvm的东西,但是又不会java语言,的确是一个遗憾。

下面的代码:

对上个版本并发代码做了性能测试,发现启动虚拟机占了一部分时间,其余时间主要花费在获取各个jvm数据上,多进程、多线程、多协程等都没有带来明显时间的缩减,因此改用串行;当然多进程、多线程、多协程都没有利用jpype.startJVM启动多个虚拟机,因为这样一来消耗内存比较大,对性能有影响,如果内存宽裕,不妨一试。
至于jvm中相关参数,第一次从网上别人用java写的代码中抠过来的,发现老是拉取不到数据,最后使用jconsole远程到测试环境,找到正确的参数名
垃圾回收的时间依然没有处理,以后有时间再处理吧
下面的脚本收集10个左右的jvm数据大概要12s左右时间
另外下面的数据收集最后使用python的“+”进行字符串拼接,感觉性能不是很好,看官们是否有好的建议呢
最后是一点心得:

建议生产上java加上-Dapplication.name=******的参数,这样可以实现完全动态监控,找到应用名,然后找到它的jmx端口,zabbix也可以进行灵活的配置。
建议使用使用pinPoint、perfino等专业apm监控工具,尤其是perfino,是非常推荐的,遗憾的是它是收费软件;同时不能忽视的是这些专业的apm监控软件或多或少都对程序有一些性能上的影响。perfino对性能影响尤其大,但是对排错有非常大的帮助,甚至能定位到某调sql执行时长。
#!/usr/bin/python
#coding: utf-8
import os
import sys
import json
import jpype
from jpype import java
from jpype import javax
#from multiprocessing.dummy import Pool as ThreadPool

参考:

https://blog.nobugware.com/post/2010/11/08/jmx-query-python-cpython/

官网:http://jpype.sourceforge.net/

#服务路径
service_prefix_path = “/data/apps/soa/”

def discovery(service_prefix_path):
r = {}
r[‘data’] = []

ret = os.popen("ls {0}".format(service_prefix_path))
res = ret.read()
for service in res.splitlines():
    if service:
        service_path = os.path.join(service_prefix_path,service)
        if os.path.islink(service_path):
            r['data'].append({'{#SERVICE}': service})
print(json.dumps(r))

def _Get_Jmx(service,port):
user = “”
password = “”
r_str = “”

URL = "service:jmx:rmi:///jndi/rmi://127.0.0.1:%d/jmxrmi" % (port)
#jpype.startJVM("C:\Program Files\Java\jre1.8.0_171\bin\server\jvm.dll")

#下面会有一个连接异常的处理,让一个连接报错,不至于影响脚本继续往下执行
try:
    jhash = java.util.HashMap()
    jarray=jpype.JArray(java.lang.String)([user,password])
    jhash.put(javax.management.remote.JMXConnector.CREDENTIALS,jarray)
    jmxurl = javax.management.remote.JMXServiceURL(URL)
    jmxsoc = javax.management.remote.JMXConnectorFactory.connect(jmxurl,jhash)
    connection = jmxsoc.getMBeanServerConnection()
except Exception as e:
    print(e)
    # 如果连接不上,直接返回空字符串
    return ""


#Threading
type_str = "Threading"
object="java.lang:type={0}".format(type_str)
for atrribute in ["ThreadCount","TotalStartedThreadCount","PeakThreadCount","DaemonThreadCount"]:
    try:
        attr=connection.getAttribute(javax.management.ObjectName(object),atrribute)
    except Exception, e:
        pass
    else:
        attr = int(attr)
        r_str += '- jmx.{0}.{1}.[{2}]'.format(type_str, atrribute, service) + " " + str(attr) + "\n"

#OperatingSystem
type_str = "OperatingSystem"
object="java.lang:type={0}".format(type_str)
for atrribute in ["MaxFileDescriptorCount","OpenFileDescriptorCount","ProcessCpuLoad"]:
    try:
        attr=connection.getAttribute(javax.management.ObjectName(object),atrribute)
    except Exception, e:
        pass
    else:
        if atrribute == "ProcessCpuLoad":
            attr = round(float(attr),4)
        else:
            attr = int(attr)
        r_str += '- jmx.{0}.{1}.[{2}]'.format(type_str, atrribute, service) + " " + str(attr) + "\n"

#ClassLoading
type_str = "ClassLoading"
object="java.lang:type={0}".format(type_str)
for atrribute in ["LoadedClassCount","TotalLoadedClassCount","UnloadedClassCount"]:
    try:
        attr=connection.getAttribute(javax.management.ObjectName(object),atrribute)
    except Exception, e:
        pass
    else:
        attr = int(attr)
        r_str += '- jmx.{0}.{1}.[{2}]'.format(type_str, atrribute, service) + " " + str(attr) + "\n"

#Runtime
type_str = "Runtime"
object="java.lang:type={0}".format(type_str)
for atrribute in ["VmName","Uptime","VmVersion"]:
    try:
        attr=connection.getAttribute(javax.management.ObjectName(object),atrribute)
    except Exception, e:
        pass
    else:
        attr = str(attr)
        r_str += '- jmx.{0}.{1}.[{2}]'.format(type_str, atrribute, service) + " " + attr + "\n"

#memory
type_str = "Memory"
object="java.lang:type={0}".format(type_str)
for atrribute in ["HeapMemoryUsage","NonHeapMemoryUsage","ObjectPendingFinalizationCount"]:
    try:
        attr=connection.getAttribute(javax.management.ObjectName(object),atrribute)
    except Exception,e:
        pass
    else:
        if atrribute == "ObjectPendingFinalizationCount":
            r_str +=  '- jmx.{0}.{1}.[{2}]'.format(type_str,atrribute,service) + " " + str(int(attr)) + "\n"
        else:
            for branch in ["committed","max","used"]:
                r_str += '- jmx.{0}.{1}.{2}.[{3}]'.format(type_str, atrribute,branch,service) + " " + str(int(attr.contents.get(branch))) + "\n"

#GarbageCollector:ok,其中时间单位是s
type_str = "GarbageCollector"
for name in ["Copy","MarkSweepCompact","PS Scavenge","ConcurrentMarkSweep","ParNew","PS MarkSweep"]:
    object = "java.lang:type={0},name={1}".format(type_str,name)
    for atrribute in ["CollectionTime","CollectionCount"]:
        try:
            attr=connection.getAttribute(javax.management.ObjectName(object),atrribute)
        except:
            pass  #如果报错直接就没有数据
            # r_str += 'jmx["{0}",{1}].[{2}]'.format(object, atrribute, service) + " " + str(0) + "\n"
        else:
            r_str += '- jmx.{0}.{1}.{2}.[{3}]'.format(type_str,name.replace(" ","_"),atrribute,service) + " " + str(int(attr)) + "\n"

#memoryPool
type_str = "MemoryPool"
for name in ["Code Cache","Metaspace","Compressed Class Space","Par Eden Space","Par Survivor Space","PS Eden Space","PS Old Gen","PS Perm Gen","PS Survivor Space","CMS Old Gen","CMS Perm Gen","Perm Gen"]:
    object = "java.lang:type={0},name={1}".format(type_str,name)
    try:
        attr=connection.getAttribute(javax.management.ObjectName(object),"Usage")
    except Exception,e:
        pass   #如果报错,直接不会有数据
        # for branch in ["committed", "used", "max"]:
        #     r_str += 'jmx["{0}",{1}.{2}].[{3}]'.format(object, "Usage", branch,service) + " " + str(0) + "\n"
    else:
        for branch in ["committed","used","max"]:
            r_str += '- jmx.{0}.{1}.{2}.{3}.[{4}]'.format(type_str,name.replace(" ","_"),"Usage",branch,service)+ " " + str(int(attr.contents.get(branch))) + "\n"

return r_str

def _Get_Port(service_path):

cmd = "ps -ef |grep %s |grep -v grep | awk -F'jmxremote.port=' '{print $2}' | awk '{print $1}'"  % (service_path)
#print(cmd)
try:
    ret = os.popen(cmd)
    res = ret.read()
except Exception as e:
    pass

#要不要在这里就做一个状态值出来jmx.jvm_status
#0是正常的,2是没有启动,1是没有开启jmx
if res:
    try:
        ret_status = int(res.splitlines()[0])
    except Exception as e:
        return None,1
    else:
        return ret_status,0
else:
    #print("This service of {0} is not running!".format(service_path.split('/')[-1]))
    return None,2

def SendData(service_prefix_path):
zbx_sender_cmd = “{0} -c {1} -i {2}”
zbx_conf = “/usr/local/services/zabbix-3.0.0/etc/zabbix_agentd.conf”
zbx_sender_file = “/tmp/.zbx_jmx_sender.txt”
zbx_sender = “/usr/local/services/zabbix-3.0.0/bin/zabbix_sender”
r_str = “”

# 启动虚拟机
jpype.startJVM("/usr/local/services/jdk1.8.0_91/jre/lib/amd64/server/libjvm.so")

#当时测试机只有一颗cpu,多线程一开就报错,有点像是jpype的问题,有时执行频繁也会抛错
#或者jpype本身对多线程或者多进程支持不是很好
#而且这里用并发,花费的时间反而更多了,因此下面并发的全部注释掉了


ret = os.popen("ls {0}".format(service_prefix_path))
res = ret.read()

for service in res.splitlines():
    if service:
        service_path = os.path.join(service_prefix_path,service)
        if os.path.islink(service_path):
            #调用_Get_Port函数,获取服务端口
            service_path = service_path + "/"  #可以定位的更准
            port,status = _Get_Port(service_path)
            if status == 0:
                r_str += _Get_Jmx(service,port)
            r_str += "- jmx.jvm_status.[{0}] {1}\n".format(service,status)


# print(r_str)

with open(zbx_sender_file,"w") as f:
    f.write(r_str)

send_ret = os.popen(zbx_sender_cmd.format(zbx_sender, zbx_conf, zbx_sender_file))
#print(zbx_sender_cmd.format(zbx_sender, zbx_conf, zbx_sender_file))
if "failed: 0" in send_ret.read():  #这一步,用一个普通的item来触发,并返回执行结果,1是正常的,0是发送异常
    print(1)
else:
    print(0)

if name == “main”:
if len(sys.argv) == 2 and sys.argv[1]==“discovery”:
discovery(service_prefix_path)
elif len(sys.argv) == 1:
SendData(service_prefix_path)
else:
sys.stderr.write(“Args is wrong!”)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
点赞
————————————————
版权声明:本文为优快云博主「jiangmingfei」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/jiangmingfei/article/details/86524570

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值