Рекомендации по развертыванию заданий таймера в средах фермы серверов с несколькими WFE - PullRequest
7 голосов
/ 02 июня 2010

У меня есть задание таймера, которое я хочу запускать только один раз в день для всей фермы. Как мне

  1. Развернуть его в среде с несколькими WFE? Запускаю ли я команду stsadm -o deploysolution в каждом WFE или только в той, где я хочу ее запустить?
  2. Где я должен активировать функцию? Должен ли он быть активирован только с определенного WFE?
  3. Каким должно быть значение SPJobLockType.

1 Ответ

7 голосов
/ 02 июня 2010

Похоже, вам нужна функция на уровне фермы, которая устанавливает службу, выполняющую это задание. Вот как я это сделал (используя код, написанный коллегой, если честно, но он не в 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)
        {
        }

Теперь в Central Admin перейдите в меню «Операции / Сервисы» на сервере. Выберите нужный сервер и запустите службу.

Чтобы ответить на ваш список вопросов: 1. Разверните решение только один раз, независимо от WFE. 2. Поскольку эта функция относится к области фермы, она должна быть активирована в Central Admin. 3. SPJobLockType - это SPJobLockType.Job

Это не совсем то, что вы себе представляли, но у него есть то преимущество, что вы можете легко выбирать, где будет выполняться задание, даже спустя долгое время после установки функции (например, если сервер перегружен другими компонентами).

Метод OnFeatureActivation может быть разумнее и проверять для каждого сервера, существует ли служба, и добавлять его при необходимости. Но проще удалить сервис из каждого сервиса в OnFeatureDeactivation. Поэтому, если вы добавляете новые серверы, деактивируйте, а затем снова включите функцию.

...