[en] Zyan Communication as Windows Service (solved)

Topics: Technical Support
Jun 27, 2015 at 10:29 AM
Hy.

I'm working with Zyan to communicate between 2 or more endpoints. This is working fine. So i put the whole application into a topshelf (windows service hosting framework).

There is an option to execute in console window and the application is working correct.
The second option is to install the app as a service and run it.

If running as a service I inspected my listening port using netstat -ano and see nothing is listening, but in my application i don't get any errors opening this ports. Working as simple console application the ports are listening.

I tried to give them all rights for the service but nothing changed. No entry in the system or application log.

Perhaps someone had the same problem and could help me.
Thanks you.
Coordinator
Jun 27, 2015 at 2:58 PM
Hi Jens,

We use TopShelf in all of our Zyan-based application servers.
It's a truly zero friction library, very easy to integrate and use.
We developed a console application and converted it into a service in a couple of minutes, that was truly amazing.
So I'm sure that TopShelf shouldn't be a problem by itself.

First, a silly question: have you tried connecting client applications to your running service?
Maybe it's a some kind of security issue that prevents netstat from seeing your service but the server actually works?

Next, are you certain that you configured your TopShelf service properly?
Do you have a logging library integrated into your service? It might be easier to diagnose problems with a proper logging.
Try adding log messages to your server startup routine, like loading configuration, creating component host, registering services, etc.
Make sure that your console version outputs just the same log entries when run as a service.

And last, I'm not sure, but it also may be a firewall issue.
As far as I remember, the application doesn't get any errors when I opt to disable network when the firewall pops up its dialog.
It continues running as nothing happened with network disabled.

Regards, Alex
Jun 27, 2015 at 4:07 PM
Edited Jun 27, 2015 at 4:12 PM
Hi Alex,

thanks for your fast reply.

Yes you're right. Topshelf is amazing and I use it in other scenarios with SQL-Server and have no security issues.

Yes I log all my application output with NLog and it works fine in any situations.

I tried it with Windows 7, Windows 8, Windows 8.1, Windows Server 2012R2 and the result is all the same. Running as console application everything is ok, as service no.
The firewall is completely disabled.

If ran as console application we can access from local, remote and remote with natting, there is no problem.

I'm not quite sure a configured topshelp correctly, here is some of my implementation:

Init topshelf service:

internal class DiscoveryAgent
{
    /// <summary>
    /// Main entry point of the service.
    /// </summary>
    /// <param name="args">The arguments.</param>
    internal static void Main(string[] args)
    {
        HostFactory.Run(hostConfigurator =>
        {
            hostConfigurator.Service<Dispatcher>(serviceConfigurator =>
            {
                serviceConfigurator.ConstructUsing(name => new Dispatcher());
                serviceConfigurator.WhenStarted(ew => ew.Start());

                //serviceConfigurator.WhenStarted(ew =>
                //    {
                //        Task.Factory.StartNew(() => 
                //            {
                //                ew.Start();
                //            });
                //    });

                serviceConfigurator.WhenStopped(ew =>
                {
                    ew.Stop();

                    // And dispose or release any component containers (e.g. Castle) 
                    // or items resolved from the container.
                });
            });

            hostConfigurator.SetDescription("Discovery Agent S...");
            hostConfigurator.SetDisplayName("S... Configuration Item Discovery Agent");
            hostConfigurator.SetServiceName("Smart360CIDA");
            hostConfigurator.StartAutomatically();
        });
    }
}

Content of the worker process:

public abstract class Worker
{
    private static Logger log = LogManager.GetCurrentClassLogger();

    /// <summary>
    /// Starting the current service instance.
     /// </summary>
    public void Start()
    {
        log.Info("(C) Copyright 2015, S...Agent, Version {0}",
            Assembly.GetExecutingAssembly().GetName().Version.ToString());
        log.Info("(R) All rights reserved.");

        log.Info("Configuration Item Discovery Agent (CIDA) is starting...");

        // Multiple thread instances cannot be created
        if (this.thread == null || this.thread.ThreadState == ThreadState.Stopped)
        {
            this.thread = new Thread(this.Run);
        }

        // Start thread if it's not running yet
        if (this.thread.ThreadState != ThreadState.Running)
        {
            this.thread.Start();
        }
    }

