在具有多个WFE的服务器农场环境中部署定时作业的最佳实践

7

我有一个计时器作业,我希望每天只运行一次,并且在整个农场中运行。如何做到这一点?

  1. 在多个WFE环境中部署它?我应该在每个WFE上运行stsadm -o deploysolution命令,还是只在想要运行它的WFE上运行?
  2. 答:您需要在每个WFE上运行stsadm -o deploysolution命令来部署该作业。
  3. 在哪里激活功能?是否应该只从特定的WFE激活?
  4. 答:您可以从任何WFE上激活该功能。
  5. SPJobLockType的值应该是什么?
  6. 答:对于只需要在整个农场中运行一次的计时器作业,SPJobLockType的值应该为SPJobLockType.Job。
1个回答

7

看起来你需要一个针对农场的功能,安装一个运行此作业的服务。以下是我是如何做到的(实际上使用了同事编写的代码,但他不在SO)。

创建一个具有功能事件接收器的feature.xml文件。

<Feature
  Id="..."
  Title="..."
  Description="..."
  Scope="Farm"
  Version="1.0.0.0"
  Hidden="FALSE"
  ReceiverAssembly="XXX.FarmService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxx"
  ReceiverClass="XXX.FarmService.XXXFarmFeatureEventReceiver"
  xmlns="http://schemas.microsoft.com/sharepoint/">
</Feature>

事件接收器内部:

public override void OnFeatureActivated(SPFeatureReceiverProperties properties)
    {
        try
        {
            SPFarm farm = SPFarm.Local;

            // Get Service, if it already exists
            JobService myService = null; // JobService is a subclass of SPService
            foreach (SPService service in farm.Services)
            {
                if (String.Compare(service.Name, JobService.XXXServiceName, true) == 0)
                {
                    myService = (service as JobService);
                    break;
                    }
                }

                if (cegService == null)
                {
                    // Create service
                    myService = new JobService(farm);
                    myService.Update();

                    // Create service instances
                    JobServiceInstance myServiceInstance; // JobServiceInstance is a subclas of SPServiceInstance
                    foreach (SPServer server in farm.Servers)
                    {
                        myServiceInstance = new JobServiceInstance(server, myService);
                        myServiceInstance.Update();
                    }
                }

                // Dayly schedule 
                SPDailySchedule schedule = new SPDailySchedule();
                schedule.BeginHour = 1;
                schedule.EndHour = 1;
                schedule.BeginMinute = 0;
                schedule.EndMinute = 59;

                // Our own job; JobCheckDocDates is a subclass of SPJobDefinition
                JobCheckDocDates newJob = new JobCheckDocDates(cegService, null, SPJobLockType.Job);
                newJob.Schedule = schedule;
                newJob.Update();
                myService.JobDefinitions.Add(newJob);
                myService.Update();
            }
            catch (Exception e)
            {
                Logger.Error("[" + properties.Feature.Definition.DisplayName + "] Error during feature activation", e);
            }
        }        

这将创建一个服务,可在农场中的每个服务器上使用。

以下是一些子类的更多细节:

public class JobCheckDocDates: Common.BaseJob
{
    /// <summary>
    /// The job name
    /// </summary>
    public static string JobName = "XXX job";

    /// <summary>
    /// Constructor
    /// </summary>
    public JobCheckDocDates() : base() { }

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="service"></param>
    /// <param name="server"></param>
    /// <param name="lockType"></param>
    public JobCheckDocDates(SPService service, SPServer server, SPJobLockType lockType)
            : base(JobName, service, server, lockType) { }

当然还有Execute方法。

public class JobService : SPService 
{
    public static string XXXServiceName = "XXX Service";

    public override string DisplayName
    {
        get
        {
            return XXXServiceName;
        }
    }

    public override string TypeName
    {
        get
        {
            return "XXX Service Type";
        }
    }

    /* An empty public constructor required for serialization. */
    public JobService() { }

    public JobService(SPFarm farm)
          : base(XXXServiceName, farm)
    {         
    }
}


    public class JobServiceInstance : SPServiceInstance
    {
        /// <summary>
        /// Eos Service Instance Name
        /// </summary>
        public static string XXXServiceInstanceName = "XXXServiceInstance";

        /// <summary>
        /// Manage Link
        /// </summary>
        private SPActionLink _manageLink;

        /// <summary>
        /// Provision Link
        /// </summary>
        private SPActionLink _provisionLink;

        /// <summary>
        /// Unprovision Link
        /// </summary>
        private SPActionLink _unprovisionLink;

        /// <summary>
        /// Roles
        /// </summary>
        private ICollection<string> _roles;

        /// <summary>
        /// Manage Link
        /// </summary>
        public override SPActionLink ManageLink
        {
            get
            {
                if (_manageLink == null)
                {
                    _manageLink = new SPActionLink(SPActionLinkType.None);
                }
                return _manageLink;
            }
        }

        /// <summary>
        /// Provision Link
        /// </summary>
        public override SPActionLink ProvisionLink
        {
            get
            {
                if (_provisionLink == null)
                {
                    _provisionLink = new SPActionLink(SPActionLinkType.ObjectModel);
                }
                return _provisionLink;
            }
        }

        /// <summary>
        /// Unprovision Link
        /// </summary>
        public override SPActionLink UnprovisionLink
        {
            get
            {
                if (_unprovisionLink == null)
                {
                    _unprovisionLink = new SPActionLink(SPActionLinkType.ObjectModel);
                }
                return _unprovisionLink;
            }
        }

        /// <summary>
        /// Roles
        /// </summary>
        public override ICollection<string> Roles
        {
            get
            {
                if (_roles == null)
                {
                    _roles = new string[1] { "Custom" };
                }
                return _roles;
            }
        }

        /// <summary>
        /// Empty constructor
        /// </summary>
        public JobServiceInstance() : base() { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="server">The server</param>
        /// <param name="service">The Eos service</param>
        public JobServiceInstance(SPServer server, JobService service)
            : base(XXXServiceInstanceName, server, service)
        {
        }

现在,在中央管理界面,进入“操作/服务器服务”。选择所需的服务器并启动服务。
回答您的问题清单: 1. 只需一次部署解决方案,独立于WFE。 2. 由于该功能是针对整个农场的,应在中央管理界面中激活它。 3. SPJobLockType为SPJobLockType.Job。
这不完全是您想象的内容,但它具有一个优点,即让您轻松选择作业在哪里运行,即使在安装功能后很长时间(例如,如果一个服务器负载过重)。
OnFeatureActivated方法可能更加智能,并检查每个服务器是否存在该服务,如有需要则添加该服务。但是,在OnFeatureDeactivated中从每个服务中删除服务更简单。因此,如果您添加了新服务器,请先停用再重新激活该功能。

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