前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MBean与JMX源码分析

MBean与JMX源码分析

作者头像
歪歪梯
发布2020-06-19 16:18:19
1.5K0
发布2020-06-19 16:18:19
举报
文章被收录于专栏:歪歪梯Club

JMX

JMX(java Management Exetensions)在Java编程语言中定义了应用程序以及网络管理和监控的体系结构、设计模式、应用程序接口以及服务。 通常使用JMX来监控系统的运行状态或管理系统的某些方面,比如清空缓存、重新加载配置文件等 优点是可以非常容易的使应用程序被管理 伸缩性的架构使每个JMX Agent Service可以很容易的放入到Agent中,每个JMX的实现都提供几个核心的Agent Service,你也可以自己编写服务,服务可以很容易的部署,取消部署。 主要作用是提供接口,允许有不同的实现 简单来说,jmx是一个用来管理javaBean并可以进行监控的扩展规范,结合MBeanServer、rmi与http等可以作为一个服务监控和提供中心

MBeanServer

MBeanServer是JMX代理的核心组件。 它是在代理中向管理操作公开的对象的注册表。 向MBeanServer注册的任何对象都对管理应用程序可见。 MBeanServer仅公开MBean的管理接口,而不是它的直接对象引用。 您要从代理的Java VM外部管理的任何资源都必须在MBeanServer中注册为MBean。 MBeanServer还提供标准化接口,用于访问同一Java VM中的MBean,为本地对象提供操作可管理资源的所有好处。 需要注意的是,一般不使用MBeanServerFactory.createMBeanServer(),使用ManagementFactory.getPlatformMBeanServer() 后者在当前尚未在ManagementFactory中注册静态成员MBeanServer时,会先使用MBeanServerFactory.createMBeanServer()创建一个MBeanServer并将其注册到静态成员,后续每次调用会直接返回该成员实例 并且ManagementFactory.getPlatformMBeanServer()方法在第一次调用createMBeanServer()实例化MBeanServer以后,会读取PlatformComponent枚举的枚举值,将一些系统必要的MBean注册到MBeanServer JConsole监控的MBeanServer就是在ManagementFactory中注册的静态成员MBeanServe 所以如果没有特殊配置的MBeanServer,jconsole是不会监控的

Agent

Java Management Extensions(JMX)Agent是一个在Java虚拟机(Java VM)中运行的管理实体. 充当MBean和管理应用程序(JConsole等)之间的联络人 Agent只是一个规范,一般会封装我们创建和启动MBeanServer以及注册MBean的过程在一个Agent行为里,方便启动

Agent Service

Agent Service是可以对MBeanServer中注册的MBean执行管理操作的对象。 通过将管理智能包含在代理中,JMX可帮助您构建更强大的管理解决方案。 Agent Service通常也是MBean,允许通过MBeanServer控制它们及其功能。 JMX规范定义了以下Agent Service: 通过管理applet(m-let)服务的动态类加载检索并实例化从网络动态下载的新类和本机库。 监视器观察MBean属性的数字或字符串值,并可以向其他对象通知几种类型的更改。 定时器提供调度机制,并且可以以预定间隔发送通知。 关系服务定义MBean之间的关联并维护关系的一致性。

Protocol Adaptors and Connectors

Protocol adaptors and connectors使代理可从远程(通过rmi或者http等协议)管理应用程序访问。 它们通过在MBean服务器中实例化和注册的MBean的特定协议提供视图。 它们使Java VM外部的管理应用程序能够: 获取或设置现有MBean的属性 对现有MBean执行操作 实例化并注册新的MBean 注册并接收MBean发出的通知 因此,要使JMX代理易于管理,它必须至少包含一个协议适配器或连接器。 Java SE平台包括标准RMI连接器。Agent可以包括任意数量的协议适配器和连接器,允许通过不同的协议同时远程管理和监视它

Protocol Adaptors

Protocol Adaptors通过给定协议提供JMX代理的管理视图。 它们将MBean和MBean服务器的操作调整为给定协议中的表示, 并可能调整为不同的信息模型,例如SNMP。Java SE平台不包括任何协议适配器作为标准。 连接到Protocol Adaptors的管理应用程序通常特定于给定的协议。 这通常是依赖于特定管理协议的传统管理解决方案的情况。 它们不是通过MBean服务器的远程表示来访问JMX代理,而是通过映射到MBeanServer的操作来访问JMX代理。

Connectors

Connectors用于将代理与为JMX技术启用的远程管理应用程序连接,即使用JMX规范的分布式服务开发的管理应用程序。 这种通信涉及Agent中的连接器服务器和管理器中的连接器客户端。 这些组件以特定协议的方式透明地传递管理操作。 JMX Remote API为MBeanServer提供远程接口,管理应用程序可以通过该接口执行操作。 Connectors特定于给定协议,但管理应用程序可以无差别地使用任何Connectors,因为它们具有相同的远程接口。

