Μικρός οδηγός στο Remoting – Μέρος Β’

Έχουν δημοσιευτεί 06 Φεβρουαρίου 06 01:26 πμ | KelMan 

Στο δεύτερο μέρος του μικρού οδηγού στο Remoting, θα δούμε πως υλοποιούνται όλα όσα παρουσιάστηκαν στο πρώτο μέρος του άρθρου.

Το Server Side

Για να μπορέσουμε να χρησιμοποιήσουμε τα remote objects θα πρέπει να «συνδεθούν» με το remoting μηχανισμό. Αυτή η διαδικασία ονομάζεται Registration και συνήθως γίνεται από μια εφαρμογή που λειτουργεί ως host και της οποίας η βασική δουλειά είναι να κάνει registration ένα η περισσότερα Channels με τα ChannelServices και ένα η περισσότερα remote objects με τα RemotingServices. Κατόπιν, περιμένει μέχρι να τερματιστεί. Όταν τερματιστεί, ό,τι έχει γίνει register αποσυνδέεται από τον remoting μηχανισμό. Οι πληροφορίες που απαιτούνται για να γίνουν τα παραπάνω είναι:

• Το type name του remote object
• To URI του object που θα χρησιμοποιήσουν οι clients για να εντοπίσουν το remote object
• Αν επιλεχθεί server-side activation, ο τρόπος λειτουργίας (Single-call/Singleton)

Για να περάσουμε όλες αυτές τις πληροφορίες κατά το registraion ενός remote object χρησιμοποιούμε δύο τεχνικές. Η πρώτη είναι να καλέσουμε τη μέθοδο RemotingConfiguration.RegisterWellKnownServiceType με τις παραπάνω πληροφορίες ως παραμέτρους. Η δεύτερη είναι να χρησιμοποιήσουμε το application confiduration αρχείο για να αποθηκέψουμε τις ίδιες πληροφορίες και τη μέθοδο RemotingConfiguration.Configure. Όπως αντιλαμβάνεστε, η δεύτερη τεχνική μας δίνει τη δυνατότητα να αλλάξουμε τις ρυθμίσεις χωρίς να απαιτηθεί recompilation.

Για παράδειγμα:

RemotingConfiguration.RegisterWellKnownServiceType( _
GetType(RemotingSamples.HelloServer), _
"SayHello", _
WellKnownObjectMode.SingleCall)

και εναλλακτικά

RemotingConfiguration.Configure("MyHello.exe.config")

Τα περιεχόμενα του config αρχείου θα τα δούμε παρακάτω

Όταν λοιπόν γίνεται το registration του remote object, o remoting μηχανισμός δημιουργεί ένα object reference. Εξάγει τα απαιτούμενα metadata για το object από το assembly του και μαζί με το URI και το όνομα του assembly αποθηκεύονται ως object reference σε έναν πίνακα που χρησιμοποιείται ως tracking μηχανισμός για τα remote objects. To ίδιο το remote object δεν υφίσταται ως instance. Το instance θα δημιουργηθεί μόνο όταν ο client επιχειρήσει να καλέσει κάποια μέθοδο του object ή γίνει ενεργοποίησή του από client side.

To Client-side

Στη μεριά του client συμβαίνουν δύο πράγματα. Το ένα είναι registration του Channel και το άλλο είναι η ενεργοποίηση του remote object.

To registration του Channel γίνεται καλώντας το ChannelServices.RegisterChannel, όπως για παράδειγμα:

ChannelServices.RegisterChannel(New TcpChannel())

Παρατηρήστε ότι δεν χρειάζεται να προσδιορίσουμε την πόρτα.

To object activation γίνεται με δύο τρόπους, με το GetObject ή το New operator. Ακόμη και τότε δεν θα έχει δημιουργηθεί το instance από το remote object. Ούτε καν έχει τρέξει ένα πακέτο στο δίκτυο λόγω των παραπάνω εντολών. Αυτό θα συμβεί μόνο όταν ο client καλέσει κάποια μέθοδο του proxy object. Αυτό, είναι εφικτό διότι αυτό που πραγματικά απαιτείται για να στηθεί ο μηχανισμός είναι metadata τα οποία είναι εκ των προτέρων γνωστά.

