开发者社区> 问答> 正文

充分利用多核优势,高效并行渲染页面--改造nutz使其成为支持并行计算的MVC框架? 400 报错

充分利用多核优势,高效并行渲染页面--改造nutz使其成为支持并行计算的MVC框架? 400 报错

首先我们来看一个场景:

 


       此图为sohu 首页的一部分。

这个页面比较典型,其实只要是复杂一些的页面通常都由许多不同模块的数据内容组成。如该页面就包含了新闻、体育、娱乐、视频等内容。每一个模块都需要单独查询,然后将结果填充到页面的各个部分。

如果由你来实现这个页面,你会怎么写呢?

通常,我们的写法是 (struts2.0):

Java代码    收藏代码
  1. public String index() {  
  2.     //查询新闻  
  3.     newsList = service.queryNewsList();  
  4.     //查询体育  
  5.     sportsList = service.querySportsList();  
  6.     //查询视频  
  7.     videoList = service.queryVideoList();  
  8.     return SUCCESS;  
  9. }  

 这样是可以完成任务的,但是每一个查询都必须等待上一个查询结束后才能开始。假设查询比较耗时(不考虑缓存的因素):新闻查了20秒,体育查了10秒,视频查了5秒,则总页面至少需要 35 秒后才能打开。

 

如果老板对性能要求较高,且 SQL优化已经起不到帮助作用,那么还有没有其它办法可以提高程序的效率呢?

 

对了,最简单的就是在页面中使用 iframe,将页面拆分成若干个子页面并行加载。这样每个部分执行快慢并不影响页面的整体打开速度,从总体上讲页面的加载速度提高了。但是过多的使用iframe,会带来许多负面影响。如session问题、内存占用问题、样式问题等等,而且页面的可维护性也将变差。因此这并不是一种好的解决方案。

或者也可以采用 ajax 动态获取各个部分并填充页面。但这样大大增加了前端的代码量及实现难度。

 

现在我们的服务器往往都是4核、甚至是8核的了,我们能不能充分利用多核优势在后台并行运算结果并统一渲染页面呢?

思路1:当用户请求到 indexAction 的时候,启动n个子线程分别查询不同模块数据。最终当所有线程处理结束后,合并结果,渲染页面。

使用多线程,就必须解决线程同步、加解锁等等问题。需要大大增加代码量,且要求程序员水平较高。如果一个项目中,程序员水平参差不齐,那么最好别采用这种方案,否则不知哪里的出了问题就会影响整个项目的质量。

 

思路2:让框架去干脏活、累活。当用户请求到 indexAction 的时候,我们利用框架的功能,动态将该请求模拟成为n个子action的请求,并在子action结束时自动合并结果,渲染页面。这样我们就通过框架虚拟出了iframe 的效果,同时避免了页面中使用 iframe 带来的问题,且降低了程序的复杂度。

采用这种方案,原有的项目无需大改,程序员也可以用熟悉的方法去开发,无需关注多线程及数据合并等等问题,可谓一举多得。

现在我们来改造 NutzMVC 框架,使其能支持并行计算功能。

Nutz是一个国产的开源框架,融合了MVC、Ioc、AOP、Dao诸多功能,且设计合理,预留了多处扩展点,用户可以很容易的动态给它增、减功能。因此今天就拿它来开刀。

Nutz地址为:http://code.google.com/p/nutz/ 有兴趣的同学可以去看看。下面的内容假设您已经对 Nutz有所了解。

为了在程序中虚拟出对 action 请求,需要用到 UrlMapping 类的实例。这个类在系统启动时会自动创建,但默认为 private 直接获取不到。因此需要自定义一个加载器,将UrlMapping 实例开放出来。

这个类很简单,只需重载下 NutLoading 即可。

 

 

 

Java代码    收藏代码
  1. /** 
  2.  * 缺省加载器的基础上公开 urlMaping 属性,使 MVC 运行时可以并行执行其它 URL 
  3.  * @author Gongqin(gongqin@gmail.com) 
  4.  */  
  5. public class ProLoading extends NutLoading {  
  6.       
  7.     private static UrlMapping urlMaping = null;  
  8.   
  9.     @Override  
  10.     public UrlMapping load(NutConfig config) {  
  11.         urlMaping = super.load(config);  
  12.         return urlMaping;  
  13.     }  
  14.   
  15.     /** 
  16.      * 返回 urlMaping 实例 
  17.      * @return  
  18.      */  
  19.     public static UrlMapping getUrlMaping() {  
  20.         return urlMaping;  
  21.     }  
  22. }  
 

 

 

新增加一个注解,用于标识需要异步请求的子 action 地址

 

