프로젝트

일반

사용자정보

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

markus / ConvertService / ServiceBase / Markus.Service.Station / ServiceStation.cs @ a5e5fff6

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

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

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

    
36
        private string ServiceID;
37
        private List<SubStationServiceItem> StationServiceList;
38
        private List<string> StationServiceIDList = new List<string>();
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 bool IsReleaseItems;
56
        private readonly System.Timers.Timer timer= null;
57
        
58
        private readonly object locker = new object();
59

    
60
        private List<string> RunProjectList = new List<string>();
61

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

    
67
        private string configFileName;
68

    
69
        public ServiceStation()
70
        {
71
            InitializeComponent();
72

    
73
            timer = new System.Timers.Timer(new TimeSpan(0, 0, 0,5).TotalMilliseconds);
74
            timer.Elapsed += timerWorkMethod;
75
        }
76

    
77
        /// <summary>
78
        /// Config 파일 
79
        /// </summary>
80
        public void GetApplicationConfig()
81
        {
82
            try
83
            {
84
                ConfigParser config = null;
85

    
86
                try
87
                {
88
                    configFileName = $"{typeof(ServiceStation).Name}.ini";
89
                    config = ConfigHelper.AppConfig(this.configFileName);
90
                }
91
                catch (Exception ex)
92
                {
93
                    throw new Exception("Config Read Error.", ex);
94
                }
95

    
96
                if (config != null)
97
                {
98
                    // CONVERT DATABASE 연결 문자열
99
                    MarkusDBConnectionString = AESEncrypter.Decrypt(config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.MARKUS_CONNECTION_STRING));
100

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

    
103
                    ServiceID = config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.SERVICE_ID, Guid.Empty.ToString());
104

    
105
                    if (IsStation)
106
                    {
107
                        var servicetList = config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.SERVICE_LIST, "");
108

    
109
                        if (!servicetList.IsNullOrEmpty())
110
                        {
111
                            StationServiceIDList = servicetList.Split(',').ToList();
112
                        }
113
                    }
114
                    else
115
                    {
116
                        var address = config.GetValue(CONFIG_DEFINE.SERVICE, CONFIG_DEFINE.STATAION_ADDRESS,"");
117

    
118
                        if (!address.IsNullOrEmpty())
119
                        {
120
                            BasicHttpBinding myBinding = new BasicHttpBinding();
121
                            EndpointAddress myEndpoint = new EndpointAddress(UriHelper.UriCreate(address));
122
                            StationClient = new WcfClient.StationServiceTask.StationServiceClient(myBinding, myEndpoint);
123
                        }
124
                    }
125

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

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

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

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

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

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

    
148
                    ReleaseWorkMemory = MathBytes.Bytes(workingMemory, DataSizeType.GB);
149

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

    
153
                    if(!projectList.IsNullOrEmpty())
154
                    {
155
                        RunProjectList = projectList.Split(',').ToList();
156
                    }
157

    
158
                    // 서비스 ENDPOINT
159
                    // http://localhost/
160
                    var endpointName = config.GetValue(CONFIG_DEFINE.WCF_ENDPOINT, CONFIG_DEFINE.STATION_SERVICE_NAME);
161
                    var port = config.GetValue(CONFIG_DEFINE.WCF_ENDPOINT, CONFIG_DEFINE.STATION_PORT);
162

    
163
                    if (!string.IsNullOrWhiteSpace(endpointName) && port.IsNumber())
164
                    {
165
                        gServiceHostAddress = UriHelper.UriCreate($"http://localhost:{port}/{endpointName}");
166
                    }
167

    
168
                    // 각  ConvertService의 원격 관리를 위한 서비스 설정 로드
169
                    using (Markus.Service.DataBase.Repositories.SERVICEPROPERTIESRepository db = new DataBase.Repositories.SERVICEPROPERTIESRepository(MarkusDBConnectionString))
170
                    {
171
                        ServiceProperty = db.GetSingleAsync(ServiceID).GetAwaiter().GetResult();
172
                    }
173
                }
174
            }
175
            catch (Exception ex)
176
            {
177
                throw new Exception("ApplicationConfig ", ex);
178
            }
179
        }
180

    
181
        protected override void OnStart(string[] args)
182
        {
183
            try
184
            {
185
                StartService();
186
            }
187
            catch (Exception ex)
188
            {
189
                logger.Error("ServiceStation Start Error - ", ex);
190
            }
191
        }
192

    
193
        /// <summary>
194
        /// alivequeue와 process를 비교하여 정리하고
195
        /// 대기중인 아이템을 분배한다.
196
        /// </summary>
197
        private void ReleaseItems()
198
        {
199
            try
200
            {
201
                IsReleaseItems = true;
202

    
203
                System.Diagnostics.Debug.WriteLine("CleanUpAliveQueueItems start");
204
                CleanUpAliveQueueItems();
205
                System.Diagnostics.Debug.WriteLine("CleanUpAliveQueueItems end");
206

    
207
                if (IsStation)
208
                {
209
                    //if (IsDataBaseWaitingList(1))
210
                    //{
211
                    System.Diagnostics.Debug.WriteLine("ReleaseItems start");
212

    
213
                    bool reflashResult = false;
214

    
215
                    if (this.StationServiceList.Count() > 0)
216
                    {
217
                        reflashResult = ReflashSubServiceAsync().GetAwaiter().GetResult();
218
                    }
219
                    else
220
                    {
221
                        reflashResult = true;
222
                    }
223
            
224
                    if (reflashResult)
225
                    {
226
                        //if (StationServiceList.All(x => x.IsOnline))
227
                        //{
228
                            CleanUpDataBaseItems();
229
                            setDataBaseWaitingList();
230
                        //}
231
                        //else
232
                        //{
233
                        //    logger.Error("Service Any Offline");
234
                        //}
235
                    }
236
                    else
237
                    {
238
                        logger.Error("ReleaseItems - ReflashSubServiceAsync result false");
239
                    }
240

    
241

    
242
                    System.Diagnostics.Debug.WriteLine("ReleaseItems end");
243
                    //}
244
                }
245
            }
246
            catch (Exception ex)
247
            {
248
                logger.Error("get Wating Item error", ex);
249
            }
250

    
251
            IsReleaseItems = false;
252
        }
253

    
254
        /// <summary>
255
        /// System.Diagnostics.Process의 ProcessorAffinity Core 선호도를 위한 초기화
256
        /// 설정된 MultiProcessCount에 대해서 프로세스의 코어의 선호도를 지정 한다.
257
        /// 코어의 선택은 비트로 이루어 진다.
258
        /// 8코어에서 1번 코어 00000001
259
        /// 8코어에서 1번 3번 코어 00000101
260
        /// 8코어에서 1번 3번 코어 00000101
261
        /// 8코어에서 1,2,3 선택   00000111
262
        /// https://dotnetgalactics.wordpress.com/2009/10/20/how-to-set-the-processor-affinity-programatically/
263
        /// </summary>
264
        private void ProcessorAffinityInit()
265
        {
266
            ProcessorAffinityList = new List<long>();
267

    
268
            int processCount = Environment.ProcessorCount;
269
            int AffinityScope = processCount;
270

    
271
            if (processCount > ServiceProperty.PROCESS_COUNT)
272
            {
273
                AffinityScope = processCount / ServiceProperty.PROCESS_COUNT;
274
            }
275

    
276
            long affinityMask = 0;
277

    
278
            while (ProcessorAffinityList.Count() < ServiceProperty.PROCESS_COUNT)
279
            {
280
                if(affinityMask <= 1)
281
                {
282
                    affinityMask = 1 << (processCount - 1);
283
                }
284
                else
285
                {
286
                    affinityMask = affinityMask >>1;
287
                }
288

    
289
                ProcessorAffinityList.Add(affinityMask);
290
            }
291

    
292
//            for (int i = 0; i < AffinityScope; i += AffinityScope)
293
//            {
294
//                var bits = new int[processCount];
295

    
296
//                for (int j = i; j < i + AffinityScope; j++)
297
//                {
298
//                    bits[j] = 1;
299
//                }
300

    
301
//                var affinity = System.Convert.ToInt64(string.Join("", bits), 2);
302

    
303
//#if DEBUG
304
//                List<int> test = bits.ToList();
305
//                test.Reverse();
306
//               System.Diagnostics.Debug.WriteLine($"index{string.Format("0#",i)} - {string.Join("", test)}");
307
//#endif
308
//                ProcessorAffinityList.Add(affinity);
309
//            }
310
        }
311

    
312
        public void StartService()
313
        {
314
            /// 서비스 설정 로드
315
            try
316
            {
317
                this.GetApplicationConfig();
318
                logger.Info("Read Config");
319

    
320
                if (ServiceProperty != null)
321
                {
322
                    ProcessorAffinityInit();
323
                }
324
                else
325
                {
326
                    throw new Exception("StartService Error. ServiceProperty is Null");
327
                }
328

    
329
            }
330
            catch (Exception e)
331
            {
332
                throw new Exception("StartService Error. ", e);
333
            }
334

    
335
            /// pThread.dll을 리소스에서 Service 실행 디렉토리로 복사.
336
            //try
337
            //{
338
            //    // MarkusPDF.dll에서 pThread lib 사용을 위해 Insatll
339
            //    var libPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "\\convert\\libpthread.dll");
340
            //    if (!File.Exists(libPath))
341
            //    {
342
            //        Markus.Library.Installer.Install();
343
            //        logger.Info("Markus.Library.Installer Install");
344
            //    }
345
            //    else
346
            //    {
347
            //        logger.Info("Markus.Library.Installer Exists.");
348
            //    }
349

    
350
            //}
351
            //catch (Exception e)
352
            //{
353
            //    throw new Exception("Stop Installer Error. ", e);
354
            //}
355

    
356
            /// 컨버터 프로세스가 실행중인 경우 모두 중지 시킨다.
357
            try
358
            {
359
                // 기존에 실행 중이던 프로세스 종료
360
                Stopprocess();
361
                logger.Info("Stopprocess");
362
            }
363
            catch (Exception e)
364
            {
365
                throw new Exception("Stop Process Error. ", e);
366
            }
367

    
368
            /// wcf 서비스 실행
369
            try
370
            {
371
                // WCF 실행
372
                StartWcfService();
373

    
374
                if (gWcfServiceHost.BaseAddresses?.Count() > 0)
375
                {
376
                    gServiceHostAddress = gWcfServiceHost.BaseAddresses.First();
377
                }
378

    
379
                //  각 sub서비스에 컨버터 아이템을 보내기 위한 서비스 초기화
380
                if (IsStation)
381
                {
382
                    SetServiceList(this.StationServiceIDList);
383
                }
384

    
385
                logger.Info($"StartWcfService {gServiceHostAddress}");
386
            }
387
            catch (Exception e)
388
            {
389
                throw new Exception("start Wcf Service Error. ", e);
390
            }
391

    
392
            //try
393
            //{
394
            //    // Status가 4이하인 Convert Item을 다시 Convert 함. 
395
            //    setDataBaseWaitingList();
396
            //    logger.Info("setDataBaseWaitingList");
397
            //}
398
            //catch (Exception e)
399
            //{
400
            //    throw new Exception("Database Waiting List Error . ", e);
401
            //}
402

    
403
            logger.Info("Start ServiceStation");
404
            timer.Start();
405
        }
406

    
407
        private void timerWorkMethod(object sender, ElapsedEventArgs e)
408
        {
409
            if (timer.Enabled)
410
            {
411
                var _timer = (System.Timers.Timer)sender;
412
                _timer.Stop();
413

    
414
                if (!IsStop)
415
                {
416
                    StartConvert();
417
                    
418
                    _timer.Start();
419
                }
420
                else
421
                {
422
                    StopService();
423
                }
424
            }
425
            else
426
            {
427
                logger.Error("timer disable");
428
            }
429
        }
430

    
431
        private bool IsStop = false;
432
    
433
        DateTime logTime;
434
        DateTime ReleaseTime;
435

    
436

    
437
        private bool StartConvert()
438
        {
439
            //stateTimer.Change(new TimeSpan(0, 0, 0), new TimeSpan(0, 0, 0, 0, -1));
440
            try
441
            {
442
               
443
                if ((DateTime.Now - ReleaseTime) >= new TimeSpan(0, 0,1))
444
                {
445
                    if (!IsReleaseItems)
446
                    {
447
                        if (System.Environment.UserInteractive)
448
                        {
449
                            Console.WriteLine("Release Items");
450
                        }
451

    
452
                        ReleaseItems();
453
                    }
454
                    else
455
                    {
456
                        if (System.Environment.UserInteractive)
457
                        {
458
                            Console.WriteLine("pass Release Items");
459
                        }
460
                    }
461

    
462
                    ReleaseTime = DateTime.Now;
463
                }
464
              
465
                if ((DateTime.Now - logTime) >= new TimeSpan(0, 5,0))
466
                {
467
                    logTime = DateTime.Now;
468
                    logger.Info("StationService Alive Check");
469
                }
470
            }
471
            catch (Exception ex)
472
            {
473
                logger.Error("Timer Error ", ex);
474
            }
475

    
476
            return true;
477
        }
478

    
479
        public void SetServiceList(List<string> serviceList)
480
        {
481
            StationServiceList = new List<SubStationServiceItem>();
482

    
483
            using (SERVICEPROPERTIESRepository database = new SERVICEPROPERTIESRepository(MarkusDBConnectionString))
484
            {
485
                foreach (var item in serviceList)
486
                {
487
                    try
488
                    {
489
                        var prop = database.GetSingleAsync(item).GetAwaiter().GetResult();
490

    
491
                        if (prop != null)
492
                        {
493
                            BasicHttpBinding httpbinding = new BasicHttpBinding();
494
                            httpbinding.CloseTimeout = new TimeSpan(0, 10, 0);
495
                            httpbinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
496
                            httpbinding.SendTimeout = new TimeSpan(0, 10, 0);
497
                            httpbinding.OpenTimeout = new TimeSpan(0, 10, 0);
498

    
499
                            EndpointAddress myEndpoint = new EndpointAddress(UriHelper.UriCreate(prop.SERVICE_ADDRESS));
500
                            var StationServiceClient = new WcfClient.StationServiceTask.StationServiceClient(httpbinding, myEndpoint);
501
                        
502
                       
503
                                //var items = StationServiceClient.AliveConvertList();
504
                            
505
                                StationServiceList.Add(new SubStationServiceItem
506
                                {
507
                                    Properties = prop,
508
                                    Service = StationServiceClient
509
                                });
510
                        }
511

    
512
                    }
513
                    catch (Exception ex)
514
                    {
515
                        logger.Error($"Service Properties Error  ID : { item }",ex);
516
                    }
517
                }
518
            }
519
        }
520

    
521
        protected override void OnStop()
522
        {
523
            try
524
            {
525
                IsStop = true;
526
            }
527
            catch (Exception e)
528
            {
529
                logger.Error("OnStop Error . ", e);
530
            }
531
        }
532

    
533
        public void StopService()
534
        {
535
            StopWcfService();
536
            try
537
            {
538
                Stopprocess();
539
            }
540
            catch (Exception ex)
541
            {
542
                System.Diagnostics.Debug.WriteLine(ex);
543
            }
544

    
545
            logger.Info("ServiceStation Stop");
546
        }
547

    
548
        #region Sleep 방지
549
        //[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
550
        //static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
551
        //[FlagsAttribute]
552
        //public enum EXECUTION_STATE : uint
553
        //{
554
        //    ES_AWAYMODE_REQUIRED = 0x00000040,
555
        //    ES_CONTINUOUS = 0x80000000,
556
        //    ES_DISPLAY_REQUIRED = 0x00000002,
557
        //    ES_SYSTEM_REQUIRED = 0x00000001
558
        //    // Legacy flag, should not be used.
559
        //    // ES_USER_PRESENT = 0x00000004
560
        //}
561
        //public static void PreventScreenAndSleep()
562
        //{
563
        //    SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS |
564
        //                            EXECUTION_STATE.ES_SYSTEM_REQUIRED |
565
        //                            EXECUTION_STATE.ES_AWAYMODE_REQUIRED |
566
        //                            EXECUTION_STATE.ES_DISPLAY_REQUIRED);
567
        //} 
568
        #endregion
569
    }
570
}
클립보드 이미지 추가 (최대 크기: 500 MB)