Όταν η κλήση θα φτάσει στον server ο remoting μηχανισμός θα εξάγει το URI από το μήνυμα και βάσει αυτού θα ψάξει στον πίνακα του για σχετικό reference. Αν το βρει, θα φτιάξει το instance του object και θα κάνει forward τη κλήση της μεθόδου στο νέο αυτό object. Τέλος, αν το object είναι SingleCall, καταστρέφεται.

Η μόνη διαφορά μεταξύ της GetObject και New είναι ότι η GetType επιτρέπει τον καθορισμό του URI με παράμετρο, ενώ η Νew το διαβάζει από το config αρχείο. Για παράδειγμα

Dim obj As HelloServer = CType(Activator.GetObject( _
GetType(RemotingSamples.HelloServer), _
"tcp://localhost:8085/SayHello"), HelloServer)

Με αυτόν τον τρόπο φτιάχουμε ένα server-activated object, και το “tcp://localhost:8085/SayHello" προσδιορίζει ότι το remote object βρίσκεται στο end-point SayHello και θα χρησιμοποιηθεί το TCP και η πόρτα 8085.

Αντίστοιχα, αν θέλαμε να κάνουμε το ίδιο με το New operator, θα έπρεπε να γράψουμε

RemotingConfiguration.Configure("MyHello.exe.config")
Dim obj As HelloServer = New HelloServer()

Σχετικά με το compilation

Όταν θα γίνει το compilation του client, θα απαιτηθούν πληροφορίες για τον τύπο του remote object.. Αυτές οι πληροφορίες μπορούν να βρεθούν με τρεις τρόπους .

Ο πρώτος τρόπος είναι να σπάσει το remote object σε διαφορετικό project σε σχέση με τον server και κατόπιν να χρησιμοποιηθεί ένα reference προς το assembly, ίσα-ίσα για το compilation. Κάτι τέτοιο όμως κάνει πιο «στενή» την ανάπτυξη του client και του server.

O δεύτερος τρόπος είναι να διαχωριστεί το remote object σε μια κλάση και ένα interface. Η κλάση θα υλοποιεί το interface ενώ παράλληλα το interface μπορεί να χρησιμοποιηθεί από τον client για να διαβαστεί ο τύπος του remote object.

Ο τρίτος τρόπος είναι να χρησιμοποιηθεί το utility Wsdl.exe το οποίο μπορεί να συνδεθεί στο end-point, να εξάγει τα metadata και να δημιουργήσει source code που θα χρησιμοποιηθεί για το compilation.

Remoting με configuration αρχεία

To registration με προγραμματιστικό τρόπο είναι σχετικά απλή διαδικασία, ωστόσο δεν είναι και πολύ πρακτικό σε production περιβάλλοντα με πολλαπλές εφαρμογές και πολλαπλά remote objects. To πρόβλημα αυτό λύνεται με τη χρήση των configuration αρχείων. Όπως είπαμε, αυτό γίνεται με τη κλήση της μεθόδου Configure της κλάσης RemotingConfiguration. Αυτή η μέθοδος φορτώνει το config αρχείο στη μνήμη, κάνει parse τα περιεχόμενα και κατόπιν καλεί τις σχετικές μεθόδους για να κάνει register τα κανάλια και τα objects. Φυσικά, μιας και το config αρχείο αποτελείται από διάφορα sections (πχ security, binding policy, κλπ), η Configure μέθοδος διαβάζει μόνο το section σχετικά με το remoting και αγνωεί τα υπόλοιπα. Ένα τυπικό config αρχείο με remoting section είναι ως εξής:

<configuration>
<system.runtime.remoting>
<application>
<lifetime>
<!--Default lifetime information for all -->
<!--objects in the application. Individual -->
<!--service objects can customize these -->
</lifetime>
<service>
<!--Specifies one or more remote objects provided by -->
<!--this service. These can be client-activated -->
<!--as well as server-activated -->
</service>
<client url = "<A href="http://someserver/endpoint1">http://someserver/endpoint1</A>">
<!--Specifies a remote object this client used. -->
<!--One or more clients can be specified -->
</client>
<client url = "<A href="http://someserver/endpoint2">http://someserver/endpoint2</A>">
</client>
<SOAPinterop>
<!--Specify type, assembly and XML information -->
<!--to use deserializing data from SOAP endpoints -->
</SOAPinterop>
<channels>
<!--List all channels the client or service require.-->
<!—-Individual clients or services can customize the -->
<!—-machine level settings -->
</channels>
</application>
<channels>
<!--Provide a list of the channel and sink providers.-->
<!--This data will normally be provided on a machine -->
<!--level in the machine configuration file -->
</channels>
<channelSinkProviders>
<clientProviders>
<!—One or more client providers -->
</clientProviders>
<serverProviders>
<!—One or more server providers -->
</serverProviders>
</channelSinkProviders>
</system.runtime.remoting>
</configuration>