    /// <summary>
    /// Running the current service main method.
    /// </summary>
    private void Run()
    {
        try
        {
            try
            {
                this.DoWork();
            }
            catch (ThreadAbortException)
            {
                Thread.ResetAbort();
            }
            finally
            {
            }
        }
        catch (Exception e)
        {
            log.Error("Error due to initialization of the working process.");
            log.Error("Probably the configuration file is corrupted or there are mismatching configuration settings. See details below.");
            log.Error(e);
            throw;
        }
    }
}

The point where I use the Zyan Framework:

public class Dispatcher : Worker
{
    private static Logger log = LogManager.GetCurrentClassLogger();
    private static QueueService _qs;
    private static DateTime StartTime;
    private static int MaxRunningJobsAllowed = 5;
    private static string AgentName = string.Empty;

    /// <summary>
    /// Running up processes to initialize the communication channels...
    /// </summary>
    protected override void DoWork()
    {
        StartTime = DateTime.Now;
        log.Info("Communication subsystem is starting...");

        // Allgemeine Einstellungen aus der Konfiguration laden
        // Name des Agenten, maximale Anzahl an Jobs, Receiveradresse und Port...
        var cfg = new SettingsManager(ConfigurationType.CommunicationConfiguration);
        AgentName = cfg.GetCommonSettings().AgentName;
        log.Info("Applying common configuration settings.");

        // initialize contract channel...
        var service = Task.Factory.StartNew(() =>
        {
            var config = new SettingsManager(ConfigurationType.CommunicationConfiguration);
            var settings = config.GetChannelSettings(ChannelTypes.ServiceChannel);
            InitializeServiceChannel(settings.Uid, settings.Port);
        }).ContinueWith(a => { });

        // initialize contract channel...
        var contract = Task.Factory.StartNew(() =>
        {
            var config = new SettingsManager(ConfigurationType.CommunicationConfiguration);
            var settings = config.GetChannelSettings();
            InitializeContractChannel(settings.Uid, settings.Port);
        }).ContinueWith(a => { });

        log.Info("Communication subsystem is up and ready.");

        // set up the queue with notifications
        _qs = new QueueService();
        _qs.DiscoveryCreated += DiscoveryCreated;
        _qs.DiscoveryRemoved += DiscoveryRemoved;
        _qs.QueueReleased += QueueReleased;
        log.Info("Queue services successfully initialized.");

        log.Info("Configuration Item Discovery Agent (CIDA) started successfully.");
    }

    /// <summary>
    /// Initializes the service maintenance channel...
    /// </summary>
    /// <param name="uid"></param>
    /// <param name="port"></param>
    private static void InitializeServiceChannel(string uid, int port)
    {
        try
        {
            var protocol = new Zyan.Communication.Protocols.Tcp.TcpBinaryServerProtocolSetup(port);
            using (var host = new ZyanComponentHost(uid, protocol))
            {
                host.RegisterComponent<IAgentService, MaintenanceService>();
                log.Info("Channel '{0}' successfully initialized for service inquiries. Ready for communication.", port);
                Console.ReadLine();
            }
        }
        catch (Exception e)
        {
            log.Error("Service channel could not be initialized. See the following messages for details.");
            log.Error(e);
        }
    }

    /// <summary>
    /// Initializes the data communication channel...
    /// </summary>
    /// <param name="uid"></param>
    /// <param name="port"></param>
    private static void InitializeContractChannel(string uid, int port)
    {
        try
        {
            var protocol = new TcpBinaryServerProtocolSetup(port);
            using (var host = new ZyanComponentHost(uid, protocol))
            {
                host.RegisterComponent<IAgentContract, DiscoveryService>();
                log.Info("Channel '{0}' successfully initialized for working inquiries. Ready for communication.", port);
                Console.ReadLine();
            }
        }
        catch (Exception e)
        {
            log.Error("Working channel could not be initialized. See the following messages for details.");
            log.Error(e);
        }
    }
}
Perhaps you see something I have not right configured on the topshelf implementation, hoping in advance.

Thank you very much.

Best regards
Jens
Coordinator
Jun 28, 2015 at 2:43 AM
Edited Jun 28, 2015 at 2:48 AM
Hi Jens,

That looks pretty obscure to me, sorry.
Why starting new threads, creating tasks, calling blocking Console.ReadLine() from a non-interactive service application?
I'm also confused with ContinueWith called with empty delegate, what's it for?
.ContinueWith(a => { });
All you need for Topshelf service is just two short non-blocking methods: Start and Stop.
Start method should create ZyanComponentHost and register components, and Stop method should simply dispose of it.
You shouldn't start new threads and block your main thread waiting for something to happen, Topshelf takes care of it by itself.