MBean

描述一个可管理的资源。是一个java对象,遵循以下一些规则: 1.必须是公用的,非抽象的类 2.必须有至少一个公用的构造器 3.必须实现它自己的相应的MBean接口或者实现javax.management.DynamicMBean接口 4.可选的,一个MBean可以实现javax.management.NotificationBroadcaster接口MBean的类型 MBean使用ObjectName以keyValue形式注册到MBeanServer上

Standard MBean

标准MBean,也是最简单的MBean,通过类实现的接口名称来识别MBean 接口名称以MBean结尾,实现类需要匹配接口名称的前半部分 ``` public interface YytMBean{ public void setName(String name); public String getName(); }

代码语言:javascript
复制
```
public class Yyt implements YytMBean{
    private String name;
    @Override
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String getName() {
        return name;
    }
}
代码语言:javascript
复制
public class YytAgent{
    private String domainName;
    private int rmiPort;
    public YytAgent(String domainName,int rmiPort) {
        this.domainName = domainName;
        this.rmiPort = rmiPort;
    }
    public void start() throws MalformedObjectNameException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, IOException {
        //MBeanServerFactory.createMBeanServer();   //don't use this
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();   //use this instead

        YytMBean yytMBean = new Yyt();
        ObjectName objectName = new ObjectName(domainName+":name=Yyt");
        mBeanServer.registerMBean(yytMBean, objectName);

        LocateRegistry.createRegistry(rmiPort);//开启rmi端口监听,在jmxConnectorServer.start()时会根据serviceUrl建立一个rmiServer监听该rmi端口

        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:"+rmiPort+"/"+domainName);
        JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mBeanServer);
        jmxConnectorServer.start();
        //开启jmx协议的监控服务器,因为java自带rmi的工具和依赖,可以直接开启,通过jconsole等支持jmx协议的客户端可以监控MBeanServer    
    }
}

使用JConsole工具监控

JConsole是java自带的监控程序,独立jre中没有,jdk下的jre具备

运行java应用时带上参数 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false 关闭ssl和密码校验,开启远程监控端口8999,这时可以直接使用jconsole进行链接

或者在jdk\jre\lib\management目录下,复制一份jmxremote.password.template的文件,改名去掉.template后缀,并去掉文件末尾两行示例的用户名和密码注释,修改密码为自己想要的 运行加上参数 -Djava.rmi.server.hostname=应用所在服务器的ip或者域名 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=监控端口 -Dcom.sun.management.jmxremote.rmi.port=rmi监控端口(一般与监控端口保持一致) -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=false 这时使用JConsole链接会提示需要输入用户名和密码

在命令行输入jconsole运行jconsole程序

jconsole] 在jconsole的界面,因为是本地,直接选择本地进程,进入监控页面 点击导航栏的MBean即可看到我们注册的MBean在列表中

jconsole 还可以对Bean的属性值进行查看和设置

jconsole]

源码分析

ManagementFactory.getPlatformMBeanServer()

代码语言:javascript
复制
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            Permission perm = new MBeanServerPermission("createMBeanServer");
            sm.checkPermission(perm);
        }

        if (platformMBeanServer == null) {
            platformMBeanServer = MBeanServerFactory.createMBeanServer();
            for (PlatformComponent pc : PlatformComponent.values()) {
                List<? extends PlatformManagedObject> list =
                    pc.getMXBeans(pc.getMXBeanInterface());
                for (PlatformManagedObject o : list) {
                    // Each PlatformComponent represents one management
                    // interface. Some MXBean may extend another one.
                    // The MXBean instances for one platform component
                    // (returned by pc.getMXBeans()) might be also
                    // the MXBean instances for another platform component.
                    // e.g. com.sun.management.GarbageCollectorMXBean
                    //
                    // So need to check if an MXBean instance is registered
                    // before registering into the platform MBeanServer
                    if (!platformMBeanServer.isRegistered(o.getObjectName())) {
                        addMXBean(platformMBeanServer, o);
                    }
                }
            }
            HashMap<ObjectName, DynamicMBean> dynmbeans =
                    ManagementFactoryHelper.getPlatformDynamicMBeans();
            for (Map.Entry<ObjectName, DynamicMBean> e : dynmbeans.entrySet()) {
                addDynamicMBean(platformMBeanServer, e.getValue(), e.getKey());
            }
            for (final PlatformManagedObject o :
                                       ExtendedPlatformComponent.getMXBeans()) {
                if (!platformMBeanServer.isRegistered(o.getObjectName())) {
                    addMXBean(platformMBeanServer, o);
                }
            }
        }
        return platformMBeanServer;

