I'm playing with Azure Service Fabric and a console app. I simply want my console app to connect to the cluster and do some stuff.
The console app try to resolve the service address with the following:
static void Main(string[] args)
{
ServicePartitionResolver resolver = null;
try
{
resolver = new ServicePartitionResolver(
new string[] {
"localhost:19000",
"localhost:19001"
});
Uri serviceUri = new Uri("fabric:/StatefullServiceTEST/MyStatefulService");
ResolvedServicePartition partition = resolver.ResolveAsync(serviceUri, new ServicePartitionKey(), CancellationToken.None).GetAwaiter().GetResult();
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
Console.WriteLine();
Console.Write("Press any key to exit...");
Console.ReadKey();
}
My problem is that resolver.ResolveAsync throws an exception that doesn't seem to have any connection with Service Fabric:
Unable to cast COM object of type 'System.__ComObject' to interface
type 'IFabricApplicationManagementClient10'. This operation failed
because the QueryInterface call on the COM component for the interface
with IID '{67001225-D106-41AE-8BD4-5A0A119C5C01}' failed due to the
following error: No such interface supported (Exception from HRESULT:
0x80004002 (E_NOINTERFACE)).
Any ideas on this?
UPDATE
I was not so clear explaining my problem and what I want to achive.
I'm playing with Azure Service Fabric (both stateless and stateful services): my question is: what's the best way to call a micro service hosted in Azure Service Fabric?
Regards,
Attilio
You have to create a public facing service (such as Asp.net Core Web Api) which will expose the functionality of your service inside service fabric to outside world (outside the service fabric cluster). FabricClient approach is to be utilzied for calling services from within the service fabric cluster and not outside.
From your Asp.net Core service you will use the FabricClient to access the service hosted, so in general your asp.net core app act as reverse proxy to expose the functionality of actual service.
You can't use ServicePartitionResolver, it is a reliable service feature and must be called from within a service running in your cluster.
I couldn't understand clearly what you want.
If you want to manage the service and get details about it, like query running instances or replicas, add or remove instances, and so on, Use the Fabric Client, below is a quick snippet, check details here and here:
`
using System.Fabric;
using System.Security.Cryptography.X509Certificates;
string clientCertThumb = "71DE04467C9ED0544D021098BCD44C71E183414E";
string serverCertThumb = "A8136758F4AB8962AF2BF3F27921BE1DF67F4326";
string CommonName = "www.clustername.westus.azure.com";
string connection = "clustername.westus.cloudapp.azure.com:19000";
var xc = GetCredentials(clientCertThumb, serverCertThumb, CommonName);
var fc = new FabricClient(xc, connection);`
or,
If you want to communicate to a running service, like an API, you should use a Reverse Proxy to resolve your services via URL, like the below snippet, more details here:
http://mycluster.eastus.cloudapp.azure.com:19081/MyApp/MyService
You cannot access service in an ASF cluster from the outside using the ServicePartitionResolver.
You have to have a public facing endpoint on your cluster, like a stateless service acting as a web api for example.
From the docs:
Services connecting to each other inside a cluster generally can directly access the endpoints of other services because the nodes in a cluster are on the same local network. In some environments, however, a cluster may be behind a load balancer that routes external ingress traffic through a limited set of ports. In these cases, services can still communicate with each other and resolve addresses using the Naming Service, but extra steps must be taken to allow external clients to connect to services.
A Service Fabric cluster in Azure is placed behind an Azure Load Balancer. All external traffic to the cluster must pass through the load balancer. The load balancer will automatically forward traffic inbound on a given port to a random node that has the same port open. The Azure Load Balancer only knows about ports open on the nodes, it does not know about ports open by individual services.
So, unless your console app is hosted in the cluster as a guest executable, you have some more work to do.
Related
I've got a standalone on-premise Service Fabric Cluster that is secured using Windows Authentication.
In this application I have a ASP.NET Core WebApi Stateless service that tries to communicate with another stateless service via the remoting. Unfortunately I'm getting the following error when the WebApi service tries to RPC to the stateless service:
System.AggregateException: One or more errors occurred. ---> System.Fabric.FabricConnectionDeniedException: Not authorized to connect ---> System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x80071C43
at Microsoft.ServiceFabric.FabricTransport.NativeServiceCommunication.IFabricServiceCommunicationClient2.EndRequest(IFabricAsyncOperationContext context)
at Microsoft.ServiceFabric.FabricTransport.Client.FabricTransportClient.EndRequest(IFabricAsyncOperationContext context)
at System.Fabric.Interop.AsyncCallOutAdapter2`1.Finish(IFabricAsyncOperationContext context, Boolean expectedCompletedSynchronously)
--- End of inner exception stack trace --
Futhermore, I can confirm that
When deploying this same application to a "development cluster" (i.e. either my local machine or another a remote Service Fabric cluster running all it's Nodes on one machine), I don't get the error - hence potentially an issue with the AD accounts I've used to setup my multi-machine cluster (I'm using a machine group account).
When creating the Client Proxy, I do setup the Security Credentials to use windows authentication - i.e.
var transportSettings = new FabricTransportRemotingSettings
{
SecurityCredentials = new WindowsCredentials()
};
Func<IServiceRemotingCallbackClient, IServiceRemotingClientFactory> clientProxyFactory = c => new FabricTransportServiceRemotingClientFactory(transportSettings);
var serviceProxyFactory = new ServiceProxyFactory(clientProxyFactory);
TService clientProxy = serviceProxyFactory.CreateServiceProxy<TService>(uri);
return clientProxy;
In the above code, if I instead use:
SecurityCredentials = new NoneSecurityCredentials() then I get a similar FabricConnectionDeniedException but the message is slightly different saying that the Client is not authorised to connect. This makes sense - but again, potentially indicates that there is an issue with my transport settings...
I have an Azure Cloud Service with a worker role that starts an OWIN web app on startup, which uses SignalR.
Separately, I have a console project that uses the SignalR client library to connect to this worker role and listen for events.
Everything is working when I run the client and the service locally using the Azure emulators.
When I publish the cloud service and point the console application to it and try to connect, I get the following in the SignalR trace logs:
WS Connecting to: ws://myapp.cloudapp.net/signalr/connect?clientProtocol=1.4&transport=webSockets&connectionData=[{"Name":"MessageBusHub"}]&connectionToken=...
OnError(System.Net.WebSockets.WebSocketException (0x80004005): An internal WebSocket error occurred. Please see the innerException, if present, for more details. ---> System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host
It then proceeds to try again using server sent events and long polling with the same error each time.
I'm using the following endpoint in my Cloud service config:
<Endpoints>
<InputEndpoint name="SignalREndpoint" protocol="http" port="80" localPort="80" />
</Endpoints>
And here is how I create my OWIN web app:
var endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["SignalREndpoint"];
string webAppUrl = $"{endpoint.Protocol}://{endpoint.IPEndpoint}";
_webApp = WebApp.Start<Startup>(webAppUrl);
Finally, here's how I configure SignalR:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.UseServerAuthentication();
GlobalHost.DependencyResolver.UseServiceBus(CloudConfigurationManager.GetSetting("ServiceBusConnectionString"), "SignalRMessageBus");
app.MapSignalR(new HubConfiguration()
{
EnableDetailedErrors = true,
});
}
}
In the client project I am simply using a HubConnection to connect using the following URL for local testing, http://localhost:80, and the following URL for connecting to the cloud instance, http://myapp.cloudapp.net
I'm not sure what's different between the actual Azure instance and my local emulator that's causing it to not work in the cloud.
Interestingly, if I use the browser to connect to the URL http://myapp.cloudapp.net/signalr/hubs, it works and returns the JS proxy file.
Have you tried using TCP instead of HTTP as a protocol?
I am not a SignalR expert in any way, but I know about it. When we host our server (XSockets.NET) on Azure worker roles we configure the protocol to be TCP (not HTTP).
Have no idea why it would work on localhost though.
Another thing to consider is if the worker role supports websockets? SignalR requires IIS8+ for websocket support and I have no idea if you have access to that in a worker role. There are no options in Azure to turn websockets on/off on a worker role (from what I can see). So my guess is that there is no Microsoft WebSockets in the worker role. By I might be wrong here!
EDIT: Looked at one of my instances and saw that I can change OS and that the default one is 2012 Server. So Microsoft websockets should be available!
I am working with azure service bus paired namespace and need to be able to simulate a failover to the secondary namespace. I did kind of have this working by entering an incorrect connection string for the primary namespace and saw it fail over and send the message to the secondary namespace. This no longer seems to do the trick. I can not find a way through the azure management portal or anywhere else to take a namespace offline. Anyone any ideas how to do this?
Here is my code for reference
var pairedNamespaceConfiguration = this.pairedNamespaceConfigurationDictionary[configurationKey];
MessagingFactory factory = MessagingFactory.CreateFromConnectionString(pairedNamespaceConfiguration.PrimaryNamespace.ConnectionString);
MessagingFactory secondaryMessagingFactory = MessagingFactory.CreateFromConnectionString(pairedNamespaceConfiguration.SecondaryNamespace.ConnectionString);
NamespaceManager secondaryNamespaceManager = NamespaceManager.CreateFromConnectionString(pairedNamespaceConfiguration.SecondaryNamespace.ConnectionString);
SendAvailabilityPairedNamespaceOptions sendAvailabilityOptions = new SendAvailabilityPairedNamespaceOptions(secondaryNamespaceManager, secondaryMessagingFactory, pairedNamespaceConfiguration.BacklogQueueCount, TimeSpan.FromSeconds(pairedNamespaceConfiguration.FailoverIntervalSeconds), false);
factory.PairNamespaceAsync(sendAvailabilityOptions).Wait();
MessageSender messageSender = factory.CreateMessageSender(pairedNamespaceConfiguration.PathName);
string messageContent = JsonConvert.SerializeObject(message);
using(BrokeredMessage brokeredMessage = new BrokeredMessage(messageContent))
{
messageSender.Send(brokeredMessage);
}
Modify your \Windows\system32\drivers\etc\hosts file to point the original namespace to something like 127.0.0.1. This will make the original namespace connection fail.
I'm using this example Geo-replication with Service Bus Relayed Messages to implement the same think. Maybe it's useful for you also.
All Service Bus entities reside in a namespace. A namespace is affiliated to a datacenter. To allow for a failover between datacenters, the user must create one Service Bus and ACS namespace (in case ACS is used) per datacenter. Any Service Bus relay that needs to remain accessible in the presence of datacenter failures must be created in both namespaces.
The server opens two NetTcp relay endpoints, one in each of the two namespaces. The server processes any request that is received via one of these endpoints. Note that the two relays have to have different names (.e.g, address of primary relay is sb://myPrimaryNamespace.servicebus.windows.net/myService-primary and b://mySecondaryNamespace.servicebus.windows.net/myService-secondary).
The client considers one of the two replicated relays as the active relay and the other one as a backup. It opens a channel to the active relay and invokes methods on the service. If the invocation fails with any exception that is not part of the service contract, the client abandons the channel, opens a channel to the backup relay, and invokes the service method again. The client will consider the new channel to be the active channel and continues to use that channel until the next fault occurs.
Please bear with me as I am new to WCF services/ Windows services. I've created a WCF service hosted in a Windows service. I want to consume that WCF service in a Silverlight in-browser application over TCP. Below is the code fragment in Silverlight to access WCF service:
var messageEncoding = new BinaryMessageEncodingBindingElement();
var tcpTransport = new TcpTransportBindingElement();
var binding = new CustomBinding(messageEncoding, tcpTransport);
// Create a channel factory for the service endpoint configured with the custom binding.
var cf = new ChannelFactory<ICalcService>(binding, new EndpointAddress("net.tcp://192.168.2.104:4508"));
// Open the channel.
ICalcService channel = cf.CreateChannel();
// Invoke the method asynchronously.
channel.BeginAdd(9, 5, AddCallback, channel);
private void AddCallback(IAsyncResult asyncResult)
{
try
{
double endAdd = ((ICalcService) asyncResult.AsyncState).EndAdd(asyncResult);
}
catch (Exception exception)
{
throw exception;
}
}
The code works fine sometimes but often it throws an infamous System.ServiceModel.CommunicationException with the following message for some reasons:
Could not connect to net.tcp://192.168.2.104:4508/. The connection attempt lasted for a time span of 00:00:00.1010058. TCP error code 10013: An attempt was made to access a socket in a way forbidden by its access permissions.. This could be due to attempting to access a service in a cross-domain way while the service is not configured for cross-domain access. You may need to contact the owner of the service to expose a sockets cross-domain policy over HTTP and host the service in the allowed sockets port range 4502-4534.
The innerException of type System.Net.Sockets.SocketException says
An attempt was made to access a socket in a way forbidden by its access permissions.
What are the possible reasons behind this exceptions? Based on what I investigated so far, I could find only one reason: Improper ClientAccessPolicy.xml. What may be the other reasons? If you have any useful resources, please provide me the same. One more question, if I want to make Windows service hosted WCF service to get consumed by other machines on LAN, what settings do I have to make? E.g. firewall settings? My code cannot access WCF service on other machine. It throws the same exception I mentioned above. Any ideas about how to get rid of this exception?
Problem sorted..!! I had to do following things:
1) Specified SecurityMode.None while creating NetTcpBinding in Windows service.
2) Created an Inbound Rule in Windows Firewall With Advanced Security to allow TCP traffic on the port I specified in the end point address.
I am new WCF programming, I did followed series of Getting Started tutorials from following link
http://msdn.microsoft.com/en-us/library/ms734712.aspx
I have hosted service in console application but when I tried to create a client and tried to add service reference I got the following exceptions.
There was an error downloading
'http: localhost:8000/GettingStarted/mex/_vti_bin/ListData.svc/$metadata'.
The request failed with HTTP status 405: Method Not Allowed. Metadata
contains a reference that cannot be resolved:
'http: localhost:8000/GettingStarted/mex'. There was no endpoint
listening at http: localhost:8000/GettingStarted/mex that could
accept the message. This is often caused by an incorrect address or
SOAP action. See InnerException, if present, for more details. The
remote server returned an error: (404) Not Found. If the service is
defined in the current solution, try building the solution and adding
the service reference again.
code of hosting application
class Program
{
static void Main(string[] args)
{
// Step 1 Create a URI to serve as the base address.
Uri baseAddress =
new Uri("http://localhost:8000/GettingStarted/");
// Step 2 Create a ServiceHost instance
ServiceHost selfHost =
new ServiceHost(typeof(CalculatorService), baseAddress);
try
{
// Step 3 Add a service endpoint.
selfHost.AddServiceEndpoint(typeof(ICalculator),
new WSHttpBinding(),
"CalculatorService");
// Step 4 Enable metadata exchange.
var smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
// Step 5 Start the service.
selfHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown.
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("exception: {0}", ce.Message);
selfHost.Abort();
}
}
}
Now I am unable to figure out what the problem is. I am using visual studio 2012 and .net platform 4.5.
I had a similar issue as well, messing with this. Yes you seem to have followed the tutorial correctly, but if you want to connect to it and consume as a service (as in make a service reference) you must also add in the MEX service enpoint. Add this line after your selfhost.Description.Behaviors.Add(smb):
selfhost.AddServiceEndpoint(
typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexHttpBinding(),
"http://localhost:8000/GettingStarted/mex");
That should enable you to connect via "Add Service Reference". Also, I have found depending on your system, you may need to run VS as admin to allow for connection to network (in case you accidentally told it no in the past).
Judging from the error message it seems that there is no service listening at the specified port. You need to have the console application which hosts the service running when you are trying to add a service reference to it.
Apparently, the service is not running which means there is no endpoint listening at the URL you are using to create the service reference.
You can host the service in IIS, or keep the console application running as Damir mentioned above.
Make sure your server is running when you are trying to access it. Also check the configuration on the server and make sure your client's endpoint matches the server's endpoint. Make sure you're using the same binding as well, while you're at it.
Make sure the server is listening and the server's firewall isn't blocking you.
If you made a change to your WCF service don't forget to regenerate the service reference for your client application.
Are you sure you have defined a MEX endpoint? This is what provides metadata information about your service, so that studio can generate a client proxy.
In the tutorial you linked to, it is this bit:
// Step 4 Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
If you are hosting the web service in IIS, check in web.config(under behaviours section)
httpsGetEnabled is set to True
I ran into a similar problem today. However, for me it wasn't needed for me to explicitly add the Endpoints, as #iMortalitySX already said.
I had a different reason for failing: I was binding to http://0.0.0.0, thinking the listen IP doesn't matter. Indeed, via SoapUI I was able to connect and to use the Service. But when trying to discover the service in another Visual Studio project, the discovery would fail, as VS would get the initial response, but then follow up links that contained http://0.0.0.0 and then fail.
So changing http://0.0.0.0 into the correct IP of my machine fixed my problem.
Try put the uri address in Your browser. In my case I was able to see an ExceptionDetail.