Base solution for your next web application
Open Closed

WebAPI and SignalR in a separate windows service #204


User avatar
0
daws created

Hey ! :)

I've a question about a specific architecture. I have to add 2 new features :

  • <ins>Memory cache accessible by WebAPI AND filled by a network connection</ins>

My goal is to have WebAPIs to retrieve DB stuff (already done) AND WebAPIs to retrieve infos from a realtime cache. I will NOT use SignalR for this part (for performance reason, i'll call webapi once each 5 minutes instead of receiving signalR datas each second) My realtime is delayed with 5-10 minutes, so it's not a problem.

This cache is not coming from DB but from a memory cache (list object) which is filled each second by a tcp stream.

Knowing that WebAPI IIS are initialized when called, i can't run a tcp service on IIS.

  • <ins>SignalR</ins>

I just need to create an windows service for that. (like you explained on <a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate/issues/174">https://github.com/aspnetboilerplate/as ... issues/174</a> )

I see multiple solutions here for the memory cache :

  • <ins>Encapsulate my tcp service & WebAPIs TCP in a windows service.</ins>I create a separated windows service which SELF HOST webAPIs TCP and signalR In this way [list:i7cspscg] [*:i7cspscg]Current project (WebAPIs DB & Web) still works in the same process ISS
  • WebAPIs TCP are SELF HOSTED in a windows service & contains SignalR Hub also.

[/:m:i7cspscg] [:i7cspscg]<ins>Split the current project (WebAPI & Web) into 2 distinct process.</ins>

  • Web Project on IIS
  • ALL WebAPI (ABP DB & TCP) on a SELF HOSTED service. (with SignalR also)[/*:m:i7cspscg][/list:o:i7cspscg]
  • What do you think is the best solution ? (or another one ?)
  • [AbpAuthorize] is still going to work if webAPI is hosted on a different thread ? (Windows service vs Web IIS project)
  • To use ABPScripts WebAPI from different windows services (ports), do I simply include <script src="~/AbpScripts/GetScripts"> with the url & port from the specific service ?

Thks for your help !


16 Answer(s)
  • User Avatar
    0
    hikalkan created
    Support Team

    Your question is very long, I will try to reply shortly :)

    In my one project I use thr approach 2. I have a Windows service which makes operations frequently on entities, so I cached they in a memory-dictionary. I also have web project (including web api) reads and changes entities. In every change, I notify Windows service to invalidate cache. It's hard, but I'm using ABP's event bus and default entity change events (<a class="postlink" href="http://www.aspnetboilerplate.com/Pages/Documents/EventBus-Domain-Events#DocPredefinedEvents">http://www.aspnetboilerplate.com/Pages/ ... inedEvents</a>) to get notification when an entity change, then send it to windows service using SignalR. Then in win service I re-generate same event, handle it and invalidate cache. But this was a little hard. I did it since we have'n time to adapt some out-of-process caching (like memcache) and it should be really fast. I may advice web apis in web. But it depends on your needs, you can go with one of the ways. Also, you can consider to run tcp based service on iss (you can start it on application_start and end it application_end)

    AbpAuthorize should work eveywhere, but surely you must configure IdentityFramework and other stuff for also windows service. GetScripts will also works if web api system works.

  • User Avatar
    0
    daws created

    Thks for the explaination regarding webapi & autorisation !

    Indeed, your project using separated project looks interestring but a little bit more complex.

    For the rest, I created a visio. Black : what's already done (default abp working process) <span style="color:#FF0000">Red : what I want to achieve. (forget about SignalR on schema)</span>

    The main problem is how to inject data in "Cache in Memory" from a tcp service.

    Is it possible to encapsulate all theses processes in one running IIS process ? (except the TCP/IP Server which is external of course)

    I don't think so, due to the TCP service (explained in previous post)

    • if yes, any idea ?
    • if not ... what's the best solution ? split to a specific process (windows service) containing cache, specific WebAPI just for this DataLayer, TCP/IP client, SignalR hub ? (AngularJS connecting to these services)

    Not related question to previous problem :

    • if I want to cache all retrieved Database datas from WebAPI calls (with same parameters), (knowing that DB data are not modified), is there something already conceived in ABP ? or should I implement custom repository with caches ? or put this cache in BusinessLayer ?
  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    You can embed TCP/IP client into web process (IIS). This simplifies your programming model. To do it;

    • Connect to TCP server on PostInitialize method of your web module and start listenin incoming messages, store it in a static variable (such a cache or dictionary object). You can create a singleton class instead of static variables here.
    • In app service, inject this TCP client or cache object and get data from the cache.
    • On Shutdown of your module, disconnect from TCP service.

    Only disadvantage is that: When no one uses application for a while (say 20 minutes) IIS shutdown the web application. To prevent it, you can configure IIS or create a simple timer that periodically requests an empty page in web application.

    This will be easier to create, deploy and manage.

    For cache stuff, if you want to cache application service responses, you can create a castle windsor interceptor and implement a simple caching mechanism.

  • User Avatar
    0
    daws created

    Great !

    I successfully created a RealTimeManager (dll, specific module) which I can insert in ApplicationService (via constructor injection) And in PostInitialize Web Project via IIocResolver like you specified in your doc.

    public override void PostInitialize()
            {
                using (var realtimeManager = IocManager.ResolveAsDisposable<RealTimeManager>())
                {
                    realtimeManager.Object.SetValue(666666666);
                }
    
    public class RealTimeManager : IRealTimeManager, ISingletonDependency
    

    indeed, my process need to be connected to TCP 24h/24 365day/year, so i'll prevent IIS to sleep process.

    thks for the big help !

  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    Congrats :) But I recomment to reset your application in IIS at least once a night.

  • User Avatar
    0
    daws created

    I think that's not a best practice to inject a tcp listener 24h/24 without interruption in a IIS process; even if I successfuly did it like you said. (if I restart it each day, there can be a data loss)

    I'll try the other practice, an Windows service containing tcp listener, WebAPI (specific port), & abp module zero integration.

    for that, i struggle a little bit; i created a ConsoleApp with SelfAPI web host, which works fine. I try to integrate Abp into this project, but i can't get the WebAPI accessible from my browser.

    I suppose that's because I need to inject routes into HttpSelfHostServer.

    if it's that, do you know how to inject Dynamic routes from abp (DynamicApiControllerBuilder) into this config ? Thks !

    using Abp;
    using Abp.Application.Services;
    using Abp.Modules;
    using Abp.WebApi.Controllers.Dynamic.Builders;
    using System;
    using System.Reflection;
    using System.Web.Http.SelfHost;
    
    namespace ConsoleApplication3
    {
    
        //#region AppService
    
        public interface IKartAppService : IApplicationService
        {
            int GetValue();
        }
    
        public class KartAppService : IKartAppService
        {
            public int GetValue()
            {
                return 5;
            }
        }
    
        //#endregion AppService
    
    
        public class Program
        {
            static void Main(string[] args)
            {
                using (var bootstrapper = new AbpBootstrapper())
                {
                    bootstrapper.Initialize();
    
                    var config = new HttpSelfHostConfiguration("http://localhost:8071");
    
                    // TEST URL http://localhost:8071/api/services/test/kart/getValue
    				
    				// HOW TO inject Dynamic routes from abp (DynamicApiControllerBuilder) into this config ?
    				
    				                //config.Routes.MapHttpRoute(
                        //"API Default", "api/{controller}/{action}/{id}",
                        //new { id = RouteParameter.Optional });
    
                    using (HttpSelfHostServer server = new HttpSelfHostServer(config))
                    {
                        server.OpenAsync().Wait();
                        Console.WriteLine("Press Enter to quit.");
                        Console.ReadLine();
                    }
    
                }
            }
        }
    
        public class TestAppModule : AbpModule
        {
            public override void Initialize()
            {
                IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
    
                DynamicApiControllerBuilder
                   .ForAll<IApplicationService>(Assembly.GetExecutingAssembly(), "test")
                   .Build();
            }
        }
    }
    
  • User Avatar
    0
    hikalkan created
    Support Team

    I'll check your code later (I've not time right now), but I want to say that: TCP Server-Client communication must be fault-tolerant. It's possible that your Windows service may stop (think that you're updating it or it's crashed or server is restarted etc.) For that reason, if you will lose data on a short-time disconnect, I recommend you to consider message queue systems (like MSMQ, RabbitMQ...).

  • User Avatar
    0
    daws created

    Indeed, TCP Server-Client communication must be fault-tolerant ;)

    I already have implemented my own mecanism to retrieve data loss, based on a company logic. It's a custom mecanism; since tcp server can be from all over the world with shitty connections sometimes (asia -> EU). (FYI, each tcp server write its own sent flux in local files. I never loss data; if the flux is shut down, I can retrieve local files)

    I could integrate this mecanism in my 1st solution (tcp client in IIS), but if it avoid me to call this mecanism each days (thks to a Windows service) ; i'll do it, for reliability purposes.

    I had a look on RabbitMQ, quite interesting Framework.

  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    I have running Windows service with ABP and hosting Web APIs in a project. I will check it tomorrow and share related code parts.

  • User Avatar
    0
    daws created

    Did you already had time to had a look on your related code parts ? :)

    thks !

  • User Avatar
    0
    hikalkan created
    Support Team

    Unfortunately, I mis-remembered. I checked codes, we did implement Web API on web project and used SignalR to pass requests to Windows Service. But, I think it should not be so different than using it without ABP. Because, we don't have a special thing for web api hosting. For running signalr, we use a startup file:

    internal class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                GlobalHost.DependencyResolver = new WindsorDependencyResolver();
    
                app.UseCors(CorsOptions.AllowAll);
                app.MapSignalR();
            }
        }
    
        public class WindsorDependencyResolver : DefaultDependencyResolver
        {
            public override object GetService(Type serviceType)
            {
                return IocManager.Instance.IocContainer.Kernel.HasComponent(serviceType) ? IocManager.Instance.IocContainer.Resolve(serviceType) : base.GetService(serviceType);
            }
    
            public override IEnumerable<object> GetServices(Type serviceType)
            {
                return IocManager.Instance.IocContainer.Kernel.HasComponent(serviceType) ? IocManager.Instance.IocContainer.ResolveAll(serviceType).Cast<object>() : base.GetServices(serviceType);
            }
        }
    

    I created a Abp module in windows service then used AbpBootstrapper to start it (like console sample here: <a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate-samples/blob/master/SimpleTaskSystem/SimpleTaskSystem.ConsoleApp/Program.cs">https://github.com/aspnetboilerplate/as ... Program.cs</a>)

  • User Avatar
    0
    byteplatz created

    Is this OOB for Abp ?

    I could not find anything ready to use with signalR.

    Do we need to implement the resolver/install signalr nuget manually ? Or does abp have something ready ?

    Bruno

  • User Avatar
    0
    hikalkan created
    Support Team

    No, there is no built-in SignalR integration.

  • User Avatar
    0
    tonid created

    <cite>hikalkan: </cite> Hi,

    I have running Windows service with ABP and hosting Web APIs in a project. I will check it tomorrow and share related code parts.

    Hi,

    Do you have code that we could use? As you said in comment above, maybe link to github repository.

    Thanks

  • User Avatar
    0
    hikalkan created
    Support Team

    Then I did write "Unfortunately, I mis-remembered". Have you seen <a class="postlink" href="http://www.aspnetboilerplate.com/Pages/Documents/SignalR-Integration">http://www.aspnetboilerplate.com/Pages/ ... ntegration</a> it may help you.

  • User Avatar
    0
    mengvisal created
    Support Team

    Hi!

    Can you let me know how to how to inject Dynamic routes from abp (DynamicApiControllerBuilder) into the HttpSelfHostConfiguration?

    Best