├── .gitignore ├── LICENSE ├── Orleans.MultiClient.sln ├── README.md ├── README.zh-cn.md ├── example ├── Orleans.Example │ ├── Orleans.Example.csproj │ └── Program.cs ├── Orleans.Grain │ ├── HelloGrain.cs │ ├── IHelloA.cs │ └── Orleans.Grain.csproj ├── Orleans.Grain2 │ ├── HelloGrain2.cs │ ├── IHelloB.cs │ └── Orleans.Grain2.csproj ├── Orleans.Host │ ├── Orleans.Host.csproj │ └── Program.cs └── Orleans.Host2 │ ├── Orleans.Host2.csproj │ └── Program.cs └── src └── Orleans.MultiClient ├── ClusterClientBuilder.cs ├── DependencyInjection ├── GenericHostExtensions.cs ├── IMultiClientBuilder.cs ├── MultiClientBuilder.cs ├── MultiClientBuilderExtensions.cs └── ServiceCollectionExtensions.cs ├── IClusterClientBuilder.cs ├── IClusterClientFactory.cs ├── IOrleansClient.cs ├── MultiClusterClientFactory.cs ├── Orleans.MultiClient.csproj ├── OrleansClient.cs └── OrleansClientOptions.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 阿凌 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Orleans.MultiClient.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29111.141 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orleans.MultiClient", "src\Orleans.MultiClient\Orleans.MultiClient.csproj", "{04493B45-81E5-49F8-9CD5-914D52864704}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{92788A18-295E-4D97-9716-7F6EA6AC8BE4}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "example", "example", "{A9738E2A-0CAD-4ABA-A368-4ABDB56A6E0C}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orleans.Example", "example\Orleans.Example\Orleans.Example.csproj", "{C7571F58-0AD4-41C5-8072-4CA07FB5EB32}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orleans.Host", "example\Orleans.Host\Orleans.Host.csproj", "{27E47CD2-6AD7-442D-B94E-B33B196C48A1}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orleans.Grain", "example\Orleans.Grain\Orleans.Grain.csproj", "{B540D7B0-3565-4F18-AA2B-290367EC571A}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orleans.Grain2", "example\Orleans.Grain2\Orleans.Grain2.csproj", "{D7D98B51-5B86-4C45-BF31-27A48B4D99E9}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orleans.Host2", "example\Orleans.Host2\Orleans.Host2.csproj", "{2CBDD05F-C056-4EED-9167-5110EABB893D}" 21 | EndProject 22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7CB5FF92-9B8A-4F55-934F-32A87680731D}" 23 | ProjectSection(SolutionItems) = preProject 24 | .gitignore = .gitignore 25 | LICENSE = LICENSE 26 | README.md = README.md 27 | EndProjectSection 28 | EndProject 29 | Global 30 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 31 | Debug|Any CPU = Debug|Any CPU 32 | Release|Any CPU = Release|Any CPU 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {04493B45-81E5-49F8-9CD5-914D52864704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {04493B45-81E5-49F8-9CD5-914D52864704}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {04493B45-81E5-49F8-9CD5-914D52864704}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {04493B45-81E5-49F8-9CD5-914D52864704}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {C7571F58-0AD4-41C5-8072-4CA07FB5EB32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {C7571F58-0AD4-41C5-8072-4CA07FB5EB32}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {C7571F58-0AD4-41C5-8072-4CA07FB5EB32}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {C7571F58-0AD4-41C5-8072-4CA07FB5EB32}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {27E47CD2-6AD7-442D-B94E-B33B196C48A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {27E47CD2-6AD7-442D-B94E-B33B196C48A1}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {27E47CD2-6AD7-442D-B94E-B33B196C48A1}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {27E47CD2-6AD7-442D-B94E-B33B196C48A1}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {B540D7B0-3565-4F18-AA2B-290367EC571A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {B540D7B0-3565-4F18-AA2B-290367EC571A}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {B540D7B0-3565-4F18-AA2B-290367EC571A}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {B540D7B0-3565-4F18-AA2B-290367EC571A}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {D7D98B51-5B86-4C45-BF31-27A48B4D99E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {D7D98B51-5B86-4C45-BF31-27A48B4D99E9}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {D7D98B51-5B86-4C45-BF31-27A48B4D99E9}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {D7D98B51-5B86-4C45-BF31-27A48B4D99E9}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {2CBDD05F-C056-4EED-9167-5110EABB893D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {2CBDD05F-C056-4EED-9167-5110EABB893D}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {2CBDD05F-C056-4EED-9167-5110EABB893D}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {2CBDD05F-C056-4EED-9167-5110EABB893D}.Release|Any CPU.Build.0 = Release|Any CPU 59 | EndGlobalSection 60 | GlobalSection(SolutionProperties) = preSolution 61 | HideSolutionNode = FALSE 62 | EndGlobalSection 63 | GlobalSection(NestedProjects) = preSolution 64 | {04493B45-81E5-49F8-9CD5-914D52864704} = {92788A18-295E-4D97-9716-7F6EA6AC8BE4} 65 | {C7571F58-0AD4-41C5-8072-4CA07FB5EB32} = {A9738E2A-0CAD-4ABA-A368-4ABDB56A6E0C} 66 | {27E47CD2-6AD7-442D-B94E-B33B196C48A1} = {A9738E2A-0CAD-4ABA-A368-4ABDB56A6E0C} 67 | {B540D7B0-3565-4F18-AA2B-290367EC571A} = {A9738E2A-0CAD-4ABA-A368-4ABDB56A6E0C} 68 | {D7D98B51-5B86-4C45-BF31-27A48B4D99E9} = {A9738E2A-0CAD-4ABA-A368-4ABDB56A6E0C} 69 | {2CBDD05F-C056-4EED-9167-5110EABB893D} = {A9738E2A-0CAD-4ABA-A368-4ABDB56A6E0C} 70 | EndGlobalSection 71 | GlobalSection(ExtensibilityGlobals) = postSolution 72 | SolutionGuid = {A5C5EA71-8A8F-4295-ABA5-C990415CE4E1} 73 | EndGlobalSection 74 | EndGlobal 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Orleans MultiClient                 [中文](https://github.com/lfzm/Orleans.MultiClient/blob/master/README.zh-cn.md) 2 | 3 | 4 | [![NuGet](https://img.shields.io/nuget/v/Orleans.MultiClient.svg?style=flat)](http://www.nuget.org/packages/Orleans.MultiClient) 5 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/dotnetcore/CAP/master/LICENSE.txt) 6 | 7 | 8 | `Orleans.MultiClient` is an Orleans multi-client, with simple configuration you can easily connect to Orleans Slio. 9 | `Orleans.MultiClient` can connect to many different Orleans Slio, Orleans.MultiClient Orleans Grain automatically finds Orleans clients upon request. 10 | 11 | *Note:Orleans.MultiClient only supports .NET Core platform.* 12 | 13 | ## Getting Started 14 | 15 | ### NuGet 16 | 17 | ``` 18 | PM> dotnet add package Orleans.MultiClient 19 | ``` 20 | 21 | ### Simple example 22 | 23 | A simple example demonstrates how Orleans.MultiClient connects to two different services (A, B). 24 | 25 | *Note:The source code for the [simple example](https://github.com/lfzm/Orleans.MultiClient/tree/master/example) is in the example folder in the Github repository* 26 | 27 | 28 | 1、Add the Grain interface package of A and B to the project of Orleans Client 29 | 30 | ``` 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ``` 39 | 40 | 41 | 2、Configure Orleans.MultiClient and inject Orleans.MultiClient into `IServiceCollection`。 42 | 43 | 44 | ```CSharp 45 | services.AddOrleansMultiClient(build => 46 | { 47 | build.AddClient(opt => 48 | { 49 | opt.ServiceId = "A"; 50 | opt.ClusterId = "AApp"; 51 | opt.SetServiceAssembly(typeof(IHelloA).Assembly); 52 | opt.Configure = (b => 53 | { 54 | b.UseLocalhostClustering(gatewayPort: 30000); 55 | }); 56 | }); 57 | build.AddClient(opt => 58 | { 59 | opt.ServiceId = "B"; 60 | opt.ClusterId = "BApp"; 61 | opt.SetServiceAssembly(typeof(IHelloB).Assembly); 62 | opt.Configure = (b => 63 | { 64 | b.UseLocalhostClustering(gatewayPort: 30001); 65 | }); 66 | }); 67 | }); 68 | ``` 69 | 70 | 3、Request A Slio and B Slio separately 71 | 72 | *Note: Before requesting, A Slio and B Slio need to be started, otherwise the request will fail.* 73 | 74 | ```CSharp 75 | // Request A Slio. 76 | var serviceA = _serviceProvider.GetRequiredService().GetGrain(1); 77 | var resultA = serviceA.SayHello("Hello World Success GrainA").GetAwaiter().GetResult(); 78 | 79 | // Request B Slio. 80 | var serviceB = _serviceProvider.GetRequiredService().GetGrain(1); 81 | var resultB = serviceB.SayHello("Hello World Success GrainB").GetAwaiter().GetResult(); 82 | 83 | ``` 84 | 85 | 86 | ## Configuration 87 | 88 | `AddOrleansMultiClient` method supports IServiceCollection and GenericHost configuration. 89 | 90 | 91 | ### Configure Orleans Slio connection 92 | 93 | 94 | Configure connectivity for a single Orleans Slio. 95 | 96 | ``` CSharp 97 | services.AddOrleansMultiClient(build => 98 | { 99 | build.AddClient(opt => 100 | { 101 | opt.ServiceId = "A"; 102 | opt.ClusterId = "AApp"; 103 | opt.SetServiceAssembly(typeof(IHelloA).Assembly); 104 | opt.Configure = (b => 105 | { 106 | b.UseLocalhostClustering(gatewayPort: 30000); 107 | // b.UseStaticClustering(...); 108 | // b.UseConsulClustering(...); // Configure Consul 109 | // b.UseKubeGatewayListProvider(); // Configure Kubernetes 110 | // b.UseAdoNetClustering(...); // Configure MySql and SQLServer 111 | }); 112 | }); 113 | } 114 | ``` 115 | 116 | Global configuration, common to all clients 117 | 118 | ```CSharp 119 | services.AddOrleansMultiClient(build =>{ 120 | build.Configure(b =>{ 121 | b.UseConsulClustering(...); 122 | // b.UseKubeGatewayListProvider(); // Configure Kubernetes 123 | // b.UseAdoNetClustering(...); // Configure MySql and SQLServer 124 | }); 125 | build.AddClient(opt => 126 | { 127 | opt.ServiceId = "A"; 128 | opt.ClusterId = "AApp"; 129 | opt.SetServiceAssembly(typeof(IHelloA).Assembly); 130 | } 131 | } 132 | ``` 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Orleans MultiClient                [English](https://github.com/lfzm/Orleans.MultiClient/blob/master/README.md) 2 | 3 | 4 | 5 | [![NuGet](https://img.shields.io/nuget/v/Orleans.MultiClient.svg?style=flat)](http://www.nuget.org/packages/Orleans.MultiClient) 6 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/dotnetcore/CAP/master/LICENSE.txt) 7 | 8 | ## 介绍 9 | `Orleans.MultiClient` 是一个 Orleans 复合客户端,只需要简单配置就可以简单高效连接和请求 Orleans 服务。 10 | 11 | `Orleans.MultiClient` 可以轻松连接多个不同服务的 Orleans 服务,在请求 Orleans 时会根据请求的接口自动寻找 Orleans 客户端,使用者无需关心底层的实现。 12 | 13 | `Orleans.MultiClient` 的开源地址和 `Demo` 可以在 `GitHub` 源代码下载查看。 14 | > https://github.com/AClumsy/Orleans.MultiClient/ 15 | 16 | ## 使用 17 | 18 | 从 `NuGet` 下载 `Orleans.MultiClient` 包。 19 | 20 | ``` 21 | dotnet add package Orleans.MultiClient 22 | ``` 23 | 24 | ### 简单例子 25 | 如果有一个服务需要同时调用 `A` 和 `B` 两个 `Orleans` 服务,看一看 `Orleans.MultiClient` 是怎么更简单的调用 Orleans 服务的。 26 | 27 | 第一步:先引用 `Orleans.MultiClient` 包和 `A`、`B` 的接口,分别为 `IHelloA`、 `IHelloB` 28 | 29 | 第二步:需要把 `Orleans.MultiClient` 注入到 `DI 容器` 中,并且配置添加两个 Orleans Client。 30 | 31 | *提示:`Orleans.MultiClient` 暂时只支持 `.NET Core` 平台上面使用。* 32 | ```CSharp 33 | services.AddOrleansMultiClient(build => 34 | { 35 | build.AddClient(opt => 36 | { 37 | opt.ServiceId = "A"; 38 | opt.ClusterId = "AApp"; 39 | opt.SetServiceAssembly(typeof(IHelloA).Assembly); 40 | opt.Configure = (b => 41 | { 42 | b.UseLocalhostClustering(); 43 | }); 44 | }); 45 | build.AddClient(opt => 46 | { 47 | opt.ServiceId = "B"; 48 | opt.ClusterId = "BApp"; 49 | opt.SetServiceAssembly(typeof(IHelloB).Assembly); 50 | opt.Configure = (b => 51 | { 52 | b.UseLocalhostClustering(gatewayPort: 30001); 53 | }); 54 | }); 55 | }); 56 | ``` 57 | 第二步:开始调用对应的 Orleans 服务。 58 | `IOrleansClient` 是 `Orleans.MultiClient` 的复合客户端,通过 `IOrleansClient` 调用 Orleans 服务。 59 | ```CSharp 60 | // 调用 A 服务。 61 | var serviceA = _serviceProvider.GetRequiredService().GetGrain(1); 62 | var resultA = serviceA.SayHello("Hello World Success GrainA").GetAwaiter().GetResult(); 63 | 64 | // 调用 B 服务。 65 | var serviceB = _serviceProvider.GetRequiredService().GetGrain(1); 66 | var resultB = serviceB.SayHello("Hello World Success GrainB").GetAwaiter().GetResult(); 67 | ``` 68 | 简单吧,只要配置好客户端之后,在使用的过程中,无需管怎么连接 `Orleans` ,只要通过依赖注入得到 `IOrleansClient` 就可以轻松的请求 Orleans 服务。 69 | 70 | ## 配置 71 | ### **注入到 DI 容器** 72 | 73 | **`AddOrleansMultiClient`** :把 `Orleans.MultiClient` 注入到 `DI 容器` 中,使用时需要通过依赖注入得到 `IOrleansClient` 。 74 | 75 | ### **添加多个 Client** 76 | **`AddClient`**: 添加多个 Orleans 客户客户端,添加客户端时需要配置 Orleans 相关选项。 `Orleans.MultiClient` 提供了函数和 `IConfiguration` 两种方式进行配置。 77 | 使用 `IConfiguration` 进行配置时需要注意配置文件的内容必须是 `IList` 类型的。 78 | 79 | ```CSharp 80 | services.AddOrleansMultiClient(build => 81 | { 82 | build.AddClient((OrleansClientOptions opt) =>{ 83 | ...// OrleansClientOptions 配置 84 | } 85 | }); 86 | ``` 87 | ### **全局 Orleans 服务配置** 88 | **`Configure`**:如果所有的 Orleans 的连接配置是一样的情况下,可以配置全局的 Orleans 服务配置。 89 | 比如:如果所有的 Orleans Silo 都是通过 `Consul` 进行服务发现的,就可以配置一个全局配置。 90 | ```CSharp 91 | services.AddOrleansMultiClient(build =>{ 92 | build.Configure(b =>{ 93 | b.UseConsulClustering(o =>{ 94 | o.Address = new Uri("https://127.0.0.1:8500"); 95 | }); 96 | }); 97 | } 98 | ``` 99 | 100 | #### **OrleansClientOptions 配置** 101 | * **`ServiceList`**:用于在 `IOrleansClient` 调用接口时和 Orleans 连接配置建立关联。ServiceList 的值时 Orleans Silo 接口的 `Assembly.FullName`, 由于 Orleans Silo 可能有多个接口,所以 ServiceList 是一个数组集合。可以通过 `SetServiceAssembly` 方法来配置 ServiceList。 102 | 103 | * **`ServiceId`**:Orleans Silo 的 ServiceId,在连接 Orleans 时需要。 104 | 105 | * **`ClusterId`**:Orleans Silo 的 ClusterId,在连接 Orleans 时需要。 106 | 107 | * **`Configure`**:Orleans 服务配置,如连接组件(`Consul`、`Zookeeperr`、等)。如果配置了 `全局 Orleans 服务配置` 这个选项可以不配置,但是这选项配置之后会覆盖上面的 `全局 Orleans 服务配置`。 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /example/Orleans.Example/Orleans.Example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/Orleans.Example/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Orleans.Grains; 3 | using Orleans.Grains2; 4 | using Orleans.Hosting; 5 | using System; 6 | 7 | namespace Orleans.Example 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | Console.WriteLine("Enter btn start"); 14 | Console.ReadKey(); 15 | IServiceCollection services = new ServiceCollection(); 16 | services.AddLogging(); 17 | services.AddOrleansMultiClient(build => 18 | { 19 | build.AddClient(opt => 20 | { 21 | opt.ServiceId = "A"; 22 | opt.ClusterId = "AApp"; 23 | opt.SetServiceAssembly(typeof(IHelloA).Assembly); 24 | opt.Configure = (b => 25 | { 26 | b.UseLocalhostClustering(); 27 | }); 28 | }); 29 | build.AddClient(opt => 30 | { 31 | opt.ServiceId = "B"; 32 | opt.ClusterId = "BApp"; 33 | opt.SetServiceAssembly(typeof(IHelloB).Assembly); 34 | opt.Configure = (b => 35 | { 36 | b.UseLocalhostClustering(gatewayPort: 30001); 37 | }); 38 | }); 39 | }); 40 | 41 | var sp = services.BuildServiceProvider(); 42 | 43 | var service = sp.GetRequiredService().GetGrain(1); 44 | var result1 = service.SayHello("Hello World Success Grain1").GetAwaiter().GetResult(); 45 | 46 | 47 | var serviceA = sp.GetRequiredService().GetClusterClient(typeof(IHelloA).Assembly) .GetGrain(1); 48 | var resultA1 = serviceA.SayHello("Hello World Success GrainA").GetAwaiter().GetResult(); 49 | 50 | 51 | var service2 = sp.GetRequiredService().GetGrain(1); 52 | var result2 = service2.SayHello("Hello World Success Grain2").GetAwaiter().GetResult(); 53 | Console.WriteLine("dev1:" + result1); 54 | Console.WriteLine("dev2:" + result2); 55 | 56 | Console.ReadKey(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /example/Orleans.Grain/HelloGrain.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Orleans.Grains 8 | { 9 | /// 10 | /// Orleans grain implementation class HelloGrain. 11 | /// 12 | public class HelloGrain : Grain, IHelloA 13 | { 14 | private readonly ILogger logger; 15 | 16 | public HelloGrain(ILogger logger) 17 | { 18 | this.logger = logger; 19 | } 20 | 21 | public Task SayHello(string greeting) 22 | { 23 | logger.LogInformation($"SayHello message received: greeting = '{greeting}'"); 24 | return Task.FromResult($"You said: '{greeting}', I say: Hello!"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example/Orleans.Grain/IHelloA.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Orleans.Grains 7 | { 8 | /// 9 | /// Orleans grain communication interface IHello 10 | /// 11 | public interface IHelloA : Orleans.IGrainWithIntegerKey 12 | { 13 | Task SayHello(string greeting); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/Orleans.Grain/Orleans.Grain.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | all 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /example/Orleans.Grain2/HelloGrain2.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Orleans.Grains2 8 | { 9 | /// 10 | /// Orleans grain implementation class HelloGrain. 11 | /// 12 | public class HelloGrain2 : Grain, IHelloB 13 | { 14 | private readonly ILogger logger; 15 | 16 | public HelloGrain2(ILogger logger) 17 | { 18 | this.logger = logger; 19 | } 20 | 21 | public Task SayHello(string greeting) 22 | { 23 | logger.LogInformation($"SayHello message received: greeting = '{greeting}'"); 24 | return Task.FromResult($"You said: '{greeting}', I say: Hello!"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example/Orleans.Grain2/IHelloB.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Orleans.Grains2 7 | { 8 | /// 9 | /// Orleans grain communication interface IHello 10 | /// 11 | public interface IHelloB : Orleans.IGrainWithIntegerKey 12 | { 13 | Task SayHello(string greeting); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/Orleans.Grain2/Orleans.Grain2.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | all 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /example/Orleans.Host/Orleans.Host.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/Orleans.Host/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Orleans.Configuration; 3 | using Orleans.Grains; 4 | using Orleans.Hosting; 5 | using System; 6 | using System.Net; 7 | using System.Threading.Tasks; 8 | 9 | namespace Orleans.Host 10 | { 11 | public class Program 12 | { 13 | public static int Main(string[] args) 14 | { 15 | return RunMainAsync().Result; 16 | } 17 | 18 | private static async Task RunMainAsync() 19 | { 20 | try 21 | { 22 | var host = await StartSilo(); 23 | Console.WriteLine("Press Enter to terminate..."); 24 | Console.ReadLine(); 25 | 26 | await host.StopAsync(); 27 | 28 | return 0; 29 | } 30 | catch (Exception ex) 31 | { 32 | Console.WriteLine(ex); 33 | return 1; 34 | } 35 | } 36 | 37 | private static async Task StartSilo() 38 | { 39 | // define the cluster configuration 40 | var builder = new SiloHostBuilder() 41 | .UseLocalhostClustering() 42 | .Configure(options => 43 | { 44 | options.ClusterId = "A"; 45 | options.ServiceId = "AApp"; 46 | }) 47 | .Configure(options => options.AdvertisedIPAddress = IPAddress.Loopback) 48 | .ConfigureApplicationParts(parts => parts.AddApplicationPart(typeof(HelloGrain).Assembly).WithReferences()) 49 | .ConfigureLogging(logging => logging.AddConsole()); 50 | 51 | var host = builder.Build(); 52 | await host.StartAsync(); 53 | return host; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /example/Orleans.Host2/Orleans.Host2.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/Orleans.Host2/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Orleans.Configuration; 3 | using Orleans.Grains2; 4 | using Orleans.Hosting; 5 | using System; 6 | using System.Net; 7 | using System.Threading.Tasks; 8 | namespace Orleans.Host2 9 | { 10 | public class Program 11 | { 12 | public static int Main(string[] args) 13 | { 14 | return RunMainAsync().Result; 15 | } 16 | 17 | private static async Task RunMainAsync() 18 | { 19 | try 20 | { 21 | var host = await StartSilo(); 22 | Console.WriteLine("Press Enter to terminate..."); 23 | Console.ReadLine(); 24 | 25 | await host.StopAsync(); 26 | 27 | return 0; 28 | } 29 | catch (Exception ex) 30 | { 31 | Console.WriteLine(ex); 32 | return 1; 33 | } 34 | } 35 | 36 | private static async Task StartSilo() 37 | { 38 | // define the cluster configuration 39 | var builder = new SiloHostBuilder() 40 | .UseLocalhostClustering(siloPort:11112,gatewayPort:30001) 41 | .Configure(options => 42 | { 43 | options.ClusterId = "B"; 44 | options.ServiceId = "BApp"; 45 | }) 46 | .Configure(options => options.AdvertisedIPAddress = IPAddress.Loopback) 47 | .ConfigureApplicationParts(parts => parts.AddApplicationPart(typeof(HelloGrain2).Assembly).WithReferences()) 48 | .ConfigureLogging(logging => logging.AddConsole()); 49 | 50 | var host = builder.Build(); 51 | await host.StartAsync(); 52 | return host; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/ClusterClientBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Logging; 3 | using Orleans.Configuration; 4 | using Orleans.Runtime; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | namespace Orleans.MultiClient 9 | { 10 | public class ClusterClientBuilder : IClusterClientBuilder 11 | { 12 | private readonly OrleansClientOptions _options; 13 | private readonly ILogger _logger; 14 | private readonly string _serviceName; 15 | 16 | public ClusterClientBuilder(IServiceProvider serviceProvider, OrleansClientOptions options,string serviceName) 17 | { 18 | this._logger = serviceProvider.GetRequiredService>(); 19 | this._options = options; 20 | this._serviceName = serviceName; 21 | } 22 | public IClusterClient Build() 23 | { 24 | IClientBuilder build = new ClientBuilder(); 25 | if (_options.Configure == null) 26 | { 27 | _logger.LogError($"{_serviceName} There is no way to connect to Orleans, please configure it in OrleansClientOptions.Configure"); 28 | } 29 | _options.Configure(build); 30 | build.Configure(opt => 31 | { 32 | if (!string.IsNullOrEmpty(_options.ClusterId)) 33 | opt.ClusterId = _options.ClusterId; 34 | if (!string.IsNullOrEmpty(_options.ServiceId)) 35 | opt.ServiceId = _options.ServiceId; 36 | }); 37 | 38 | var client = build.Build(); 39 | return this.ConnectClient(_serviceName, client); 40 | } 41 | 42 | private IClusterClient ConnectClient(string serviceName, IClusterClient client) 43 | { 44 | try 45 | { 46 | var res = client.Connect(RetryFilter).Wait(TimeSpan.FromSeconds(10)); 47 | if (!res) 48 | { 49 | throw new Exception($"Connection {serviceName} timeout..."); 50 | } 51 | _logger.LogDebug($"Connection {serviceName} Sucess..."); 52 | return client; 53 | } 54 | catch (Exception ex) 55 | { 56 | throw new Exception($"Connection {serviceName} Faile...", ex); 57 | } 58 | } 59 | 60 | private int attempt = 0; 61 | private async Task RetryFilter(Exception exception) 62 | { 63 | if (exception.GetType() != typeof(SiloUnavailableException)) 64 | { 65 | _logger.LogError(exception,$"Cluster client failed to connect to cluster with unexpected error. "); 66 | return false; 67 | } 68 | attempt++; 69 | _logger.LogError(exception,$"Cluster client attempt {attempt} of {10} failed to connect to cluster."); 70 | if (attempt > 10) 71 | { 72 | return false; 73 | } 74 | await Task.Delay(TimeSpan.FromSeconds(4)); 75 | return true; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/DependencyInjection/GenericHostExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Orleans.MultiClient.DependencyInjection; 3 | using System; 4 | 5 | namespace Microsoft.Extensions.Hosting 6 | { 7 | public static class GenericHostExtensions 8 | { 9 | public static IHostBuilder UseOrleansMultiClient(this IHostBuilder hostBuilder, Action startup) 10 | { 11 | hostBuilder.ConfigureServices((context, service) => service.AddOrleansMultiClient(startup)); 12 | return hostBuilder; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/DependencyInjection/IMultiClientBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Orleans.MultiClient.DependencyInjection 6 | { 7 | public interface IMultiClientBuilder 8 | { 9 | IServiceCollection Services { get; } 10 | 11 | Action OrleansConfigure { get; set; } 12 | IList ClientOptions { get; set; } 13 | void Build(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/DependencyInjection/MultiClientBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Orleans.Runtime; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Orleans.MultiClient.DependencyInjection 7 | { 8 | public class MultiClientBuilder : IMultiClientBuilder 9 | { 10 | public IServiceCollection Services { get; private set; } 11 | public Action OrleansConfigure { get; set; } 12 | public IList ClientOptions { get; set; } = new List(); 13 | public MultiClientBuilder(IServiceCollection services) 14 | { 15 | this.Services = services; 16 | } 17 | public void Build() 18 | { 19 | if (this.ClientOptions.Count <= 0) 20 | { 21 | throw new ArgumentNullException($"Please add silo via MultiClientBuilderExtensions.AddClient"); 22 | } 23 | foreach (var client in this.ClientOptions) 24 | { 25 | if (client.Configure == null) 26 | client.Configure = this.OrleansConfigure; 27 | if (client.ServiceList.Count == 0) 28 | throw new ArgumentNullException($"Request to go to the configuration OrleansClientOptions.SetServiceAssembly Orleans interface"); 29 | foreach (var serviceName in client.ServiceList) 30 | { 31 | //if (!client.ExistAssembly(serviceName)) 32 | // throw new ArgumentNullException($"{serviceName} service does not exist in the assembly"); 33 | 34 | Services.AddSingletonNamedService(serviceName, (sp, key) => 35 | { 36 | return new ClusterClientBuilder(sp, client, key); 37 | }); 38 | } 39 | } 40 | 41 | this.Services.AddSingleton(typeof(IKeyedServiceCollection<,>), typeof(KeyedServiceCollection<,>)); 42 | this.Services.AddSingleton(); 43 | this.Services.AddSingleton(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/DependencyInjection/MultiClientBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Orleans; 3 | using Orleans.MultiClient; 4 | using Orleans.MultiClient.DependencyInjection; 5 | using Orleans.Runtime; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Reflection; 9 | 10 | namespace Microsoft.Extensions.DependencyInjection 11 | { 12 | public static class MultiClientBuilderExtensions 13 | { 14 | public static IMultiClientBuilder AddClient(this IMultiClientBuilder builder, Action startup) 15 | { 16 | OrleansClientOptions options = new OrleansClientOptions(); 17 | startup.Invoke(options); 18 | return builder.AddClient(options); 19 | } 20 | public static IMultiClientBuilder AddClient(this IMultiClientBuilder builder, OrleansClientOptions options) 21 | { 22 | 23 | builder.ClientOptions.Add(options); 24 | 25 | 26 | return builder; 27 | } 28 | public static IMultiClientBuilder AddClient(this IMultiClientBuilder builder, IConfiguration config) 29 | { 30 | var optionList = config.Get>(); 31 | foreach (var options in optionList) 32 | { 33 | builder.AddClient(options); 34 | } 35 | return builder; 36 | } 37 | public static IMultiClientBuilder Configure(this IMultiClientBuilder builder, Action OrleansConfigure) 38 | { 39 | builder.OrleansConfigure = OrleansConfigure; 40 | return builder; 41 | } 42 | 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/DependencyInjection/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Orleans.MultiClient.DependencyInjection; 2 | using System; 3 | 4 | namespace Microsoft.Extensions.DependencyInjection 5 | { 6 | public static class ServiceCollectionExtensions 7 | { 8 | public static IServiceCollection AddOrleansMultiClient(this IServiceCollection services, Action startup) 9 | { 10 | var build = new MultiClientBuilder(services); 11 | startup.Invoke(build); 12 | build.Build(); 13 | return services; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/IClusterClientBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Orleans.MultiClient 2 | { 3 | public interface IClusterClientBuilder 4 | { 5 | IClusterClient Build(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/IClusterClientFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace Orleans.MultiClient 5 | { 6 | public interface IClusterClientFactory 7 | { 8 | IGrainFactory Create(); 9 | 10 | IGrainFactory Create(Type type); 11 | 12 | IGrainFactory Create(Assembly assembly); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/IOrleansClient.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Runtime; 2 | using Orleans.Streams; 3 | using System; 4 | using System.Reflection; 5 | using System.Threading.Tasks; 6 | 7 | namespace Orleans 8 | { 9 | /// 10 | /// Orleans Client 11 | /// 12 | public interface IOrleansClient 13 | { 14 | /// 15 | /// get stream provider 16 | /// 17 | /// Assembly of silo interface 18 | /// stream provider name 19 | /// 20 | IStreamProvider GetStreamProvider(Assembly assembly, string name); 21 | /// 22 | /// get 23 | /// 24 | /// Assembly of silo interface 25 | /// 26 | IClusterClient GetClusterClient(Assembly assembly); 27 | void BindGrainReference(Assembly assembly, IAddressable grain); 28 | Task CreateObjectReference(IGrainObserver obj) where TGrainObserverInterface : IGrainObserver; 29 | Task DeleteObjectReference(IGrainObserver obj) where TGrainObserverInterface : IGrainObserver; 30 | TGrainInterface GetGrain(Guid primaryKey, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithGuidKey; 31 | TGrainInterface GetGrain(long primaryKey, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithIntegerKey; 32 | TGrainInterface GetGrain(string primaryKey, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithStringKey; 33 | TGrainInterface GetGrain(Guid primaryKey, string keyExtension, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithGuidCompoundKey; 34 | TGrainInterface GetGrain(long primaryKey, string keyExtension, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithIntegerCompoundKey; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/MultiClusterClientFactory.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Runtime; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Reflection; 5 | 6 | namespace Orleans.MultiClient 7 | { 8 | public class MultiClusterClientFactory : IClusterClientFactory 9 | { 10 | private readonly ConcurrentDictionary clusterClientCache = new ConcurrentDictionary(); 11 | private readonly IServiceProvider _serviceProvider; 12 | public MultiClusterClientFactory(IServiceProvider serviceProvider) 13 | { 14 | _serviceProvider = serviceProvider; 15 | } 16 | 17 | public IGrainFactory Create() 18 | { 19 | return this.Create(typeof(TGrainInterface).Assembly); 20 | } 21 | 22 | public IGrainFactory Create(Type type) 23 | { 24 | return this.Create(type.Assembly); 25 | } 26 | 27 | public IGrainFactory Create(Assembly assembly) 28 | { 29 | var name = assembly.FullName; 30 | return clusterClientCache.GetOrAdd(name, (key) => 31 | { 32 | IClusterClient client = this._serviceProvider.GetRequiredServiceByName(key).Build(); 33 | if (client.IsInitialized) 34 | { 35 | return client; 36 | } 37 | else 38 | { 39 | throw new Exception("Can not initialized clusterClient"); 40 | } 41 | }); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/Orleans.MultiClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | true 6 | 阿凌 7 | 阿凌 8 | Orleans' Multi Client 9 | 10 | https://github.com/OrleansContrib/Orleans.MultiClient 11 | https://raw.githubusercontent.com/dotnet/orleans/gh-pages/assets/logo_128.png 12 | https://github.com/OrleansContrib/Orleans.MultiClient 13 | 14 | Orleans MultiClient Client 15 | 3.3.0 16 | false 17 | 18 | 19 | 20 | bin\Orleans.MultiClient.xml 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/OrleansClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Threading.Tasks; 4 | using Orleans.Runtime; 5 | using Orleans.Streams; 6 | 7 | namespace Orleans.MultiClient 8 | { 9 | public class OrleansClient : IOrleansClient 10 | { 11 | private readonly IServiceProvider _serviceProvider; 12 | private readonly IClusterClientFactory _clusterClientFactory; 13 | 14 | 15 | public OrleansClient(IServiceProvider serviceProvider, IClusterClientFactory clusterClientFactory) 16 | { 17 | _serviceProvider = serviceProvider; 18 | _clusterClientFactory = clusterClientFactory; 19 | } 20 | 21 | public void BindGrainReference(Assembly assembly, IAddressable grain) 22 | { 23 | _clusterClientFactory.Create(assembly).BindGrainReference(grain); 24 | } 25 | public IStreamProvider GetStreamProvider(Assembly assembly, string name) 26 | { 27 | return this.GetClusterClient(assembly).GetStreamProvider(name); 28 | } 29 | public IClusterClient GetClusterClient(Assembly assembly) 30 | { 31 | return (IClusterClient)_clusterClientFactory.Create(assembly); 32 | } 33 | public Task CreateObjectReference(IGrainObserver obj) where TGrainObserverInterface : IGrainObserver 34 | { 35 | return _clusterClientFactory.Create().CreateObjectReference(obj); 36 | } 37 | 38 | public Task DeleteObjectReference(IGrainObserver obj) where TGrainObserverInterface : IGrainObserver 39 | { 40 | return _clusterClientFactory.Create().DeleteObjectReference(obj); 41 | } 42 | 43 | public TGrainInterface GetGrain(Guid primaryKey, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithGuidKey 44 | { 45 | return _clusterClientFactory.Create().GetGrain(primaryKey, grainClassNamePrefix); 46 | } 47 | public TGrainInterface GetGrain(long primaryKey, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithIntegerKey 48 | { 49 | return _clusterClientFactory.Create().GetGrain(primaryKey, grainClassNamePrefix); 50 | } 51 | public TGrainInterface GetGrain(string primaryKey, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithStringKey 52 | { 53 | return _clusterClientFactory.Create().GetGrain(primaryKey, grainClassNamePrefix); 54 | } 55 | 56 | public TGrainInterface GetGrain(Guid primaryKey, string keyExtension, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithGuidCompoundKey 57 | { 58 | return _clusterClientFactory.Create().GetGrain(primaryKey, keyExtension, grainClassNamePrefix); 59 | } 60 | public TGrainInterface GetGrain(long primaryKey, string keyExtension, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithIntegerCompoundKey 61 | { 62 | return _clusterClientFactory.Create().GetGrain(primaryKey, keyExtension, grainClassNamePrefix); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Orleans.MultiClient/OrleansClientOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System; 5 | 6 | namespace Orleans.MultiClient 7 | { 8 | public class OrleansClientOptions 9 | { 10 | public OrleansClientOptions() 11 | { 12 | ServiceList = new List(); 13 | } 14 | public IList ServiceList { get; set; } 15 | public string ServiceId { get; set; } 16 | public string ClusterId { get; set; } 17 | public Action Configure { get; set; } 18 | 19 | public void SetServiceAssembly(params Assembly[] assemblys) 20 | { 21 | foreach (var assembly in assemblys) 22 | { 23 | var name = assembly.FullName; 24 | if (!this.ServiceList.Contains(name)) 25 | { 26 | this.ServiceList.Add(name); 27 | } 28 | } 29 | } 30 | 31 | } 32 | } 33 | --------------------------------------------------------------------------------