简体   繁体   中英

Host WCF in the Worker Role, Timeout

Basically, I'm following this: GUIDE

What I want is to publish my Cloudapp to azure with one web role and one worker role .

Worker role

I'm hosting a WCF service in my worker role where all the sql communication will happen, (DTO objects).

Web role

My web role project is just a normal MVC app that has a service reference to the WCF service. For an example, if a user wants to register, the data is sent to the WCF service which then sends it to the DB.

Problem

Error message:

An exception of type 'System.ServiceModel.FaultException' occurred in mscorlib.dll but was not handled in user code

Additional information: The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.

I also got a timeout error before which I can't seem to reproduce anymore..

I have followed the guide very thoroughly. The one thing that is different is that I would like to host them in one publish, not two. Also, the guy publish the project by clicking on packages which I don't, (I click on publish button).

I'm all out of ideas. I have only found two guides about hosting your WCF service in a worker role, the one I'm following now and THIS one. Both are very outdated and doesn't really do what I want to do. Both guides host the WCF service as a standalone Cloudapp. What I want is to publish both the worker role and the web role in the same solution, one publish.

If you have any suggestions, please leave them here. I appreciate all the help I can get. Thank you very much.

/Dying programmer

EDIT 1 - CODE -

INTERFACE & IMPLEMENTATION

namespace Fildela_Worker
{
    [ServiceContract]
    public interface IFildelaService
    {
        [OperationContract]
        void InsertUser(UserDTO user);
    }
}
[System.ServiceModel.ServiceBehaviorAttribute(IncludeExceptionDetailInFaults=tru‌​e)]
public class FildelaService : IFildelaService
    {
        DataLayer DB = new DataLayer();

        public void InsertUser(UserDTO user)
        {
            User newUser = new User(user.FirstName.ToLower().Trim(), user.LastName.ToLower().Trim(),
                user.Email.ToLower().Trim(), user.Password.Trim(), user.PasswordSalt.Trim(), user.AgreeUserAgreement);

            DB.User.Add(newUser);
            DB.SaveChanges();
        }
}

SERVICE DEFINITION

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="Fildela_Web.Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2014-01.2.3">
  <WebRole name="Fildela Web" vmsize="Small">
    <Sites>
      <Site name="Web">
        <Bindings>
          <Binding name="Endpoint1" endpointName="Endpoint1" />
        </Bindings>
      </Site>
    </Sites>
    <Endpoints>
      <InputEndpoint name="Endpoint1" protocol="http" port="80" />
    </Endpoints>
    <Imports>
      <Import moduleName="Diagnostics" />
    </Imports>
  </WebRole>
  <WorkerRole name="Fildela Worker" vmsize="Small">
    <Imports>
      <Import moduleName="Diagnostics" />
    </Imports>
    <Endpoints>
      <InputEndpoint name="port" protocol="tcp" port="9001" />
      <InputEndpoint name="mexport" protocol="tcp" port="8001" />
    </Endpoints>
  </WorkerRole>
</ServiceDefinition>

WORKER ROLE ONSTART:

public override bool OnStart()
    {
        // Set the maximum number of concurrent connections 
        ServicePointManager.DefaultConnectionLimit = 12;

        // Create the host
        ServiceHost host = new ServiceHost(typeof(FildelaService));

        // Read config parameters
        string hostName = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["port"].IPEndpoint.Address.ToString();
        int port = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["port"].IPEndpoint.Port;
        int mexport = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["mexport"].IPEndpoint.Port;

        // Create Metadata
        ServiceMetadataBehavior metadatabehavior = new ServiceMetadataBehavior();
        host.Description.Behaviors.Add(metadatabehavior);

        Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
        string mexendpointurl = string.Format("net.tcp://{0}:{1}/FildelaServiceMetadata", hostName, 8001);
        host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, mexendpointurl, new Uri(mexendpointurl));

        // Create end point
        string endpointurl = string.Format("net.tcp://{0}:{1}/FildelaService", hostName, 9001);
        host.AddServiceEndpoint(typeof(IFildelaService), 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();
    }

