590 likes | 723 Views
.NET Distributed Computing. Notes from “Distributed .NET Programming in C#” by Tom Barnaby and “.NET Programming” by Pradeep Tapadiya. Remote Objects. We can’t call methods on remote objects directly. We call methods on remote objects through a remote reference to that object.
E N D
.NET Distributed Computing Notes from “Distributed .NET Programming in C#” by Tom Barnaby and “.NET Programming” by Pradeep Tapadiya
Remote Objects • We can’t call methods on remote objects directly. We call methods on remote objects through a remote reference to that object. • Parameters to and from the remote method are “marshaled”
Marshaling Classification • Marshal-by-value • Marshal-by-reference • Nonremotable
Marshal-By-Value Objects • Objects marked with the custom attribute [serializable] are pass-by-value objects • The entire object is copied into the remote domain • Any local change to the object is not reflected in the remote domain
Marshal-By-Reference Object • MBR objects derive from MarshalByRefObject • The client of such an object is actually making calls on a proxy object created by the runtime
MBR Objects (1) using System; using System.Threading; public class Foo : MarshalByRefObject { public void DisplayDomainInfo() { String s = "Domain=" + AppDomain.CurrentDomain.FriendlyName + " Thread ID=" + AppDomain.GetCurrentThreadId() + " Context ID=" + Thread.CurrentContext.ContextID; Console.WriteLine(s); } }
public class MyApp { public static void Main() { Foo fDefault = new Foo(); // No proxy is used because we are fDefault.DisplayDomainInfo(); // not crossing domains } } D:\..\46-690\MBRandMBV>mbvandmbr.exe Domain=mbvandmbr.exe Thread ID=2284 Context ID=0
MBR Objects (2) public class MyApp { public static void Main() { Foo fDefault = new Foo(); fDefault.DisplayDomainInfo(); // force a call by remote reference // fNew points to a proxy AppDomain ad = AppDomain.CreateDomain("MyNewAppDomain"); Foo fNew = (Foo) ad.CreateInstanceAndUnwrap("mbvandmbr", "Foo"); fNew.DisplayDomainInfo(); } } D:\..\46-690\MBRandMBV>mbvandmbr Domain=mbvandmbr.exe Thread ID=2096 Context ID=0 Domain=MyNewAppDomain Thread ID=2096 Context ID=0
MBR Objects (3) using System; using System.Threading; [Serializable] // make Serializable and remove MarshalByReference public class Foo { public void DisplayDomainInfo() { String s = "Domain=" + AppDomain.CurrentDomain.FriendlyName + " Thread ID=" + AppDomain.GetCurrentThreadId() + " Context ID=" + Thread.CurrentContext.ContextID; Console.WriteLine(s); } }
public class MyApp { public static void Main() { Foo fDefault = new Foo(); fDefault.DisplayDomainInfo(); AppDomain ad = AppDomain.CreateDomain("MyNewAppDomain"); Foo fNew = (Foo) ad.CreateInstanceAndUnwrap("mbvandmbr", "Foo"); fNew.DisplayDomainInfo(); } } D:\...\46-690\MBRandMBV>mbvandmbr Domain=mbvandmbr.exe Thread ID=588 Context ID=0 Domain=mbvandmbr.exe Thread ID=588 Context ID=0
Remoting Model R E M O T I N G R E M O T I N G L A Y E R L A Y E R Server Object Client Object Channel Proxy Namespaces: System.Runtime.Remoting.Channels.Tcp binary serialization System.Runtime.Remoting.Channels.Http uses Microsoft SOAP serialization Classes: TcpServerChannel, HttpServerChannel, TcpClientChannel, HttpClientChannel
Remote Objects May Be • Server-Activated Objects (The server controls the lifetime) • Client-Activated Objects (The client controls the lifetime)
Server Activated Objects Server activated objects – with a well-known name assigned on the server The server code -- Registers a server side channel -- Registers the name of the remote object -- Provides a mechanism to keep the server alive -- Creates the object on the first call to the object trough the client side proxy The Client code -- Registers a client side channel -- Create a proxy with Activator.GetObject passing a type and a location -- Make calls on the proxy as though they were calls on the remote object
Single-Call and Singletons • Server Activated objects are published as either single-call or singleton objects • Single-call => on each call from the client the object is created and then torn down (WellKnownObjectMode.SingleCall) • Singleton => only one object exists and is shared by callers (no synchronization) (WellKnownObjectMode.Singleton) • Neither of these is appropriate for holding state
Client Activated Objects • The client requests that an instance be created on the server • For each instance requested a proxy is returned to the client • The client may store instance specific state in the object (through the proxy) • The objects are garbage collected when their leases expire
The Object To Be Served // A Remote Student Object using System; public class RemoteStudent : MarshalByRefObject { private int age; private String name; public int getAge() { return age; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setName(string name) { this.name = name; } }
Client Code (along with Student.dll) using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting; using System; using System.Runtime.Remoting.Activation; class MyClient { public static void Main() { ChannelServices.RegisterChannel(new TcpClientChannel()); RemoteStudent r = (RemoteStudent) Activator.CreateInstance( typeof(RemoteStudent),null, new object[] { new UrlAttribute( "tcp://localhost:6502") } );
r.setName("Bob"); r.setAge(23); RemoteStudent s = (RemoteStudent) Activator.CreateInstance( typeof(RemoteStudent),null, new object[] { new UrlAttribute( "tcp://localhost:6502") } ); s.setName("Alice"); s.setAge(22); Console.WriteLine("Name: " + s.getName() + " age " + s.getAge()); Console.WriteLine("Name: " + r.getName() + " age " + r.getAge()); } }
Server Code (with Student.dll) using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting; using System; class MyApp { public static void Main() { ChannelServices.RegisterChannel( new TcpServerChannel(6502)); RemotingConfiguration.RegisterActivatedServiceType( Type.GetType("RemoteStudent, Student")); Console.WriteLine("Press a key to exit server"); Console.Read(); } }
Output ..46-690\RemoteObjects3\client>MyClient Name: Alice age 22 Name: Bob age 23
Leases • .NET uses a lease to manage the lifetime of remote (client activated) objects. • A lease may be extended by the client • When the lease expires the object is garbage collected and subsequent calls on the proxy will generate a RemotingException
The Client Side Lets a Lease Expire using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting; using System; using System.Runtime.Remoting.Activation; using System.Threading;
class MyClient { public static void Main() { try { ChannelServices.RegisterChannel(new TcpClientChannel()); RemoteStudent r = (RemoteStudent) Activator.CreateInstance( typeof(RemoteStudent),null, new object[] { new UrlAttribute( "tcp://localhost:6502") } );
r.setName("Bob"); r.setAge(23); Thread.Sleep(45000); // wait 45 seconds after creating Bob RemoteStudent s = (RemoteStudent) Activator.CreateInstance( typeof(RemoteStudent),null, new object[] { new UrlAttribute( "tcp://localhost:6502") } ); s.setName("Alice"); s.setAge(22); Console.WriteLine("Name: " + s.getName() + " age " + s.getAge()); Console.WriteLine("Name: " + r.getName() + " age " + r.getAge()); } catch(RemotingException e) { Console.WriteLine("Caught exception " + e); // Bob causes trouble } } }
A Student With A Lease // A Remote Student Object using System; using System.Runtime.Remoting.Lifetime; public class RemoteStudent : MarshalByRefObject { private int age; private String name; public int getAge() { return age; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setName(string name) { this.name = name; }
public override Object InitializeLifetimeService() { Console.WriteLine("Lease code called"); // whenever called call base class method ILease lease = (ILease) base.InitializeLifetimeService(); // if first call make some changes if (lease.CurrentState == LeaseState.Initial) { lease.InitialLeaseTime = TimeSpan.FromSeconds(30); lease.RenewOnCallTime = TimeSpan.FromSeconds(10); } return lease; } }
Same Server Code using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting; using System; class MyApp { public static void Main() { ChannelServices.RegisterChannel( new TcpServerChannel(6502)); RemotingConfiguration.RegisterActivatedServiceType( Type.GetType("RemoteStudent, Student")); Console.WriteLine("Press a key to exit server"); Console.Read(); } }
Output D:..\46-690\RemoteObjects3\server>Server Press a key to exit server Lease code called Lease code called ..\46-690\RemoteObjects3\client>MyClient Name: Alice age 22 Caught exception System.Runtime.Remoting.RemotingException: No receiver registered
The soapsuds tool • Perhaps we do not want the client to have access to the Student code • We only want the client to have access to meta information • The soapsuds tool allows to download and then use meta information
The server code using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting; using System; class MyApp { public static void Main() { ChannelServices.RegisterChannel( new HttpServerChannel(6502));
RemotingConfiguration.RegisterWellKnownServiceType( Type.GetType("RemoteStudent, Student"), "SomeStudent/Student.soap", WellKnownObjectMode.SingleCall); Console.WriteLine("Press a key to exit server"); Console.Read(); } } // Compile the server code // csc -t:library Student.cs // csc -r:Student.dll Server.cs
The Client Code using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting; using System; class MyClient { public static void Main() { ChannelServices.RegisterChannel(new HttpClientChannel()); RemoteStudent r = (RemoteStudent) Activator.GetObject( typeof(RemoteStudent), "http://localhost:6502/SomeStudent/Student.SOAP");
String name = r.getName(); int age = r.getAge(); Console.WriteLine("Student Name: " + name + " Age: " + age); } }
Run The Client But this time we do not have the Student.dll available. So, MyClient.csc will not compile. 1) Fetch the WSDL document from the server and generate the proxy class using soapsuds: soapsuds -url:http://localhost:6502/SomeStudent/Student.soap?wsdl -oa:Student.dll 2) Compile and run the client: csc -r:Student.dll MyClient.cs MyClient Student Name: Mike Age: 23
Web Services “The internet is evolving from a collection of isolated web sites and applications into a general communication bus for distributed applications.” From page 247 of the course text
ASP.NET Web Services 0) Check if IIS is running by attempting to visit http://localhost 1) If it's not running click Start/Settings/Control Panel/Add Remove Programs/ Add Remove Windows Components and enable IIS. 2) If .NET was installed after IIS reconfigure IIS by running aspnet_regiis.exe /i from a command prompt.
ASP.NET Web Services Suppose we want to provide a student name given a student ID
ASP.NET Server Code <%@ WebService Language="C#" Class="Student.QueryService" %> // CoolService.asmx using System.Web.Services; using System.Collections; namespace Student { [WebService(Namespace="http://localhost/ACoolQueryService/")] public class QueryService : WebService { private static Hashtable nameValuePairs;
static QueryService() { nameValuePairs = new Hashtable(); nameValuePairs.Add("12345","Moe"); nameValuePairs.Add("01234","Curly Joe"); nameValuePairs.Add("54321","Larry"); } [WebMethod] public string GetName(string id) { return (string)nameValuePairs[id]; } } } // We can’t compile this code with csc.
Create a virtual directory under IIS • Start • Settings • Control Panel • Administrative Tools • Select Internet Information Services • Expand and select the default web site • Click Action/New/Virtual Directory • Provide a name (ACoolQueryService in this case) and browse to the directory holding the .asmx file • Select everything but write
Checking the service • Visit the service with your browser • http://localhost/ACoolQueryService/CoolService.asmx • HTML is generated that allows you to test the service via standard HTTP
Testing With HTTP Get Request GET /ACoolQueryService/CoolService.asmx/GetName?id=string HTTP/1.1 Host: localhost HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length Response <?xml version="1.0" encoding="utf-8"?> <string xmlns="http://localhost/ACoolQueryService/">string</string>
Testing with SOAP POST /ACoolQueryService/CoolService.asmx HTTP/1.1 Host: localhost Content-Type: text/xml; charset=utf-8 Content-Length: length of document SOAPAction: "http://localhost/ACoolQueryService/GetName" <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body> <GetName xmlns="http://localhost/ACoolQueryService/"> <id>string</id> </GetName> </soap:Body> </soap:Envelope>
SOAP Response HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body> <GetNameResponse xmlns= "http://localhost/ACoolQueryService/"> <GetNameResult>string</GetNameResult> </GetNameResponse> </soap:Body> </soap:Envelope>
WSDL Structure message operation(message) message operation(message) message operation(message) • Operation names (method names) • Message types (parameters and return values) • Interfaces (groups of methods) are called ports • location of service { interface type type
Examine the WSDL Namespaces disambiguate. <?xml version="1.0" encoding="utf-8"?> <definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s0="http://localhost/ACoolQueryService/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" targetNamespace="http://localhost/ACoolQueryService/" xmlns="http://schemas.xmlsoap.org/wsdl/">
<types> <s:schema elementFormDefault="qualified" targetNamespace="http://localhost/ACoolQueryService/"> <s:element name="GetName"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="id" type="s:string" /> </s:sequence> </s:complexType> </s:element> message type <GetName> <id> string</id> </GetName
<s:element name="GetNameResponse"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1“ name="GetNameResult" type="s:string" /> </s:sequence> </s:complexType> </s:element> <s:element name="string" nillable="true" type="s:string" /> </s:schema> </types> Message type <GetNameResponse> <GetNameResult>string</GetNameResult> </GetNameResponse> Message type <string>string</string>