高性能服务通信框架Gaea的详细实现--server启动流程
2014-05-07 18:19
531 查看
#<i class="icon-file">Gaea启动过程</i>
启动流程如下:
##<i class="icon-share">1. 配置文件加载</i>
Gaea服务端配置文件分为两种,且文件名相同,一种是Gaea框架默认配置,一种是Gaea具体服务的个性配置;
配置文件路径(前一种为框架默认,后一种为具体服务个性配置):
我们可以看出来,gaea有两套完全一样的配置文件,那么主要以哪个为主呢?
在gaea加载配置文件的时候,其实会先去加载gaea/conf/gaea_config.xml和gaea/conf/gaea_log4j.xml,放入一个Map中,并初始化配置文件类ServiceConfig.java,再去加载service/deploy/{servicename}/下的配置文件,这样的话就能够覆盖掉conf下的配置文件,最终以servicename下的配置文件为准。
还有一种比较特殊的配置文件的存在,那就是需要bin和servicename下的都起作用。这点需要如何设置呢?
从代码中可以看出在配置文件中加入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}/
做了如下处理:
将所有jar包放入一个list中
在DynamicClassLoader中,主要的是这个方法:
将所有jar包加入到classpath中
##<i class="icon-share">3. 生成代理工厂IProxyFactory</i>
此过程中实现一个代理类的简单工厂,并将其放入Global中
生成此代理工厂,可在具体方法调用的时候,根据方法名,找到对应的代理,然后由代理去执行真正的方法。
生成代理工厂的过程如下:
服务接口信息提取 ContractInfo
生成代理类的信息 List<ClassFile>
生成代理工厂类信息 ClassFile
生成代理工厂类的实例 IProxyFactory
其中ClassFile中是类名和类的二进制字节码
###3.1 服务接口信息提取
有两种方式可以获得这些信息,一种是基于配置文件的,一种是基于注解的。
serviceContract对象中详细记录了所有提供服务的接口信息。现在基本都是使用注解方式,关于使用方式,祥看Gaea的使用文档。
关于具体根据配置和注解生成ContractInfo的详细过程,这里不再赘述,具体请看代码实现。
###3.2 生成代理类的信息
在这里使用了javassist工具,对类进行拼接,链接,编译。
根据服务接口信息ContractInfo中的信息,将按照类拼接各个服务接口的代理类
###3.3 生成代理工厂类信息
代理工厂类用来生成所有的代理,根据类名,即可生成对应代理
###3.4 生成代理工厂类的实例
根据以上生成的代理类信息和代理工厂类信息,最终生成代理工厂类的实例
以上步骤就完成了代理工厂类的构造生成
##<i class="icon-share">4. 初始化模块加载</i>
在gaea启动的时候,服务需要加载的东西即可以在这里实现。至于如何实现,查看高级使用。
从配置文件gaea.init中取出实现IInit接口的初始化类,然后利用反射机制,再次对其进行实例化。最终调用接口的init方法。成功加载初始化类。
##<i class="icon-share">5. 请求过滤器加载</i>
在一个具体的方法调用的时候,需要经过一个请求过滤器,然后才能真正的调用到具体方法。在此对其请求过滤器类进行实例化。
通过以上代码我们可以看出来,请求过滤器也是从配置文件中加载进来的,并继承了IFilter接口。将其实例化的请求过滤器,放入了一个单例的类Gloabl中,提供给整个系统在任意位置使用。
##<i class="icon-share">6. 返回过滤器加载</i>
与请求过滤器一样,只是最终放入Global时,放到了response中
##<i class="icon-share">7. 连接过滤器加载</i>
与请求过滤器一样
##<i class="icon-share">8. 权限认证系统加载</i>
在gaea/service/deploy/{servicename}/ 下加载授权配置文件secure.xml
##<i class="icon-share">9. 信号注册</i>
实现sun的SignalHandler接口;目前只有在Gaea重启的时候起作用,将服务的状态进行标记,标记为reboot
##<i class="icon-share">10. 启动各项服务</i>
依然是从配置文件取出来并实例化的。实现IServer接口。这几个标准的配置都在bin的配置文件中,如果自己写了什么服务的话,也可以在这里实现IServer接口,并加到配置文件中。
##<i class="icon-share">11. 热部署</i>
热部署是在服务启动后,一直监控gaea/service/deploy/{servicename}/下的文件,如若有更高,就进行更新
##<i class="icon-share">12. 服务关闭处理</i>
关闭服务,进行资源的回收
这个方法的意思就是在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。
###le284
//程序入口 com.bj58.spat.gaea.server.bootstrap.Main
启动流程如下:
##<i class="icon-share">1. 配置文件加载</i>
Gaea服务端配置文件分为两种,且文件名相同,一种是Gaea框架默认配置,一种是Gaea具体服务的个性配置;
配置文件路径(前一种为框架默认,后一种为具体服务个性配置):
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下的都起作用。这点需要如何设置呢?
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}/
做了如下处理:
将所有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<?>类信息。
将所有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的类加载器对象。
生成此代理工厂,可在具体方法调用的时候,根据方法名,找到对应的代理,然后由代理去执行真正的方法。
生成代理工厂的过程如下:
服务接口信息提取 ContractInfo
生成代理类的信息 List<ClassFile>
生成代理工厂类信息 ClassFile
生成代理工厂类的实例 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
相关文章推荐
- 高性能服务通信框架Gaea的详细实现--server请求处理流程
- 【C/S通信交互之Http篇】Cocos2dx(Client)使用Curl与Jetty(Server)实现手机网游Http通信框架(内含解决curl.h头文件找不到问题)
- 【C/S通信交互之Http篇】使用Curl与Jetty(Server)实现手机网游Http通信框架&解决curl.h头文件找不到问题
- Thrift可扩展高性能的通信服务框架
- 基于NIO实现的一个高性能通信框架
- 硬件访问服务4之Android硬件访问服务框架及系统函数全详细实现
- 腾讯高性能RPC开发框架Tars实现服务治理(微服务)
- tornado实现高性能无阻塞udp通信(1)——server端实现
- centos启动流程及实现service管理apache服务
- 服务通信框架Gaea--client负载均衡和服务重连策略
- 【C/S通信交互之Http篇】Cocos2dx(Client)使用Curl与Jetty(Server)实现手机网游Http通信框架(内含解决curl.h头文件找不到问题)
- 【C/S通信交互之Http篇】Cocos2dx(Client)使用Curl与Jetty(Server)实现手机网游Http通信框架(内含解决curl.h头文件找不到问题)
- 写书计划启动!《.NET通信框架的设计、实现与应用》
- neutron-server服务启动流程-基于mitaka版本
- Gaea是支持跨平台具有高并发、高性能、高可靠性,并提供异步、多协议、事件驱动的中间层服务框架
- Linux系统从开机到正常提供服务的详细开机启动流程
- 【C/S通信交互之Http篇】Cocos2dx(Client)使用Curl与Jetty(Server)实现手机网游Http通信框架(内含解决curl.h头文件找不到问题)
- 服务通信框架Gaea---client的请求处理模型
- Ambari-Server Rest API处理2(Ambari-Server通过Rest API进行服务安装、部署、操作流程+操作源码分析)
- TCP-IP学习笔记八:RPC(Netty和Spring)实现webServer框架