That's all you need:
static void Main()
{
    HostFactory.Run(x =>
    {
        //x.UseNLog(); // uncomment to enable TopShelf logging
        x.Service<Dispatcher>(s =>
        {
            s.ConstructUsing(name => new Dispatcher());
            s.WhenStarted(d => d.Start());
            s.WhenStopped(d => d.Stop());
        });

        // these options can be overridden via the command line:
        // service.exe install --localservice -servicename:Cida -description:Demo -displayname:CidaDemo
        x.RunAsNetworkService();
        x.SetDescription("Configuration Item Discovery Agent");
        x.SetDisplayName("CidaService");
        x.SetServiceName("CidaService");
        x.EnableServiceRecovery(rc => rc.RestartService(1)); // restart after one minute
    });

    Console.WriteLine("Topshelf application is finished. Exiting...");
}

internal class Dispatcher
{
    private ZyanComponentHost Host { get; set; }

    public void Start()
    {
        var protocol = new TcpDuplexServerProtocolSetup(TcpPortNumber);
        Host = new ZyanComponentHost(ZyanHostName, protocol);
        Host.RegisterComponent<ISampleService, SampleService>();
        Console.WriteLine("Server is up and running. Ready to serve the clients.");
    }

    public void Stop()
    {
        if (Host != null)
        {
            Host.Dispose();
            Host = null;
            Console.WriteLine("Server is stopped.");
        }
    }
}
This simple Topshelf application can run both as console app or a non-interactive service.
It depends on the command-line arguments, I'm sure you are already aware of it (run, install, start/stop, etc).

The complete source code for this example (as well as a typical command line session) is here:
https://gist.github.com/yallie/7a3a4a16ce8f4b9e74da

My example application accepts the following arguments:
  • empty command line — start console server application
  • install — install the service (requires administrator privileges)
  • start — start the service (requires administrator privileges)
  • client — start the console client
  • stop — stop the service (requires administrator privileges)
  • uninstall — uninstall the service (requires administrator privileges)
I've just tested the application in both console and service modes, and it works just fine.
Let me know if it helps.

Regards, Alex
Marked as answer by yallie on 6/28/2015 at 6:36 AM
Jun 28, 2015 at 12:06 PM
Hi Alex,

thank you very much. You solved my problem. You're great.

I just implemented as you described and everything is working well. Console application and as service are working without any problem.
Many thanks for your effort.

About my obscuring code, perhaps I didn't really know how to implement the Zyan Components into a Topshelf service. Eearlier I used it only as a console application.

Once again. Many thanks.
Regards, Jens
Coordinator
Jun 29, 2015 at 1:09 AM
You're welcome Jens!

Regards, Alex
Apr 15 at 9:39 PM


Hi Yallie,


in the past you helped me with my Zyan and Topshelf problems. Thanks again.

Hope you can help me once more with your answers.


I decided to transfer my zyan client to linux.

I saw you use mono for it, right. I want to use zyan with ubuntu mate on a rasperry pi 2/3.

do you have experience with this. does it work, and does it work with mono?


Is it possible to run the client on linux as a service like on windows with topshelf?


Many thanks in advance and best regards.


Jens.



Coordinator
Apr 17 at 12:25 AM
Hi Jens,

Zyan should run on recent Mono stack (3.x+).
But I'm not sure Topshelf supports Linux.
It supports Mono and console apps, but not services.
http://docs.topshelf-project.com/en/latest/overview/faq.html#is-topshelf-just-for-windows

There are third-party extension for Topshelf and Linux, but I never tried using it, sorry.
You'd better check it out yourself: https://github.com/pruiz/Topshelf.Linux

Let me know if it helps.

Regards, Alex
Apr 17 at 11:17 AM

Hi Alex,


thanks for your answer.

I'll try it.


Regards, Jens




Von: yallie <[email removed]>
Gesendet: Sonntag, 17. April 2016 01:25
An: [email removed]
Betreff: Re: [en] Zyan Communication as Windows Service (solved) [zyan:640533]

From: yallie

Hi Jens,

Zyan should run on recent Mono stack (3.x+).
But I'm not sure Topshelf supports Linux.
It supports Mono and console apps, but not services.
http://docs.topshelf-project.com/en/latest/overview/faq.html#is-topshelf-just-for-windows

There are third-party extension for Topshelf and Linux, but I never tried using it, sorry.
You'd better check it out yourself: https://github.com/pruiz/Topshelf.Linux

Let me know if it helps.

Regards, Alex