统一消息对象编程web框架详解——框架单例实现原理

    1、实现演变过程

   消息对象框架启动顺序是首先启动模块工厂,然后工厂启动模块运行。在最开始用于web框架时,由于缺乏经验,我设计由每个线程单独启动一个工厂,然后工厂启动各个模块,线程结束时,工厂及所有模块销毁。这样实现功能是没有问题的,但是效率很低,因为每次一个客户端的request都要引起一个线程从工厂建立、模块初始化的过程。为了避免每次工厂和模块的初始化,借鉴数据库的连接池,我又采取了工厂池的方案,当系统启动时初始化一定数量的工厂,像传统的servlet一样,模块实例化后一直在内存中,这样避免了每次线程的工厂、模块的初始化。通过工厂池的运用,运行效率极大提高。但这又有个问题一直隐含着,就是每个线程都有一套框架,从工厂到各个模块,同样一个模块在所有线程中都有一个。虽然单线程的运行效率高,线程数据隔离,但是整体来说占用内存的,如果N个线程,就N个框架,整体内存是一个框架的N倍。这个问题我一直是我个心病、bug,也一直没有好的解决方案,可以用一个map保存所有的线程数据,但是如何区分呢,一直没想出来。有一天我百度时无意看到一句话---springmvc是单例模式,之前我对其他框架不了解,包括springmvc。这句话引起我很大的震动——springmvc是单例模式,它怎么实现的的? 通过百度了解它用了ThreadLoacal这东西,大概了解下其原理,真是给我当头一棒,我怎么忘记了线程ID,可以做它mapkey来区分线程数据啊。(当时一个感触,很简单的一个问题自己竟想了好久,看来一个人做事情真有思维局限的,如果有另一个人提醒下就很快解决了)

     这就是现在消息对象web框架的单例原理,建立一个map为所有线程公用,用线程id来区分数据,自己只能取自己线程的数据。下面看实现过程。

2、实现

    许多多线程实现是用了ThreadLoacal来实现数据共享,但在我这里没有用。因为消息对象框架一个特点是所有的模块都由工厂创建,并且创建后工厂把自己放入所有模块里,工厂成为其所创建模块的一个属性,工厂本身就是个共享数据了。借助这个特点,工厂的数据就自然可以被其他模块或线程使用了。现在看代码:

   这是Tomcat过滤器,用于将所有访问转到框架,过滤器负责启动工厂,框架开始运行。

public class TLFilterWithSingleFactory implements Filter{

    protected String configFile;
    protected String configDir;
    protected Map<String,HttpServletRequest> requestMap =new ConcurrentHashMap<>();
    protected Map<String,HttpServletResponse>responseMap =new ConcurrentHashMap<>();
    protected Map<String,HashMap<String ,Object>>sessionDatas =new ConcurrentHashMap<>();
    protected TLMsg startAppMsg = new TLMsg().setAction("start");
    protected ServletContext context;
    protected TLObjectFactory moduleFactory;

    public void init(FilterConfig config) throws ServletException {
        Filter.super.init(config);
        context = config.getServletContext();
        String  initConfigPath=config.getInitParameter("configPath");
        if(initConfigPath==null || initConfigPath.isEmpty())
            initConfigPath="conf";
        configDir = context.getRealPath("/WEB-INF/"+initConfigPath);
        String  initConfigFile=config.getInitParameter("configFile");
        if(initConfigFile==null || initConfigFile.isEmpty())
            initConfigFile="moduleFactory_config.xml";
        configFile = configDir + File.separator + initConfigFile;
        TLObjectFactory.setConfigDir(configDir,configFile);
        moduleFactory = new TLObjectFactory("moduleFactory");    
        registInfactory(moduleFactory, "servletContext", context);
        registInfactory(moduleFactory, "servletRequest", requestMap);
        registInfactory(moduleFactory, "servletResponse", responseMap);
        registInfactory(moduleFactory, "sessionDatas", sessionDatas);
        moduleFactory.boot();
    }
    public void destroy() {
        moduleFactory.destroyModule();
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException
    {
        long startTime = System.currentTimeMillis();
        String threadName=Thread.currentThread().getName();
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        HashMap<String ,Object> datas =new HashMap<>();
        requestMap.put(threadName, request);
        responseMap.put(threadName,  response);
        sessionDatas.put(threadName,datas);
        moduleFactory.putMsg("appCenter", startAppMsg);
        requestMap.remove(threadName);
        responseMap.remove(threadName);
        sessionDatas.remove(threadName);
        Long nowTime = System.currentTimeMillis();
        Long runtime = nowTime - startTime;
        TLLog.setLog(TLFilterWithSingleFactory.class, "运行时间:" + runtime, LogLevel.INFO);
    }

    protected void registInfactory(TLObjectFactory modulefactory, String name, Object object)
    {
        TLMsg registInFactoryMsg = new TLMsg().setAction("registInFactory")
                .setParam(FACTORY_MODULENAME, name)
                .setParam(INSTANCE, object);
        modulefactory.putMsg(modulefactory, registInFactoryMsg);
    }
}

    过滤器定义了三个对象全局变量 requesMap、responseMap、sessionDatas,分别用于每个请求的request、response及可以在线程中共享的数据。

  工厂实例化后,通过   registInfactory(moduleFactory, "servletRequest", requestMap) 方法,将建立的全局变量保存到工厂里面,这样其他模块就可以访问这些数据了。

   有客户端访问时,过滤器开始工作, 运行doFilter方法,在其方法内,对于每个线程的数据,保存到共享数据里。

       requestMap.put(threadName, request);
        responseMap.put(threadName,  response);

  这样线程的其他模块就可以通过工厂获得自己线程的数据。在基本模块TLWServModule里:

例如获得requet:

protected  HttpServletRequest getRequest(){
        Map<String,HttpServletRequest> requestMap = (Map<String, HttpServletRequest>) getModuleInFactory("servletRequest");
        String threadName=Thread.currentThread().getName();
        return requestMap.get(threadName);
    }

代码非常简单,首先从工厂中获得共享数据requesMap,然后根据自己的线程名称获取自己的数据。由于只是取自己的数据,因此不涉及线程数据竞争问题。

  如果像把数据保存起来给其他模块用,那么可以存入共享数据中:

protected void setSessionData(String varname ,Object value)
{
    Map<String,HashMap<String ,Object>> sessionDatas = (Map<String, HashMap<String, Object>>) getModule("sessionDatas");
    String threadName=Thread.currentThread().getName();
    HashMap<String ,Object> datas =sessionDatas.get(threadName);
    datas.put(varname,value);
}

  其他模块可以在任意地方取自己线程的数据:

protected Object getSessionData(String varname)
{
    Map<String,HashMap<String ,Object>> sessionDatas = (Map<String, HashMap<String, Object>>) getModule("sessionDatas");
    String threadName=Thread.currentThread().getName();
    HashMap<String ,Object> datas =sessionDatas.get(threadName);
    return datas.get(varname);
}

框架的单例有效节省了内存,提高了效率。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值