프로젝트

일반

사용자정보

통계
| 브랜치(Branch): | 개정판:

markus / ConvertService / ServiceBase / Markus.Service.Station / ServiceStation.cs @ 950e6b84

이력 | 보기 | 이력해설 | 다운로드 (17.8 KB)

1
using log4net;
2
using Markus.EntityModel;
3
using Markus.Service.Extensions;
4
using Markus.Service.Helper;
5
using Markus.Service.IWcfService;
6
using Markus.Service.WcfService;
7
using Salaros.Configuration;
8
using System;
9
using System.Collections.Generic;
10
using System.ComponentModel;
11
using System.Data;
12
using System.Diagnostics;
13
using System.Globalization;
14
using System.IO;
15
using System.Linq;
16
using System.Runtime.InteropServices;
17
using System.ServiceModel;
18
using System.ServiceModel.Channels;
19
using System.ServiceModel.Dispatcher;
20
using System.ServiceProcess;
21
using System.Text;
22
using System.Threading.Tasks;
23
using System.Timers;
24
using static Markus.Service.Extensions.Encrypt;
25

    
26
namespace Markus.Service
27
{
28
    public partial class ServiceStation : ServiceBase
29
    {
30
        protected ILog logger = LogManager.GetLogger(typeof(ServiceStation));
31
        protected ServiceHost gWcfServiceHost;
32
        private SERVICE_PROPERTIES ServiceProperty;
33

    
34
        private static System.Timers.Timer timer;
35
        
36
        private string ServiceID;
37
        private List<SubStationServiceItem> StationServiceList;
38
        private List<string> StationServiceIDList;
39
        private Service.WcfClient.StationServiceTask.StationServiceClient StationClient;
40
        private bool IsStation;
41

    
42
        private Uri gServiceHostAddress;
43

    
44
        private string MarkusDBConnectionString;
45
        private string DownloadTempFolder;
46
        private string FontsFolder;
47
        private int MultiThreadMaxPages;
48
        private int MinFontSize;
49
        private int UseResolution;
50
        private bool CreateProcessWindow;
51
        private long ReleaseWorkMemory;
52

    
53
        private int SaveStatusInterval;
54

    
55
        private static bool IsReleaseItems;
56

    
57
        private List<string> RunProjectList = new List<string>();
58

    
59
        /// <summary>
60
        /// 프로세스 카운터 자주 람다식을 사용해서 list<int>로 함.
61
        /// </summary>
62
        private List<Int64> ProcessorAffinityList;
63

    
64
        private string configFileName;
65

    
66
        public ServiceStation()
67
        {
68
            InitializeComponent();
69
        }
70

    
71
        /// <summary>
72
        /// Config 파일 
73
        /// </summary>
74
        public void GetApplicationConfig()
75
        {
76
            try
77
            {
78
                ConfigParser config = null;
79

    
80
                try
81
                {
82
                    configFileName = $"{typeof(ServiceStation).Name}.ini";
83
                    config = ConfigHelper.AppConfig(this.configFileName);
84
                }
85
                catch (Exception ex)
86
                {
87
                    throw new Exception("Config Read Error.", ex);
88
                }
89

    
90
                if (config != null)
91
                {
92
                    // CONVERT DATABASE 연결 문자열
93
                    MarkusDBConnectionString = AESEncrypter.Decrypt(config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.MARKUS_CONNECTION_STRING));
94

    
95
                    IsStation = System.Convert.ToBoolean(config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.IS_STATAION, "false"));
96

    
97
                    ServiceID = config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.SERVICE_ID, Guid.Empty.ToString());
98

    
99
                    if (IsStation)
100
                    {
101
                        var servicetList = config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.SERVICE_LIST, "");
102

    
103
                        if (!servicetList.IsNullOrEmpty())
104
                        {
105
                            StationServiceIDList = servicetList.Split(',').ToList();
106
                        }
107
                    }
108
                    else
109
                    {
110
                        var address = config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.STATAION_ADDRESS,"");
111

    
112
                        if (!address.IsNullOrEmpty())
113
                        {
114
                            BasicHttpBinding myBinding = new BasicHttpBinding();
115
                            EndpointAddress myEndpoint = new EndpointAddress(UriHelper.UriCreate(address));
116
                            StationClient = new WcfClient.StationServiceTask.StationServiceClient(myBinding, myEndpoint);
117
                        }
118
                    }
119

    
120
                    CreateProcessWindow = System.Convert.ToBoolean(config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.CREATE_WINDOW, "false"));
121

    
122
                    // PDF 임시 다운로드 폴더
123
                    DownloadTempFolder = config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.DOWNLOAD_TEMP_FOLDER,"C:\\temp");
124
                    FontsFolder = config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.FONTS_FOLDER, "C:\\MarkusFonts"); 
125
                    // pThraed를 이용한 컨버팅 설정.
126
                    //최대 페이지가 MultiThreadMaxPages설정까지 pThread를 적용하여 컨버팅함.
127
                    MultiThreadMaxPages = System.Convert.ToInt16(config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.MULTI_TRHEAD_MAX_PAGE, "500"));
128

    
129
                    // 서비스 상태 Log 저장간격
130
                    SaveStatusInterval = System.Convert.ToInt16(config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.SAVE_STATUS_INTERVAL, "5"));
131

    
132
                    //PDF에 폰트가 최소 폰트 사이즈보다 작은 사이증의 폰트가 있으면 해상도를 높여서 컨버팅함.
133
                    MinFontSize = System.Convert.ToInt16(config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.MIN_FONT_SIZE, "10"));
134

    
135
                    // 사용자 설정의 해상도
136
                    // MinFontSize를 -1로 하고 UseResolution를 설정하면 해당 해상도로 컨버팅됨
137
                    UseResolution = System.Convert.ToInt16(config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.USE_RESOLUTION, "0"));
138

    
139
                    // 각 프로세스의 최대 메모리 사용량
140
                    var workingMemory = System.Convert.ToDouble(config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.RELEASE_WORK_MEMORY, "1.5"));
141

    
142
                    ReleaseWorkMemory = MathBytes.Bytes(workingMemory, DataSizeType.GB);
143

    
144
                    // 서비스 사용 프로젝트
145
                    var projectList = config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.FITER_PROCECT,"");
146

    
147
                    if(!projectList.IsNullOrEmpty())
148
                    {
149
                        RunProjectList = projectList.Split(',').ToList();
150
                    }
151

    
152
                    // 서비스 ENDPOINT
153
                    // http://localhost/
154
                    var endpointName = config.GetValue(CONFIG_DEFINE.WCF_ENDPOINT, CONFIG_DEFINE.STATION_SERVICE_NAME);
155
                    var port = config.GetValue(CONFIG_DEFINE.WCF_ENDPOINT, CONFIG_DEFINE.STATION_PORT);
156

    
157
                    if (!string.IsNullOrWhiteSpace(endpointName) && port.IsNumber())
158
                    {
159
                        gServiceHostAddress = UriHelper.UriCreate($"http://localhost:{port}/{endpointName}");
160
                    }
161

    
162
                    // 각  ConvertService의 원격 관리를 위한 서비스 설정 로드
163
                    using (Markus.Service.DataBase.ConvertDatabase db = new Markus.Service.DataBase.ConvertDatabase(MarkusDBConnectionString))
164
                    {
165
                        ServiceProperty = db.GetServiceProperties(ServiceID);
166
                    }
167
                }
168
            }
169
            catch (Exception ex)
170
            {
171
                throw new Exception("ApplicationConfig ", ex);
172
            }
173
        }
174

    
175
        protected override void OnStart(string[] args)
176
        {
177
            try
178
            {
179
                StartService();
180
            }
181
            catch (Exception ex)
182
            {
183
                logger.Error("ServiceStation Start Error - ", ex);
184
            }
185
        }
186
        
187
        /// <summary>
188
        /// alivequeue와 process를 비교하여 정리하고
189
        /// 대기중인 아이템을 분배한다.
190
        /// </summary>
191
        private void ReleaseItems()
192
        {
193
            try
194
            {
195
                IsReleaseItems = true;
196

    
197
                CleanUpAliveQueueItems();
198

    
199
                if (IsStation)
200
                {
201
                    //if (IsDataBaseWaitingList(1))
202
                    //{
203
                    System.Diagnostics.Debug.WriteLine("ReleaseItems start");
204
                    ReflashSubServiceAsync();
205

    
206
                    if (StationServiceList.All(x => x.IsOnline))
207
                    {
208
                        CleanUpDataBaseItems();
209
                        setDataBaseWaitingList();
210
                    }
211
                    else
212
                    {
213
                        System.Diagnostics.Debug.WriteLine("Service Any Offline");
214
                    }
215

    
216
                    System.Diagnostics.Debug.WriteLine("ReleaseItems end");
217
                    //}
218
                }
219
            }
220
            catch (Exception ex)
221
            {
222
                logger.Error("get Wating Item error", ex);
223
            }
224

    
225
            IsReleaseItems = false;
226
        }
227

    
228
        /// <summary>
229
        /// System.Diagnostics.Process의 ProcessorAffinity Core 선호도를 위한 초기화
230
        /// 설정된 MultiProcessCount에 대해서 프로세스의 코어의 선호도를 지정 한다.
231
        /// 코어의 선택은 비트로 이루어 진다.
232
        /// 8코어에서 1번 코어 00000001
233
        /// 8코어에서 1번 3번 코어 00000101
234
        /// 8코어에서 1번 3번 코어 00000101
235
        /// 8코어에서 1,2,3 선택   00000111
236
        /// https://dotnetgalactics.wordpress.com/2009/10/20/how-to-set-the-processor-affinity-programatically/
237
        /// </summary>
238
        private void ProcessorAffinityInit()
239
        {
240
            ProcessorAffinityList = new List<long>();
241

    
242
            int processCount = Environment.ProcessorCount;
243
            int AffinityScope = 1;
244

    
245
            if (processCount > ServiceProperty.PROCESS_COUNT)
246
            {
247
                AffinityScope = processCount / ServiceProperty.PROCESS_COUNT;
248
            }
249

    
250
            for (int i = 0; i < processCount - AffinityScope; i += AffinityScope)
251
            {
252
                var bits = new int[processCount];
253

    
254
                for (int j = i; j < i + AffinityScope; j++)
255
                {
256
                    bits[j] = 1;
257
                }
258

    
259
                var affinity = System.Convert.ToInt64(string.Join("", bits), 2);
260

    
261
                ProcessorAffinityList.Add(affinity);
262
            }
263
        }
264

    
265
        public bool StartService()
266
        {
267
          
268
            /// 서비스 설정 로드
269
            try
270
            {
271
                this.GetApplicationConfig();
272
                logger.Info("Read Config");
273

    
274
                if (ServiceProperty != null)
275
                {
276
                    ProcessorAffinityInit();
277
                }
278
                else
279
                {
280
                    throw new Exception("StartService Error. ServiceProperty is Null");
281
                }
282

    
283
            }
284
            catch (Exception e)
285
            {
286
                throw new Exception("StartService Error. ", e);
287
            }
288

    
289
            /// pThread.dll을 리소스에서 Service 실행 디렉토리로 복사.
290
            try
291
            {
292
                // MarkusPDF.dll에서 pThread lib 사용을 위해 Insatll
293
                if (!File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "libpthread.dll")))
294
                {
295
                    Markus.Library.Installer.Install();
296
                    logger.Info("Markus.Library.Installer Install");
297
                }
298
                else
299
                {
300
                    logger.Info("Markus.Library.Installer Exists.");
301
                }
302
                
303
            }
304
            catch (Exception e)
305
            {
306
                throw new Exception("Stop Installer Error. ", e);
307
            }
308

    
309
            /// 컨버터 프로세스가 실행중인 경우 모두 중지 시킨다.
310
            try
311
            {
312
                // 기존에 실행 중이던 프로세스 종료
313
                Stopprocess();
314
                logger.Info("Stopprocess");
315
            }
316
            catch (Exception e)
317
            {
318
                throw new Exception("Stop Process Error. ", e);
319
            }
320

    
321
            /// wcf 서비스 실행
322
            try
323
            {
324
                // WCF 실행
325
                StartWcfService();
326

    
327
                if (gWcfServiceHost.BaseAddresses?.Count() > 0)
328
                {
329
                    gServiceHostAddress = gWcfServiceHost.BaseAddresses.First();
330
                }
331

    
332
                //  각 서비스에 컨버터 아이템을 보내기 위한 서비스 초기화
333
                if (IsStation)
334
                {
335
                    SetServiceList(this.StationServiceIDList);
336
                }
337

    
338
                logger.Info($"StartWcfService {gServiceHostAddress}");
339
            }
340
            catch (Exception e)
341
            {
342
                throw new Exception("start Wcf Service Error. ", e);
343
            }
344

    
345
            //try
346
            //{
347
            //    // Status가 4이하인 Convert Item을 다시 Convert 함. 
348
            //    setDataBaseWaitingList();
349
            //    logger.Info("setDataBaseWaitingList");
350
            //}
351
            //catch (Exception e)
352
            //{
353
            //    throw new Exception("Database Waiting List Error . ", e);
354
            //}
355

    
356

    
357
            logger.Info("Start ServiceStation");
358
            var autoEvent = new System.Threading.AutoResetEvent(false);
359

    
360
            stateTimer = new System.Threading.Timer(new System.Threading.TimerCallback(TimerCallBack), null, 0, 500);
361
            //timer = new System.Timers.Timer(new TimeSpan(0, 0, 0, 10).TotalMilliseconds);
362
            //timer.Elapsed += Timer_Elapsed;
363
            //timer.AutoReset = true;
364
            //timer.Start();
365
            //Timer_Elapsed(null, null);
366
            return true;
367
        }
368

    
369
        System.Threading.Timer stateTimer;
370
        DateTime logTime;
371
        DateTime ReleaseTime;
372

    
373
        private void TimerCallBack(object state)
374
        {
375
            System.Threading.AutoResetEvent autoEvent = (System.Threading.AutoResetEvent)state;
376
            //stateTimer.Change(-1, -1);
377
            try
378
            {
379
               
380
                if ((DateTime.Now - ReleaseTime) >= new TimeSpan(0, 0,15))
381
                {
382
                    if (!IsReleaseItems)
383
                    {
384
                        ReleaseItems();
385
                    }
386
                    ReleaseTime = DateTime.Now;
387
                }
388
              
389

    
390
                if ((DateTime.Now - logTime) >= new TimeSpan(0, 5,0))
391
                {
392
                    logTime = DateTime.Now;
393
                    logger.Info("StationService Alive Check");
394
                }
395

    
396

    
397
            }
398
            catch (Exception ex)
399
            {
400
                logger.Error("Timer Error ", ex);
401
            }
402

    
403
            ///System.Threading.Thread.SpinWait(100000);
404
            //stateTimer.Change(0, 10000);
405
        }
406

    
407
        public void SetServiceList(List<string> serviceList)
408
        {
409
            StationServiceList = new List<SubStationServiceItem>();
410

    
411
            using (DataBase.ConvertDatabase database = new DataBase.ConvertDatabase(MarkusDBConnectionString))
412
            {
413
                foreach (var item in serviceList)
414
                {
415
                    try
416
                    {
417
                        var prop = database.GetServiceProperties(item);
418

    
419
                        if (prop != null)
420
                        {
421
                            BasicHttpBinding httpbinding = new BasicHttpBinding();
422
                            httpbinding.CloseTimeout = new TimeSpan(0, 10, 0);
423
                            httpbinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
424
                            httpbinding.SendTimeout = new TimeSpan(0, 10, 0);
425
                            httpbinding.OpenTimeout = new TimeSpan(0, 10, 0);
426

    
427
                            EndpointAddress myEndpoint = new EndpointAddress(UriHelper.UriCreate(prop.SERVICE_ADDRESS));
428
                            var StationServiceClient = new WcfClient.StationServiceTask.StationServiceClient(httpbinding, myEndpoint);
429
                        
430
                       
431
                                //var items = StationServiceClient.AliveConvertList();
432
                            
433
                                StationServiceList.Add(new SubStationServiceItem
434
                                {
435
                                    Properties = prop,
436
                                    Service = StationServiceClient
437
                                });
438
                        }
439

    
440
                    }
441
                    catch (Exception ex)
442
                    {
443
                        logger.Error($"Service Properties Error  ID : { item }",ex);
444
                    }
445
                }
446
            }
447
        }
448

    
449
        protected override void OnStop()
450
        {
451
            try
452
            {
453
                if (timer != null)
454
                {
455
                    timer.Stop();
456
                }
457

    
458
                StopWcfService();
459
                Stopprocess();
460

    
461
                logger.Info("ServiceStation Stop");
462
            }
463
            catch (Exception e)
464
            {
465
                logger.Error("OnStop Error . ", e);
466
            }
467
        }
468

    
469
        #region Sleep 방지
470
        //[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
471
        //static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
472
        //[FlagsAttribute]
473
        //public enum EXECUTION_STATE : uint
474
        //{
475
        //    ES_AWAYMODE_REQUIRED = 0x00000040,
476
        //    ES_CONTINUOUS = 0x80000000,
477
        //    ES_DISPLAY_REQUIRED = 0x00000002,
478
        //    ES_SYSTEM_REQUIRED = 0x00000001
479
        //    // Legacy flag, should not be used.
480
        //    // ES_USER_PRESENT = 0x00000004
481
        //}
482
        //public static void PreventScreenAndSleep()
483
        //{
484
        //    SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS |
485
        //                            EXECUTION_STATE.ES_SYSTEM_REQUIRED |
486
        //                            EXECUTION_STATE.ES_AWAYMODE_REQUIRED |
487
        //                            EXECUTION_STATE.ES_DISPLAY_REQUIRED);
488
        //} 
489
        #endregion
490
    }
491
}
클립보드 이미지 추가 (최대 크기: 500 MB)