• 我记录网站综合系统 技术原理解析[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发布后再介绍

  • 相关阅读:
    社区专家谈 12306
    一致性Hash算法(分布式算法)
    ASP.NET二级域名站点共享Session状态
    树莓派2 安装 win10Iot 和 Ubuntu mate
    Maven运行时异常java.lang.UnsupportedClassVersionError的解决方案
    Ubuntu安装steam游戏平台的解决方案
    Spring配置JNDI的解决方案
    Intellij Idea无法从Controller跳转到视图页面的解决方案
    电话激活windows server 2012的解决方案
    如何下载Red Hat Enterprise Linux系统
  • 原文地址:https://www.cnblogs.com/TextEditor/p/2085461.html
Copyright © 2020-2023  润新知