ManagementFactory中定义一个platformMBeanServer成员来装载创建好的MBeanServer 在执行该方法的第一步会先校验当前安全权限 在platformMBeanServer尚未绑定一个实例时,会先使用MBeanServerFactory.createMBeanServer()实例一个MBeanServer对象 接下来主要是读取PlatformComponent.values()以及别的枚举类和helper类的值和成员,将系统预设的MBean注册到MBeanServer

代码语言:javascript
复制
enum PlatformComponent {

    /**
     * Class loading system of the Java virtual machine.
     */
    CLASS_LOADING(
        "java.lang.management.ClassLoadingMXBean",
        "java.lang", "ClassLoading", defaultKeyProperties(),
        true, // singleton
        new MXBeanFetcher<ClassLoadingMXBean>() {
            public List<ClassLoadingMXBean> getMXBeans() {
                return Collections.singletonList(ManagementFactoryHelper.getClassLoadingMXBean());
            }
        }),

    /**
     * Compilation system of the Java virtual machine.
     */
    COMPILATION(
        "java.lang.management.CompilationMXBean",
        "java.lang", "Compilation", defaultKeyProperties(),
        true, // singleton
        new MXBeanFetcher<CompilationMXBean>() {
            public List<CompilationMXBean> getMXBeans() {
                CompilationMXBean m = ManagementFactoryHelper.getCompilationMXBean();
                if (m == null) {
                   return Collections.emptyList();
                } else {
                   return Collections.singletonList(m);
                }
            }
        }),
    .....
    }

MBeanServerFactory.createMBeanServer()

代码语言:javascript
复制
    public static MBeanServer createMBeanServer() {
        return createMBeanServer(null);
    }
     public static MBeanServer createMBeanServer(String domain)  {
        checkPermission("createMBeanServer");

        final MBeanServer mBeanServer = newMBeanServer(domain);
        addMBeanServer(mBeanServer);
        return mBeanServer;
    }
    public static MBeanServer newMBeanServer(String domain)  {
        checkPermission("newMBeanServer");

        // Get the builder. Creates a new one if necessary.
        //
        final MBeanServerBuilder mbsBuilder = getNewMBeanServerBuilder();
        // Returned value cannot be null.  NullPointerException if violated.

        synchronized(mbsBuilder) {
            final MBeanServerDelegate delegate  =
                    mbsBuilder.newMBeanServerDelegate();
            if (delegate == null) {
                final String msg =
                        "MBeanServerBuilder.newMBeanServerDelegate() " +
                        "returned null";
                throw new JMRuntimeException(msg);
            }
            final MBeanServer mbeanServer =
                    mbsBuilder.newMBeanServer(domain,null,delegate);
            if (mbeanServer == null) {
                final String msg =
                        "MBeanServerBuilder.newMBeanServer() returned null";
                throw new JMRuntimeException(msg);
            }
            return mbeanServer;
        }
    }

createMBeanServer方法会调用newMBeanServer方法实例化MBeanServer newMBeanServer方法中会先使用MBeanServerBuilder.newMBeanServerDelegate()实例化一个MBeanServerBuilder 再使用MBeanServerBuilder.newMBeanServer(domain,null,delegate)方法实例化一个MBeanServer

代码语言:javascript
复制
        public MBeanServer newMBeanServer(String  defaultDomain,
                                      MBeanServer  outer,
                                      MBeanServerDelegate delegate) {
        // By default, MBeanServerInterceptors are disabled.
        // Use com.sun.jmx.mbeanserver.MBeanServerBuilder to obtain
        // MBeanServers on which MBeanServerInterceptors are enabled.
        return JmxMBeanServer.newMBeanServer(defaultDomain,outer,delegate,
                                             false);
    }

newMBeanServer方法实际实例化并返回的是一个JmxMBeanServer对象

代码语言:javascript
复制
    //in JmxMBeanServer
    public static MBeanServer newMBeanServer(String defaultDomain,
                                             MBeanServer outer,
                                             MBeanServerDelegate delegate,
                                             boolean interceptors) {
        // Determine whether to use fair locking for the repository.
        // Default is true.
        final boolean fairLock = DEFAULT_FAIR_LOCK_POLICY;

        checkNewMBeanServerPermission();

        // This constructor happens to disregard the value of the interceptors
        // flag - that is, it always uses the default value - false.
        // This is admitedly a bug, but we chose not to fix it for now
        // since we would rather not have anybody depending on the Sun private
        // interceptor APIs - which is most probably going to be removed and
        // replaced by a public (javax) feature in the future.
        //
        return new JmxMBeanServer(defaultDomain,outer,delegate,null,
                                  interceptors,fairLock);
    }

    JmxMBeanServer(String domain, MBeanServer outer,
                   MBeanServerDelegate    delegate,
                   MBeanInstantiator      instantiator,
                   boolean                interceptors,
                   boolean                fairLock)  {

        if (instantiator == null) {
            final ModifiableClassLoaderRepository
                clr = new ClassLoaderRepositorySupport();
            instantiator = new MBeanInstantiator(clr);
        }

        final MBeanInstantiator fInstantiator = instantiator;
        this.secureClr = new
            SecureClassLoaderRepository(AccessController.doPrivileged(new PrivilegedAction<ClassLoaderRepository>() {
                @Override
                public ClassLoaderRepository run() {
                    return fInstantiator.getClassLoaderRepository();
                }
            })
        );
        if (delegate == null)
            delegate = new MBeanServerDelegateImpl();
        if (outer == null)
            outer = this;

        this.instantiator = instantiator;
        this.mBeanServerDelegateObject = delegate;
        this.outerShell   = outer;

        final Repository repository = new Repository(domain);
        this.mbsInterceptor =
            new DefaultMBeanServerInterceptor(outer, delegate, instantiator,
                                              repository);
        this.interceptorsEnabled = interceptors;
        initialize();
    }

实例化一个Repository并聚合到新生成的一个DefaultMBeanServerInterceptor Repository就是MBeanServer存放MBean的容器类,其内部维护一个Map<string,map> domainTb的成员 此处由于是第一次实例化MBeanServer,传入的domain为null,最终会使用ServiceName的静态的成员值DefaultDomain作为默认域</string,map

代码语言:javascript
复制
public class ServiceName {

    /**
     * The default domain.
     * <BR>
     * The value is <CODE>DefaultDomain</CODE>.
     */
    public static final String DOMAIN = "DefaultDomain";
    .....
}

NamedObject类是聚合ObjectName和DynamicMBean的辅助类

代码语言:javascript
复制
public class NamedObject  {


    /**
     * Object name.
     */
    private final ObjectName name;

    /**
     * Object reference.
     */
    private final DynamicMBean object;
    .....
}

后续的注册bean和获取bean等方法实现最终都是由mbsInterceptor成员去操作

LocateRegistry.createRegistry

创建注册中心并开启端口监听,返回创建的RegistryImpl实例 源码分析请参照本人的另一篇博客——java-rmi源码解析java-rmi源码解析

MBeanServer.registerMBean

在JmxMBeanServer中的实现

代码语言:javascript
复制
    public ObjectInstance registerMBean(Object object, ObjectName name)
        throws InstanceAlreadyExistsException, MBeanRegistrationException,
               NotCompliantMBeanException  {

        return mbsInterceptor.registerMBean(object, cloneObjectName(name));
    }

上面讲到,默认的MBeanServer的mbsInterceptor成员由new DefaultMBeanServerInterceptor(outer, delegate, instantiator,repository)实例化

代码语言:javascript
复制
    public ObjectInstance registerMBean(Object object, ObjectName name)
        throws InstanceAlreadyExistsException, MBeanRegistrationException,
        NotCompliantMBeanException  {

        // ------------------------------
        // ------------------------------
        Class<?> theClass = object.getClass();

        Introspector.checkCompliance(theClass);

        final String infoClassName = getNewMBeanClassName(object);

        checkMBeanPermission(infoClassName, null, name, "registerMBean");
        checkMBeanTrustPermission(theClass);

        return registerObject(infoClassName, object, name);
    }
    private ObjectInstance registerObject(String classname,
                                          Object object, ObjectName name)
        throws InstanceAlreadyExistsException,
               MBeanRegistrationException,
               NotCompliantMBeanException {

        if (object == null) {
            final RuntimeException wrapped =
                new IllegalArgumentException("Cannot add null object");
            throw new RuntimeOperationsException(wrapped,
                        "Exception occurred trying to register the MBean");
        }

        DynamicMBean mbean = Introspector.makeDynamicMBean(object);

        return registerDynamicMBean(classname, mbean, name);
    }    

DefaultMBeanServerInterceptor会将要注册的MBean封装为DynamicMBean,调用registerDynamicMBean进行注册

代码语言:javascript
复制
    private ObjectInstance registerDynamicMBean(String classname,
                                                DynamicMBean mbean,
                                                ObjectName name)
        throws InstanceAlreadyExistsException,
               MBeanRegistrationException,
               NotCompliantMBeanException {


        name = nonDefaultDomain(name);

        if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
            MBEANSERVER_LOGGER.logp(Level.FINER,
                    DefaultMBeanServerInterceptor.class.getName(),
                    "registerMBean", "ObjectName = " + name);
        }

        ObjectName logicalName = preRegister(mbean, server, name);

        // preRegister returned successfully, so from this point on we
        // must call postRegister(false) if there is any problem.
        boolean registered = false;
        boolean registerFailed = false;
        ResourceContext context = null;

        try {
            if (mbean instanceof DynamicMBean2) {
                try {
                    ((DynamicMBean2) mbean).preRegister2(server, logicalName);
                    registerFailed = true;  // until we succeed
                } catch (Exception e) {
                    if (e instanceof RuntimeException)
                        throw (RuntimeException) e;
                    if (e instanceof InstanceAlreadyExistsException)
                        throw (InstanceAlreadyExistsException) e;
                    throw new RuntimeException(e);
                }
            }

            if (logicalName != name && logicalName != null) {
                logicalName =
                        ObjectName.getInstance(nonDefaultDomain(logicalName));
            }

            checkMBeanPermission(classname, null, logicalName, "registerMBean");

            if (logicalName == null) {
                final RuntimeException wrapped =
                    new IllegalArgumentException("No object name specified");
                throw new RuntimeOperationsException(wrapped,
                            "Exception occurred trying to register the MBean");
            }

            final Object resource = getResource(mbean);

            // Register the MBean with the repository.
            // Returns the resource context that was used.
            // The returned context does nothing for regular MBeans.
            // For ClassLoader MBeans the context makes it possible to register these
            // objects with the appropriate framework artifacts, such as
            // the CLR, from within the repository lock.
            // In case of success, we also need to call context.done() at the
            // end of this method.
            //
            context = registerWithRepository(resource, mbean, logicalName);


            registerFailed = false;
            registered = true;

        } finally {
            try {
                postRegister(logicalName, mbean, registered, registerFailed);
            } finally {
                if (registered && context!=null) context.done();
            }
        }
        return new ObjectInstance(logicalName, classname);
    }

registerDynamicMBean方法最终调用registerWithRepository方法将DynamicMBean对象注册到Repository里

代码语言:javascript
复制
    private ResourceContext registerWithRepository(
            final Object resource,
            final DynamicMBean object,
            final ObjectName logicalName)
            throws InstanceAlreadyExistsException,
            MBeanRegistrationException {

        // Creates a registration context, if needed.
        //
        final ResourceContext context =
                makeResourceContextFor(resource, logicalName);


        repository.addMBean(object, logicalName, context);
        // May throw InstanceAlreadyExistsException

        // ---------------------
        // Send create event
        // ---------------------
        if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
            MBEANSERVER_LOGGER.logp(Level.FINER,
                    DefaultMBeanServerInterceptor.class.getName(),
                    "addObject", "Send create notification of object " +
                    logicalName.getCanonicalName());
        }

        sendNotification(
                MBeanServerNotification.REGISTRATION_NOTIFICATION,
                logicalName);

        return context;
    }

前面已经讲过在创建DefaultMBeanServerInterceptor实例时,会实例化一个repository对象并装载到成员 注册bean会调用repository的addMBean方法

代码语言:javascript
复制
    public void addMBean(final DynamicMBean object, ObjectName name,
            final RegistrationContext context)
        throws InstanceAlreadyExistsException {

        if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
            MBEANSERVER_LOGGER.logp(Level.FINER, Repository.class.getName(),
                    "addMBean", "name = " + name);
        }

        // Extract the domain name.
        String dom = name.getDomain().intern();
        boolean to_default_domain = false;

        // Set domain to default if domain is empty and not already set
        if (dom.length() == 0)
            name = Util.newObjectName(domain + name.toString());

        // Do we have default domain ?
        if (dom == domain) {  // ES: OK (dom & domain are interned)
            to_default_domain = true;
            dom = domain;
        } else {
            to_default_domain = false;
        }

        // Validate name for an object
        if (name.isPattern()) {
            throw new RuntimeOperationsException(
             new IllegalArgumentException("Repository: cannot add mbean for " +
                                          "pattern name " + name.toString()));
        }

        lock.writeLock().lock();
        try {
            // Domain cannot be JMImplementation if entry does not exist
            if ( !to_default_domain &&
                    dom.equals("JMImplementation") &&
                    domainTb.containsKey("JMImplementation")) {
                throw new RuntimeOperationsException(
                        new IllegalArgumentException(
                        "Repository: domain name cannot be JMImplementation"));
            }

            // If domain does not already exist, add it to the hash table
            final Map<String,NamedObject> moiTb = domainTb.get(dom);
            if (moiTb == null) {
                addNewDomMoi(object, dom, name, context);
                return;
            } else {
                // Add instance if not already present
                String cstr = name.getCanonicalKeyPropertyListString();
                NamedObject elmt= moiTb.get(cstr);
                if (elmt != null) {
                    throw new InstanceAlreadyExistsException(name.toString());
                } else {
                    nbElements++;
                    addMoiToTb(object,name,cstr,moiTb,context);
                }
            }

        } finally {
            lock.writeLock().unlock();
        }
    }
    private void addMoiToTb(final DynamicMBean object,
            final ObjectName name,
            final String key,
            final Map<String,NamedObject> moiTb,
            final RegistrationContext context) {
        registering(context);
        moiTb.put(key,new NamedObject(name, object));
    }

