Azure Service Fabric Multi-Tenancy
Asked Answered
P

1

6

I'm trying to get a little follow up to this question that's already been answered...

Service Fabric multi-tenant

If I were to setup my tenants as Azure Service Fabric Stateless Services (they'll get their state out-of-band), how can I put more than one tenant on each node in the cluster? In testing, it seems that Service Fabric gags if you try and make your instance count greater than the node count.

These tenants are very lightweight, so I should be able to run dozens of them on each node (we will have hundreds overall), and I don't want to have to do a node for each one. Specifically, the tenants are more-or-less just opening a long-polling HTTP connection to an external customer service and streaming that data back into our system - so there's no public endpoints in play here. I just need the ability to spin up a lot of these workers that'll each open up their own long-polling connection.

Can somebody point me in the right direction?

FYI, I've explained a little bit more here... https://social.msdn.microsoft.com/Forums/en-US/efd172e2-0783-489b-b3ab-ec62fb7b8ee4/multiple-instances-per-node?forum=AzureServiceFabric

Thanks in advance!

Paintbrush answered 6/2, 2016 at 23:21 Comment(0)
M
8

You'll need to somehow partition your service.

There is several options, but the two that aligns good here (and also with the SO question you linked are):

Have a SF application where each tenant gets an instance of your service. You will then need to have a shared service in front to route requests to the correct service. It should look something like this.

MyAwesomeApp
    SharedStatelessApi <- External API points here
    MyTenantService_Tenant1 <- ServiceType: MyTenantService
    MyTenantService_Tenant2 <- ServiceType: MyTenantService
    ...

The other solution is to have one (or more) service fabric application per tenant, and would look something along the lines of:

MySharedApp
    SharedStatelessApi <- External API points here
Tenant1 <- ApplicationType: MyTenantApp
    MyTenantService <- ServiceType: MyTenantService
Tenant2 <- ApplicationType: MyTenantApp
    MyTenantService <- ServiceType: MyTenantService

It's the same concept as the first example, but the partition is done on a higher lever.

Personally, I prefer the second case. It feels more right. In both cases, you'll have to either create the services/application manually when a new customer signs up, or do it in code. If you want to do it in code, you should look at the FabricClient. If you need an example of that, let me know.

Also, as you can see, you should have a shared public endpoint, and in that endpoint route the request to the correct service based on something (header, auth token, uri, whatever is inline with your app).

Example of using FabricClient to create a service:

First you need a FabricClient. For a unsecured cluster (your local dev cluster), the following is enough:

var fabricClient = new FabricClient("localhost:19000");

When you have deployed to a secured cluster (for example in Azure), you need to authenticate the FabricClient, like so:

var creds = new X509Credentials
{
    FindType = X509FindType.FindByThumbprint,
    FindValue = clientCertThumbprint,
    RemoteCertThumbprints = {clientCertThumbprint},
    StoreLocation = StoreLocation.LocalMachine,
    StoreName = "My"
};

var clusterEndpoint = "CLUSTERNAME.LOCATION.cloudapp.azure.com:19000"
// or whatever your cluster endpoint is

var fabricClient = new FabricClient(creds, clusterEndpoint);

Then, when you have a FabricClient, you can create a stateless service like this:

var statelessDescriptor = new StatelessServiceDescription
{
    ApplicationName = new Uri("fabric:/MYAPP"),
    InstanceCount = 1, // How many instances.
    PartitionSchemeDescription = new SingletonPartitionSchemeDescription(),
    ServiceName = new Uri("fabric:/MYAPP/TenantA"),
    ServiceTypeName = "YourServiceTypeName",
    InitializationData = DATA_TO_PASS_TO_SERVICE_BYTE[] // Only if needed.
};

await _client.ServiceManager.CreateServiceAsync(statelessDescriptor)

If you passed any data in the "InitializationData" prop, it will be available in the service as ServiceInitializationParameters.InitializationData

Mitinger answered 7/2, 2016 at 0:35 Comment(13)
Thanks for taking the time to provide feedback. Perhaps I need to do more research on the FabricClient and the solution you're proposing to figure out if that's part of the answer that I'm looking for. Keep in mind that our multi-tenant service is really just a process for opening up a long-polling HTTP connection and streaming data from customers - no public endpoints. The problem is that we need to run several instances-worth of those tenants on each node since they're very lightweight and it doesn't make sense to use one per node, which would completely underutilize that node's horsepower.Paintbrush
I've reworded part of my question to better clarify the underlying workload in hopes that that will make the problem better defined. Basically looking to build an army of long-polling HTTP connections that can run for long periods of time and failover, load balance, etc. by using Service Fabric.Paintbrush
Ok, so assuming you have some internal way of triggering the initiation of the connection, my post above still stands. You could for example, instead of triggering a connection to Tenant A, spin up an instance of your stateless service for Tenant A, and that service opens it's connection in it's RunAsync method. Does that make sense ?Mitinger
Or maybe I can try to understand your requirements a bit better. You would like one instance of a stateful service per long-running connection? Is the number of connections/tenants fixed, or are they added/removed as time goes along?Mitinger
Yes, the idea would be to have a service for each "Customer", but the service, when it is "initialized", will open up that long-running connection and continuously stream data on that open connection. Customers will come and go in onesy-twosy fashion, but I can imagine always having extra instances activated that hit the the configuration server once an hour to see if there's a new customer that needs to start streaming, and that'll be the initialization process. Similarly on shutting down a connection, if customer cancels, we'll just add that instance back to the heap of available workers.Paintbrush
Ok, I understand. Then I would do as I said above, create an instance of the Tenant service per customer in code when needed. You can then pass in needed data in the InitializationData field so that the instance know who to connect to. Do the same when a customer leaves, just remove the instance. Unless timing is very critical, I don't see any need to have standby instances either, since creating a stateless service in our experience takes seconds.Mitinger
Wonderful - I'm so glad that somebody finally has taken some time to get me pointed in the right direction. However I'm still pretty new to Service Fabric (aren't we all), so I might need a little bit of a push... When you say "create an instance", can you give me a little bit more of a clarification? Is that where the FabricClient comes in? Also, is that concept particular to either a Stateful or Stateless service, or will that approach work for either? I did a quick search on FabricClient, but didn't come up with much on the first try - just MSDN docs, which don't give much practical help.Paintbrush
Great :) Yes, creating a Service from code requires the use of FabricClient. With FabricClient, you can control most of your cluster from code, and it works both for Stateful and Stateless services. I'll update my answer with some basic example code to show you how you can do it.Mitinger
@Mitinger - how does external clients traffic get to each application in the 2nd scenario? do all tenents go through the SharedStatelessApi?Nabob
@Nabob That would be the simplest way yes. Then you could use for example something from authentication to redirect the request to the correct application.Mitinger
@Mitinger How should we redirect requests to a specific application? Could you please provide an example?Rockery
You can use any kind of reverse proxy. For example, Service Fabric ships with one: learn.microsoft.com/en-us/azure/service-fabric/…. Or you deploy Ocelot: ocelot.readthedocs.io/en/latest/features/servicefabric.html. Or deplot nginx/haproxy or even roll your own (that's what we did).Mitinger
@Mitinger could you please answer the question in here - #51025279 - it's based on the multi-application-instance approach you've specified, I'm unable to solve the problem of deploying an application type with zero instanceCount for some services.Threescore

© 2022 - 2024 — McMap. All rights reserved.