java配置方式

簡介

以前使用JMX管理也沒有太在乎,最近從新看到JMX配置,發現這也是一種比較靈活的配置管理方式。主要是有不少現成的工具能夠用。java

在看JMX以前咱們先回顧一下以前的配置方式。直接寫死在程序裏就不說了,這個至關於沒有配置,可能只是讓配置集中一點方便管理而已。每次修改以後都得從新打包,直接能夠放棄。服務器

下面咱們直接看使用配置文件的方式。dom

配置文件配置

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

public class ConfigUtil {

    private static Properties config;

    static {
        config = new Properties();
        InputStream is = ConfigUtil.class.getResourceAsStream("config.properties");
        try {
            config.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static String getConfig(String key){
        return config.getProperty(key);
    }

    public static void main(String[] args) {
        for(int i = 0;i<1000;i++) {
            System.out.println(ConfigUtil.getConfig("port"));
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

config.properties直接放在類所在包下面就能夠了。這個版本的配置有一個問題就是,修改了配置文件必須重啓應用。重啓應用也是個比較麻煩的事情。ide

全部咱們修改一下來個動態配置版的,可使用apach的common.io中的文件監控。不過我以爲沒有必要,直接簡單判斷一下最後修改時間就能夠了。直接上代碼。工具

動態配置文件

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

public class ConfigPromoteUtil {

    public static final String FILE_NAME = "config.properties";

    private static Properties config = new Properties();

    private static long lastModify;

    private static File file;

    static {
        URL url = ConfigPromoteUtil.class.getResource(FILE_NAME);
        String path = url.getFile();
        file = new File(path);
        loadConfig();
    }

    private static void loadConfig(){
        lastModify = file.lastModified();
        try {
            FileInputStream fis = new FileInputStream(file);
            config.load(fis);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 配置文件是否被修改
     * @return true 被修改 不然 false
     */
    private static boolean isModify(){
        long lastModified = file.lastModified();
        return lastModify != lastModified;
    }

//    private static boolean isModify() throws IOException {
//        URL url = ConfigPromoteUtil.class.getResource(FILE_NAME);
//        String path = url.getFile();
//        System.out.println(path);
//        Path p = Paths.get(path.substring(1));
//        FileTime ft = Files.getLastModifiedTime(p);
//        long lastModified = ft.toMillis();
//        return lastModify != lastModified;
//    }

    public static String getConfig(String key) throws IOException {
        if(isModify())
            loadConfig();
        return config.getProperty(key);
    }

    public static void main(String[] args) throws IOException {
        for(int i = 0;i<1000;i++) {
            System.out.println(ConfigPromoteUtil.getConfig("port"));
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

邏輯仍是蠻簡單的,初始化的時候就先加載配置文件,記錄最後修改時間,而後每一次獲取配置的時候,都去檢查文件的最後修改時間有沒有變化,若是變了就從新加載配置文件,並修改最後修改時間。測試

剛剛開始的時候再idea中發現獲取的配置文件最後修改時間怎麼都不對,嘗試了不少方法都沒有效果,後來直接用命令行運行了一下,既然好了,而後在idea中也好了,真是奇怪。this

不過解決這個問題的過程當中也發現一些有趣的事情,好比說Paths,Path,Files,FileTime等類。以前處理文件通常都是使用apach的common.io,今天發現jdk1.7開始的Paths,Path,Files,FileTime這些類也能夠很方便的處理遍歷,複製等操做了。有興趣能夠嘗試一下。url

JMX配置

這裏咱們使用standard MBean,standard MBean它能管理的資源(包括屬性,方法,時間)必須定義在接口中,而後MBean必須實現這個接口。它的命名也必須遵循必定的規範,例如咱們的MBean爲Congfig,則接口必須爲CongfigMBean。idea

先上代碼:.net

standard MBean接口

public interface ConfigMBean {

    String getName();

    void setName(String name);

    int getPort();

    void setPort(int port);

    String printName();

    int printPort();

}

standard MBean

public class Config implements ConfigMBean {

    private String name;

    private int port;

    public String printName(){
        System.out.println(name);
        return name;
    }

    public int printPort(){
        System.out.println(port);
        return port;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int getPort() {
        return port;
    }

    @Override
    public void setPort(int port) {
        this.port = port;
    }
}

測試代理類

import javax.management.*;
import javax.management.remote.*;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class ConfigAgent {

    public static void main(String[] args) throws Exception {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        ObjectName on = null;
        try {
            on = new ObjectName("jmxBean:name=config");
        } catch (MalformedObjectNameException e) {
            e.printStackTrace();
        }
        try {
            server.registerMBean(new Config(), on);
        } catch (InstanceAlreadyExistsException e) {
            e.printStackTrace();
        } catch (MBeanRegistrationException e) {
            e.printStackTrace();
        } catch (NotCompliantMBeanException e) {
            e.printStackTrace();
        }
        try
        {
            LocateRegistry.createRegistry(9999);
            JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
            JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);
            jcs.start();
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        final Config config = new Config();
        for(int i=0;i<1000;i++){
            System.out.println(config.getName());
            System.out.println(config.getPort());
            config.printName();
            config.printPort();
            print();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        final Scanner scanner = new Scanner(System.in);
        scanner.nextLine();
    }

    public static void print() throws Exception {
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.connect(url,null);
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
        ObjectName mbeanName = new ObjectName("jmxBean:name=config");
//        String[] domains = mbsc.getDomains();
//        mbsc.getMBeanCount()
//        mbsc.setAttribute(mbeanName, new Attribute("Name","tim"));
//        mbsc.setAttribute(mbeanName, new Attribute("Port","3306"));
//        int port = (Integer) mbsc.getAttribute(mbeanName, "Port");
//        String name = (String)mbsc.getAttribute(mbeanName, "Name");
//        System.out.println("port=" + port + ";name=" + name);
        ConfigMBean proxy = MBeanServerInvocationHandler.newProxyInstance(mbsc, mbeanName, ConfigMBean.class, false);
        proxy.printPort();
        proxy.printName();
        System.out.println(proxy.getName());
        System.out.println(proxy.getPort());

    }
}
MBeanServer server = ManagementFactory.getPlatformMBeanServer();

獲取一個MBean管理服務器的實例。

ObjectName on = new ObjectName("jmxBean:name=config");

給MBean一個標識,方便外部識別和訪問。

server.registerMBean(new Config(), on);

在MBean服務器上註冊一個Config MBean。

到這裏就算是註冊MBean完了,而且可使用JConsole這樣的工具鏈接查看修改了。可是若是要提供一些外部的Adapter和遠程的RMI調用就還須要一些工做。

LocateRegistry.createRegistry(9999);
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url,null, server);
jcs.start();

註冊一個JMX服務,而且使用的是rmi協議。

從上面的測試中能夠看到直接在new一個Config是不行的,若是要統一配置,獲取外部能夠修改的Config仍是得JMXServiceURL來訪問。

public static void print() throws Exception {
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.connect(url,null);
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
        ObjectName mbeanName = new ObjectName("jmxBean:name=config");
//        String[] domains = mbsc.getDomains();
//        mbsc.getMBeanCount()
//        mbsc.setAttribute(mbeanName, new Attribute("Name","tim"));
//        mbsc.setAttribute(mbeanName, new Attribute("Port","3306"));
//        int port = (Integer) mbsc.getAttribute(mbeanName, "Port");
//        String name = (String)mbsc.getAttribute(mbeanName, "Name");
//        System.out.println("port=" + port + ";name=" + name);
        ConfigMBean proxy = MBeanServerInvocationHandler.newProxyInstance(mbsc, mbeanName, ConfigMBean.class, false);
        proxy.printPort();
        proxy.printName();
        System.out.println(proxy.getName());
        System.out.println(proxy.getPort());
    }

咱們看print方法,仍是得經過JMXServiceURL,鏈接到MBean服務暴露的接口,經過MBeanServerInvocationHandler獲取到代理的Config MBean。

這樣咱們能夠經過JConsole等工具鏈接上MBean服務器,修改了配置,在程序中也能獲取到。以下圖:

JConsole

上圖是在JConsole中修改Config配置,注意屬性是Config中的屬性,操做是定義在ConfigMBean中的非getter,setter方法。

總結

對於簡單的配置直接使用動態配置文件就能夠了,若是配置管理比較多久能夠考慮JMX的方式。JMX方式的限制也比較多,開發的成本也會大一些。

相關文章
相關標籤/搜索