1.0 Alogic-FrameWork介绍

Alogic-FrameWork是一个轻量级的Java服务框架,源代码位于Alogic-Github。具有快速开发服务的特点,在alogic-framework下,一个成熟的Java开发者可以快速的开发出实现自己业务逻辑的Restful服务。在这里我们不谈具体的开发逻辑,而是专注于分析该框架的源码。
Alogic-FrameWork的一个HelloWorld级别代码如下:Alogic的HelloWorld-Github
其中主要包括以下几个部分:

  1. HelloWorld.java 服务调用的具体内容

  2. web.xml web项目构建的配置文件

  3. settings.xml Alogic服务目录配置文件

  4. servant.xml 服务描述配置文件

1.1 服务初始化入口

服务由servlet进行拦截,servlet-class对请求进行处理,并返回响应。

在一个HelloWorld级别的应用中,一个典型的web.xml配置如下:

<servlet>
    <display-name>MessageRouter</display-name>
    <servlet-name>MessageRouter</servlet-name>
    <servlet-class>com.anysoft.webloader.ServletAgent</servlet-class>
    <init-param>
        <param-name>handler</param-name>
        <param-value>com.logicbus.backend.server.http.MessageRouterServletHandler</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>MessageRouter</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>

服务通过注册ServletAgent类拦截/services/*的全部路由,其中初始化参数handler为MessageRouterServletHandler类。我们来看一下如何进行这个过程:

1.2 服务上下文的处理

ServletAgent是一个代理类,继承自HttpServlet,主要代理了Servlet的初始化、执行和销毁,其中重写了init(ServletConfig servletConfig),它通过ClassLoader类加载器加载实际处理的ServletHandler,并由ServletHandler去执行它的初始化方法。

public void init(ServletConfig servletConfig) throws ServletException {
    // 获取handler参数
    String handlerClass = servletConfig.getInitParameter("handler");
    // 获取当前Servlet的上下文
    ServletContext sc = servletConfig.getServletContext();
    // 获取当前web项目类加载器
    ClassLoader classLoader = (ClassLoader) sc.getAttribute("classLoader");
    if (classLoader != null) {
        try {
            //创建Handler实例
            handler = (ServletHandler) classLoader.loadClass(handlerClass)
                    .newInstance();
            //执行Handler的初始化方法
            handler.init(servletConfig);
        } catch (Exception e) {
            logger.error("Error occurs when creating handler:"
                    + handlerClass, e);
        }
    } else {
        logger.error("Can not find classLoader");
    }
}

ServletHandler是一个接口,重新封装了关于Servlet的init、service、destory方法;在web.xml中需要配置它的实现方法,MessageRouterServletHandler是它的具体实现类,在init实现方法中,设置了一些关键属性,如Http的编码、跨域、缓存等属性,以及获取服务id、目录、访问控制等。

在doService实现方法中,将上述初始化的一些列属性设置,达到重新封装Http请求的目的;service方法初始化Context,并将参数作为输入传入到action方法中。如下:

    // 初始化HttpContext,HttpContext是Context类的一个实现,它是一个封装后的Http请求的上下文。
    HttpContext ctx = new HttpContext(request,response,encoding,interceptMode);
    // 获取当前服务路径的id
    Path id = normalizer.normalize(ctx, request);
    MessageRouter.action(id,ctx,ac);

ServletAgent代理ServletHandler处理UML图

1.3 服务请求过程

上文提到过,MessageRouterServletHandler实现了ServletHandler接口,将Http上下文封装起来,同时将doService方法中获取得到的服务id,访问控制给MessageRouter的action方法。MessageRouter是Alogic的消息路由器,是具体负责每一个请求的路由过程。在action方法中,包括以下逻辑:

  1. 处理路由追踪

  2. 获取服务实例池

  3. 通过访问控制器分配访问优先级

  4. 从服务实例池获取实例

  5. 日志记录

首先,MessageRouter根据获取得到的服务id来获取一个服务实例池,通过资源池模式来保证服务实例的不断重复利用。资源池获取代码如下:

        // 获取服务实例池
        ServantFactory factory = servantFactory;
        // 根据服务id获取服务工厂
        pool = factory.getPool(id);        
        if (!pool.isRunning()){
            throw new ServantException("core.service_paused",
                "The Service is paused:service id:" + id);
        }

而接着,对于已经获得的资源池中根据优先级获得服务实例。在非线程模式下调用execute()方法,在多线程模式下建立服务工作线程。当执行结束后,向服务池归还资源。

        //从服务实例池中拿服务实例,并执行
        servant = pool.borrowObject(priority);
        // 判断是否获取到了服务并输出错误日志
        if (servant == null){
            logger.warn("Can not get a servant from pool in the limited time,check servant.queueTimeout variable.");
            ctx.setReturn("core.time_out", "Can not get a servant from pool in the limited time,check servant.queueTimeout variable.");
        }else{
               if (!threadMode){
                //在非线程模式下,不支持服务超时
                execute(servant,ctx);
            }else{
                // 构建CountDownLatch,用于等待服务工作线程建立。
                CountDownLatch latch = new CountDownLatch(1);
                //建立服务工作线程
                ServantWorkerThread thread = new ServantWorkerThread(servant,ctx,latch,tc != null ? tc.newChild() : null);
                thread.start();
                // 判断服务工作线程是否在指定的时间内建立完成。如果超时则取消主线程阻塞状态,并
                if (!latch.await(servant.getTimeOutValue(), TimeUnit.MILLISECONDS)){
                    ctx.setReturn("core.time_out","Time out or interrupted.");
                }
                thread = null;
            }
        }
    }catch (ServantException ex){
        ctx.setReturn(ex.getCode(), ex.getMessage());
        logger.error(ex.getCode() + ":" + ex.getMessage());
    }catch (Exception ex){
        ctx.setReturn("core.fatalerror",ex.getMessage());
        logger.error("core.fatalerror:" + ex.getMessage(),ex);
    }catch (Throwable t){
        ctx.setReturn("core.fatalerror",t.getMessage());
        logger.error("core.fatalerror:" + t.getMessage(),t);            
    }
    finally {
            ctx.setEndTime(System.currentTimeMillis());
            if (ctx != null){
                // 完成Context
                ctx.finish();
            }
            if (pool != null){
                if (servant != null){
                    // 向服务池归还资源
                    pool.returnObject(servant);        
                }
                // 服务池访问一次                
                pool.visited(ctx.getDuration(),ctx.getReturnCode());
                if (ac != null){
                    ac.accessEnd(sessionId,id, pool.getDescription(), ctx);
                }                
            }                        
            if (bizLogger != null){                
                //需要记录日志
                log(id,sessionId,pool == null ? null : pool.getDescription(),ctx);
            }
            if (tracerEnable){
                boolean ok = ctx.getReturnCode().equals("core.ok");
                Tool.end(tc, "ALOGIC", id.getPath(), ok ?"OK":"FAILED", ok ? ctx.getQueryString() : ctx.getReason(), ctx.getContentLength());
            }
        }

在非线程模式下的execute方法执行了服务调用的前置方法、执行方法和后置方法。

    protected static int execute(Servant servant,Context ctx) throws Exception {
        servant.actionBefore(ctx);
        servant.actionProcess(ctx);
        servant.actionAfter(ctx);
        return 0;
    }

在多线程模式下,同样也是在服务线程中执行Servant接口的三种方法。

    public void run(){
        TraceContext tc = null;
        if (traceCtx != null){
            tc = Tool.start(traceCtx.sn(), traceCtx.order());
        }
        boolean error = false;
        try
        {
            m_servant.actionBefore(m_ctx);
            m_servant.actionProcess(m_ctx);
            m_servant.actionAfter(m_ctx);
        }
    }

1.4 服务响应

在MessageRouter的acion方法中,服务调用的最后会调用ctx.finish(),在这个方法中调用了msg的finish方法。

    try {
            if (!isIgnore()){
                if (msg == null){
                    if (getReturnCode().equals("core.ok")){
                        response.sendError(404, "No message is found,check servant implemention.");
                    }else{
                        response.sendError(404, getReturnCode() + ":" + getReason());
                    }
                }else{
                    response.setCharacterEncoding(encoding);
                    msg.finish(this,!cometMode());
                }
            }
        }

Message是一个接口,主要代表服务输出的消息实例。在Alogic中,Message可以有XML、JSON等协议的消息实例,如输出为RawMessage时,finish方法如下:

    public void finish(Context ctx,boolean closeStream) {
        // 设置输出流
        OutputStream out = null;
        try {
            // 设置返回内容格式
            ctx.setResponseContentType(contentType);
            out = ctx.getOutputStream();
            byte [] bytes = buf.toString().getBytes(ctx.getEncoding());
            contentLength += bytes.length;
            // 将字符串写到输出流中
            Context.writeToOutpuStream(out, bytes);
            // 输出打印
            out.flush();
        }catch (Exception ex){
            logger.error("Error when writing data to outputstream",ex);
        }finally{
            if (closeStream)
            IOTools.close(out);
        }
    }

到此,一个服务的执行逻辑如下:
服务时序图


jungieve
2 声望4 粉丝

北海虽赊,扶摇可接。