domainTb成员就是前面说过的repository中存储MBean的Map,类型是Map<string,map> add方法里会通过writelock进行同步 如果获取到的moiTb,代表该MBean的域名尚未加入domainTb,会将域名加入map并将MBean也加入</string,map

JmxMBeanServer

JmxMBeanServer 不提供直接返回MBean对象的方法 但提供了很多行为如 setAttributes,getAttribute,invoke等方法,根据ObjectName参数进行MBean识别 调用成员的DefaultMBeanServerInterceptor实例去执行对应的修改属性,获取属性值,执行方法等行为 DefaultMBeanServerInterceptor最终是获取repository中的MBean去执行对应行为 通过getObjectInstance可以获得该MBean的类名和域名信息的封装对象

代码语言:javascript
复制
    public ObjectInstance getObjectInstance(ObjectName name)
        throws InstanceNotFoundException {

        return mbsInterceptor.getObjectInstance(cloneObjectName(name));
    }
    public Object getAttribute(ObjectName name, String attribute)
        throws MBeanException, AttributeNotFoundException,
               InstanceNotFoundException, ReflectionException {

        return mbsInterceptor.getAttribute(cloneObjectName(name), attribute);
    }
    public void setAttribute(ObjectName name, Attribute attribute)
        throws InstanceNotFoundException, AttributeNotFoundException,
               InvalidAttributeValueException, MBeanException,
               ReflectionException  {

        mbsInterceptor.setAttribute(cloneObjectName(name),
                                    cloneAttribute(attribute));
    }
    public Object invoke(ObjectName name, String operationName,
                         Object params[], String signature[])
        throws InstanceNotFoundException, MBeanException,
               ReflectionException {
        return mbsInterceptor.invoke(cloneObjectName(name), operationName,
                                     params, signature);
    }

JMXConnectorServerFactory.newJMXConnectorServer

该方法创建一个JMXConnectorServer,将MBeanServer和JMXServiceURL传递过去 最终url、MbeanServer等对象会被聚合到JMXConnectorServer,作为成员

代码语言:javascript
复制
    public static JMXConnectorServer
        newJMXConnectorServer(JMXServiceURL serviceURL,
                              Map<String,?> environment,
                              MBeanServer mbeanServer)
            throws IOException {
        Map<String, Object> envcopy;
        if (environment == null)
            envcopy = new HashMap<String, Object>();
        else {
            EnvHelp.checkAttributes(environment);
            envcopy = new HashMap<String, Object>(environment);
        }

        final Class<JMXConnectorServerProvider> targetInterface =
                JMXConnectorServerProvider.class;
        final ClassLoader loader =
            JMXConnectorFactory.resolveClassLoader(envcopy);
        final String protocol = serviceURL.getProtocol();
        final String providerClassName = "ServerProvider";

        JMXConnectorServerProvider provider =
            JMXConnectorFactory.getProvider(serviceURL,
                                            envcopy,
                                            providerClassName,
                                            targetInterface,
                                            loader);

        IOException exception = null;
        if (provider == null) {
            // Loader is null when context class loader is set to null
            // and no loader has been provided in map.
            // com.sun.jmx.remote.util.Service class extracted from j2se
            // provider search algorithm doesn't handle well null classloader.
            if (loader != null) {
                try {
                    JMXConnectorServer connection =
                        getConnectorServerAsService(loader,
                                                    serviceURL,
                                                    envcopy,
                                                    mbeanServer);
                    if (connection != null)
                        return connection;
                } catch (JMXProviderException e) {
                    throw e;
                } catch (IOException e) {
                    exception = e;
                }
            }
            provider =
                JMXConnectorFactory.getProvider(
                    protocol,
                    PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
                    JMXConnectorFactory.class.getClassLoader(),
                    providerClassName,
                    targetInterface);
        }

        if (provider == null) {
            MalformedURLException e =
                new MalformedURLException("Unsupported protocol: " + protocol);
            if (exception == null) {
                throw e;
            } else {
                throw EnvHelp.initCause(e, exception);
            }
        }

        envcopy = Collections.unmodifiableMap(envcopy);

        return provider.newJMXConnectorServer(serviceURL,
                                              envcopy,
                                              mbeanServer);
    }

JMXConnectorServerFactory的newJMXConnectorServer最终是返回由provider生产的JMXConnectorServer provider是接口,在jdk里对于该方法有两个实现类,一个是返回基于rmi协议,另一个是返回基于iiop协议的

代码语言:javascript
复制
    //ServiceProvider for rmi
    public JMXConnectorServer newJMXConnectorServer(JMXServiceURL serviceURL,
                                                    Map<String,?> environment,
                                                    MBeanServer mbeanServer)
            throws IOException {
        if (!serviceURL.getProtocol().equals("rmi")) {
            throw new MalformedURLException("Protocol not rmi: " +
                                            serviceURL.getProtocol());
        }
        return new RMIConnectorServer(serviceURL, environment, mbeanServer);
    }    

需要留意的是,在serviceProvider中调用生成RMIConnectorServer时,最终会调用传递(RMIServerImpl) null为形参参数的构造函数构造 也就是生成的RMIConnectorServer中是没有指定服务端对象的 RMIServerImpl会在JMXConnectorServer调用start方法后结合前面聚合到JMXConnectorServer中的rmi的url去实例化

代码语言:javascript
复制
    public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment,
                              MBeanServer mbeanServer)
            throws IOException {
        this(url, environment, (RMIServerImpl) null, mbeanServer);
    }
    public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment,
                              RMIServerImpl rmiServerImpl,
                              MBeanServer mbeanServer)
            throws IOException {
        super(mbeanServer);

        if (url == null) throw new
            IllegalArgumentException("Null JMXServiceURL");
        if (rmiServerImpl == null) {
            final String prt = url.getProtocol();
            if (prt == null || !(prt.equals("rmi") || prt.equals("iiop"))) {
                final String msg = "Invalid protocol type: " + prt;
                throw new MalformedURLException(msg);
            }
            final String urlPath = url.getURLPath();
            if (!urlPath.equals("")
                && !urlPath.equals("/")
                && !urlPath.startsWith("/jndi/")) {
                final String msg = "URL path must be empty or start with " +
                    "/jndi/";
                throw new MalformedURLException(msg);
            }
        }

        if (environment == null)
            this.attributes = Collections.emptyMap();
        else {
            EnvHelp.checkAttributes(environment);
            this.attributes = Collections.unmodifiableMap(environment);
        }

        this.address = url;
        this.rmiServerImpl = rmiServerImpl;
    }

jmxConnectorServer.start()

启动JMX链接服务器,该过程会完成rmiServerImpl的实例化(开启监听rmi服务端口) 所以如果前面没有调用LocateRegistry.createRegistry(rmiPort)对端口进行注册,会导致报错

代码语言:javascript
复制
public synchronized void start() throws IOException {
        final boolean tracing = logger.traceOn();

        if (state == STARTED) {
            if (tracing) logger.trace("start", "already started");
            return;
        } else if (state == STOPPED) {
            if (tracing) logger.trace("start", "already stopped");
            throw new IOException("The server has been stopped.");
        }

        if (getMBeanServer() == null)
            throw new IllegalStateException("This connector server is not " +
                                            "attached to an MBean server");

        // Check the internal access file property to see
        // if an MBeanServerForwarder is to be provided
        //
        if (attributes != null) {
            // Check if access file property is specified
            //
            String accessFile =
                (String) attributes.get("jmx.remote.x.access.file");
            if (accessFile != null) {
                // Access file property specified, create an instance
                // of the MBeanServerFileAccessController class
                //
                MBeanServerForwarder mbsf;
                try {
                    mbsf = new MBeanServerFileAccessController(accessFile);
                } catch (IOException e) {
                    throw EnvHelp.initCause(
                        new IllegalArgumentException(e.getMessage()), e);
                }
                // Set the MBeanServerForwarder
                //
                setMBeanServerForwarder(mbsf);
            }
        }

        try {
            if (tracing) logger.trace("start", "setting default class loader");
            defaultClassLoader = EnvHelp.resolveServerClassLoader(
                    attributes, getMBeanServer());
        } catch (InstanceNotFoundException infc) {
            IllegalArgumentException x = new
                IllegalArgumentException("ClassLoader not found: "+infc);
            throw EnvHelp.initCause(x,infc);
        }

        if (tracing) logger.trace("start", "setting RMIServer object");
        final RMIServerImpl rmiServer;

        if (rmiServerImpl != null)
            rmiServer = rmiServerImpl;
        else
            rmiServer = newServer();

        rmiServer.setMBeanServer(getMBeanServer());
        rmiServer.setDefaultClassLoader(defaultClassLoader);
        rmiServer.setRMIConnectorServer(this);
        rmiServer.export();

        try {
            if (tracing) logger.trace("start", "getting RMIServer object to export");
            final RMIServer objref = objectToBind(rmiServer, attributes);

            if (address != null && address.getURLPath().startsWith("/jndi/")) {
                final String jndiUrl = address.getURLPath().substring(6);

                if (tracing)
                    logger.trace("start", "Using external directory: " + jndiUrl);

                String stringBoolean = (String) attributes.get(JNDI_REBIND_ATTRIBUTE);
                final boolean rebind = EnvHelp.computeBooleanFromString( stringBoolean );

                if (tracing)
                    logger.trace("start", JNDI_REBIND_ATTRIBUTE + "=" + rebind);

                try {
                    if (tracing) logger.trace("start", "binding to " + jndiUrl);

                    final Hashtable<?, ?> usemap = EnvHelp.mapToHashtable(attributes);

                    bind(jndiUrl, usemap, objref, rebind);

                    boundJndiUrl = jndiUrl;
                } catch (NamingException e) {
                    // fit e in the nested exception if we are on 1.4
                    throw newIOException("Cannot bind to URL ["+jndiUrl+"]: "
                                         + e, e);
                }
            } else {
                // if jndiURL is null, we must encode the stub into the URL.
                if (tracing) logger.trace("start", "Encoding URL");

                encodeStubInAddress(objref, attributes);

                if (tracing) logger.trace("start", "Encoded URL: " + this.address);
            }
        } catch (Exception e) {
            try {
                rmiServer.close();
            } catch (Exception x) {
                // OK: we are already throwing another exception
            }
            if (e instanceof RuntimeException)
                throw (RuntimeException) e;
            else if (e instanceof IOException)
                throw (IOException) e;
            else
                throw newIOException("Got unexpected exception while " +
                                     "starting the connector server: "
                                     + e, e);
        }

        rmiServerImpl = rmiServer;

        synchronized(openedServers) {
            openedServers.add(this);
        }

        state = STARTED;

        if (tracing) {
            logger.trace("start", "Connector Server Address = " + address);
            logger.trace("start", "started.");
        }
    }

