通過weblogic API解析如何獲取weblogic中服務的IP和端口操作

我們的服務是部署在weblogic上的,最近遇到一個需求,需要在代碼中獲取weblogic部署當前服務的IP地址和端口。

後來搜到一段代碼,親測有效:

public static String getIpAndPort(){
    try {
        InitialContext initialContext = new InitialContext();
		MBeanServer tMBeanServer;
		MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
			tMBeanServer = (MBeanServer) initialContext.lookup("java:comp/env/jmx/runtime");	
		ObjectName tObjectName = new ObjectName(
				"com.bea:Name=RuntimeService,Type=weblogic.management.mbeanservers.runtime.RuntimeServiceMBean");
		ObjectName serverrt = (ObjectName) tMBeanServer.getAttribute(tObjectName, "ServerRuntime");
		String port = String.valueOf(tMBeanServer.getAttribute(serverrt, "ListenPort"));
		String listenAddr = (String) tMBeanServer.getAttribute(serverrt, "ListenAddress");
		String[] tempAddr = listenAddr.split("/");
		if (tempAddr.length == 1) {
			listenAddr = tempAddr[0];
		} else if (tempAddr[tempAddr.length - 1].trim().length() != 0) {
			listenAddr = tempAddr[tempAddr.length - 1];
		} else if (tempAddr.length > 2) {
			listenAddr = tempAddr[tempAddr.length - 2];
		}
		StringBuilder sBuilder = new StringBuilder(listenAddr);
		sBuilder.append(":");
		sBuilder.append(port);
		System.out.print(sBuilder);
		return sBuilder.toString();
		} catch (NamingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MalformedObjectNameException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstanceNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (AttributeNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ReflectionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MBeanException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}	

可要理解這段代碼後面的原理和思路,真是費勁瞭,需要瞭解以下知識:

JMX

JNDI

RMI

EJB

總結成一句話就是,通過web應用通過weblogic提供的JNDI訪問weblogic的JMX中的對象。JNDI後臺用的技術就是EJB,而EJB是RMI在java語言上的實現。上述幾個概念的具體含義,讀者可以自行查詢,網上資料很多。

下面回歸正題,先從思路上詳細分析下標題中的內容如何實現。

作為服務端代碼,最後都是生成一個war放到服務器上去運行的。那從代碼本身的程序來說,是肯定無法知道自己會被放到什麼類型的web容器中、自己可以被訪問的IP地址和端口號的。那誰知道的呢?隻有web容器知道。換句話說,從這次要解決的問題上看,隻有weblogic自己知道在其內部部署的應用被放到瞭哪個IP下,端口是多少。也就是說,解決這個問題的關鍵是,我們的服務程序如何去“問”weblogic容器,自己的IP和端口是多少。

好的,我們繼續來想這個問題。能不能從weblogic容器中獲取到服務的IP和端口號,取決於weblogic願不願意把這些信息開放給你,換句話說,取決於weblogic是否對外開放瞭可以獲取其內部服務IP和端口的通道。

目前來看,必然是提供瞭的,查瞭weblogic的官網,發現瞭這樣一段說明:

文章的鏈接地址為(oracle的官方文檔):

https://docs.oracle.com/cd/E13222_01/wls/docs81/jmx/overview.html

隻要獲取到weblogic的MBeanServer,然後從MBeanServer中取出對應的ObjectName的屬性,就可以獲取到IP和端口瞭。這裡面提到瞭JMX和RMI的概念,不清楚的,可以從上文找博文鏈接查看。

有一點是比較好理解的,就是weblogic必定會把自己處在runtime的服務信息寫入到MBeanServer,然後我們通過MBeanServer把這些信息拿出來就行瞭。至於為什麼要有MBeanServer,又是和JMX相關,這裡就不再贅述。現在的關鍵問題是,我們的本地程序,如何訪問到weblogic的MBeanServer?答案是通過InitialContext的lookup函數,而lookup函數最終的訪問方式是JNDI。也就是說,我們最終是通過weblogic對外提供的JNDI訪問到weblogic的MBeanServer的。MBeanServer在兩個程序(weblogic和服務程序)之間的傳遞是通過EJB的。

拿到weblogic的MBeanServer之後,如何獲取程序的IP的端口呢?這個當然要看weblogic是怎麼設置進去的。按照設置進去的規則取出來就可以瞭。那如何知道weblogic的設置規則呢?我們繼續看weblogic的文檔。

原文鏈接:

https://docs.oracle.com/cd/E13222_01/wls/docs90/jmx/understandWLS.html

發現瞭什麼問題,紅框中的文字,不就是剛才樣例代碼中的文字嗎?再來看下面這段代碼,通過本地程序訪問Runtime MBean Server

If the classes for the JMX client are located in a J2EE module, such as an EJB or Web application, then the JNDI name for the Runtime MBeanServer is:

java:comp/env/jmx/runtime

翻譯下,如果JMX客戶端(EJB或者Web程序)在J2EE本地,那麼通過JNDI訪問Runtime MBean Server的名稱為java:comp/env/jmx/runtime。

Runtime MBean Server是MBeanServer的一種,通過下面的說明可以看到:

所以可以把Runtime MBean Server賦值給MBeanServer.

好,下一步,我們繼續來調查,從Runtime MBean Server中如何取到端口和IP。通過以下代碼獲取RuntimeServerMBean的ServerRuntime屬性。

        ObjectName tObjectName = new ObjectName(
                "com.bea:Name=RuntimeService,Type=weblogic.management.mbeanservers.runtime.RuntimeServiceMBean");
        ObjectName serverrt = (ObjectName) tMBeanServer.getAttribute(tObjectName, "ServerRuntime");

點開ServerRuntime屬性,看看它還有什麼二級屬性,果然:

ListenPort和ListenAddress就是ServerRuntime的二級屬性。通過以下代碼獲取到:

		String port = String.valueOf(tMBeanServer.getAttribute(serverrt, "ListenPort"));
		String listenAddr = (String) tMBeanServer.getAttribute(serverrt, "ListenAddress");

至此,所有代碼解析完畢。

但是仔細想想,這段代碼其實是有瑕疵的。換句話說,健壯性還不夠。如果我們用的web容器不是weblogic怎麼辦?那代碼豈不是就不管用瞭。所以我建議,完善下這段代碼,增加對web容器的判斷。其他web容器中如果獲取IP和端口,還請讀者自己探索。先通過下面的函數判斷下當前的web容器:

public static String getServerName() {
		String serverName = null;
		if (ServerDetector.isWebLogic()) {
			serverName = "WebLogic";
		} else if (ServerDetector.isTomcat()) {
			serverName = "Tomcat";
		} else if (ServerDetector.isWebSphere()) {
			serverName = "WebSphere";
		} else if (ServerDetector.isSupportsComet()) {
			serverName = "SupportsComet";
		} else if (ServerDetector.isResin()) {
			serverName = "Resin";
		} else if (ServerDetector.isOC4J()) {
			serverName = "OC4J";
		} else if (ServerDetector.isJOnAS()) {
			serverName = "JOnAS";
		} else if (ServerDetector.isJetty()) {
			serverName = "Jetty";
		} else if (ServerDetector.isJBoss()) {
			serverName = "JBoss";
		} else if (ServerDetector.isGeronimo()) {
			serverName = "Geronimo";
		} else if (ServerDetector.isGlassfish()) {
			serverName = "Glassfish";
		} else if (ServerDetector.isGlassfish2()) {
			serverName = "Glassfish2";
		} else if (ServerDetector.isGlassfish3()) {
			serverName = "Glassfish3";
		}
		return serverName;
	}
 

ServerDetector需要對應jar包,利用maven引入的配置為:

<dependency>
		    <groupId>com.liferay.portal</groupId>
		    <artifactId>portal-kernel</artifactId>
		    <version>5.2.3</version>
		    <scope>provided</scope>
 
</dependency>

遇到問題,一定要多探索,與其看別人的博文,不如自己深入研究API,找樣例代碼。用一手資料。以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: