İçeriğe geç

Kolayca .NET Windows Servis Oluşturmanın Topshelf Hali

Windows Servisler yazılım ihtiyaçlarına göre sıklıkla kullanılan yapılardır. Windows Servisler, Windows işletim sisteminin temel bileşenlerinden olmakla beraber uzun süre çalışması gereken süreçlerin oluşturulmasını ve yönetilmesini sağlarlar. Normal uygulamaların aksine herhangi bir kullanıcının sistemde oturum açmasını gerektirmeden arka planda başlatılabilir, kullanıcı oturumunu sonlandırdığında dahi çalışmaya devam edebilirler.

Uygulamaların Windows Servis olarak yazılmasının başka sebeplerinden biri de güvenilir olmalarıdır. Güvenirlilikten kasıt ise Windows işletim sistemi çalıştığında servisin ayakta ve çalışır durumda olduğundan emin olunma durumudur.

Windows Servis Uygulaması ve Topshelf

Visual Studio ile Windows Servis uygulamaları geliştirmenin çeşitli yöntemleri mevcut. Biz burada açık kaynak kodlu bir kütüphane olan Topshelf ile servis geliştirmelerinin nasıl kolaylaştığını inceleyeceğiz.

Klasik yöntemlerle Windows servis uygulaması geliştirmek ve özellikle debug etmek bazı ek işlevler gerektirebilmektedir. Servis kodunda lokal için farklı kodlar yazarak geçici çözümler uygulamak veya servisi Windows’a yükleyerek debugger’ı ilgili process’e attach etmek gibi geliştiricilerin işini zorlaştıran durumlar bazen can sıkıcı olabilmekte. İşte bu noktada, bu yazının konusu olan Topshelf devreye giriyor.

Topshelf, .NET platformunda kolayca Windows servis yazmayı destekleyen bir framework. Bunun yanında servisi test etmek, debug etmek ve Windows Service Control Manager (SCM)’a yükleme ve kaldırma işlemlerinde de kolaylıklar sağlamaktadır.

Topshelf geliştiricilerin sadece kodu yazmasına ve business’a odaklanmasını sağlar. Geliştiricilerin InstallUtil ile servis yüklemeye çalışmak veya debugger’ın servise attach olarak hata ayıklamaya çalışmak gibi karmaşık detaylarla ilgilenmesine gerek bırakmaz.

Topshelf kullanarak bir Windows Servis geliştirmek, normal bir konsol uygulaması geliştirmekten farksızdır. Konsol uygulamasına birkaç satırlık konfigürasyon yazarak servisin bağlantısı ve çalışma şekli belirlenir.

Geliştirilen Windows Servis’in debug edilmesi Topshelf ile normal projelerin debug edilmesinden farksızdır. Projeyi debug modda çalıştırmak veya F5 butonuna basmak yeterli olacaktır.

Topshelf ile Windows Servis Uygulaması Geliştirmek

Bu yazıda basit olarak loglama işlemi yapan bir Windows Servis uygulaması oluşturacağız. Servis, her saniyede bir text dosyasına veri yazacak ve bu şekilde çalışır durumda olduğunu gözlemlemiş olacağız. Ayrıca diğer başlama, durma, devam etme gibi olayları da aynı text dosyasına loglayarak bu eventlerin de çalıştığını göreceğiz.

Buradaki temel odağımız servis uygulamamıza Topshelf’i entegre etmek ve servisin nasıl kurulacağını görmek olacaktır. Başlamadan önce Topshelf’i bana ilk söyleyen ekip arkadaşım Bora’ya buradan teşekkürlerimi iletiyorum. Şimdi gelin birlikte nasıl çalıştığını görelim.

Temel bir .NET Framework Console uygulaması oluşturdum ve nuget ile Topshelf kütüphanesini projeye ekledim. Sonrasında projede loglama işleminin yapılması için birkaç satır kod ekledim (.NET Core uygulamanız varsa aynı işlemler burada da yapılabilir).
Genel proje yapımız şu şekilde;
Loglama işlevini yerine getiren FileLogger.cs dosyamız;

using System.IO;

namespace LoggerWindowsService.Logger
{
    using System;

    public class FileLogger
    {
        private static FileLogger _instance;
        public static FileLogger Instance => _instance ?? (_instance = new FileLogger()); 

        public void WriteLog(string message)
        {
            var logFilePath = @"C:\LoggerService\Log.txt";
            Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));

            string formattedMessage = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}\n";

            File.AppendAllText(logFilePath, formattedMessage);
        }
    }
}

MyService.cs dosyamızda oluşturmaya çalıştığımız servisin Start, Stop, Pause ve Continue gibi çalışma zamanı yaşam döngüsü adımları yer alıyor. Servisin ilgili yaşam döngüsü adımlarından haberdar olmak için her adımda bir log atacak şekilde mesajlarımızı yazdık. Servis çalıştığında her saniye “Servis çalışıyor” mesajımızı dosyaya yazacaktır.

namespace LoggerWindowsService
{
    using System.Threading;
    using System.Threading.Tasks;

    using LoggerWindowsService.Logger;

    public class MyService
    {
        private bool isStopRequestReceived;

        public void OnStart()
        {
            string message = $"Servis başladı.";
            FileLogger.Instance.WriteLog(message);

            Task.Run(() => InitService());
        }

        public void OnStop()
        {
            isStopRequestReceived = true;
            string message = $"Servis durdu.";
            FileLogger.Instance.WriteLog(message);
        }

        public void OnPause()
        {
            isStopRequestReceived = true;
            string message = $"Servis duraklatıldı.";
            FileLogger.Instance.WriteLog(message);
        }

