JointCode.Shuttle 是一個用於進程內 AppDomain 間通訊的服務架構(不支持跨進程),它旨在取代運行時庫提供的 MarshalByrefObject 的功能。git
本文經過一個簡單的示例來演示如何使用 JointCode.Shuttle。github
JointCode.Shuttle 的發行包編程
在 JointCode.Shuttle 的發行包中,包含兩個文件:JointCode.Shuttle.dll 和 JointCode.Shuttle.Library.dll,其中 JointCode.Shuttle.dll 是使用託管語言編寫的庫文件,JointCode.Shuttle.Library.dll 則是前者依賴的、使用非託管語言編寫的一個組件。緩存
準備工做架構
要使用 JointCode.Shuttle,咱們首先須要在項目中引用 JointCode.Shuttle.dll 這個程序集,同時要把 JointCode.Shuttle.Library.dll 複製到項目編譯以後 JointCode.Shuttle.dll 所在的文件夾中(例如,假設項目編譯後,JointCode.Shuttle.dll 被複制到 c:/projects/sampleproject 文件夾中,則須要手動將 JointCode.Shuttle.Library.dll 複製到此文件夾)。dom
開始編碼ide
JointCode.Shuttle 是面向接口編程的,因此咱們首先須要編寫一個服務接口(也叫服務契約),並對其應用 ServiceInterface 屬性。ui
1 [ServiceInterface] 2 public interface ISimpleService 3 { 4 string GetOutput(string input); 5 }
接着編寫一個實現該契約的服務類,並對其應用 ServiceClass 屬性。this
1 [ServiceClass(typeof(ISimpleService), Lifetime = LifetimeEnum.Transient)] 2 public class SimpleService : ISimpleService 3 { 4 public string GetOutput(string input) 5 { 6 return string.Format 7 ("SimpleService.GetOutput says: now, we are running in AppDomain: {0}, and the input passed from the caller is: {1}", 8 AppDomain.CurrentDomain.FriendlyName, input); 9 } 10 }
因爲要實現跨 AppDomain 通訊,所以這裏咱們須要編寫一個用於啓動遠程服務的類,並讓該類繼承自 MarshalByRefObject。編碼
1 public class ServiceEnd : MarshalByRefObject 2 { 3 // 這裏必須使用一個字段來持有 ShuttleDomain 實例的引用,由於它是當前 AppDomain 與外部 AppDomain 之間通訊的橋樑。 4 // 若是該實例被垃圾回收,經過該實例註冊的全部服務會被註銷,且當前 AppDomain 與外部 AppDomain 之間將沒法通訊。 5 // We need a field to keep the _shuttleDomain alive, because if it is garbage collected, we'll lose all communications 6 // with other AppDomains. 7 ShuttleDomain _shuttleDomain; 8 9 public void RegisterServices() 10 { 11 // 註冊服務組時,須要傳遞一個 Guid 對象 12 // A Guid is needed when registering service group 13 var guid = Guid.NewGuid(); 14 _shuttleDomain.RegisterServiceGroup(ref guid, 15 new ServiceTypePair(typeof(ISimpleService), typeof(SimpleService))); 16 } 17 18 public void CreateShuttleDomain() 19 { 20 // 建立一個 ShuttleDomain 21 // Create a ShuttleDomain object 22 _shuttleDomain = ShuttleDomainHelper.Create("domain1", "domain1"); 23 } 24 25 public void DisposeShuttleDomain() 26 { 27 _shuttleDomain.Dispose(); 28 } 29 }
如今,能夠開始使用 JointCode.Shuttle 了。有關使用方法,能夠參見注釋,代碼以下:
1 class Program 2 { 3 const string SimpleServiceEndDll = "JoitCode.Shuttle.SimpleServiceEnd.dll"; 4 const string SimpleRemoteServiceEndType = "JoitCode.Shuttle.SimpleServiceEnd.SimpleRemoteServiceEnd2"; 5 6 static void Main(string[] args) 7 { 8 Console.WriteLine("Tests begin..."); 9 10 // 要使用 JointCode.Shuttle 進行跨 AppDomain 通訊,首先必須初始化 ShuttleDomain。 11 // 這個初始化操做通常在默認 AppDomain 執行,但也能夠在其餘 AppDomain 中執行,都是同樣的。 12 // To make cross-AppDomain communication with JointCode.Shuttle, initialize the ShuttleDomain at first. 13 // It doesn't matter whether the initialization is done in default AppDomain or any other AppDomains, 14 // but it must be done before any ShuttleDomain instance is created. 15 ShuttleDomain.Initialize(); 16 17 // 在默認 AppDomain 中,建立一個 ShuttleDomain。 18 // 事實上,在須要與其餘 AppDomain 進行通訊的每一個 AppDomain 中,都要有一個且只能有一個 ShuttleDomain 對象。 19 // 嘗試在一個 AppDomain 中建立多個 ShuttleDomain 對象時將會拋出異常。 20 // 該對象用於與其餘 AppDomain 中的 ShuttleDomain 對象通訊。 21 // Creating a ShuttleDomain instance in default AppDomain. 22 // Actually, we needs one and only one ShuttleDomain instance in every AppDomain that needs to communicate 23 // with others. Trying to create another ShuttleDomain in the same AppDomain causes exceptions. 24 // The ShuttleDomain instances communicates with each other across AppDomains. 25 var str = Guid.NewGuid().ToString(); 26 var shuttleDomain = ShuttleDomainHelper.Create(str, str); 27 28 if (CallServicesDefineInThisAssembly(shuttleDomain) 29 && CallServicesDefinedInAnotherAssembly(shuttleDomain)) 30 { 31 Console.WriteLine("Tests completed..."); 32 } 33 else 34 { 35 Console.WriteLine("Tests failed..."); 36 } 37 38 shuttleDomain.Dispose(); 39 40 Console.Read(); 41 } 42 43 static bool CallServicesDefineInThisAssembly(ShuttleDomain shuttleDomain) 44 { 45 Console.WriteLine(); 46 Console.WriteLine("====================================="); 47 48 // 在默認 AppDomain 中建立一個子 AppDomain。 49 // Creating a child AppDomain in default AppDomain. 50 var serviceEnd1Domain = AppDomain.CreateDomain("ServiceEndDomain1", null, null); 51 52 // 建立一個 ServiceEnd 對象以用於操做該子 AppDomain。 53 // Creating a ServiceEnd instance for operating that child AppDomain. 54 var serviceEnd = (ServiceEnd)serviceEnd1Domain.CreateInstanceAndUnwrap 55 (typeof(Program).Assembly.FullName, "JoitCode.Shuttle.SimpleSample.ServiceEnd"); 56 57 // 在子 AppDomain 中,建立一個 ShuttleDomain 實例。 58 // Creating a ShuttleDomain instance in the child AppDomain. 59 serviceEnd.CreateShuttleDomain(); 60 61 // 在子 AppDomain 中,註冊 ISimpleService 服務。 62 // Registering ISimpleService service in the child AppDomain. 63 serviceEnd.RegisterServices(); 64 65 66 // 在默認 AppDomain 中,獲取子 AppDomain 中註冊的 ISimpleService 服務實例。 67 // 目前服務實例的默認生存期爲 1 分鐘。每次調用服務方法時,服務實例的生存期延長 30 秒。 68 // Get the ISimpleService service in default AppDomain, which is registered by the child AppDomain. 69 // The lifetime of service is default to 1 minute, every call to the service method extends that time for 30 seconds. 70 ISimpleService service; 71 if (shuttleDomain.TryGetService(out service)) 72 { 73 try 74 { 75 Console.WriteLine("Currently, we are running in AppDomain {0}, " + 76 "and we are trying to call a remote serivce that defined in the same library...", 77 AppDomain.CurrentDomain.FriendlyName); 78 79 Console.WriteLine(); 80 // 調用子 AppDomain 中註冊的 ISimpleService 服務實例的服務方法。 81 // Call the service method of ISimpleService service. 82 var output = service.GetOutput("Bingo"); 83 Console.WriteLine(output); 84 85 Console.WriteLine(); 86 } 87 catch 88 { 89 Console.WriteLine(); 90 Console.WriteLine("Failed to invoke the remote service method..."); 91 return false; 92 } 93 } 94 else 95 { 96 Console.WriteLine(); 97 Console.WriteLine("Failed to create remote service instance..."); 98 return false; 99 } 100 101 // 通知子 AppDomain 當即釋放 ISimpleService 服務實例,而不用等待其生存期結束。 102 // 此爲可選操做,由於即便不手動釋放 ISimpleService 服務實例,在其生命期結束之時系統也會自動釋放該實例 103 //(若是 ISimpleService 實現了 IDisposable,還會調用其 Dispose 方法) 104 // Indicating the child AppDomain to release the ISimpleService service immediately, instead of waiting for its lifetime to end. 105 // This is optional, because even if we don't do this explicitly, the ISimpleService service will still get released in the 106 // child AppDomain automatically when its lifetime ends. 107 // And, if the ISimpleService derives from IDisposable, the Dispose method will also get called at that time. 108 shuttleDomain.ReleaseService(service); 109 110 // 在子 AppDomain 中,釋放緩存的 ShuttleDomain 實例。這將會註銷經過該實例註冊的全部服務(在本示例中,即 ISimpleService 服務), 111 // 並切斷該 AppDomain 與全部 AppDomain 的通訊。 112 // Releasing the ShuttleDomain instance in the child AppDomain, this will unregister all services registered by that 113 // instance, and shut down all communications between that child AppDomain and all other AppDomains. 114 serviceEnd.DisposeShuttleDomain(); 115 116 return true; 117 } 118 119 static bool CallServicesDefinedInAnotherAssembly(ShuttleDomain shuttleDomain) 120 { 121 Console.WriteLine(); 122 Console.WriteLine("====================================="); 123 124 var remoteDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, null); 125 126 var currentDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); 127 var simpleServiceEndPath = Path.Combine(currentDir, SimpleServiceEndDll); 128 var asmName = AssemblyName.GetAssemblyName(simpleServiceEndPath); 129 var simpleRemoteServiceEnd = (SimpleRemoteServiceEnd)remoteDomain.CreateInstanceAndUnwrap 130 (asmName.FullName, SimpleRemoteServiceEndType); 131 132 simpleRemoteServiceEnd.CreateShuttleDomain(); 133 simpleRemoteServiceEnd.RegisterServices(); 134 135 ISimpleService2 service2; 136 if (shuttleDomain.TryGetService(out service2)) 137 { 138 try 139 { 140 Console.WriteLine("Trying to call a remote serivce that defined in another library from AppDomain {0}...", 141 AppDomain.CurrentDomain.FriendlyName); 142 143 Console.WriteLine(); 144 // 調用子 AppDomain 中註冊的 ISimpleService2 服務實例的服務方法。 145 // Call the service method of ISimpleService2 service. 146 var output = service2.GetOutput("Duang"); 147 Console.WriteLine(output); 148 149 Console.WriteLine(); 150 } 151 catch 152 { 153 Console.WriteLine(); 154 Console.WriteLine("Failed to invoke the remote service method..."); 155 return false; 156 } 157 } 158 else 159 { 160 Console.WriteLine(); 161 Console.WriteLine("Failed to create remote service instance..."); 162 return false; 163 } 164 165 simpleRemoteServiceEnd.DisposeShuttleDomain(); 166 return true; 167 } 168 }
如需完整代碼,請移步前往 此處 下載。