JMX(二)----connectors
程序员文章站
2022-06-14 16:09:51
...
继续之前的,之前介绍了MBean server (http://90haofang-163-com.iteye.com/blog/1901416),这次主要简述如果使用JMX技术管理MBean,执行远程操作。通过RMI 作为connectors访问MBeans。这块需要实现服务端和客户端。
首先编写服务端:
1.创建MBean接口类SimpleStandardMBean
package git.git.tudou.manage.jmx.test.connectors; /** * Date: 13-7-9 * Time: 下午4:06 */ public interface SimpleStandardMBean { //获取当前state 值 public String getState(); //修改state public void setState(String state); //获取状态改变次数 public int getNuberChanges(); //重置state和计数器 为初始值 public void reset(); }然后编写MBean实现类SimpleStandard,名字必须是MBean的前缀,不然创建的MBean会出现问题。
package git.git.tudou.manage.jmx.test.connectors; import javax.management.AttributeChangeNotification; import javax.management.NotificationBroadcasterSupport; import java.util.Date; /** * standard MBean支持notification广播功能 * Date: 13-7-9 * Time: 下午4:05 */ public class SimpleStandard extends NotificationBroadcasterSupport implements SimpleStandardMBean{ private String state = "initial stae"; private int nuberChanges=0; private int nbReset = 0; public String getState() { return this.state; } public void setState(String state) { this.state =state; //状态次数加1 this.nuberChanges++; } public int getNuberChanges() { return this.nuberChanges; } public int getNbReset(){ return this.nbReset; } public void reset() { state = "init state" ; this.nuberChanges = 0; nbReset++; /** * 属性变更通知 * 参数说明 * p1:指示通知的来源 在这里就是Hello MBean,用this替代 * p2: 通知消息的序列号 * p3:时间戳 * p4:通知的内容 * p5:变更的属性名 * p6:变更属性的数据类型 * p7: 属性的原值 * p8:属性的新值 */ AttributeChangeNotification acn = new AttributeChangeNotification( this,nbReset,new Date().getTime(),"状态改变次数重置 ,state重置","Nbchanges","int",nuberChanges,0); sendNotification(acn); } }该类继承了NotificationBroadcasterSupport ,实现通知功能在reset方法中发送了一个通知,说明nbchanges被重置,后面在客户端实现一个监听器,来接受通知。
服务端类Server
package git.git.tudou.manage.jmx.test.connectors; import javax.management.*; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import java.rmi.registry.LocateRegistry; import java.util.Scanner; /** * MBean server * encode gbk * Date: 13-7-9 * Time: 下午2:57 */ public class Server { public static void main(String args[]){ try{ Scanner sc = new Scanner(System.in); LocateRegistry.createRegistry(9999); //必须加上这句 不然就报错了 //创建一个MBean server MBeanServer mBeanServer = MBeanServerFactory.createMBeanServer(); //停顿一下 sc.nextInt(); //获得mBeanServer 默认域 String domain = mBeanServer.getDefaultDomain(); System.out.println("mBeanServer defualt domain:"+domain); sc.nextInt(); //创建一个并且注册一个 SimpleStandard MBean String mbeanClassName = "git.git.tudou.manage.jmx.test.connectors.SimpleStandard"; String mbeanObjectNameStr = domain+":type="+mbeanClassName+",name=1"; //在MBean server创建一个bean ObjectName mBeanObjectName = createSimpleMBean(mBeanServer,mbeanClassName,mbeanObjectNameStr); //输出MBean info printMBeanInfo(mBeanServer,mBeanObjectName,mbeanObjectNameStr); sc.nextInt(); //通过MBean server管理MBean 类 manageSimpleMBean(mBeanServer,mBeanObjectName,mbeanClassName); sc.nextInt(); //创建RMI server 工程应用程序调用 JMXServiceURL url = new JMXServiceURL(//url 绑定9999端口,这个可以自定义 "service:jmx:rmi:///jndi/rmi://10.12.145.22:9999/server"); //通过JMXConnectorServerFactory工厂 创建一个RMI连接实例 JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url,null,mBeanServer); //启动RMI连接服务器 connectorServer.start(); System.out.println("RMI 服务已经启动..等待接受请求."); sc.nextInt(); System.out.println("RMI服务关闭.."); connectorServer.stop(); }catch (Exception e){ System.out.println("创建mbean server 异常"+e); } } //管理simpleMBean类 public static void manageSimpleMBean(MBeanServer mbs,ObjectName mbeanObjectName,String mbeanClassName){ try{ //打印最开始的属性值 printSimpleAttributes(mbs,mbeanObjectName); System.out.println("--------------------------------"); System.out.println("修改state值为new state"); Attribute attribute = new Attribute("State","new state"); mbs.setAttribute(mbeanObjectName,attribute); System.out.println("--------------------------------"); System.out.println("修改后的属性值:"); printSimpleAttributes(mbs,mbeanObjectName); //调用reset方法 mbs.invoke(mbeanObjectName,"reset",null,null); System.out.println("重置后所有属性值:"); printSimpleAttributes(mbs,mbeanObjectName); }catch (Exception e){ System.out.println("变更Simple Bean异常"+e); } } /** * 答应出MBean 的属性 * @param mbs * @param mbeanObjectName */ public static void printSimpleAttributes(MBeanServer mbs,ObjectName mbeanObjectName){ System.out.println("属性集合:"); try{ System.out.println("--------------------------------"); System.out.println("SimpleMBean 属性值:"); String state =(String)mbs.getAttribute(mbeanObjectName,"State"); System.out.println("\tstate:"+state); int nuberChanges = (Integer)mbs.getAttribute(mbeanObjectName,"NuberChanges"); System.out.println("\tnuberChanges:"+nuberChanges); }catch (Exception e){ System.out.println("获取mbean属性异常"+e); } } /** * 打印MBean 信息 * @param mbs * @param mbeanObjectName * @param mbeanClassName */ public static void printMBeanInfo(MBeanServer mbs,ObjectName mbeanObjectName,String mbeanClassName){ MBeanInfo info = null; try{ info = mbs.getMBeanInfo(mbeanObjectName); }catch (Exception e){ e.printStackTrace(); System.out.println("获取"+mbeanClassName+"MBeanInfo 失败"); } System.out.println("-------------------------------------------"); System.out.println("类名:\t"+info.getClassName()); System.out.println("类描述:\t"+info.getDescription()); System.out.println("-------------------------------------------"); System.out.println("属性:"); MBeanAttributeInfo[] attributeInfos = info.getAttributes(); for(MBeanAttributeInfo ainfo:attributeInfos){ System.out.println("属性名:\t"+ainfo.getName()); System.out.println("属性描述:\t"+ainfo.getDescription()); System.out.println("属性数据类型\t"+ainfo.getType()); System.out.println("属性是否可读\t"+ainfo.isReadable()); System.out.println("属性是否可写\t"+ainfo.isWritable()); } System.out.println("-------------------------------------------"); System.out.println("构造方法:"); MBeanConstructorInfo[] constructorInfos = info.getConstructors(); for(MBeanConstructorInfo cinfo:constructorInfos){ System.out.println("方法名:\t"+cinfo.getName()); System.out.println("方法描述:"+cinfo.getDescription()); System.out.println("参数个数:\t"+cinfo.getSignature().length); } System.out.println("-------------------------------------------"); System.out.println("方法:"); MBeanOperationInfo[] operationInfos= info.getOperations(); for(MBeanConstructorInfo oinfo:constructorInfos){ System.out.println("方法名:\t"+oinfo.getName()); System.out.println("方法描述:"+oinfo.getDescription()); System.out.println("参数个数:\t"+oinfo.getSignature().length); } System.out.println("-------------------------------------------"); System.out.println("通知:"); MBeanNotificationInfo[] notificationInfos = info.getNotifications(); for(MBeanNotificationInfo ninfo:notificationInfos){ System.out.println("通知名:\t"+ninfo.getName()); System.out.println("通知描述:"+ninfo.getDescription()); String notificationType[] = ninfo.getNotifTypes(); for(int i=0;i<notificationType.length;i++){ System.out.println("type \t"+notificationType[i]); } } } public static ObjectName createSimpleMBean(MBeanServer mbs,String mbeanClassName,String mbeanObjectNameStr){ try{ System.out.println("创建一个MBean "+mbeanClassName+"并在server 注册"); ObjectName mbeanObjectName = ObjectName.getInstance(mbeanObjectNameStr); //MBean server创建一个bean mbs.createMBean(mbeanClassName,mbeanObjectName); return mbeanObjectName; }catch (Exception e){ e.printStackTrace(); System.exit(1); } return null; } }这里类首先创建一个MBserver.然后通过RMI发布出去。供远程应用程序调用,要注意的是
LocateRegistry.createRegistry(9999); //这个很重要。不然直接无发绑定端口号,10.12.145.22是我的server运行的ip地址,应该替换为自己的server运行的ip地址。
创建mbean server 异常java.io.IOException: Cannot bind to URL [rmi://10.12.145.22:9999/server]: javax.naming.ServiceUnavailableException [Root exception is java.rmi.ConnectException: Connection refused to host: 10.12.145.22; nested exception is: java.net.ConnectException: Connection refused: connect] Disconnected from the target VM, address: '127.0.0.1:51094', transport: 'socket'编写好服务端后,不着急写客户端首先可以用jconsole连接下这个服务地址 service:jmx:rmi:///jndi/rmi://10.12.145.22:9999/server 查看MBean,如图
下图是我执行了操作reset,一共执行了2次,所以收到了两个通知,通知的发送定义在SimpleStandardMBean.reset方法中。
接下自己编写客户端来管理MBean,通过RMI连接到MBserver,首先创建一个客户端监听器ClientListener,用来处理来自MBserver的通知
package git.git.tudou.manage.jmx.test.connectors; import javax.management.Notification; import javax.management.NotificationListener; /** * 客户端监听通知监听器 * Date: 13-7-9 * Time: 下午6:54 */ public class ClientListener implements NotificationListener { public void handleNotification(Notification notification, Object handback) { System.out.println("来自MBean server通知:"+notification.getMessage()); } }ClientListener实现NotificationListener接口,实现handleNotification,用来处理来自客户端的通知。
接下来编写客户端Cient类
package git.git.tudou.manage.jmx.test.connectors; import javax.management.Attribute; import javax.management.JMX; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import java.util.Iterator; import java.util.Scanner; import java.util.Set; /** * 创建一个RMI连接客户端 * Date: 13-7-9 * Time: 下午6:56 */ public class Client { public static void main(String[] args) { try{ Scanner sc = new Scanner(System.in); //通过rmi连接远程MBean JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://10.12.145.22:9999/server"); //获得远程JMX连接 JMXConnector jmxConnector = JMXConnectorFactory.connect(url,null); ClientListener listener = new ClientListener(); //获得MBserverConnection MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection(); sc.nextInt(); System.out.println("domain:"); //获Mbean server所有连接 String domains[] = mBeanServerConnection.getDomains(); for(int i=0;i<domains.length;i++){ System.out.println("domains["+i+"]="+domains[i]); } sc.nextInt(); String domain=mBeanServerConnection.getDefaultDomain(); String mbeanObjectNameStr = domain+":type=SimpleStandard,name=2"; //新建一个ObjectName实例 ObjectName mbeanObjectName = ObjectName.getInstance(mbeanObjectNameStr); //创建一个MBean mBeanServerConnection.createMBean("git.git.tudou.manage.jmx.test.connectors.SimpleStandard",mbeanObjectName,null,null); sc.nextInt(); //获取所有的Object名字 Set names = mBeanServerConnection.queryNames(null, null); for (Iterator i= names.iterator();i.hasNext();){ System.out.println("ObjectName"+(ObjectName)i.next()); } sc.nextInt(); //管理MBean 首先变更属性 System.out.println("state:"+(String)mBeanServerConnection.getAttribute(mbeanObjectName,"State")); mBeanServerConnection.setAttribute(mbeanObjectName, new Attribute("State", "new state2")); System.out.println("修改后 state:"+(String)mBeanServerConnection.getAttribute(mbeanObjectName,"State")); //方式二 直接通过MXBean代理 /** * p1 MBean server连接 * p2 MBean name * p3 接口类 * p4 标记是否是NotificationBroadSupport */ SimpleStandardMBean proxy = JMX.newMXBeanProxy(mBeanServerConnection,mbeanObjectName,SimpleStandardMBean.class,true); System.out.println("state 使用proxy:"+proxy.getState()); //添加Notification监听器 System.out.println("添加Notification监听器"); mBeanServerConnection.addNotificationListener(mbeanObjectName,listener,null,null); //mBeanServerConnection 调用SimpleStandard reset方法 mBeanServerConnection.invoke(mbeanObjectName,"reset",null,null); // proxy 调用reset方法 System.out.println("proxy 调用reset方法"); proxy.reset(); sc.nextInt(); Thread.sleep(2000); System.out.println("关闭连接。。。。"); sc.nextInt(); jmxConnector.close(); }catch (Exception e){ System.out.println("连接远程rmi异常,无法获得MBserver 连接"+e); } } }改客户端类首先连接RMIserver 获得一个连接器,从连接器获得MBean server连接mBeanServerConnection,然后通过这个连接来管理远程MBean,可以通过它改变MBean属性,调用其方法
代码里面用两种方式获得MBean,一种是直接通过MBean创建,一种是proxy模式。通过MBean接口实例,调用MBean方法或者修改它的属性。
然后启动服务端,控制台输出:
1 mBeanServer defualt domain:DefaultDomain 1 创建一个MBean git.git.tudou.manage.jmx.test.connectors.SimpleStandard并在server 注册 ------------------------------------------- 类名: git.git.tudou.manage.jmx.test.connectors.SimpleStandard 类描述: Information on the management interface of the MBean ------------------------------------------- 属性: 属性名: State 属性描述: Attribute exposed for management 属性数据类型 java.lang.String 属性是否可读 true 属性是否可写 true 属性名: NuberChanges 属性描述: Attribute exposed for management 属性数据类型 int 属性是否可读 true 属性是否可写 false ------------------------------------------- 构造方法: 方法名: git.git.tudou.manage.jmx.test.connectors.SimpleStandard 方法描述:Public constructor of the MBean 参数个数: 0 ------------------------------------------- 方法: 方法名: git.git.tudou.manage.jmx.test.connectors.SimpleStandard 方法描述:Public constructor of the MBean 参数个数: 0 ------------------------------------------- 通知: 1 属性集合: -------------------------------- SimpleMBean 属性值: state:initial stae nuberChanges:0 -------------------------------- 修改state值为new state -------------------------------- 修改后的属性值: 属性集合: -------------------------------- SimpleMBean 属性值: state:new state nuberChanges:1 重置后所有属性值: 属性集合: -------------------------------- SimpleMBean 属性值: state:init state nuberChanges:0 1 SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. RMI 服务已经启动..等待接受请求.执行客户端main方法,输出:
1 domain: domains[0]=JMImplementation domains[1]=DefaultDomain 1 1 ObjectNameDefaultDomain:type=SimpleStandard,name=2 ObjectNameDefaultDomain:type=git.git.tudou.manage.jmx.test.connectors.SimpleStandard,name=1 ObjectNameJMImplementation:type=MBeanServerDelegate 1 state:initial stae 修改后 state:new state2 state 使用proxy:new state2 添加Notification监听器 proxy 调用reset方法 来自MBean server通知:状态改变次数重置 ,state重置 来自MBean server通知:状态改变次数重置 ,state重置可以最后两行客户端已经收到通话,说明客户端和服务进行了交互,能够通过RMI连接远程的MBean用于应用程序的管理,具体的业务实现类似,回头再写一个和spring整合的JMX。