Java代码    收藏代码
  1. /** 
  2.  * 声明一个需要异步请求的 url 地址<br/> 
  3.  * 例如:  
  4.  * <code> 
  5.  * @Asyn({"retb:/b", "retc:/c"}) 
  6.  * </code>  
  7.  * 对 /b 和 /c 请求的返回值会分别存入 req 的 retb 和 retc 的属性中。 
  8.  *  
  9.  * @author Gongqin(gongqin@gmail.com) 
  10.  */  
  11. @Retention(RetentionPolicy.RUNTIME)  
  12. @Target({ElementType.TYPE, ElementType.METHOD})  
  13. @Documented  
  14. public @interface  Asyn {  
  15.       
  16.     /** 
  17.      * 需要异步请求的 url,支持同时进行多个请求<p/>  
  18.      * 每行的格式为 key:url 。运行完成后会将 key 做为主键存入 req 中去。如果没有写 key,则认为您已经自己把返回值做了处理 
  19.      */  
  20.     String[] value();  
  21.       
  22.     /** 
  23.      * 异步执行的子action最长处理时间,默认为 30 秒 
  24.      * @return  
  25.      */  
  26.     int timeout() default 30;  
  27.       
  28. }  

 

 

 这里到了今天的核心:定制一个动作链处理器,用于解析注解,并异步执行子 action ,最后合并处理结果。

 

Java代码    收藏代码
  1. /** 
  2.  * 异步执行的动作链处理器 
  3.  * @author Gongqin(gongqin@gmail.com) 
  4.  */  
  5. public class AsynProcessor extends AbstractProcessor {  
  6.   
  7.     /** 
  8.      * 解析 Asyn 注解并异步执行子 action,最后合并处理结果 
  9.      */  
  10.     @Override  
  11.     public void process(final ActionContext ac) throws Throwable {  
  12.         Asyn asyn = ac.getMethod().getAnnotation(Asyn.class);  
  13.         // 如果没有异步任务,则处理完毕后直接返回  
  14.         if (null == asyn || null == asyn.value() || asyn.value().length == 0) {  
  15.             doNext(ac);  
  16.             return;  
  17.         }  
  18.   
  19.         // 开始处理异步任务  
  20.         ExecutorService executor = Executors.newCachedThreadPool();  
  21.         List<Future<Pair<Object>>> tasks = new ArrayList<Future<Pair<Object>>>(asyn.value().length);  
  22.         for (final String url : asyn.value()) {  
  23.             //增加一个异步请求  
  24.             tasks.add(executor.submit(new Callable<Pair<Object>>() {  
  25.                 @Override  
  26.                 public Pair<Object> call() throws Exception {  
  27.                     if (url == null)  
  28.                         return null;  
  29.                     String key = null;  
  30.                     String tourl = url;  
  31.                     int pos = url == null ? 0 : url.indexOf(":");  
  32.                     if (pos > 0) {  
  33.                         key = url.substring(0, pos);  
  34.                         tourl = url.substring(pos + 1);  
  35.                     }  
  36.                     ActionContext subAc = new ActionContext();  
  37.                     subAc.setRequest(new PathInfoRequest(tourl, ac.getRequest())).setResponse(ac.getResponse());  
  38.   
  39.                     ActionInvoker ai = ProLoading.getUrlMaping().get(subAc);  
  40.                     ai.invoke(subAc);  
  41.                     return new Pair<Object>(key, subAc.getMethodReturn());  
  42.                 }  
  43.   
  44.             }));  
  45.         }  
  46.   
  47.         doNext(ac);  
  48.   
  49.         // 获取其它 action 的返回值,供页面渲染  
  50.         for (Future<Pair<Object>> future : tasks) {  
  51.             try {  
  52.                 Pair<Object> ret = future.get(asyn.timeout(), TimeUnit.SECONDS);  
  53.                 if (ret != null && !Strings.isBlank(ret.getName())) {  
  54.                     ac.getRequest().setAttribute(ret.getName(), ret.getValue());  
  55.                 }  
  56.             }  
  57.             catch (TimeoutException e) {  
  58.                 //忽略处理超时的任务  
  59.             }  
  60.         }  
  61.     }

展开
收起
爱吃鱼的程序员 2020-05-29 20:00:05 1397 0
1 条回答
写回答
取消 提交回答
  • https://developer.aliyun.com/profile/5yerqm5bn5yqg?spm=a2c6h.12873639.0.0.6eae304abcjaIB

    Nutz VS Jfinal ,元芳你怎么看######搞不懂,一直不成功啊。。######

    引用来自“黄道坤”的答案

    搞不懂,一直不成功啊。。
    哪里有问题? 
    ######

    引用来自“天天64”的答案

    引用来自“黄道坤”的答案

    搞不懂,一直不成功啊。。
    哪里有问题? 
    只能触发一个action呢
    ######

    回帖只为收藏。

    ######

    例子举的不太好,这种页面都是静态化。

    注意控制总线程数

    2020-05-29 20:00:07
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
Java Spring Boot开发实战系列课程【第7讲】:Spring Boot 2.0安全机制与MVC身份验证实战(Java面试题) 立即下载
Web服务架构变化及性能优化 立即下载
高性能Web架构之缓存体系 立即下载