我有一个程序,它需要动态地(即在运行时)打开一个可用的套接字并在其上启动一个JMX代理。此JMX参数在Java代码中设置,而不是通过命令行设置。这很好用。此后,需要通过Java Visual VM监视(即发出JMX命令等) 远程
程序中的RMI服务器代理在以下框中描述的开箱即用管理: http://download.oracle.com/javase/6/docs/technotes/guides/management/agent.html
我的问题可归纳为: 如何将此类命令行属性设置为系统级别 通过Java代码,以便可以使用远程分析?
-Dcom.sun.management.jmxremote.port=1234
如果通过命令行设置“jmxremote.port”和其他参数, 远程监控工作正常。我试图通过Java找到一种方法 而不是通过命令行。
程序无法通过命令行指定端口,因为必须在运行时计算出新的可用端口。
该过程需要远程监控,并且在本地工作正常。 如果未在命令行中指定以下参数,则Java Visual VM不会连接到该进程。
-Dcom.sun.management.jmxremote.port=1234
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djava.rmi.server.hostname=10.0.0.128
我试过了。
System.setProperty("com.sun.management.jmxremote.port",Integer.toString(port));
这是在启动JMXConnectorServer之前在程序中完成的第一件事。不幸的是,它没有被认可。只有运行时属性(即通过命令行指定,由Java Visual VM识别JMX连接)。
还遇到了可以从java集合类中提取属性的方法但无法到达如何跟踪属性“com.sun.management.jmxremote.port =”
public static void setEnv(Map<String, String> newenv) throws Exception {
Class[] classes = Collections.class.getDeclaredClasses();
Map<String, String> env = System.getenv();
for(Class cl : classes) {
if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
Object obj = field.get(env);
Map<String, String> map = (Map<String, String>) obj;
//map.clear();
map.putAll(newenv);
}
}
}
任何帮助将不胜感激!
答案 0 :(得分:10)
kbec的回答显示了方式,但对我没有用 - 但是通过查看this post我能够修改它并获得有效的解决方案。
public static String loadJMXAgent(int port) throws IOException,
AttachNotSupportedException, AgentLoadException,
AgentInitializationException {
String name = ManagementFactory.getRuntimeMXBean().getName();
VirtualMachine vm = VirtualMachine.attach(name.substring(0,
name.indexOf('@')));
String lca = vm.getAgentProperties().getProperty(
"com.sun.management.jmxremote.localConnectorAddress");
if (lca == null) {
Path p = Paths.get(System.getProperty("java.home")).normalize();
if (!"jre".equals(p.getName(p.getNameCount() - 1).toString()
.toLowerCase())) {
p = p.resolve("jre");
}
File f = p.resolve("lib").resolve("management-agent.jar").toFile();
if (!f.exists()) {
throw new IOException("Management agent not found");
}
String options = String.format("com.sun.management.jmxremote.port=%d, " +
"com.sun.management.jmxremote.authenticate=false, " +
"com.sun.management.jmxremote.ssl=false", port);
vm.loadAgent(f.getCanonicalPath(), options);
lca = vm.getAgentProperties().getProperty(
"com.sun.management.jmxremote.localConnectorAddress");
}
vm.detach();
return lca;
}
这适用于Eclipse,但是在命令行中使用它是另一回事 - 这里有一些讨论Why does using the Java Attach API fail on linux? (even though maven build completes) 但我发现在我的类路径中添加$ JAVA_HOME / lib / tools.jar解决了这个问题。
答案 1 :(得分:4)
你正在以一种错误的方式解决这个问题。当您的代码被调用时,您错过了这些属性产生任何影响的机会。
您需要创建一个RmiRegistry,然后创建一个链接到平台MBeanServer的JMXConnectorServer,如下所示:
private void createJmxConnectorServer() throws IOException {
LocateRegistry.createRegistry(1234);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://localhost/jndi/rmi://localhost:1234/jmxrmi");
JMXConnectorServer svr = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
svr.start();
}
答案 2 :(得分:2)
如果未指定任何jmxremote
env作为运行参数,则未加载JMX管理代理。您可以尝试将其用于动态加载:
public static String loadJMXAgent(int port) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
System.setProperty("com.sun.management.jmxremote.port", Integer.toString(port));
String name = ManagementFactory.getRuntimeMXBean().getName();
VirtualMachine vm = VirtualMachine.attach(name.substring(0, name.indexOf('@')));
String lca = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
if (lca == null) {
Path p = Paths.get(System.getProperty("java.home")).normalize();
if (!"jre".equals(p.getName(p.getNameCount()-1).toString().toLowerCase())) p = p.resolve("jre");
File f = p.resolve("lib").resolve("management-agent.jar").toFile();
if (!f.exists()) throw new IOException("Management agent not found");
vm.loadAgent(f.getCanonicalPath(), "com.sun.management.jmxremote");
lca = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
}
vm.detach();
return lca;
}
您必须包含jdk/lib/tools.jar
答案 3 :(得分:1)
我尝试了一些方法来指定java代码中的jmxremote端口,以便在特定端口上建立连接并找出以下内容:
如果指定了jmxremote arg: 在我的代码修改必要的jmxremote System.properties之前,平台mbean服务器由JVM启动。每个mbean服务器都有一个自己的bean注册表。平台和JVM mbeans无法以其他方式向它注册自己的bean。
设置jmx端口属性后,可以创建备用mbean服务器。那将监听你指定的正确的jmx端口。
这样您就可以选择平台服务器:
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
这样你自己的:
System.setProperty("com.sun.management.jmxremote.port","9991");
//...
MBeanServer mbsCustom=MBeanServerFactory.createMBeanServer();
还要考虑linux有 loopback接口,所以你应该明确指定正确的主机名来监听。
根据手册,这是不推荐使用其他MBeanServer而不是平台,但我可以想象一些命令行选项不是你可以启动服务器的方式。
答案 4 :(得分:0)
System.setProperty()
与-D
命令行选项相同。但显然你必须尽早调用它,以便在读取属性之前设置它。
答案 5 :(得分:0)
这对我有用。参考Oracle JMX Tutorial。我假设你已经知道如何在下面的例子中使用SimpleMXBean。
package sample;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.util.HashMap;
import java.util.Map;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
public class MBServerTest {
public static void loadJMXAgent(int port, MBeanServer mbs) throws IOException {
LocateRegistry.createRegistry(port);
System.out.println("Initialize the environment map");
Map<String,Object> env = new HashMap<String,Object>();
env.put("com.sun.management.jmxremote.authenticate", "false");
env.put("com.sun.management.jmxremote.ssl", "false");
System.out.println("Create an RMI connector server");
JMXServiceURL url =
new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:"+port+"/jmxrmi");
JMXConnectorServer cs =
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
// Start the RMI connector server.
//
System.out.println("Start the RMI connector server");
cs.start();
}
public static void main(String[] args) throws Exception {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
loadJMXAgent(1199,mbs);
SimpleStandard cache = new SimpleStandard();
ObjectName name = new ObjectName(
"org.javalobby.tnt.jmx:type=ApplicationCacheMBean");
mbs.registerMBean(cache, name);
imitateActivity(cache);
}
private static void imitateActivity(SimpleStandard cache) {
while (true) {
try {
cache.cacheObject("hello");
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}