高性能服务通信框架Gaea的详细实现--server启动流程

#<i class="icon-file">Gaea启动过程</i>

    //程序入口
    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}/

做了如下处理:

  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

转载于:https://my.oschina.net/le284/blog/261809

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Gaeaphp 是一个基于 flightphp 的 PHP 框架基础环境要求php 5.4 安装WebServer,具体配置方法参见Flight按照要求Php-Memcached扩展,具体配置方法参见pecl结构设计项目目录结构|-app/|    |-configs/    配置文件夹|    |-bootstrap.php   引导文件|-src/      代码目录|    |-libs/       类库方法库|    |  |-classes/|    |  |-functions/|    |-modules/    模块文件夹|    |  |-xxxmodules/|    |  |  |-controller/|    |  |  |-libs/|    |  |  |-model/|    |-templates/|    |  |-default/|-temp/     临时目录|    |-caches/|    |-compiled/|    |-logs/url访问框架采用mvc设计模式开发,采用单一入口方式部署和访问。 访问示例http://yourdomain.com/module/controller/action/param1/param2其中将会访问/src/module/controller.php下的action方法引导文件/app/bootstrap.php引导文件完成了命名空间的注册,扩展模版引擎,以及路由的注册。其中常量定义:ROOT_PATH 系统根目录APP_PATH app目录TEMP_PATH 临时目录SSRC_PATH     系统代码目录WEB_PATH      系统WEB资源目录系统配置所有的配置文件均已数组形式返回。具体配置文件项在/app/configs下|-db.php    数据库(Mysql)配置文件|-memcache.php       缓存(Memcache)配置文件|-route.php     路由配置文件|-smarty.php    模版引擎配置文件|-system.php    系统配置文件系统配置结构为一维数组,各项参数见注释return  array(     'theme'     =>      'default',   //主题     'debug'     =>      true,       //是否调试信息     'log_level' =>      'DEBUG',    //日志打印级别EMERGENCY|ALERT|CRITICAL|ERROR|WARNING|NOTICE|INFO|DEBUG     'cache_expire'  =>  3600,       //缓存时间,单位秒     'prefix'  =>  'sobey_',   //存储键值前缀,包含db和cache );数据库配置结构为二维数组,默认使用default,可参考default结构配置多个数据库配置如testreturn  array(     'default'   =>  array(             'database_type'=>'mysql',             'database_name'=>'ecshop',             'server'=>'127.0.0.1',             'username'=>'root',             'password'=>'123456',             //其他可选参数参见php手册             //http://www.php.net/manual/en/pdo.setattribute.php             'port'=>3306     ),     //test配置非系统默认     'test'      =>  array(         //.........     ), );Memcached配置结构为二维数组,默认使用default,可参考default结构配置多个数据库配置如testreturn array(     'servers'=>array(         //array('域名','端口','权重')      array('127.0.0.1','11211'),     ),     //options为Memcached::setOptions参数     'options'=>array(         //Memcached::OPT_HASH => Memcached::HASH_MURMUR,         //Memcached::OPT_PREFIX_KEY => "widgets"     ), );smarty配置结构为二维数组,默认使用default,可参考default结构配置多个数据库配置如testreturn array(     'servers'=>array(         //array('域名','端口','权重')      array('127.0.0.1','11211'),     ),     'options'=>array(         //Memcached::OPT_HASH => Memcached::HASH_MURMUR,         //Memcached::OPT_PREFIX_KEY => "widgets"     ), );模块modules一个模块的基本结构为,请遵循以下的规则:|-src/      代码目录|    |-modules/    模块文件夹|    |  |-xxxmodules/         模块根目录|    |  |  |-api/    接口文件目录|    |  |  |-controller/     控制器文件夹 |    |  |  |-libs/       工具类库|    |  |  |-model/     模型类库项目开发技巧Core文件Core::config($file,[$key,[$dafault = false,[$reload = false]]])   获取配置文件Core::load_func($func,[$path= '']) 加载函数Core::log($module,$level,$message,[$context = array()]) 日志记录Core::db([$key='default',[$option=array()]]) 获取db实例Core::memcached([$key='default',[$servers=array(),[$options=array()]]]) 获取memcached实例项目函数库一些项目的全局函数位于/src/libs/functions/global.php,该文件会被自动加载。 自己可新建模块的函数库并使用Core::load_func()引入使用模版模版文件位于: /src/teplate/{风格}/sso(模块)下,为smarty语法文件 标签:Gaeaphp  Web框架

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值