Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1574912
  • 博文数量: 399
  • 博客积分: 8508
  • 博客等级: 中将
  • 技术积分: 5302
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-14 09:28
个人简介

能力强的人善于解决问题,有智慧的人善于绕过问题。 区别很微妙,小心谨慎做后者。

文章分类

全部博文(399)

文章存档

2018年(3)

2017年(1)

2016年(1)

2015年(69)

2013年(14)

2012年(17)

2011年(12)

2010年(189)

2009年(93)

分类: 架构设计与优化

2015-04-17 17:06:18

批量处理是很多公司在开发Mobile或者Web 服务后端时都会想要拥有的一种服务,典型应用场景为数据的批量导入import,以及批量的更新update

这里希望基于以往的实践经验给大家提供参考。

情景1) 批量处理同质化的请求
           可以将这种功能内置到某个REST controller 的内部,将的参数封装在一个数组里面,传来controller method ,这样HTTP body 类似与
            {
                    "parameters": [
                
                        {
                            "size": 12
                            .......
                        },

                        {
                            
"size": 12
                            .......
                        },         
                        
                        .............
                    ]
             }

情景二)  批量处理异质化的请求,且请求之间没有相互依赖关系
                  
                 这种就没有办法放在某个已有的 controller 里面了,我们要有真正的 BatchController了,注意我们的request body  要能够 wrap 各个请求的参数,而且我们的 response body也要能够 wrap 各个请求的响应。处理的时候需要遍历每个子请求,使用每个URL对应的dispatcher (参考RestLifecycleListener) 派送子请求并保存对应的响应。
        

  protected BatchResult[] performBatchOperations( BatchOperation[] operations, String basePath,    HttpServletRequest request )

  {
  

  for ( BatchOperation operation: operations )

    {


        BatchRestRequest batchRequest = new BatchRestRequest( request, operation, url );

        String uri = batchRequest.getRequestURI();

        dispatcher = getRequestDispatcher( request, url );

        if ( dispatcher != null )

        {

          try

          {

            BatchRestResponse responseWrapper = new BatchRestResponse();

            try

            {

              dispatcher.forward( batchRequest, responseWrapper );

            }

            rawBody = wrapper.getRawBody();

            status = wrapper.getStatus();

            headers = wrapper.getHeaders();

          }
      }

      BatchResult res = new BatchResult( operation.getMethod(), operation.getRelativeUrl(), url, status, rawBody, exception, headers );

      result[ i++ ] = res;
      ................
}


并发优化后的版本为:
注意第一个参数从  BatchOperation数组  变为处理单个请求,方便单核并发或多核并行处理。
protected BatchResult performBatchOperation( BatchOperation operation, String basePath, HttpServletRequest request ) {}
保留结果顺序,与请求一一对应。
class BatchResultGenerator implements Callable
 {
   private int _order;
   private BatchOperation _batchOperation;
   private String _basePath;
   private HttpServletRequest _request;

   public BatchResultGenerator( BatchOperation batchOperation, String basePath, HttpServletRequest request )
   {
     _batchOperation = batchOperation;
     _basePath = basePath;
     _request = request;
   }

   public BatchResultGenerator withIndex( int order )
   {
     _order = order;
     return this;
   }

   @Override
   public BatchResultWrapper call() throws Exception
   {
     return new BatchResultWrapper().setOrder( _order ).setResult( performBatchOperation( _batchOperation, _basePath,
                                                                                          _request ) );
   }

}

protected BatchResult[] performBatchOperations( BatchOperation[] operations, String basePath, HttpServletRequest request )
{
BatchResult[] result = new BatchResult[ operations.length ];

CompletionService service = new ExecutorCompletionService<>( Executors.newCachedThreadPool() );

for ( int count = 0; count < operations.length; count++ ) 
{
    service.submit( new BatchResultGenerator( operations[ count ], basePath, request ).withIndex( count ) );
}

for ( int count = 0; count < operations.length; count++ )
    BatchResultWrapper wrapper = service.take().get();
    result[ wrapper.getOrder() ] = wrapper.getResult();
}
return result;
}

情景三)  批量处理异质化的请求,且请求之间有相互依赖关系
,即后面的request需要依赖前来某个请求的response提供参数

这也是最复杂的一种,不过也有两种解决方案:

方案1,基于情景二dispatch 模型,但是需要为每个BatchRequest指定一个ID方便被引用, 对象中存入所依赖的BatchRequest的ID,然后拓扑排序拿到对应的拓扑结构,由此决定串行线和并行线。分别发出请求,处理请求,再发请求等。优点是只调用相关服务并处理请求的派送与结果的组装,缺点是效率相对直接代码调用要低。

方案2,不基于dispatch,同样的,需要为每个BatchRequest指定一个ID方便被引用, 对象中存入所依赖的BatchRequest的ID,服务器端拓扑排序拿到对应的拓扑结构,由此决定串行线和并行线,直接调用代码而不是内部转发HTTP请求,优点是效率高,缺点是需要直接touch具体处理请求的模块之代码,Controller方法要被直接调用,增加了代码的耦合度。

情景四)  批量处理异质化的请求,且请求之间有相互依赖关系同时具有事务性,即后面的request需要依赖前来某个请求的response提供参数,有一个依赖不满足,整个孤岛请求就不成功,部分处理成功的请求还需要回滚。
基于情景三,是事务性的问题,每种请求都要定义回滚的请求,比如 POST 创建 =》 DELETE 删除 , POST 更新 =》POST ROLL Back, DELETE 删除 =》 POST ROLL Back
后面两需要来自数据库的支持。

情景五) 批量处理请求,并返回各个任务进度
由于REST并不支持长连接,客户端需要请求相应的进度API来得到最新的进度,服务器端的进度数据可以存到数据库里面以供查询。

阅读(824) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~