PageRenderTime 15ms CodeModel.GetById 1ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

/src/NuGet.Services.Work/WorkService.cs

https://github.com/ewilde/NuGet.Services.Work
C# | 194 lines | 161 code | 23 blank | 10 comment | 6 complexity | 562321dfe1944c5aecdef3f56b4306b1 MD5 | raw file
  1using System;
  2using System.Reactive.Linq;
  3using System.Collections.Generic;
  4using System.Diagnostics.Tracing;
  5using System.IO;
  6using System.Linq;
  7using System.Text;
  8using System.Threading.Tasks;
  9using Microsoft.Practices.EnterpriseLibrary.SemanticLogging;
 10using Microsoft.Practices.EnterpriseLibrary.SemanticLogging.Formatters;
 11using Microsoft.Practices.EnterpriseLibrary.SemanticLogging.Sinks;
 12using Microsoft.WindowsAzure.Diagnostics;
 13using Microsoft.WindowsAzure.ServiceRuntime;
 14using Microsoft.WindowsAzure.Storage;
 15using Microsoft.WindowsAzure.Storage.Table;
 16using System.Reactive.Subjects;
 17using System.Reactive.Disposables;
 18using Microsoft.WindowsAzure.Storage.Blob;
 19using NuGet.Services.Work.Monitoring;
 20using System.Net;
 21using NuGet.Services.Storage;
 22using NuGet.Services.Work.Configuration;
 23using Autofac.Core;
 24using Autofac;
 25using NuGet.Services.ServiceModel;
 26using NuGet.Services.Work.Api.Models;
 27using NuGet.Services.Configuration;
 28using NuGet.Services.Work.Models;
 29using System.Threading;
 30using System.Diagnostics;
 31using Autofac.Features.ResolveAnything;
 32using NuGet.Services.Http;
 33using Microsoft.Owin;
 34using System.Web.Http.Routing;
 35using NuGet.Services.Work.Azure;
 36
 37namespace NuGet.Services.Work
 38{
 39    public class WorkService : NuGetApiService
 40    {
 41        internal const string InvocationLogsContainerBaseName = "ng-work-invocations";
 42        public static readonly int DefaultWorkersPerCore = 2;
 43        private static readonly PathString _basePath = new PathString("/work");
 44
 45        private List<ILifetimeScope> _scopes = new List<ILifetimeScope>();
 46
 47        public override PathString BasePath
 48        {
 49            get { return _basePath; }
 50        }
 51
 52        public IEnumerable<JobDescription> Jobs { get; private set; }
 53        public IEnumerable<Worker> Workers { get; private set; }
 54        public int? MaxWorkers { get; protected set; }
 55        public int? WorkersPerCore { get; protected set; }
 56
 57        protected InvocationQueue Queue { get; set; }
 58
 59        public WorkService(ServiceName name, ServiceHost host)
 60            : base(name, host)
 61        {
 62            var workConfig = host.Config.GetSection<WorkConfiguration>();
 63
 64            MaxWorkers = MaxWorkers ?? workConfig.MaxWorkers;
 65            WorkersPerCore = WorkersPerCore ?? (workConfig.WorkersPerCore ?? DefaultWorkersPerCore);
 66        }
 67
 68        protected override async Task<bool> OnStart()
 69        {
 70            try
 71            {
 72                // Discover jobs
 73                DiscoverJobs();
 74
 75                // Determine how many workers to create
 76                Debug.Assert(WorkersPerCore.HasValue); // Constructor should have set a default value
 77                int workerCount = WorkersPerCore.Value * Environment.ProcessorCount;
 78                if (MaxWorkers != null)
 79                {
 80                    workerCount = Math.Min(workerCount, MaxWorkers.Value);
 81                }
 82
 83                // Create the workers
 84                Workers = Enumerable.Range(0, workerCount).Select(CreateWorker);
 85
 86                return await base.OnStart();
 87            }
 88            catch (Exception ex)
 89            {
 90                // Exceptions that escape to this level are fatal
 91                WorkServiceEventSource.Log.StartupError(ex);
 92                return false;
 93            }
 94        }
 95
 96        protected override Task OnRun()
 97        {
 98            return Task.WhenAll(Workers.Select(w => w.StartAndRun(Host.ShutdownToken)));
 99        }
100
101        protected override void OnShutdown()
102        {
103            foreach (var scope in _scopes)
104            {
105                scope.Dispose();
106            }
107        }
108
109        private Worker CreateWorker(int id)
110        {
111            // Create a scope for this worker
112            var scope = Container.BeginLifetimeScope(builder =>
113            {
114                // Register components
115                builder.RegisterModule(
116                    new JobComponentsModule(
117                        ServiceName.ToString() + "#" + id.ToString(),
118                        Queue));
119
120                // Register the worker
121                builder
122                    .RegisterType<Worker>()
123                    .WithParameter(new NamedParameter("id", id))
124                    .AsSelf()
125                    .SingleInstance();
126            });
127            _scopes.Add(scope);
128            return scope.Resolve<Worker>();
129        }
130
131        private void DiscoverJobs()
132        {
133            Jobs = Container.Resolve<IEnumerable<JobDescription>>();
134
135            foreach (var job in Jobs)
136            {
137                // Record the discovery in the trace
138                WorkServiceEventSource.Log.JobDiscovered(job);
139            }
140        }
141
142        public override void RegisterComponents(ContainerBuilder builder)
143        {
144            base.RegisterComponents(builder);
145
146            var jobdefs = GetAllAvailableJobs();
147            builder.RegisterInstance(jobdefs).As<IEnumerable<JobDescription>>();
148
149            // Register an invocation queue for the API to use. The workers will register
150            // their own in a sub scope
151            if (Queue == null)
152            {
153                builder
154                    .RegisterType<InvocationQueue>()
155                    .AsSelf()
156                    .UsingConstructor(
157                        typeof(Clock),
158                        typeof(string),
159                        typeof(StorageHub),
160                        typeof(ConfigurationHub))
161                    .WithParameter(
162                        new NamedParameter("instanceName", ServiceName.ToString() + "#api"));
163            }
164            else
165            {
166                builder.RegisterInstance(Queue).As<InvocationQueue>();
167            }
168        }
169
170        public static IEnumerable<JobDescription> GetAllAvailableJobs()
171        {
172            var jobdefs = typeof(WorkService)
173                   .Assembly
174                   .GetExportedTypes()
175                   .Where(t => !t.IsAbstract && typeof(JobHandlerBase).IsAssignableFrom(t))
176                   .Select(t => JobDescription.Create(t))
177                   .Where(d => d != null);
178            return jobdefs;
179        }
180
181        public override async Task<object> GetCurrentStatus()
182        {
183            return (await Task.WhenAll(Workers.Select(async w => Tuple.Create(w.Id, await w.GetCurrentStatus())))).ToDictionary(
184                t => ServiceName.ToString() + "#" + t.Item1.ToString(),
185                t => t.Item2);
186        }
187
188        protected override void ConfigureAttributeRouting(DefaultInlineConstraintResolver resolver)
189        {
190            base.ConfigureAttributeRouting(resolver);
191            resolver.ConstraintMap.Add("invocationListCriteria", typeof(EnumConstraint<InvocationListCriteria>));
192        }
193    }
194}