高性能服務通訊框架Gaea的詳細實現--server啓動流程

#<i class="icon-file">Gaea啓動過程</i>java

//程序入口
    com.bj58.spat.gaea.server.bootstrap.Main

啓動流程以下:bootstrap

##<i class="icon-share">1. 配置文件加載</i>session

Gaea服務端配置文件分爲兩種,且文件名相同,一種是Gaea框架默認配置,一種是Gaea具體服務的個性配置; 配置文件路徑(前一種爲框架默認,後一種爲具體服務個性配置):app

gaea_config: gaea/conf/gaea_config.xml 和 gaea/service/deploy/{servicename}/gaea_config.xml
gaea_log4j: gaea/conf/gaea_log4j.xml 和 gaea/service/deploy/{servicename}/gaea_log4j.xml

咱們能夠看出來,gaea有兩套徹底同樣的配置文件,那麼主要以哪一個爲主呢?框架

  public static ServiceConfig getServiceConfig(String... paths) throws Exception {}

在gaea加載配置文件的時候,其實會先去加載gaea/conf/gaea_config.xml和gaea/conf/gaea_log4j.xml,放入一個Map中,並初始化配置文件類ServiceConfig.java,再去加載service/deploy/{servicename}/下的配置文件,這樣的話就可以覆蓋掉conf下的配置文件,最終以servicename下的配置文件爲準。   還有一種比較特殊的配置文件的存在,那就是須要bin和servicename下的都起做用。這點須要如何設置呢?jvm

Node append = valueNode.getAttributes().getNamedItem("append");
				if(append != null && append.getNodeValue() != null && append.getNodeValue().equalsIgnoreCase("true")) {
					String key = nameNode.getTextContent();
					String value = property.get(nameNode.getTextContent());
					if(value != null) {
						value += "," + valueNode.getTextContent();
					} else {
						value = valueNode.getTextContent();
					}
					property.put(key, value);
				} else {
					property.put(nameNode.getTextContent(), valueNode.getTextContent());
  				}

     從代碼中能夠看出在配置文件中加入append=」true」便可實現。函數

##<i class="icon-share">2. 項目jar包預處理</i>工具

DynamicClassLoader.java 是Gaea的動態加載jar包的類。 DynamicClassLoader extends SecureClassLoader SecureClassLoader extends ClassLoader。ClassLoader是JDK類加載器的實現。 類加載的文件來自於如下三個文件夾:gaea/lib; gaea/service/lib; gaea/service/deploy/{servicename}/spa

作了以下處理:debug

  1. 將全部jar包放入一個list中
DynamicClassLoader classLoader = new DynamicClassLoader();
		classLoader.addFolder(
				rootPath + "service/deploy/" + sc.getString("gaea.service.name") + "/",
				rootPath + "service/lib/",
				rootPath + "lib"
				);

在DynamicClassLoader中,主要的是這個方法:

Class<?> findClass(...) //根據類名,在jar包中找到相關類,返回Class<?>類信息。
  1. 將全部jar包加入到classpath中
GlobalClassLoader.addSystemClassPathFolder(
				rootPath + "service/deploy/" + sc.getString("gaea.service.name") + "/",
				rootPath + "service/lib/",
				rootPath + "lib"
				);

##<i class="icon-share">3. 生成代理工廠IProxyFactory</i>

此過程當中實現一個代理類的簡單工廠,並將其放入Global中

IProxyFactory proxyFactory = ProxyFactoryLoader.loadProxyFactory(classLoader); //classLoader就是上一步中Gaea的類加載器對象。

生成此代理工廠,可在具體方法調用的時候,根據方法名,找到對應的代理,而後由代理去執行真正的方法。

生成代理工廠的過程以下:

  1. 服務接口信息提取 ContractInfo
  2. 生成代理類的信息 List<ClassFile>
  3. 生成代理工廠類信息 ClassFile
  4. 生成代理工廠類的實例 IProxyFactory

其中ClassFile中是類名和類的二進制字節碼

###3.1 服務接口信息提取

有兩種方式能夠得到這些信息,一種是基於配置文件的,一種是基於註解的。

public static final String SERVICE_CONTRACT = "serviceframe.xml";
    String configPath = serviceRootPath + "/" + Constant.SERVICE_CONTRACT;
		File file = new File(configPath);
		ContractInfo serviceContract = null;
		
		if (file != null && file.exists()) {
			serviceContract = ContractConfig.loadContractInfo(configPath, classLoader);
		} else {
			serviceContract = ScanClass.getContractInfo(serviceRootPath + "/", classLoader);
		}

serviceContract對象中詳細記錄了全部提供服務的接口信息。如今基本都是使用註解方式,關於使用方式,祥看Gaea的使用文檔。

public class ContractInfo {

	private List<SessionBean> sessionBeanList; //服務的N個接口類
 //...........................................
 
    public static class SessionBean {
		private String interfaceName; 
		private Map<String, String> instanceMap; //key:接口名, value:實現類的名字
		private ClassInfo interfaceClass;
		//................................
	}
 }
 
 public class ClassInfo {

	private Class<?> cls;
	private List<MethodInfo> methodList;
	private ClassType classType; //Interface, Class
	private String lookUP;
//......................................
}

關於具體根據配置和註解生成ContractInfo的詳細過程,這裏再也不贅述,具體請看代碼實現。

###3.2 生成代理類的信息

在這裏使用了javassist工具,對類進行拼接,連接,編譯。 根據服務接口信息ContractInfo中的信息,將按照類拼接各個服務接口的代理類

for (ContractInfo.SessionBean sessionBean : serviceContract.getSessionBeanList()) {} //取出全部接口類,在for循環中進行拼接類
    
    public static final String IPROXYSTUB_CLASS_NAME = IProxyStub.class.getName();
    
    String proxyClassName = lookup + "ProxyStub" + time; //代理類名稱
    CtClass ctProxyClass = pool.makeClass(proxyClassName, null); //生成代理類
    CtClass localProxy = pool.getCtClass(Constant.IPROXYSTUB_CLASS_NAME);
    ctProxyClass.addInterface(localProxy); //生成的代理,繼承IProxyStub接口
    
    public interface IProxyStub {
	
	    public GaeaResponse invoke(GaeaContext context)  throws ServiceFrameException;
    }
    
        //method
		for(Method m : uniqueMethodList) { //拼接全部方法
			logger.debug("create method:" + m.getName());
			String methodStr = createMethods(proxyClassName, m.getName(), allMethodList, uniqueNameList);
			logger.debug("method("+m.getName()+") source code:"+methodStr);
			CtMethod methodItem = CtMethod.make(methodStr, ctProxyClass); 
			ctProxyClass.addMethod(methodItem);
		}
		clsList.add(new ClassFile(proxyClassName, ctProxyClass.toBytecode())); //ctProxyClass.toBytecode():class文件,字節碼。封裝在ClassFile中,放入List返回

###3.3 生成代理工廠類信息

代理工廠類用來生成全部的代理,根據類名,便可生成對應代理

