在ASP.NET-MVC中捕获每个用户的页面浏览量的最佳方法是什么?

32

如何在不影响网站性能的情况下,以人为单位捕获页面浏览量?我看到stackoverflow上到处都显示页面浏览量。他们是每次单击页面时都要插入数据库吗?

在asp.net-mvc中,有没有推荐的方法来跟踪每个用户的页面访问量(我的网站有登录屏幕),以便我可以查看人们经常访问哪些页面和多久访问一次。


你正在使用哪个版本的ASP.NET MVC? - Shane Courtrille
4个回答

32

首先,如果您真正关心的是客户如何使用我的网站,那么您最有可能想要研究 Google Analytics 或类似的服务。

但是如果您想要一个快速而简单的页面浏览记录,并且正在使用ASP.Net MVC 3,则可以像Chris Fulstow提到的那样,使用全局操作筛选器和缓存的混合。以下是一个示例。

PageViewAttribute.cs 

    public class PageViewAttribute : ActionFilterAttribute
{
    private static readonly TimeSpan pageViewDumpToDatabaseTimeSpan = new TimeSpan(0, 0, 10);

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var calledMethod = string.Format("{0} -> {1}",
                                         filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
                                         filterContext.ActionDescriptor.ActionName);

        var cacheKey = string.Format("PV-{0}", calledMethod);

        var cachedResult = HttpRuntime.Cache[cacheKey];

        if(cachedResult == null)
        {
            HttpRuntime.Cache.Insert(cacheKey, new PageViewValue(), null, DateTime.Now.Add(pageViewDumpToDatabaseTimeSpan) , Cache.NoSlidingExpiration, CacheItemPriority.Default,
                                  onRemove);
        }
        else
        {
            var currentValue = (PageViewValue) cachedResult;

            currentValue.Value++;
        }
    }

    private static void onRemove(string key, object value, CacheItemRemovedReason reason)
    {
        if (!key.StartsWith("PV-"))
        {
            return;
        }

        // write out the value to the database
    }

    // Used to get around weird cache behavior with value types
    public class PageViewValue
    {
        public PageViewValue()
        {
            Value = 1;
        }

        public int Value { get; set; }
    }
}

并且在你的 Global.asax.cs 文件中

public class MvcApplication : HttpApplication 
{
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new PageViewAttribute());
        }
}

仅适用于Pre-ASP.Net MVC 3版本,您需要手动将相同的属性应用于所有操作。

[PageView]
public ActionResult CallOne()
{
}

[PageView]
public ActionResult CallTwo()
{
}

2
不清楚你的数据库设置方式..这部分就由你来完成吧 :) 在onRemove函数中看到了吗,它说//将值写入数据库。 - Shane Courtrille
@ShaneC - OnRemove 方法是什么时候被调用的?每次页面加载都会执行吗? - leora
是的,您会想要更新数据库。如果您重新启动服务器,理论上可以编写一些代码来处理Application_End()场景,但前提是您关心最后N分钟的数据。个人认为这不值得花费那么多精力。 - Shane Courtrille
1
@ usr-您不必关心其他内容(例如内容请求) - RPM1984
这个方法可以正常工作,但它也会计算子操作、通过Ajax获取JSON内容、重定向等等... 你可能想使用 OnActionExecuted 来代替,并检查 filterContext.Result is ViewResult,以获得更高的准确性。 - Xavier Poinas
显示剩余6条评论

19

最好的方式可能是使用全局操作过滤器来拦截所有控制器上所有操作的请求,然后在数据库中为当前用户和页面递增计数器。为了避免对数据库产生太大的影响,您可以缓存这些值,并根据处理的流量情况每隔几分钟使它们无效。


2
你能详细解释一下如何缓存值并每隔几分钟使其失效吗? - leora
请查看 System.Web.Caching.Cache 类 - http://msdn.microsoft.com/zh-cn/library/system.web.caching.cache.aspx - 它可以让您将值缓存在内存中并设置过期日期。 - Chris Fulstow
一个操作过滤器不是正确的选择。你不会收到所有请求,只有MVC请求。 - usr
2
@usr 不要认为我们想要跟踪所有请求?只是页面浏览量,这些都可以合理地假定为MVC控制器操作。 - Chris Fulstow