MVC WEB.CONFIG:

<system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="NetTcpBinding_IFildelaService">
          <security mode="None" />
        </binding>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint address="net.tcp://MYADDRESS.cloudapp.net:9001/FildelaService"
        binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IFildelaService"
        contract="FildelaServiceReference.IFildelaService" name="NetTcpBinding_IFildelaService" />
    </client>
....

MVC PROXY USE:

FildelaServiceClient proxy = new FildelaServiceClient();

                                UserDTO newUser = new UserDTO()
                                {
parameters....
                                };

                                //Insert user
proxy.InsertAccountOwner(newUser);

EDIT 2 - CODE -

Worker role

public override bool OnStart()
    {
        // Set the maximum number of concurrent connections 
        ServicePointManager.DefaultConnectionLimit = 12;

        StartWCFHost();

        return base.OnStart();
    }

private void StartWCFHost()
{
    var baseaddress = string.Format("net.tcp://{0}/", RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["FildelaService"].IPEndpoint);
    var host = new ServiceHost(typeof(FildelaService), new Uri(baseaddress));
    host.AddServiceEndpoint(typeof(IFildelaService), new NetTcpBinding(SecurityMode.None), "Database");
    host.Open();
}

ServiceDefinition

<WorkerRole name="Fildela Worker" vmsize="Small">
    <Imports>
      <Import moduleName="Diagnostics" />
    </Imports>
    <Endpoints>
      <InternalEndpoint name="FildelaService" protocol="tcp" />
    </Endpoints>
  </WorkerRole>

Controller

private EndpointAddress GetRandomEndpoint()
    {
        var endpoints = RoleEnvironment.Roles["Fildela Worker"].Instances.Select(i => i.InstanceEndpoints["FildelaService"].IPEndpoint).ToArray();
        var r = new Random(DateTime.Now.Millisecond);
        return new EndpointAddress(string.Format("net.tcp://{0}/Database", endpoints[r.Next(endpoints.Count() - 1)]));
    }





[HttpGet]
    [AllowAnonymous]
    public ViewResult Contact()
    {
        var factory = new ChannelFactory<IFildelaService>(new NetTcpBinding(SecurityMode.None));
        factory.Endpoint.Binding.SendTimeout = new TimeSpan(0, 5, 0);
        var channel = factory.CreateChannel(GetRandomEndpoint());

        List<CategoryDTO> contactCategories = channel.GetContactCategories().ToList();

        return View(contactCategories);
    }

First, you have the contract and service , to host your service delete all the code you have added to your OnStart methode and add this methode

 private void StartWCFHost()
    {
        var baseaddress = string.Format("net.tcp://{0}/", RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["port"].IPEndpoint);
        var host = new ServiceHost(typeof(FildelaService), new Uri (baseaddress));
        host.AddServiceEndpoint(typeof(IFildelaService), new NetTcpBinding(SecurityMode.None), "Inserter");
        host.Open();
    }

That is it just call this method from your OnStart method and the host is done

Now the question is about consuming the webservice

in your controller when you are calling the webservice add this code (we're creating a Channel factory no need to add service reference)

First add this method to your controller

 private EndpointAddress GetRandomEndpoint()
    {
        var endpoints= RoleEnvironment.Roles["PutWorkerRoleName"].Instances.Select(i=>i.InstanceEndpoints["port"].IPEndpoint).ToArray();
        var r = new Random(DateTime.Now.Millisecond);
        return new EndpointAddress(string.Format("net.tcp://{0}/Inserter", endpoints[r.Next(endpoints.Count() - 1)]));
    }

then add this code to call the service in your controller method(since you're using MVC)

 var factory = new ChannelFactory<IFildelaService>(new NetTcpBinding(SecurityMode.None));
        var channel = factory.CreateChannel(GetRandomEndpoint());
        channel.InsertAccountOwner(newUser);

I may have forgotten to rename some variables from my working project so feel free to verify the conformity of your code

Update

I uploaded the project to my github : Worker Role WCF host+Web Role

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM