Host an Azure cloud service on regular vds

I am writing web projects in visual studio, and with each new version of the studio it seems to be sharpened to work with Windows Azure. I like Azura, although I use only a small set of features. The main thing for me is the Cloud Service. The cloud service is great for deploying a distributed server.

So, I create a cloud service in which I add one web role (virtual machine with IIS), one worker (virtual machine without IIS) and a common class library. After publishing, my roles are assigned an IP address and different ports. That is, immediately there is a tcp network, manual settings are minimal and can be done in the studio itself. You can, for example, make a public access point for the worker, but I do not need this. My worker will be hidden from outside eyes and a wcf server will hang on it, and my roles will communicate over a fast local network.

I place the general classes in the library (which I connect to all roles), for example, the interface and the communication channel:

    [ServiceContract]
    public interface IwcfChat
    {
        [OperationContract]
        string SendMessage(string userId, string userName, string text);
        [OperationContract]
        string GetMessages(string userId, string userName);
    }
//================
    public sealed class wcfChat:IDisposable
    {
        IwcfChat _channel;
        ChannelFactory factory = null;
        public wcfChat()
        {
            NetTcpBinding b = new NetTcpBinding();
            b.Security.Mode = SecurityMode.None;
            b.Security.Message.ClientCredentialType = MessageCredentialType.None;
            #if(DEBUG)
                EndpointAddress address = new EndpointAddress("net.tcp://127.255.0.1:9003/wcfChat");
            #else
                EndpointAddress address = new EndpointAddress("net.tcp://"+spr.wcfIP+":9003/wcfChat"); 
            #endif
                factory = new ChannelFactory(b, address);
            factory.Faulted += OnChannelFaulted;
            factory.Open();
        }
        public IwcfChat channel
        {
            get
            {
                if (factory != null && factory.State == CommunicationState.Opened)
                {
                    if(_channel==null) _channel = factory.CreateChannel();
                    return _channel;
                }
                return null;
            }
        }
        void OnChannelFaulted(object sender, EventArgs e)
        {
            factory.Abort();
        }
        public void Dispose()
        {
            factory.Close();
        }
    }


Methods in a web role are called like this:

 using (var chat = new wcfChat())
 {
  res = chat.channel.SendMessage(id, name, text);
 }


Accordingly, in the worker I have an implementation of the methods from the interface (SendMessage, GetMessage), I will not paint them, and the same code is executed in the worker when it starts, which makes it the host for wcf:


 public override bool OnStart()
 {
  // Задайте максимальное число одновременных подключений 
  ServicePointManager.DefaultConnectionLimit = 12;
  // Create the host
  ServiceHost host = new ServiceHost(typeof(wcfChat));
  // Read config parameters
  string hostName = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["port"].IPEndpoint.Address.ToString();
  // Create Metadata
  ServiceMetadataBehavior metadatabehavior = new ServiceMetadataBehavior();
  host.Description.Behaviors.Add(metadatabehavior);
  Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
  string mexendpointurl = string.Format("net.tcp://{0}:{1}/wcfChatMetadata", hostName, 8003);
  host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, mexendpointurl, new Uri(mexendpointurl));
  // Create end point
  string endpointurl = string.Format("net.tcp://{0}:{1}/wcfChat", hostName, 9003);
  host.AddServiceEndpoint(typeof(IwcfChat), new NetTcpBinding(SecurityMode.None), endpointurl, new Uri(endpointurl));
  // Open the host
  host.Open();
  // Trace output
  Trace.WriteLine("WCF Listening At: " + endpointurl);
  Trace.WriteLine("WCF MetaData Listening At: " + mexendpointurl);
  return base.OnStart();
 }

Here I showed methods of type string, but the type of the return value can be absolutely any, for example, a complex class, which is described in my shared library. That's it, I don’t have any other settings in web config, no packages in xml or SOAP. There are no automatically generated contract files. When I tried to do this, on the Internet, when searching for information about wcf, the setting for http always pops up with all these packages / decompressions. Yes, wcf technology was originally invented for communication of heterogeneous systems, where it is necessary to serialize and transmit as a string. But if we have one programming language, and the wcf client and server are all our development, then you do not need to fence the garden, everything works fine.

Thus, I get a distributed server, for costly operations I do a separate worker, here, for example, chat is shown. All chat functions are moved to a separate virtual machine and I can increase the performance of the site itself (web role), separately, and chat.

Such an architecture seems correct to me and can withstand a stronger load than a monolithic project. All this is also developed and debugged as a single project in the studio, that is, when I call the method on wcf, I can go deeper into the code in debugging, without messages like “I can’t debug inside, I don’t know what’s happening there, such a result returned” .

With Azurov tools, this is all done easily and simply, I launch the entire project with one button and debug it as if it were one project. I remember once upon a time, one of the projects had similar architecture, but I was young, and it was very difficult for me there: some facades, and if the parameters of the method changed, I made changes in four places.

I need to do something similar on regular servers. Not on their physical servers, but on vds / vps virtual machines of some necessarily Russian hoster. I found a funny technology at one of the hosters ( 1gb.ru ), called the resource Windows Azure Pack / COSN. According to the description, this is a stripped-down version of Azura and everything should have “flown up”. Of course, they have a trial period, I looked. In the browser, the controls seem to be from the old version of the Azura portal (if anyone remembers).
image
Of the possibilities, there is only the creation of virtual machines, their integration into a network (manually), there is a service bus - and that’s all. There is also no way to upload this subscription to the studio, and this is a lot of amenities. Plus there is no way to create a cloud service. The description says that this “private cloud” will be further developed and supplemented with capabilities from the original Azura, but support said that everything has been frozen for a long time, and in general, these are incompatible technologies (“this Azure Pack is absolutely not compatible with real Azure. There that is, that is, and there is not enough. The ideas are common there, but the implementation is completely different, the APIs are different, and so on. ”(c)).
In short, it does not fit.

We are looking further. I go over the Windows hosters, looking for the possibility of renting regular vps / vds but with the possibility of organizing a local network between them. Many simply do not have such an opportunity. Yes, you can rent a virtual machine, raise a VPN on it and hook onto this network with another virtual machine. But such a "local" network is pulled over http, so it is obviously slower. I already thought that I won’t succeed, but the 1cloud hoster sees the desired “Just a couple of clicks, unite your virtual machines in a private network with a bandwidth of 1 Gb / s.” By the way, they have regular servers in St. Petersburg and high-performance in Moscow. Support says local networks with 10Gbps bandwidth are being deployed on Moscow infrastructure.

Great, let's try to do the same as above, only for regular hosting.

In the studio we create a solution, I called it sharedTest, into which we add:
1. MVC application with the same name.
2. The windows class library, I just called Lib
3. The windows console application, I called it worker
image

Next, you need to link these projects together, for this, right-click on our solution and go to properties.
image
Here you need to select the item in the project that starts, me a few start-ups and my Worker raise higher than the site.
Immediately, the next property is project dependency. The library is not dependent on anyone. The sharedTest site depends on both the library and the worker. Worker depends only on the library.

Do not forget in the properties of the worker (also right-click the properties) to specify the object to be launched, so that the worker starts first and does not wait for anyone.
image

The next step is to add the dependencies. In the Lib library, right-click on the links (Reference) -> add a link ... and we need the System.ServiceModel package. We need the same package in the worker. In the same way, add a link to our library from the Lib project in the site and worker.

We put in the library (conveniently in separate files) the public interface IwcfChat and the public sealed class wcfChat: IDisposable (my first listing is at the top). Also there is a directory with general settings or structures, there I have only the IP address (where I got it will be a little later).

    public static class spr
    {
        public static string wcfIP = "10.0.0.6";
    }


We will make our worker the host for the service (similar to the top listing, only transferred to the Main function, because I have a console application)
class Program
    {
        static void Main(string[] args)
        {
            // Задайте максимальное число одновременных подключений 
            ServicePointManager.DefaultConnectionLimit = 12;
            // Create the host
            ServiceHost host = new ServiceHost(typeof(wcfChat));
            // Read config parameters
#if (DEBUG)
            string hostName = "127.255.0.1";
#else
            string hostName = spr.wcfIP;
#endif
            // Create Metadata
            ServiceMetadataBehavior metadatabehavior = new ServiceMetadataBehavior();
            host.Description.Behaviors.Add(metadatabehavior);
            Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
            string mexendpointurl = string.Format("net.tcp://{0}:{1}/wcfChatMetadata", hostName, 8003);
            host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, mexendpointurl, new Uri(mexendpointurl));
            // Create end point
            string endpointurl = string.Format("net.tcp://{0}:{1}/wcfChat", hostName, 9003);
            host.AddServiceEndpoint(typeof(IwcfChat), new NetTcpBinding(SecurityMode.None), endpointurl, new Uri(endpointurl));
            // Open the host
            host.Open();
            Console.WriteLine("Host opened on "+hostName);
            Console.ReadLine();
        }
    }


I took the ports 8003 and 9003 out of my head, the main thing is that they are not standard and not busy.

Add the wcfChat class to the worker - an implementation of our interface
using Lib;
namespace Worker
{
    class wcfChat : IwcfChat
    {
        public string SendMessage(string userId, string userName, string text)
        {
            return "Sending message....";
        }
        public string GetMessages(string userId, string userName)
        {
            return "Get messages";
        }
    }
}


I didn’t paint anything here, but the values ​​come. It's time to try - we modify our Home controller and its presentation
        public ActionResult Index(bool isGet = true)
        {
            string res = "";
            using (var chat = new wcfChat())
            {
                res = isGet ? chat.channel.GetMessages("1", "name") : chat.channel.SendMessage("1", "name", "text");
            }
            return View((object)res);
        }

@model string
@{
    ViewBag.Title = "Home Page";
}


@Model


We start and see that everything works:
image

Excellent, very little is left - lay out the project with the hoster, I remind you, I work with 1cloud . We register, through the control panel we create two virtual machines of the configuration we need (I took the minimum ones in the St. Petersburg data center, but that doesn’t matter). They have a test period of several hours, support has expanded to me at the request, and well done, in general, are meeting me.
image

Next, create a local network, also a couple of clicks, the second item from the control panel, I called it testLocal. By the way, I turned off the checkbox of dynamic IPs (DHCP), since my service depends on a static IP, so I’ll have to tinker a bit more.
image

After creation, all the same in the control panel, go to each server, go to the Settings tab, find Private networks and switch the flag that we are using the newly created network. This flag will add us a new adapter and a new network inside the virtual machine. There we are given an internal IP address, I had it 10.0.0.5 and 10.0.0.6 for the site and the worker. To make the network work, we climb on the remote desktop (RDP) to our virtual machines and manually drive these values ​​into the network settings. Instructions for those who do not understand, are nearby .

I want to warn you that it didn’t work right away. I pointed out the networks as public, so I had to redo them into private networks like this. And you also need to add my used ports to the firewall exceptions. To do this, I called the start (Run) key win + R and typed there netsh.exe
and then at the command prompt I typed the command
firewall set portopening protocol = TCP port = 9003 name = myService mode = ENABLE scope = SUBNET profile = CURRENT


Now I will tell about the publication. I configured IIS for this manual . You can check the performance from the browser using an external IP address. The picture from IIS tells us that everything works.
image

I did not do anything with ftp, or even more so with setting up publishing from a version control system, this is the topic of another article. I corrected the IP address for the issued one, published my web application in the file system and threw it in the folder C: \ inetpub \ wwwroot on the server with pens. I updated the page and saw the expected error at the Home / Index address, since the service did not exist yet.

Actually, I also published my worker in the file system, and I got the installer. There is only one thing, you need not to get confused with DEBUG / RELEASE. I dragged the whole daddy onto the desktop of the second virtual machine, installed and launched it with the left mouse button. My black window got out, which said that the service was working. We are updating the site, we see that everything was successful.

image

So I got a working distributed server. I gave the chat as an example, although in the comments they say that you need to beat hands) In general, I use this technology in a browser game, where the functions of calculating the duel are transferred to a separate server. Of the pluses is that I can improve the performance of only one specific server, if necessary. In the same way it can be done for two websites, for example, the first server will process the photos, and the second server on the subdomain will be used to store the images (the image is transmitted once, and then the second only displays).
Naturally, the implementation is not perfect, it is only a reference point. You need to set up a correct and convenient publication. In addition, there is a tight connection 1 to 1, and if you need several workers, you will need a load balancer. Also, in a good way, you need to get rid of static internal IPs. Well, in general, for performance, you will need to replace the wcf connection with an implementation directly through tcp sockets.

Also popular now: