继续之前的,之前介绍了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。