String pfClsName = "ProxyFactory" + time;//工廠類類名
    CtClass ctProxyClass = pool.makeClass(pfClsName, null);
    public static final String IPROXYFACTORY_CLASS_NAME = IProxyFactory.class
			.getName();
    CtClass proxyFactory = pool.getCtClass(Constant.IPROXYFACTORY_CLASS_NAME);
		ctProxyClass.addInterface(proxyFactory);//繼承IProxyFactory接口
	
	for (ContractInfo.SessionBean sessionBean : serviceContract.getSessionBeanList()) {... //將全部代理類加入到這個接口工廠之中。生成字符串。
	
	CtConstructor cc = new CtConstructor(new CtClass[]{pool.get("java.util.List")}, ctProxyClass);//工廠類的構造函數
	cc.setBody(sbConstructor.toString()); //構造函數體,上面for循環拼接的字符串
	 ctProxyClass.addConstructor(cc);//工廠類中加入此構造函數
		
		
		
	public interface IProxyFactory { //代理工廠類
	 public IProxyStub getProxy(String lookup);
    }

###3.4 生成代理工廠類的實例

根據以上生成的代理類信息和代理工廠類信息,最終生成代理工廠類的實例

List<IProxyStub> localProxyAry = new ArrayList<IProxyStub>();
		for(ClassFile cf : localProxyList) {
			Class<?> cls = classLoader.findClass(cf.getClsName(), cf.getClsByte(), null);
			logger.info("dynamic load class:" + cls.getName());
			localProxyAry.add((IProxyStub)cls.newInstance());
		}
		
		Class<?> proxyFactoryCls = classLoader.findClass(cfProxyFactory.getClsName(), cfProxyFactory.getClsByte(), null);
		Constructor<?> constructor = proxyFactoryCls.getConstructor(List.class);
		IProxyFactory pfInstance = (IProxyFactory)constructor.newInstance(localProxyAry); //根據上一步的構造函數,生成代理工廠類

以上步驟就完成了代理工廠類的構造生成

##<i class="icon-share">4. 初始化模塊加載</i>

在gaea啓動的時候,服務須要加載的東西便可以在這裏實現。至於如何實現,查看高級使用。

  loadInitBeans(classLoader, sc);
  List<String> initList = sc.getList("gaea.init", ",");
  IInit initBean = (IInit)classLoader.loadClass(initBeans).newInstance();
  initBean.init();

從配置文件gaea.init中取出實現IInit接口的初始化類,而後利用反射機制,再次對其進行實例化。最終調用接口的init方法。成功加載初始化類。

##<i class="icon-share">5. 請求過濾器加載</i>

在一個具體的方法調用的時候,須要通過一個請求過濾器,而後才能真正的調用到具體方法。在此對其請求過濾器類進行實例化。

IFilter filter = (IFilter)classLoader.loadClass(filterName).newInstance();
instanceList.add(filter);
Global.getSingleton().addGlobalRequestFilter(filter);

經過以上代碼咱們能夠看出來,請求過濾器也是從配置文件中加載進來的,並繼承了IFilter接口。將其實例化的請求過濾器,放入了一個單例的類Gloabl中,提供給整個系統在任意位置使用。

##<i class="icon-share">6. 返回過濾器加載</i>

與請求過濾器同樣,只是最終放入Global時,放到了response中

##<i class="icon-share">7. 鏈接過濾器加載</i>

與請求過濾器同樣

##<i class="icon-share">8. 權限認證系統加載</i>

  loadSecureKey(sc,serviceFolderPath);

在gaea/service/deploy/{servicename}/ 下加載受權配置文件secure.xml

##<i class="icon-share">9. 信號註冊</i>

實現sun的SignalHandler接口;目前只有在Gaea重啓的時候起做用,將服務的狀態進行標記,標記爲reboot

  Global.getSingleton().setServerState(ServerStateType.Reboot);

##<i class="icon-share">10. 啓動各項服務</i>

  loadServers(classLoader, sc);
List<String> servers = sc.getList("gaea.servers", ",");
		if(servers != null) {
			for(String server : servers) {
				try {
					if(sc.getBoolean(server + ".enable")) {
						logger.info(server + " is starting...");
						IServer serverImpl = (IServer) classLoader.loadClass(sc.getString(server + ".implement")).newInstance();
						Global.getSingleton().addServer(serverImpl);
						serverImpl.start();

依然是從配置文件取出來並實例化的。實現IServer接口。這幾個標準的配置都在bin的配置文件中,若是本身寫了什麼服務的話,也能夠在這裏實現IServer接口,並加到配置文件中。

##<i class="icon-share">11. 熱部署</i>

熱部署是在服務啓動後,一直監控gaea/service/deploy/{servicename}/下的文件,如如有更高,就進行更新

  addFileMonitor(rootPath, sc.getString("gaea.service.name"));

##<i class="icon-share">12. 服務關閉處理</i>

關閉服務,進行資源的回收

private static void registerExcetEven() {
  		Runtime.getRuntime().addShutdownHook(new Thread() {}}

這個方法的意思就是在jvm中增長一個關閉的鉤子,當jvm關閉的時候,會執行系統中已經設置的全部經過方法addShutdownHook添加的鉤子,當系統執行完這些鉤子後,jvm纔會關閉。

###le284

相關文章
相關標籤/搜索