5
我们使用开源的Piwik:http://piwik.org/,它被设置在自己的服务器上。在_Layout页面中插入一行JavaScript代码,在页面加载后调用Piwik(将JS放在末尾),不会影响页面加载性能。
除了计数之外,您还可以获得有关用户来源、浏览器、屏幕分辨率、已安装插件等大量信息。此外,您还可以跟踪转化并使用同一工具跟踪营销活动等。
我无法想象有什么情况下您会更好地在MVC或您的Web应用程序中实现此功能。这些东西根本不属于您的Web应用程序,是一个元关注点,应该分离出来。这种方法使我们能够以统一的方式跟踪所有应用程序(32个应用程序:mvc 2/3、webforms、php……)的分析数据。
如果您真的不想使用另一个工具来完成此目的,我建议利用IIS日志并从那里获取统计信息。同样,要从中获得任何真正的决策权,您需要在其上放置一个良好的分析器。我推荐Splunk:http://www.splunk.com/

将此类分析的JavaScript放在页面顶部有一个论点,因为它将能够记录被放弃的页面请求或非常快速的页面请求-例如,如果我点击“产品”,并且在页面加载完成之前我发现了一个“相机”的链接并单击它-页面底部的脚本实际上不会记录我访问“产品”页面的情况。 - Fenton
@Sohnee:同意。Piwik还有一种通过嵌入1像素图像来跟踪点击的方法。如果JavaScript性能成为问题,这将是一个略微不那么强大的替代方案。 - Shashi Penumarthy
1
@xameeramir 是的,但我们的网络团队发现Google Analytics更好,所以我们正在切换过程中。此外,我们不是PHP的粉丝。 - Shashi Penumarthy

1
我想为那些有兴趣的人发布Shane答案的更新版本。需要考虑以下几点:
  1. 必须将操作属性设置为服务,使用如下语法装饰方法:

    [ServiceFilter(typeof(PageViewAttribute))]

  2. 据我所知,在.NET Core中没有HttpRuntime.Cache.Insert,因此我使用简单的IMemoryCache实现(您可能需要在startup.cs中添加此行以使用接口):

    services.AddMemoryCache();

  3. 因为我们将IMemoryCache注入到不是控制器的类中,所以我们需要在startup.cs中将属性注册为服务,如下所示:

    services.AddScoped<[PageViewAttribute]>(); - 不带方括号!

  4. 创建cacheKey时返回的任何对象都将分配给OnRemove方法的'value'参数。

以下是代码。

public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var controllerActionDescriptor = filterContext.ActionDescriptor as ControllerActionDescriptor;

        var arguments = filterContext.ActionArguments;
        ActionId = arguments["id"].ToString();

        var calledMethod = string.Format("{0} -> {1}",
                                         controllerActionDescriptor.ControllerName,
                                         controllerActionDescriptor.ActionName);

        var cacheKey = string.Format("PV-{0}", calledMethod);

        var cachedResult = _memoryCache.Get(cacheKey);

        if (cachedResult == null)
        {
            //Get cacheKey if found, if not create cache key with following settings
            _memoryCache.GetOrCreate(cacheKey, cacheKey =>
            {
                cacheKey.AbsoluteExpirationRelativeToNow
                              = pageViewDumpToDatabaseTimeSpan;
                cacheKey.SetValue(1);
                cacheKey.RegisterPostEvictionCallback(onRemove);
                return cacheKey.Value;
            });
        }
        else
        {
            _memoryCache.Get(cacheKey);
        }
    }

    //Called when Memory entry is removed
    private void onRemove(object key, object value, EvictionReason reason, object state)
    {
        if (!key.ToString().StartsWith("PV-"))
        {
            return;
        }

        // write out the value to the database
        SaveToDataBase(key.ToString(), (int)value);

    }

作为参考,这是针对.NET Core 5 MVC应用程序完成的。
敬礼。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接