본문 바로가기

.Net/SharePoint 2013

[Sample Code]다중 서버 환경에 타이머 잡을 이용한 웜업(Warm Up) 구현

안녕하세요. 김승진입니다.

 

SharePoint 기반의사이트를 운영하다 보면 매일 아침 첫사용자에게 속도가 너무 느리다며 불평을 종종 듣게 됩니다.

이것은 근본적으로는 SharePoint Server의 문제리 보다 ASP.NET의 동적 컴파일(Diynamic Compiled) 때문에 발생하는 문제 입니다. (http://msdn.microsoft.com/ko-kr/library/ms366723(v=vs.100).aspx)

 

매일 새벽 IIS 서버는 재생(Recycle)이 되고 재생된 이후 첫사용자에게 컴파이 과정을 거치게 되어 있습니다.

사이트의 규모가 크고 서버가 여러대인 경우 불편을 격는 사용자가 생각보다 많아지기도 합니다.

 

따라서 이거을 해결하고자 구글링을 해보시면 Warm Up, Wake Up 등의 Powershell, C# 코드이 많이 올려져 있습니다.

이러한 코드들의 해결 방법은 기본적으로 사용자가 자주 방문하게되는 페이지나 기능을 첫사용자 보다 먼저 호출해서 동적 컴파일이 되도록 만드는 내용입니다.

 

오늘 공개하는 샘플 코드는 가장 SharePoint 스럽게 Warm Up을 하는 방법입니다.

각 서버별 프로그램을 실행하는 것이 아니라 SharePoint 패키지를 이용하여 타이머 잡을 만들었습니다.

이렇게 하면 중앙 관리기를 통해서 여러대의 서버를 동시에 Warm Up할 수 있는 장점이 생깁니다.

 

워낙 간단한 코드라 별도의 설명이 없어도 충분히 이해하시리라 생각됩니다.

아래 코드를 이요하시면 Web Applcation에 등록된 모든 사이트 모음 - 서브 사이트를 순차적으로 로드하게 됩니다.

또한 별도로 지정된 URL를 미리 로드 할 수 있도록 지정도 가능합니다.

OWSTimer.exe.config 에 기본적인 사용자 계정 정보와 설정 정보를 미리 등록하셔야 합니다.

감사합니다.

 

 

WarmUpTimerJob.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Utilities;
using System.Web;
using System.Collections;
using System.Net;
using System.Data;
using System.IO;
using System.Configuration;


namespace Jincrom.WarmUp
{
    public class WarmupTimerJob : SPJobDefinition
    {
        public const string jobName = "warm-up-timer-job";
        
        public WarmupTimerJob()
            : base()
        {
        }

        public WarmupTimerJob(string jobName, SPWebApplication webApplication, SPServer server, SPJobLockType lockType)
            : base(jobName, webApplication, server, lockType)
        {
            Title = "Warm Up Timer Job";
        }


        public override string Description
        {
            get
            {
                return "Custom Timer Job built by Jincrom. Warms Up Application Pool after recycling";
            }
        }

        public override void Execute(Guid targetInstanceId)
        {
            string userName = ConfigurationManager.AppSettings["userName"];
            string password = ConfigurationManager.AppSettings["password"];
            string domain = ConfigurationManager.AppSettings["domain"];
            string authType = ConfigurationManager.AppSettings["authType"];
            string[] additionalURLGroup = ConfigurationManager.AppSettings["additionalURL"].ToString().Split('}');
            
            string rootURL = sWebApp.GetResponseUri(SPUrlZone.Default).AbsoluteUri;
            int countWarmUp = 0;

            try
            {
                Exceptions.SaveTextLog("Starting Warm Up " + sWebApp.DisplayName);
                foreach (SPSite spSite in sWebApp.Sites)
                {
                    try
                    {
                        SPWebCollection subWebs = spSite.AllWebs;
                        foreach (SPWeb sWeb in subWebs)
                        {
                            try
                            {
                                bool jobSuccess = GetWebPage(sWeb.Url, userName, password, domain, authType);
                                if (jobSuccess)
                                {
                                    Exceptions.SaveTextLog("Warm Up Success in " + sWeb.Title + " " + sWeb.Url);
                                    countWarmUp++;
                                }
                                else
                                {
                                    Exceptions.SaveTextLog("Warm Up failed in " + sWeb.Title + " " + sWeb.Url);
                                }
                            }
                            finally
                            {
                                if (sWeb != null)
                                    sWeb.Dispose();
                            }
                          
                        }
                    }
                    finally
                    {
                        if (spSite != null)
                            spSite.Dispose();
                    }

                }

                Exceptions.SaveTextLog("Warm Up Additional URL Start");

                // {/sites/pkb/aaa.aspx}{/Search/result.aspx}....
                foreach (string additionalURL in additionalURLGroup)
                {
                    string _additionalURL = additionalURL.TrimStart('{', '/');

                    if(String.IsNullOrEmpty(_additionalURL))
                    {
                        continue;
                    }
                    else
                    {
                        string pURL = String.Concat(rootURL, _additionalURL);

                        bool jobSuccess = GetWebPage(pURL, userName, password, domain, authType);
                        if (jobSuccess)
                        {
                            Exceptions.SaveTextLog("Warm Up Success in " + pURL);
                            countWarmUp++;
                        }
                        else
                        {
                            Exceptions.SaveTextLog("Warm Up failed in " + pURL);
                        }
                    }
                   
                }

                Exceptions.SaveTextLog("Warm Up Additional URL Finished");

                Exceptions.SaveTextLog("Warm Up Finished " + sWebApp.DisplayName);
                Exceptions.SaveTextLog("Total Warm Up Sites : " + countWarmUp.ToString());
            }
            catch (Exception e)
            {
                Exceptions.ShowException("ULSLOG", "Warm Up Timer Job", e);
            }
          
        }

        private static bool GetWebPage(string URL, string userName, string password, string domain, string authType)
        {
            try
            {
                WebRequest request = WebRequest.Create(URL);
                request.Proxy = null;

                if ((userName == null) || (userName == ""))
                {
                    request.Credentials = CredentialCache.DefaultCredentials;
                }
                else
                {
                    CredentialCache myCache = new CredentialCache();
                    myCache.Add(new Uri(URL), authType, new NetworkCredential(userName, password, domain));
                    request.Credentials = myCache;
                }

                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                Stream dataStream = response.GetResponseStream();

                StreamReader reader = new StreamReader(dataStream);

                string responseFromServer = reader.ReadToEnd();

                reader.Close();
                dataStream.Close();
                response.Close();
                return true;
            }
            catch (Exception e)
            {
                Exceptions.ShowException("ULSLOG", "Warm Up Timer Job", e);
                return false;
            }

        }

    }
}

Warmup Timer Job Feature.EventReceiver.cs

using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Administration;

namespace Jincrom.WarmUp.Features.Warmup_Timer_Job_Feature
{
    [Guid("")]
    public class Warmup_Timer_Job_FeatureEventReceiver : SPFeatureReceiver
    {
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            SPWebApplication webApp = properties.Feature.Parent as SPWebApplication;
            DeleteJob(webApp.JobDefinitions);
        
            WarmupTimerJob taskTimerJob = new WarmupTimerJob(WarmupTimerJob.jobName ,webApp, null, SPJobLockType.None);
            SPDailySchedule schedule = new SPDailySchedule();
            schedule.BeginHour = 2;
            schedule.BeginMinute = 0;
            schedule.EndHour = 3;
            schedule.EndMinute = 0;
            taskTimerJob.Schedule = schedule;
            taskTimerJob.Update();               

        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            SPWebApplication webApp = properties.Feature.Parent as SPWebApplication;
            DeleteJob(webApp.JobDefinitions);
        }

        private void DeleteJob(SPJobDefinitionCollection jobs)
        {
            foreach (SPJobDefinition job in jobs)
            {
                if (job.Name.Equals(WarmupTimerJob.jobName, StringComparison.OrdinalIgnoreCase))
                {
                    job.Delete();
                }
            }
        }

    }
}