Παράδειγμα: Ας τα βάλουμε όλα μαζί.

Θα φτιάξουμε ένα solution με τρία project. Στο ίδιο PC θα βάλουμε να παίξουν και ο server και ο client. Εναλλακτικά, μπορείτε να σετάρετε ένα μικρό virtual δικτυάκι ώστε να είναι το project πιο ρεαλιστικό.

Λοιπόν, στο πρώτο project έχουμε ένα console application που θα τρέχει στον server και θα …σερβίρει instances από remote objects

Imports System
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Tcp
Imports System.Runtime.Remoting.Channels.Http

Namespace RemotingSamples

    Public Class Server
        Public Shared Sub Main()
            Dim chan1 As New Tcp.TcpChannel(8085)
            ChannelServices.RegisterChannel(chan1)

            RemotingConfiguration.RegisterWellKnownServiceType( _
                GetType(RemotingSamples.HelloServer), _
                "SayHello", _
                WellKnownObjectMode.Singleton)    '    WellKnownObjectMode.SingleCall)

            Console.WriteLine("Press Enter key to exit")
            Console.ReadLine()
        End Sub
    End Class

End Namespace

Στο δεύτερο project έχουμε τα objects που σερβίρονται remotely και δημιουργούνται από αυτή τη κλάση:

Imports System

Namespace RemotingSamples

    Public Class HelloServer
        Inherits MarshalByRefObject

        Public callCounter As Integer = 0

        Public Sub HelloServer()
            Console.WriteLine("HelloServer activated")
        End Sub

        Public Function HelloMethod(ByVal name As String, ByRef counter As Integer) As String
            callCounter += 1
            counter = callCounter
            Console.WriteLine("Server Hello.HelloMethod : {0} Counter :{1}", name, callCounter)
            Return "Hi there " & name
        End Function

    End Class

End Namespace

Και στο τρίτο Project έχουμε τον client που είναι άλλη μια console εφαρμογούλα

Imports System
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Tcp
Imports System.Runtime.Remoting.Channels.Http
Imports Microsoft.VisualBasic
Imports System.IO

Namespace RemotingSamples
    Public Class Client
        Public Sub CallRemoteObject()

            Dim counter As Integer

            Dim chan1 As New TcpChannel
            Try
                ChannelServices.RegisterChannel(chan1)
                Dim obj1 As HelloServer = CType(Activator.GetObject(GetType(RemotingSamples.HelloServer), "tcp://localhost:8085/SayHello"), HelloServer)
                Try
                    obj1.HelloServer()
                    Console.WriteLine("Client1 TCP HelloMethod {0} Counter {1}", obj1.HelloMethod("Caveman", counter), counter)
                Catch ioExcep As IOException
                    Console.WriteLine("Remote IO Error" & vbCrLf & "Exception:" & vbCrLf & ioExcep.ToString())
                End Try
            Catch regex As RemotingException
                Console.WriteLine(regex.Message)
            Catch ex As Exception
                Console.WriteLine(ex.Message)
            Finally
                ChannelServices.UnregisterChannel(chan1)
            End Try
        End Sub
    End Class
End Namespace

Για να απλουστεφθεί το compilation, βάλτε ένα reference από το πρώτο και το τρίτο project προς το δεύτερο. Κατόπιν κάντε compile αλλά μην τρέξετε το solution. Θα πρέπει με τον Windows Explorer να πάτε στον φάκελο Bin του πρώτου project (του server) και να τρέξετε τον server. Κατόπιν, τρέξτε τον client από το Bin φάκελο του τρίτου project. Δοκιμάστε να αλλάξετε το Activation Mode ώστε να δείτε τη διαφορά, σύμφωνα με αυτά που λέγαμε για το Single-call / Singleton στο προηγούμενο άρθρο.

Δημοσίευση στην κατηγορία:

Σχόλια:

Χωρίς Σχόλια
Έχει απενεργοποιηθεί η προσθήκη σχολίων από ανώνυμα μέλη

Search

Go

Συνδρομές