| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| = Remote Assembly Loading |
| |
| == Overview |
| |
| Many Ignite APIs involve remote code execution. For example, Ignite compute tasks are serialized, sent to remote nodes, and executed there. |
| However, by default, .NET assemblies (DLL files) with those tasks in, must be loaded on remote nodes in order to instantiate |
| and deserialize tasks' instances. |
| |
| Before version 2.1 you had to manually load assemblies (using `-assembly` swith with `Apache.Ignite.exe` or some other ways). |
| Starting Ignite 2.1 you can take advantage of the remote assembly loading feature, that can be enabled with the |
| `IgniteConfiguration.PeerAssemblyLoadingMode` flag. This configuration property needs to have the same value on all nodes |
| in the cluster. Another available mode is `CurrentAppDomain`. |
| |
| == CurrentAppDomain Mode |
| |
| `PeerAssemblyLoadingMode.CurrentAppDomain` enables automatic on-demand assembly requests to other nodes in cluster, |
| loading assemblies into https://msdn.microsoft.com/en-us/library/system.appdomain.aspx[AppDomain, window=_blank] where Ignite node runs. |
| |
| Consider the following code: |
| |
| [tabs] |
| -- |
| tab:C#[] |
| [source,csharp] |
| ---- |
| // Print Hello World on all cluster nodes. |
| ignite.GetCompute().Broadcast(new HelloAction()); |
| |
| class HelloAction : IComputeAction |
| { |
| public void Invoke() |
| { |
| Console.WriteLine("Hello World!"); |
| } |
| } |
| ---- |
| -- |
| * Ignite serializes the `HelloAction` instance and broadcasts to every node in the cluster. |
| * Remote nodes attempt to deserialize the `HelloAction` instance. If there is no such class in the currently loaded or referenced assemblies, |
| the nodes request an assembly with the class from the node that initiated the compute task or from other nodes (if necessary). |
| * The assembly file is sent from the originating or other node as a byte array and loaded with the `Assembly.Load(byte[])` method. |
| |
| === Versioning |
| |
| https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname.aspx[Assembly-qualified type name, window=_blank] |
| includes the assembly version and is used to resolve types. |
| |
| If you keep the cluster running, do this change in the logic and see how the assembly gets reloaded automatically: |
| |
| * Modify `HelloAction` intance to print something else |
| * Change https://msdn.microsoft.com/en-us/library/system.reflection.assemblyversionattribute.aspx[AssemblyVersion, window=_blank] |
| * Recompile and run the application code |
| * The new version of the assembly will be deployed and executed on other nodes. |
| |
| Note, if you keep the `AssemblyVersion` unchanged, Ignite will use existing assembly that was previously loaded, since |
| there are no changes in the type name. |
| |
| Assemblies with different versions can co-exist and be used side by side. Some nodes can continue running old code, while |
| other nodes can execute computations with a newer version of the same class. |
| |
| The `AssemblyVersion` attribute can include asterisk (`*`) to enable the auto-increment on build: `[assembly: AssemblyVersion("1.0.*")]`. |
| This way you can keep the cluster running, repeatedly modify and run computations, and new assembly versions will be deployed every time. |
| |
| === Dependencies |
| |
| Dependent assemblies are also loaded automatically, e.g. when `ComputeAction` calls some code from a different assembly. |
| Keep that in mind when using heavy frameworks and libraries: single compute call can cause lots of assemblies to be sent over the network. |
| |
| === Unloading |
| |
| .NET does not allow assembly unloading. Instead, only the entire `AppDomain` can be unloaded with all assemblies. |
| Currently available `CurrentAppDomain` mode uses existing `AppDomain`, which means all peer-deployed assemblies will stay |
| loaded while current `AppDomain` lives. This may cause increased memory usage. |
| |
| == Example |
| |
| https://github.com/apache/ignite/blob/56975c266e7019f307bb9da42333a6db4e47365e/modules/platforms/dotnet/examples/Apache.Ignite.Examples/Compute/PeerAssemblyLoadingExample.cs[PeerAssemblyLoadingExample, window=_blank] can be used |
| to try out the remote assembly loading feature in practice: |
| |
| * Create a new Console Application in Visual Studio |
| * Install the Ignite.NET NuGet package `Install-Package Apache.Ignite` |
| * Open the `packages\Apache.Ignite.2.1\lib\net40` folder |
| * Add the `peerAssemblyLoadingMode='CurrentAppDomain'` attribute to `<igniteConfiguration>` element |
| * Run `Apache.Ignite.exe` (one or more times), leave the processes running |
| * Change `[AssemblyVersion]` in `AssemblyInfo.cs` to `1.0.*` |
| * Modify `Program.cs` in Visual Studio as shown below |
| + |
| [tabs] |
| -- |
| tab:C#[] |
| [source,csharp] |
| ---- |
| using System; |
| using Apache.Ignite.Core; |
| using Apache.Ignite.Core.Compute; |
| using Apache.Ignite.Core.Deployment; |
| |
| namespace ConsoleApp |
| { |
| class Program |
| { |
| static void Main(string[] args) |
| { |
| var cfg = new IgniteConfiguration |
| { |
| PeerAssemblyLoadingMode = PeerAssemblyLoadingMode.CurrentAppDomain |
| }; |
| |
| using (var ignite = Ignition.Start(cfg)) |
| { |
| ignite.GetCompute().Broadcast(new HelloAction()); |
| } |
| } |
| |
| class HelloAction : IComputeAction |
| { |
| public void Invoke() |
| { |
| Console.WriteLine("Hello, World!"); |
| } |
| } |
| } |
| } |
| ---- |
| tab:Apache.Ignite.exe.config[] |
| [source,xml] |
| ---- |
| <igniteConfiguration peerAssemblyLoadingMode='CurrentAppDomain' /> |
| ---- |
| tab:AssemblyInfo.cs[] |
| [source,csharp] |
| ---- |
| ... |
| [assembly: AssemblyVersion("1.0.*")] |
| ... |
| ---- |
| -- |
| * Run the project and observe the `"Hello, World!"` output in the console of all `Apache.Ignite.exe` windows. |
| * Change the `"Hello, World!"` text to something else and run the program again |
| * Observe different output on the nodes started with `Apache.Ignite.exe` earlier. |