zoukankan      html  css  js  c++  java
  • 我记录网站综合系统 技术原理解析[11:ActionProcessor流程wojilu核心]

    这篇文章不是专门讲数据缓存的,但是这里有大量缓存内容,wojilu的缓存系统近期升级了,可能我的理解不是最正确的。

    改日将会写专门的缓存文章。

    源代码位置:\Web\Mvc\Processors:ActionProcessor.cs

    Config ->

        wojilu.Web.Mvc.CoreHandler.ProcessRequest - >

              wojilu.Web.Mvc.CoreHandler.ProcessRequest: ProcessContext.Begin ->

                   RouteProcess
                        InitContextProcess

            ActionMethodChecker

            ForbiddenActionChecker

            LoginActionChecker 
            HttpMethodChecker

            PermissionChecker

              ActionProcessor

    wojilu是一个MVC系统,以前的流程只是一些外围的东西,从这里开始,有点MVC的味道了。

    下面对于ActionProcessor的主要处理分段介绍:

    1。和其他Processor一样的处理,做广播,告知其他侦听者;如果可以跳过这步,则跳过,不过,由于是核心部分,这里跳过的可能性不多。获得上下文内容,进而获得其中的Controller。

    1             MvcEventPublisher.Instance.BeginProcessAction( context.ctx );
    2             if (context.ctx.utils.isSkipCurrentProcessor()) return;
    3 
    4             MvcContext ctx = context.ctx;
    5 
    6             ControllerBase controller = context.getController();

    wojilu拥有一套缓存系统:下面这个步骤就是检查缓存。关于wojilu的缓存系统,曾经咨询过 作者 掷鸡蛋者。下面引用当时的邮件的内容:

    wojilu ORM中的缓存分成两部分:ContextCache(一级缓存)和ApplicationCache(二级缓存)。

    ContextCache(一级缓存)这里的ContextCache仅仅存在于一个上下文中,具体到网站访问上,就是页面开始到页面结束,之后就不存在了。虽然缓存时间是瞬间的(比如只有几十毫秒),但也很有意义。它不是静态缓存,作为一级缓存,在web情况下,它是放在HttpContext中的。它的特点是随用随弃,不用考虑缓存策略,不会给内存增加负担,而在实战中很有价值,比如一个很大的页面分成了5个部分,在每个部分进行数据绑定的时候,可能出现重复的数据读取,比如页面头部查询了user,页面中部、数据列表等也查询了user,这些重复的查询都会默认直接从ContextCache中读取。如果是自己手写sql,往往就要细心避免这里的sql重复查询问题。

    ApplicationCache(二级缓存)能长时间存在于内存中,作为二级缓存,往往是为了应付性能方面的压力。

    ContextCache基本上是必须的、默认的,而ApplicationCache则需要根据实际的应用场景来实施。通常情况下,只有从一级缓存中检索不到数据,才去二级缓存中继续检索,如果二级缓存中也没有,才去数据库读取。

    一级缓存是默认自动开启的,你不需要管他。

    二级缓存需要手动打开。对于一个网站来说,最容易看出优化效果的,是二级缓存。它的主要目的就是实现透明缓存,所谓“透明缓存”,也就是不需要手工控制缓存的失效或过期,由ORM自动管理。
    实现“透明缓存”的关键之处在于,在启用了ApplicationCache之后,每次查询出数据并加入缓存的时候,同时也缓存一个加入时间的时间戳

    我们以 ApplicationPool(对象缓存池) 为例,当将一个对象加入缓存的时候——
            public void Add( IEntity obj ) {

                if (obj == null) return;

                String key = CacheKey.getObject( obj.GetType(), obj.Id );
                addToApplication( key, obj );
                CacheTime.updateObject( obj ); // 加入缓存的时候,设置最新 timestamp
            }

    除了将对象放入缓存 addToApplication ,还保存了一个时间戳 CacheTime.updateObject( obj ) 。有了这个时间戳,那么,ORM就会知道哪些对象是更新过的,哪些对象是没有更新过的。在对象更新或删除的时候,会让缓存的对象刷新时间戳,比如在 UpdateOperation 中——
    CacheTime.updateTable( entityInfo.Type );
    表明这个对象已经被更新过了,所以下次客户端再检索就不会从缓存中取过期的对象。

    对于一系列对象的列表,会在缓存中维护一个对象的id的列表,而不是直接将List<Object>放进去,请看 ApplicationPool 的 addList 方法,
                List<int> ids = new List<int>();
                foreach (IEntity obj in objList) ids.Add( obj.Id );
                addToApplication( key, ids );

    缓存中存放的是 ids。

    总结一下:二级缓存的好处有两个:一是大幅度提高性能,降低数据库压力,不用重复查询数据库;二是全自动管理缓存,避免手工管理缓存的复杂度和bug。
     
    目前是三级缓存体系
    1)对象缓存(ORM层,内部又分成ContextCache上下文缓存和ApplicationCache应用缓存)
    2)Action缓存
    3)Page缓存
    2)和3)是1.7版新增的,
     
    下面的文章里面的缓存不属于ORM缓存。同时在1.7正式版里面缓存的名字可能变化。
    【从这里开始的以下内容,可能会有变化,是1.7正式版针对1.7beta版有变化】
    你可以通过下面两个属性来打开或者关闭Action和Page缓存。
    1         /// <summary>
    2         /// 是否启用action缓存(如果关闭,所有action缓存都会失效)
    3         /// </summary>
    4         public Boolean IsActionCache { get { return _isActionCache; } }
    5 
    6         /// <summary>
    7         /// 是否启用页面缓存(如果关闭,所有页面缓存都会失效)
    8         /// </summary>
    9         public Boolean IsPageCache { get { return _isPageCache; } }

          如果曾经这个Controller被访问过,第一次访问时候的数据将被缓存,直到数据被更新为止,缓存一直有效。下面的代码就是检查缓存。cacheKey 是Action的一个Key,在所有的Action缓存中,找寻缓存是否存在,如果存在的话,取得缓存内容,将内容放入当前的上下文中,接着从缓存里面获得页面的内容。由于直接从缓存中获得了需要的数据,整个过程在这里为止。

     1             // 检查缓存
     2             String cacheKey = null;
     3             if (MvcConfig.Instance.IsActionCache) {
     4 
     5                 IActionCache actionCache = ControllerMeta.GetActionCache( controller.GetType(), ctx.route.action );
     6 
     7                 cacheKey = getCacheKey( actionCache, ctx );
     8                 if (strUtil.HasText( cacheKey )) {
     9                     Object cacheContent = checkCache( cacheKey );
    10                     if (cacheContent != null) {
    11                         logger.Info( "load from actionCache=" + cacheKey );
    12                         context.setContent( cacheContent.ToString() );
    13                         getPageMetaFromCache( ctx, cacheKey );
    14                         return;
    15                     }
    16                 }
    17             }

     getCacheKey

    这里的ActionCache只是在GET的时候起作用。GET一般为了获取内容,POST一般是为了处理事件,所以这里的GET设计是非常好的。

    1         private String getCacheKey( IActionCache actionCache, MvcContext ctx ) {
    2             if (actionCache == nullreturn null;
    3             if (ctx.HttpMethod.Equals( "GET" ) == falsereturn null;
    4             return actionCache.GetCacheKey( ctx, ctx.route.action );
    5         }

      getPageMetaFromCache

     从缓存里面获取页面内容

    1         private static void getPageMetaFromCache( MvcContext ctx, String cacheKey ) {
    2 
    3             PageMeta p = CacheManager.GetApplicationCache().Get( cacheKey + "_pageMeta" ) as PageMeta;
    4             if (p != null) {
    5                 ctx.utils.setPageMeta( p );
    6             }
    7         }

    这里的一个概念先说明一下:缓存里面的东西有2种,数据和页面布局.[应该是这样的]

    我们在这里根据Action进行数据的获取或者处理,GET是获取,POST是处理。同时将获得的数据放入缓存(Action),以便下次使用,注意,这里是不分GET和POST的。这里的ActionRunner是实行业务逻辑的入口,在这里我们开始执行Action,也就是业务逻辑。同时将业务逻辑的结果放入缓存中。如果页面里面有INPUT的话,由于不能进行简单的替换,我们通过PostValueProcessor进行Input控件的值的设置和输入控件的验证错误的表示(在输入控件旁边标红,错误是前面业务逻辑执行的结果)。关于这部分,也和作者确认过了:以下是邮件原文:

    这段的内容是做Action处理,加入缓存,最后将页面上的Input设置Value是吧。
    例如 ActionRUn的时候做检索,结果加入缓存,然后设置结果页面上的Input?——是的,就是这个流程。其中ProcessPostValue是自动给表单赋值,如果有错误,同时给出错误提示。它的优点在于“自动”,我见过的所有框架,都需要手动在视图中提供错误的占位符,唯独wojilu是自动在form的顶部自动显示。见 http://www.wojilu.com/Common/Page/10 中的效果图:

     1         MethodInfo actionMethod = ctx.ActionMethodInfo; // context.getActionMethod();
     2 
     3             // 设值模板并载入全局变量
     4             setControllerView( controller, actionMethod );
     5 
     6             // 运行并处理post值
     7             ActionRunner.runAction( ctx, controller, actionMethod, controller.utils.runAction );
     8             String actionContent = controller.utils.getActionResult();
     9 
    10             // 加入缓存
    11             if (MvcConfig.Instance.IsActionCache) {
    12                 if (strUtil.HasText( cacheKey )) {
    13                     addContentToCache( cacheKey, actionContent );
    14                     // 加入PageMeta
    15                     addPageMetaToCache( ctx, cacheKey );
    16                 }
    17             }
    18 
    19             actionContent = PostValueProcessor.ProcessPostValue( actionContent, ctx );

         如果这个Action是一个Ajax局部页面的更新的话,我们将这个页面内容直接输出,ShowEnd可以指示后面的过程是否需要继续。showEnd 会跳过下面所有处理器,除了RenderProcessor[这个Processor不在前面所述的列表中,是一个单独的Processor,用来生成HTML字符串] 既然是一个AJAX,我们直接调用最后的RenderProcessor,生成HTML结果。如果是更新一个iFrame,也算是页面的一部分,我们获得需要的框架的HMTL后,同样ShowEnd。如果前面由于某些原因isEnd了,例如错误等等,这里也ShowEnd。不然的话,我们将Action的内容保存到上下文中,等待后续操作。最后如果是POST方法的话,数据可能有改动,所以我们updateActionCache,更新ActionCache。GET的时候不需要更新。 

     1             actionContent = PostValueProcessor.ProcessPostValue( actionContent, ctx );
     2 
     3             if (ctx.utils.isAjax) {
     4                 context.showEnd( actionContent );
     5             }
     6             else if (ctx.utils.isFrame()) {
     7 
     8                 int intNoLayout = ctx.utils.getNoLayout();
     9 
    10                 if (intNoLayout == 0) {
    11 
    12                     String content = MvcUtil.getFrameContent( actionContent );
    13                     context.showEnd( content );
    14                 }
    15                 else {
    16                     context.setContent( actionContent );
    17                 }
    18 
    19 
    20             }
    21             else if (ctx.utils.isEnd()) {
    22                 context.showEnd( actionContent );
    23             }
    24             else {
    25                 context.setContent( actionContent );
    26             }
    27 
    28             updateActionCache( ctx );

     这篇文章是MVC的中核,关于数据缓存等内容,等1.7发布后再介绍

  • 相关阅读:
    BNU 51002 BQG's Complexity Analysis
    BNU OJ 51003 BQG's Confusing Sequence
    BNU OJ 51000 BQG's Random String
    BNU OJ 50999 BQG's Approaching Deadline
    BNU OJ 50998 BQG's Messy Code
    BNU OJ 50997 BQG's Programming Contest
    CodeForces 609D Gadgets for dollars and pounds
    CodeForces 609C Load Balancing
    CodeForces 609B The Best Gift
    CodeForces 609A USB Flash Drives
  • 原文地址:https://www.cnblogs.com/TextEditor/p/2085461.html
Copyright © 2011-2022 走看看