zoukankan      html  css  js  c++  java
  • 《rose portal & pipe技术介绍》之《变革:结构&范围》

    http://code.google.com/p/paoding-rose/wiki/Rose_Portal_Inside


    结构:从单整体到多窗口

            网页是指通过HTTP协议向服务器获取的资源表示。一个网页原本可能是很简单的信息,比如就包含一个用户信息,但现在互联网的应用即使是展示一个用户的信息也不再是简单了,不只是姓名、性别、工作,而是一个信息聚合页,在这个页面中可能有“我的好友”、“最近来访”,“我的日志”、“我的相册”、“我的新鲜事”等等。

    独立性

            “我的好友”和“最近来访”等等子内容它们之间毫无关系:在执行顺序上不需要有先后依赖的设定,在页面内容显示上不需要互相“渗透”。因此,当用户访问一个这个用户信息页的时候,我们应该把这些分解为独立模块,这些独立的模块在rose portal & pipe中称之为“小窗口”。

            在这些小窗口需要有自己的地址,假设“个人信息页”的地址是 /profile/123456/,  那么这些小窗口的地址可能就是:

    •  我的好友 /profile/123456/window/friend
    •  最近来访 /profile/123456/window/footprint
    •  我的日志 /profile/123456/window/blog
    •  我的相册 /profile/123456/window/album
    •  新 鲜 事 /profile/123456/window/feed

          在rose portal & pipe框架下,您应该像开发其它请求一样开发这些窗口,有自己的地址、自己的控制器、自己的模型数据、自己的页面内容:框架下,您应该像开发其它请求一样开发这些窗口,有自己的地址、自己的控制器、自己的模型数据、自己的页面内容:

                    package com.renren.sns.controllers.window;

                    public class FriendController {

                           @Get

                           public String list(Invocation inv, @Param("userId") long userId) {

                                  inv.addModel("friends", ...);

                                  return "firend-list";

                           }

                    }

                    package com.renren.sns.controllers.window;

                    public class FootprintController {

                           @Get

                           public String list(Invocation inv, @Param("userId") long userId)  {

                                  inv.addModel("footprints", ...);

                                  return "footprint-list";

                           }

                    }

                   ……

            从以上代码可以看出,开发一个小窗口时,完全看不出它原来其实是属于一个页面的子页面。

            再回过来看主页面“个人信息页”的代码,在主页面中将调用portal的的addWindow将五个独立的小窗口加进来:

                    package com.renren.sns.controllers.profile;

                    @Path("")

                    public class ProfileController {

                           @Get

                           public String index(Portal portal, @Param("userId") String userId) {

                                  portal.addWindow("friend", "/profile/" + userId + "/window/firend");  

                                  portal.addWindow("footprint", "/profile/" + userId + "/window/footprint");

                                  portal.addWindow("blog", "/profile/" + userId + "/window/blog");

                                  portal.addWindow("album", "/profile/" + userId + "/window/album");

                                  portal.addWindow("feed", "/profile/" + userId + "/window/feed");

                                  return "profile";

                           }

                    }

    复用性

             我们经常遇到一些不同的页面,它们需要一些共同的内容,但又不是所有页面都需要这些,也就是说“这些共同内容”要复用到不同的页面中去。

             “我的首页”是一个和“个人页”很不同的页面(虽然有些人的确分不清),它是登录者登录到网站后自己的“主战场”,接受网站推送的有关他或他好友等信息的页面,“个人页”则是用于他人访问,通过它向访问者展示专属于自己的一些信息。

             “我的首页”很可能也需要“最近访问者”,这个小窗口有助于登录者了解到自己最近被关注的情况,这是很人性的设计。

           在rose portal & pipe 下,“最近访问者”就可以被复用了:

            package com.renren.sns.controllers;

            @Path("")

            public class HomeController {

                   @Get

                   public String index(Portal portal) {

                          String myId = …;

                          portal.addWindow("footprint ", "/profile/ " + myId + "/window/footprint ");

                          return "home ";

                   }

            }

            更高级的,您可以根据页面、时段、登录者、访问者、负载等不同因素来决定是否要往portal增加或减少一个可复用的Window,这样使得您的网站定制化非常灵活。这个特性不是什么没用的东西,一般人我不告诉他,这个特性能挣钱、对销售有正作用。

    范围:从点到线

            让我讲一个故事。2009年,我们曾经设计过这么一个工具:该工具提供一个通用接口Task,每个单独的模块都实现为一个具体的Task类,同时提供一个TaskManager类负责管理和执行(注1),这样在“个人页”的主页面我们这样写:

                    TaskManager manager = new TaskManager(request, response);

                    manager.addTask(new FriendTask());

                    …

                    manager.addTask(new FeedTask());

                    manager.execute();

                    return " profile";

                    // end

           每个TaskManager和本次请求有关,开发者能够把每个需要执行的Task对象注册到该Manager中,最后由这个manager执行!由于Task可以通过TaskManager对request进行操作,所以每个Task都可以把自己的模型数据设置给request属性。执行完毕后返回profile.jsp,该jsp能够把task设置的属性渲染出来最终把页面“吐”给浏览器。

           现在来稍微比较一下TaskManager和rose portal & pipe的2大区别:

    • TaskManager的设计要求开发者需要把能够独立的逻辑实现为另一个接口内;而rose portal & pipe承诺开发者无论开发一个普通页面还是窗口模块都是一样的写法;
    • TaskManager的设计只负责逻辑的独立封装;而rose portal & pipe承诺开发者,您可以把逻辑、页面内容通通进行独立化;

           rose portal & pipe的优势不仅仅体现在这两大点,但已经能够带来很大改进和开发效率的提高。一、开发者们可以像开发普通请求那样去开发每个独立的小窗口,之前为普通请求开发的过滤器、拦截器、超时切换、并发最大控制、统计、监控等基础设施可以无缝应用到小窗口中;二、之前开发普通页面的习惯、规范、模式可以无缝移植到小窗口中等等。

           我把TaskManager称是一个单兵种,而把rose portal & pipe称是海陆空式到一条线作战:

           上述提到的所有基础服务、开发习惯可以无缝移植,这是集团作战的体现之一;

           每个单独的小窗口都有自己独立控制的、完整的请求参数解析、控制器业务、页面返回和渲染,而不仅仅局限在只是对控制器业务的封装,这是集团作战的体现之二。

           请看代码:

            package com.renren.sns.controllers.window;

            public class FootprintController {

                   @Get

                   public String list(Invocation inv, @Param("userId") long userId)  {

                          inv.addModel("footprints", ...);

                          return "footprint-list";

                   }

            }

            这是“最近来访”小窗口的控制器代码。rose portal & pipe负责把userId参数解析好送到这个控制器;支持控制器可以有自己的数据”footprints”,而且这个数据的key即使和其他窗口的数据产生了同名,仍旧不会有冲突(TaskManager会冲突);支持控制器返回footprint-list.jsp自己负责渲染自己这个小窗口的内容。

            同时,正是这种机制,您除了在“个人页”上看到这个“最近来访”,完全还可能在浏览器中单独去请求它,对程序调试也起了正面作用。

    ----

    注1:

    关于Task&TaskManager设计:Task、TaskManage实际只是那个“曾经”的设计的简化版本,并进行了别名化的结果。原设计为了使得在非web情况下也能使用做了一些封装,为了演示方便,在不改变其设计思想的条件下,作者在此对其设计上进行了适当裁减,使它和servlet api进行紧密联系。

    计算:从串行到并行

           如引导中所提及,rose portal / pipe 能够使独立的窗口并发执行。当一个请求执行到主控制器后,开发者通过 addWindow 派发出不同的窗口线程,以下以 pipe 作为图例:


            当一个请求到来时候,起初只有一个线程处理这个请求(t0 - t1),addWindow 被调用后, 整个请求被分解为多个窗口并处于并行下(t1 - t2)。当窗口执行完毕后退出该线程,最后由主控线程负责向客户端输出页面内容(t2 - t3)。

           线程池的参与:poral / pipe 的 addWindow方法,将请求封装为一个实现 Runnable 接口的 WindowTask,把这个 WindowTask 提交到统一的 java.util 的 executorService (线程池)执行。

           主控线程的执行:在 t1 - t2 时间段,主控线程并不是完全停滞不执行,它仍旧会执行 addWindow 之后的代码。对于 portal 而言,他会在控制器程序结束之时停下来等待所有的窗口的完成(t2),而后才继续之后的拦截器以及最后的页面输出;而 pipe 则不等待窗口的执行是否完毕紧接执行后续的程序(拦截器等)并输出主框架页面(这个页面尚不会包含窗口内容),当主框架页面输出完毕后,主控线程才停下来等待窗口的完成(t2),每完成一个窗口,主线程就将该窗口输出给客户端。

    传输:从单一到分包

    既然一个页面可以有不同的窗口组成、不同的窗口可以并行执行,那有的窗口可能执行的快、有的窗口就会执行的慢速。portal的做法就是等最慢的那个窗口完成后,才最终把页面输出给客户端,而pipe则并不依照此:哪个窗口先执行完毕就把哪个窗口送到客户端,从而实现在传输上从单一到分包的策略。

    每当一个窗口执行完毕后,主控线程就能收到通知,并把它包装成一个 javascript 串送至浏览器,由浏览器执行这个 javascript 把内容设置到窗口该显示的位置上。

  • 相关阅读:
    虚拟机Linux5下安装MyEclipse2014的步骤
    鉴赏
    Linux环境下安装WebStorm
    Bootstrap模态框
    AngularJs
    按钮颜色
    虚拟机里的weblogic之后怎样进入
    搭建GlusterFS文件系统
    邮件服务器fixpost服务(1)
    unbound域名解析
  • 原文地址:https://www.cnblogs.com/danghuijian/p/4400039.html
Copyright © 2011-2022 走看看