前面讲过rmiServerImpl成员是空的,所以这时会调用newServer方法进行创建

代码语言:javascript
复制
    RMIServerImpl newServer() throws IOException {
        final boolean iiop = isIiopURL(address,true);
        final int port;
        if (address == null)
            port = 0;
        else
            port = address.getPort();
        if (iiop)
            return newIIOPServer(attributes);
        else
            return newJRMPServer(attributes, port);
    }

接下来会调用objectToBind 通过toStub方法创建当前的RMIServer对象的远程代理对象

代码语言:javascript
复制
    private static RMIServer objectToBind(
            RMIServerImpl rmiServer, Map<String, ?> env)
        throws IOException {
        return RMIConnector.
            connectStub((RMIServer)rmiServer.toStub(),env);
    }
    public Remote toStub() throws IOException {
        return RemoteObject.toStub(this);
    }
    public static Remote toStub(Remote obj) throws NoSuchObjectException {
        if (obj instanceof RemoteStub ||
            (obj != null &&
             Proxy.isProxyClass(obj.getClass()) &&
             Proxy.getInvocationHandler(obj) instanceof
             RemoteObjectInvocationHandler))
        {
            return obj;
        } else {
            return sun.rmi.transport.ObjectTable.getStub(obj);
        }
    }

然后调用bind方法将RMIServer的远程代理对象注册到注册中心

代码语言:javascript
复制
    void bind(String jndiUrl, Hashtable<?, ?> attributes,
              RMIServer rmiServer, boolean rebind)
        throws NamingException, MalformedURLException {
        // if jndiURL is not null, we nust bind the stub to a
        // directory.
        InitialContext ctx =
            new InitialContext(attributes);

        if (rebind)
            ctx.rebind(jndiUrl, rmiServer);
        else
            ctx.bind(jndiUrl, rmiServer);
        ctx.close();
    }

至此,完成了JmxConnectorServer的启动,程序可以通过jmx规范,访问MBeanServer与其上的MBean 关于remote和rmi的细节可以参考本人另一篇博客——java-rmi源码解析java-rmi源码解析

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 歪歪梯Club 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JMX
  • MBeanServer
  • Agent
  • Agent Service
  • Protocol Adaptors and Connectors
    • Protocol Adaptors
      • Connectors
      • MBean
        • Standard MBean
        • 使用JConsole工具监控
        • 源码分析
          • ManagementFactory.getPlatformMBeanServer()
            • MBeanServerFactory.createMBeanServer()
              • LocateRegistry.createRegistry
                • MBeanServer.registerMBean
                • JmxMBeanServer
                • JMXConnectorServerFactory.newJMXConnectorServer
                • jmxConnectorServer.start()
                相关产品与服务
                微服务引擎 TSE
                微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档