實現dubbo隨機埠,解決重啟Address already in use異常

一渣程式猿發表於2016-08-20

dubbo每次從tomcat中undeploy似乎埠都還佔用著,網上查了下,說dubbo的關閉是繫結tomcat的關閉事件,然而在專案開發時並不可能每次都關閉tomcat,可能僅僅只需要更新一下提供者就可以了

如有其它解決方案的感謝留言,如今方案純屬下策,由於初次接觸dubbo,也沒有太熟練.所以就只有先這麼玩了..


那麼如何避免dubbo的埠配置呢,這個網上有說,不過網上沒有提到一些要點,避免其他人跟我一樣糾結的調了半天原始碼才懵懵懂懂的找到原因,將其中的一些細節補充一下

首先是dubbo的配置檔案

此處主要是配置dynamicDubboPortReaderDao的這個Bean,class指向你的自定義實現類

PS:<dubbo:protocol name="dubbo">此處不能省,雖然之後dubbo會預設產生一個dubbo的protocol,但據我測試就是不行,不配他會先去生成一個似乎.具體沒看原始碼

<span style="white-space:pre">	</span> <!-- 提供方應用資訊,用於計算依賴關係 -->
    <dubbo:application name="base-service" logger="log4j"  />
    
    <bean id="dynamicDubboPortReaderDao" class="com.cykj.base.service.dubbo.DynamicDubboPortReaderImpl" 
    init-method="init" />
 
    <!-- 使用multicast廣播註冊中心暴露服務地址  -->
<!--     <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181" timeout="5000" /> -->
    <dubbo:registry protocol="zookeeper" address="192.168.2.18:2181" timeout="5000" />
 
    <!-- 用dubbo協議在20880埠暴露服務 -->
    <dubbo:protocol name="dubbo" />
    <dubbo:annotation package="com.cykj.base.service.provideImpl"/> 
    
    <dubbo:provider  retries="0" timeout="10000" />
    <span style="white-space:pre">	</span>
    <dubbo:consumer retries="0" timeout="10000" /><span style="white-space:pre">	</span>
    
   <!--  <bean id="sysUserService" class="com.cykj.base.service.provideImpl.sys.SysUserServiceImpk" />-->  
    
     <!-- 宣告需要暴露的服務介面 --> 
    <!-- <dubbo:service  ref="sysUserService" interface="com.cykj.base.core.provide.sys.SysUserService"/>
      --> 

接下來貼出自定義實現類的程式碼,

PS:此處有個細節,讓我折騰了半天,注意spring配置檔案處一定不能寫class:applicationContext-*.xml,一定不要讓spring走掃描配置檔案,否則spring配置檔案載入順序出錯,會導致spring在沒有初始化dubbo配置檔案時初始化service,那麼這時候service在初始化,而service暴露給了dubbo,那麼dubbo會在此時強制初始化一次,就會先開始dubbo的初始化配置了,而上面配置的bean並沒有被用到,所以被延遲例項化了.導致前幾個服務會註冊到20880埠上

順便提一下spring配置檔案載入順序:

1.a)如果配置的是*.xml,則掃到一個檔案初始化一個檔案,此時並沒有例項化,只是記錄

   b)如果配置的是a.xml,b.xml,c.xml的具體檔案,則spring按照指定順序開始掃描

2.spring此時開始例項化掃描到的第一個配置檔案從上至下,如果遇到引用的/autoWired等需要注入的物件時,spring將會從例項快取中尋找該物件,如果有則注入,如果無則例項化該物件後注入,以此類推,完成整個bean的所有注入.

故而產生了上方所述情況,service先載入了,強制例項化了dubbo,而dubbo中配置的bean並沒有被引用,所以bean還沒有例項化,那麼修改埠的操作也就延遲執行了.SO....you know...

package com.cykj.base.service.dubbo;

import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.PostConstruct;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.config.ProtocolConfig;

/**
 * 在dubbo載入配置檔案時,會例項化該類,執行init-method配置的init方法
 * 此時通過spring獲取所有ProtocolConfig的實體(實際上好像就一個),並將其埠設為隨機一個未被使用的埠
 * 原始碼中隨機埠通過new Socket實現,而後將Socket關閉,更多細節不再說
 * @author LeiYong
 */
@Component
public class DynamicDubboPortReaderImpl implements ApplicationContextAware {

	@Autowired
	private ApplicationContext applicationContext;
	private int port = 20880;

	@PostConstruct
	public void init() {
		Map<String, ProtocolConfig> beansOfType = applicationContext.getBeansOfType(ProtocolConfig.class);
		for (Entry<String, ProtocolConfig> item : beansOfType.entrySet()) {
			port = NetUtils.getAvailablePort();
			System.out.println("##################use sure###########################"+port);
			item.getValue().setPort(port);
		}
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = (ConfigurableApplicationContext) applicationContext;
	}
}


相關文章