        public void OnContinue()
        {
            isStopRequestReceived = false;
            string message = $"Servis devam ettirildi.";
            FileLogger.Instance.WriteLog(message);
        }

        private void InitService()
        {
            while (!isStopRequestReceived)
            {
                string message = "Servis çalışıyor.";
                FileLogger.Instance.WriteLog(message);
                Thread.Sleep(1000);
            }
        }
    }
}

Program.cs’de Topshelf’in devreye girdiği kısımı görüyoruz. Burada https://topshelf.readthedocs.io/en/latest/ dökümantasyondan sayfasından ulaşabileceğimiz configurable bir çok parametresi mevcut.

namespace LoggerWindowsService
{
    using System;

    using Topshelf;

    class Program
    {
        static void Main(string[] args)
        {
            var hostRun = HostFactory.Run(
                x =>
                    {
                        x.Service<MyService>(
                            service =>
                                {
                                    service.ConstructUsing(() => new MyService());
                                    service.WhenStarted(s => s.OnStart());
                                    service.WhenStopped(s => s.OnStop());
                                    service.WhenPaused(s => s.OnPause());
                                    service.WhenContinued(s => s.OnContinue()); 
                                });

                        x.RunAsLocalSystem();
                        x.StartAutomatically();
                        x.SetServiceName("LoggerWindowsService");
                        x.SetDisplayName("LoggerWindowsService");
                        x.SetDescription(@"LoggerWindowsService description.");
                    });

            var exitCode = (int)Convert.ChangeType(hostRun, hostRun.GetTypeCode());
            Environment.ExitCode = exitCode;
        }
    }
}

Kod satırları üzerinden incelediğimizde;

  • 17. satırda servis new’lenerek bir instance üretiliyor. Burada bir IoC container kullanılıyorsa servis instance’ı o şekilde alınabilir.
  • Sonraki satırlarda servisin çeşitli eventlerini bizim kodumuzdaki metodlarla bağlıyoruz.
  • 24. satırda servisin hangi user tarafından çalıştırıldığı belirtiliyor. Burada servis, NetworkService veya bir kullanıcı hesabıyla çalıştırılabilmekte.
  • 25. satırda servisin otomatik, gecikmeli ve manuel gibi başlatılma seçenekleri yazılabilmekte.
  • Alt kısımlar ise servisin Windows Service Control Manager (SCM)’da nasıl göründüğü ile ilgili.

Geliştirme süreçlerimizin ardından servisi yükleyerek çalıştıralım. Bunun için ister debug ister release modda olan uygulama çıktımızı komut satırından şu şekilde çağırmak yeterli oluyor. Buradaki install komutu sadece yükleme işlemini yapıyor, servisin başlaması için start parametresini de geçmek gerekli.

LoggerWindowsService install start

Kaldırmak için ise startı kaldırıp, install yerine uninstall yazmak gerekiyor. Servisin yüklenebilmesi için yönetici yetkisi gerektiğini unutmayalım.
Yükleme başarıyla tamamlandıktan sonra servisimiz şu şekilde görünecektir.
Log dosyamızı kontrol ettiğimizde servisin sorunsuz çalıştığını görebiliyoruz.

Topshelf .NET Core’u da desteklediği için .NET Core türündeki konsol uygulamalarında da aynı şekilde Windows Servis yazabiliriz. Ancak şunu belirtmekte fayda var; Topshelf, çalışmak için bir Windows işletim sistemine ve .NET runtime’ına ihtiyaç duyduğu için Linux işletim sisteminde kullanılamamaktadır.

.NET Core konsol uygulaması üzerinden Topshelf kullanmak için yapmamız gereken uygulamanın ilgili platform için bir çıktısını almak ve terminalden aşağıdaki gibi gerekli yükleme komutunu çalıştırmaktır.

dotnet publish -r win-x64 -c Release

Bu kod ile proje adımızın aynı olduğunu varsayarsak LoggerWindowsService.exe dosyası çıktısı üretilecektir. Daha önce kullandığımız gibi aynı kodu kullanarak servis yükleme işlemini şu şekilde yapabiliriz.

LoggerWindowsService install start

Nasıl debug yapılabildiğini görmek için kısa bir animasyon hazırladım. Öncesinde gerekli breakpoint’lerimi koyduktan sonra Debug modda projeyi çalıştırıyorum ve çalışma zamanında istediğim breakpoint’lerde uygulamayı durdurabiliyorum.

topshelf ile windows service debug işlemi

Genel olarak Topshelf paketinin ne işe yaradığını ve en basit haliyle nasıl kullanıldığını tanımış olduk. Benim en sevdiğim bir diğer özelliği ise servis bağımlılıklarını kolayca yönetebilmesi.

Örneğin SQL server üzerinde bir tablonuz var ve bu tablonun belli aralıklarla içinin boşaltılması gerekiyor. Bu işlemi yapan servisi yazdınız ancak servisin çalışması için, ilk olarak SQL servislerinin ayağa kalkması gerekiyor. Bu şekilde yazdığınız servisin başka bir servise bağımlı olduğu durumlarda, Topshelf‘in Service Dependency özelliği ile ihtiyacınızı kolaylıkla giderebilirsiniz.

Service Dependency olarak MSMQ(Microsoft Message Queue), SQL Server, IIS(Internet Information Services) gibi native olarak desteklediği servislerin dışında diğer bağımlı servisler için servis adını parametre olarak geçerek bağımlılıkları yönetebilirsiniz.

Farklı bir yazıda görüşmek üzere 😊

Kaynaklar

Tek Yorum

  1. Fatih BÜYÜKAKÇALI Fatih BÜYÜKAKÇALI

    Hocam diline, zihnine sağlık. Amaca odaklı karmaşık olmayan güzel bir anlatım. Teşekkür ederim.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir