├── .gitignore ├── ApiGateWay ├── ApiGateWay.csproj ├── Controllers │ └── RouteGateController.cs ├── Program.cs ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json └── oxygen.json ├── LICENSE ├── Oxygen.sln ├── icon └── icon.png ├── k8s ├── .dockerignore ├── ApiGateway.Dockerfile ├── ClientSample.Dockerfile ├── ServerSample.Dockerfile ├── build.bat ├── k8sSample.yaml ├── start-istio.sh ├── start.bat ├── start.sh ├── stop.bat └── stop.sh ├── nugetpack ├── License │ └── License.txt ├── Oxygen.CsharpClientAgent.nuspec ├── Oxygen.nuspec ├── images │ └── icon.png ├── nugetpack.exe └── pack.bat ├── readme.md ├── src ├── Oxygen.CommonTool │ ├── CustomerIp.cs │ ├── EnumMeshType.cs │ ├── EnumProtocolType.cs │ ├── ExpressionMapper.cs │ ├── GlobalCommon.cs │ ├── Logger │ │ ├── IOxygenLogger.cs │ │ └── OxygenConsoleLogger.cs │ ├── Module.cs │ ├── Oxygen.CommonTool.csproj │ ├── OxygenIocContainer.cs │ ├── OxygenSetting.cs │ ├── RpcInterfaceType.cs │ ├── TaskExtension.cs │ └── TraceHeaderHelper.cs ├── Oxygen.CsharpClientAgent │ ├── ActorServiceAttribute.cs │ ├── Oxygen.CsharpClientAgent.csproj │ └── RemoteServiceAttribute.cs ├── Oxygen.DaprActorProvider │ ├── ActorServiceBuilder.cs │ ├── Module.cs │ ├── Oxygen.DaprActorProvider.csproj │ ├── OxygenActor.cs │ ├── ServerProxyFactoryExtension.cs │ ├── StateManage │ │ ├── ActorStateMessage.cs │ │ └── ActorStateSubscriber.cs │ └── VirtualActorProxyServer.cs ├── Oxygen.DotNettyRpcProviderService │ ├── BootstrapFactory.cs │ ├── MessageCoder.cs │ ├── Module.cs │ ├── Oxygen.DotNettyRpcProviderService.csproj │ ├── ProtocolMessageBuilder.cs │ ├── RpcClientHandler.cs │ ├── RpcClientProvider.cs │ ├── RpcServerHandler.cs │ └── RpcServerProvider.cs ├── Oxygen.IRpcProviderService │ ├── IRpcClientProvider.cs │ ├── IRpcServerProvider.cs │ ├── Oxygen.IRpcProviderService.csproj │ └── RpcGlobalMessageBase.cs ├── Oxygen.ISerializeService │ ├── ISerialize.cs │ └── Oxygen.ISerializeService.csproj ├── Oxygen.IServerProxyFactory │ ├── ILocalMethodDelegate.cs │ ├── ILocalProxyGenerator.cs │ ├── IRemoteMethodDelegate.cs │ ├── IRemoteProxyGenerator.cs │ ├── IServerProxyFactory.cs │ ├── IVirtualProxyServer.cs │ ├── MethodDelegateInfo.cs │ └── Oxygen.IServerProxyFactory.csproj ├── Oxygen.KestrelRpcProviderService │ ├── Module.cs │ ├── Oxygen.KestrelRpcProviderService.csproj │ ├── ProtocolMessageBuilder.cs │ ├── RpcClientProvider.cs │ ├── RpcServerHandler.cs │ └── RpcServerProvider.cs ├── Oxygen.MessagePackSerializeService │ ├── Module.cs │ ├── Oxygen.MessagePackSerializeService.csproj │ └── Serialize.cs ├── Oxygen.ServerProxyFactory │ ├── LocalMethodAopProvider.cs │ ├── LocalMethodDelegate.cs │ ├── LocalProxyGenerator.cs │ ├── Module.cs │ ├── Oxygen.ServerProxyFactory.csproj │ ├── ProxyClientBuilder.cs │ ├── RemoteMethodDelegate.cs │ ├── RemoteProxyDecorator.cs │ ├── RemoteProxyDecoratorBuilder.cs │ ├── RemoteProxyGenerator.cs │ ├── ServerProxyFactory.cs │ └── VirtualProxyServer.cs └── Oxygen │ ├── Oxygen.csproj │ ├── OxygenClientService.cs │ ├── OxygenHostService.cs │ └── RpcConfigurationModule.cs └── test ├── Application.Interface ├── Application.Interface.csproj ├── ApplicationBaseResult.cs ├── Interfaces │ ├── IUserActorService.cs │ └── IUserLoginUseCase.cs └── UseCase │ └── Dto │ ├── LoginInput.cs │ └── Register.cs ├── Application.Service ├── Actor │ └── UserActorService.cs ├── ActorBase.cs ├── Application.Service.csproj ├── UseCase │ └── UserLoginUseCase.cs └── UseCaseBase.cs ├── Client.Sample ├── Client.Sample.csproj ├── CustomHostService.cs ├── Program.cs └── oxygen.json └── Server.Sample ├── Program.cs ├── Server.Sample.csproj └── oxygen.json /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | /ApiGateWay/Properties 263 | /test/Server.Sample/Properties/PublishProfiles 264 | /src/Oxygen/Properties/PublishProfiles 265 | /test/Client.Sample/Properties/PublishProfiles 266 | /k8s/ServerSample 267 | /k8s/ApiGateWay 268 | /nugetpack/lib 269 | *.nupkg 270 | /k8s/ClientSample 271 | /k8s/ServerBSample 272 | /nugetpack/lib-debugger 273 | /.sonarqube 274 | -------------------------------------------------------------------------------- /ApiGateWay/ApiGateWay.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ApiGateWay/Controllers/RouteGateController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Primitives; 4 | using Oxygen.CommonTool; 5 | using Oxygen.DaprActorProvider; 6 | using Oxygen.IServerProxyFactory; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | 11 | namespace ApiGateWay.Controllers 12 | { 13 | [Route("api/{*service}")] 14 | [ApiController] 15 | public class RouteGateController : ControllerBase 16 | { 17 | private readonly IServerProxyFactory _serverProxyFactory; 18 | public RouteGateController(IServerProxyFactory serverProxyFactory) 19 | { 20 | _serverProxyFactory = serverProxyFactory; 21 | } 22 | // GET api/values 23 | [HttpPost] 24 | public async Task Invoke(Dictionary input) 25 | { 26 | if (input != null) 27 | { 28 | var remoteProxy = _serverProxyFactory.CreateProxy(Request.Path); 29 | if (remoteProxy != null) 30 | { 31 | try 32 | { 33 | var rempteResult = await remoteProxy.SendAsync(input); 34 | if (rempteResult != null) 35 | { 36 | return new JsonResult(rempteResult); 37 | } 38 | } 39 | catch(Exception e) 40 | { 41 | return Content($"调用远程服务失败,原因:{e.Message}"); 42 | } 43 | } 44 | else 45 | { 46 | return Content("创建代理失败"); 47 | } 48 | } 49 | return Content("无返回值"); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ApiGateWay/Program.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sd797994/Oxygen/70e4b7cf4335cbd7bd5d30fb2fe43c3eda558e59/ApiGateWay/Program.cs -------------------------------------------------------------------------------- /ApiGateWay/Startup.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sd797994/Oxygen/70e4b7cf4335cbd7bd5d30fb2fe43c3eda558e59/ApiGateWay/Startup.cs -------------------------------------------------------------------------------- /ApiGateWay/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ApiGateWay/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /ApiGateWay/oxygen.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sd797994/Oxygen/70e4b7cf4335cbd7bd5d30fb2fe43c3eda558e59/ApiGateWay/oxygen.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 sd797994 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 | -------------------------------------------------------------------------------- /Oxygen.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29123.88 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5E0BADCE-71FC-4950-ACFE-1340DD435402}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxygen.DotNettyRpcProviderService", "src\Oxygen.DotNettyRpcProviderService\Oxygen.DotNettyRpcProviderService.csproj", "{6C8E9658-82D0-400E-AFAE-606B237F521D}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxygen.CsharpClientAgent", "src\Oxygen.CsharpClientAgent\Oxygen.CsharpClientAgent.csproj", "{7CAB5C8E-DC76-422F-9C4F-F02FC155526B}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxygen.IRpcProviderService", "src\Oxygen.IRpcProviderService\Oxygen.IRpcProviderService.csproj", "{342CDFFB-A85F-4081-8B50-88A34243DFAD}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxygen.ISerializeService", "src\Oxygen.ISerializeService\Oxygen.ISerializeService.csproj", "{96708E9A-2B62-49A8-904C-26E142EDD6E3}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxygen.MessagePackSerializeService", "src\Oxygen.MessagePackSerializeService\Oxygen.MessagePackSerializeService.csproj", "{9AC2BB39-6734-4A91-98D0-D5A28FD97270}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxygen", "src\Oxygen\Oxygen.csproj", "{E7F26141-34CE-428E-8AEC-BD211CE272EF}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxygen.ServerProxyFactory", "src\Oxygen.ServerProxyFactory\Oxygen.ServerProxyFactory.csproj", "{43D64D3E-CDA0-488B-ACC8-F051504B8E42}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxygen.CommonTool", "src\Oxygen.CommonTool\Oxygen.CommonTool.csproj", "{7BC01184-5F31-40E5-AE3E-65C007E9DC31}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxygen.IServerProxyFactory", "src\Oxygen.IServerProxyFactory\Oxygen.IServerProxyFactory.csproj", "{6B0E24A4-AF46-4FCB-B6F7-BF968B776140}" 25 | EndProject 26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InterFace", "InterFace", "{C7655257-3E2F-40BD-B738-C445652C83A5}" 27 | EndProject 28 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E813619C-7632-46E8-B571-30D642D13D99}" 29 | EndProject 30 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application.Interface", "test\Application.Interface\Application.Interface.csproj", "{31BFBAD4-FED9-4004-882E-3EAB8798A4CB}" 31 | EndProject 32 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application.Service", "test\Application.Service\Application.Service.csproj", "{283FB71D-FBFA-4502-91C2-D866ABF2194D}" 33 | EndProject 34 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client.Sample", "test\Client.Sample\Client.Sample.csproj", "{CB011875-E267-4E0E-8901-BEDB433E2603}" 35 | EndProject 36 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.Sample", "test\Server.Sample\Server.Sample.csproj", "{E8853DE6-584D-494E-ABC0-8BEDC937532C}" 37 | EndProject 38 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiGateWay", "ApiGateWay\ApiGateWay.csproj", "{1137DDF2-FBEF-4192-B911-D3D60592D453}" 39 | EndProject 40 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "icon", "icon", "{35180843-9481-44FC-AE3C-F9AC42D949C5}" 41 | ProjectSection(SolutionItems) = preProject 42 | icon\icon.png = icon\icon.png 43 | EndProjectSection 44 | EndProject 45 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxygen.KestrelRpcProviderService", "src\Oxygen.KestrelRpcProviderService\Oxygen.KestrelRpcProviderService.csproj", "{13B70208-AE21-44FE-98A6-FA42852445A7}" 46 | EndProject 47 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxygen.DaprActorProvider", "src\Oxygen.DaprActorProvider\Oxygen.DaprActorProvider.csproj", "{492CF046-3282-4097-9B5C-C740706A8CBE}" 48 | EndProject 49 | Global 50 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 51 | Debug|Any CPU = Debug|Any CPU 52 | Release|Any CPU = Release|Any CPU 53 | EndGlobalSection 54 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 55 | {6C8E9658-82D0-400E-AFAE-606B237F521D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {6C8E9658-82D0-400E-AFAE-606B237F521D}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {6C8E9658-82D0-400E-AFAE-606B237F521D}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {6C8E9658-82D0-400E-AFAE-606B237F521D}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {7CAB5C8E-DC76-422F-9C4F-F02FC155526B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {7CAB5C8E-DC76-422F-9C4F-F02FC155526B}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {7CAB5C8E-DC76-422F-9C4F-F02FC155526B}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {7CAB5C8E-DC76-422F-9C4F-F02FC155526B}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {342CDFFB-A85F-4081-8B50-88A34243DFAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {342CDFFB-A85F-4081-8B50-88A34243DFAD}.Debug|Any CPU.Build.0 = Debug|Any CPU 65 | {342CDFFB-A85F-4081-8B50-88A34243DFAD}.Release|Any CPU.ActiveCfg = Release|Any CPU 66 | {342CDFFB-A85F-4081-8B50-88A34243DFAD}.Release|Any CPU.Build.0 = Release|Any CPU 67 | {96708E9A-2B62-49A8-904C-26E142EDD6E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 68 | {96708E9A-2B62-49A8-904C-26E142EDD6E3}.Debug|Any CPU.Build.0 = Debug|Any CPU 69 | {96708E9A-2B62-49A8-904C-26E142EDD6E3}.Release|Any CPU.ActiveCfg = Release|Any CPU 70 | {96708E9A-2B62-49A8-904C-26E142EDD6E3}.Release|Any CPU.Build.0 = Release|Any CPU 71 | {9AC2BB39-6734-4A91-98D0-D5A28FD97270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 72 | {9AC2BB39-6734-4A91-98D0-D5A28FD97270}.Debug|Any CPU.Build.0 = Debug|Any CPU 73 | {9AC2BB39-6734-4A91-98D0-D5A28FD97270}.Release|Any CPU.ActiveCfg = Release|Any CPU 74 | {9AC2BB39-6734-4A91-98D0-D5A28FD97270}.Release|Any CPU.Build.0 = Release|Any CPU 75 | {E7F26141-34CE-428E-8AEC-BD211CE272EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 76 | {E7F26141-34CE-428E-8AEC-BD211CE272EF}.Debug|Any CPU.Build.0 = Debug|Any CPU 77 | {E7F26141-34CE-428E-8AEC-BD211CE272EF}.Release|Any CPU.ActiveCfg = Release|Any CPU 78 | {E7F26141-34CE-428E-8AEC-BD211CE272EF}.Release|Any CPU.Build.0 = Release|Any CPU 79 | {43D64D3E-CDA0-488B-ACC8-F051504B8E42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 80 | {43D64D3E-CDA0-488B-ACC8-F051504B8E42}.Debug|Any CPU.Build.0 = Debug|Any CPU 81 | {43D64D3E-CDA0-488B-ACC8-F051504B8E42}.Release|Any CPU.ActiveCfg = Release|Any CPU 82 | {43D64D3E-CDA0-488B-ACC8-F051504B8E42}.Release|Any CPU.Build.0 = Release|Any CPU 83 | {7BC01184-5F31-40E5-AE3E-65C007E9DC31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 84 | {7BC01184-5F31-40E5-AE3E-65C007E9DC31}.Debug|Any CPU.Build.0 = Debug|Any CPU 85 | {7BC01184-5F31-40E5-AE3E-65C007E9DC31}.Release|Any CPU.ActiveCfg = Release|Any CPU 86 | {7BC01184-5F31-40E5-AE3E-65C007E9DC31}.Release|Any CPU.Build.0 = Release|Any CPU 87 | {6B0E24A4-AF46-4FCB-B6F7-BF968B776140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 88 | {6B0E24A4-AF46-4FCB-B6F7-BF968B776140}.Debug|Any CPU.Build.0 = Debug|Any CPU 89 | {6B0E24A4-AF46-4FCB-B6F7-BF968B776140}.Release|Any CPU.ActiveCfg = Release|Any CPU 90 | {6B0E24A4-AF46-4FCB-B6F7-BF968B776140}.Release|Any CPU.Build.0 = Release|Any CPU 91 | {31BFBAD4-FED9-4004-882E-3EAB8798A4CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 92 | {31BFBAD4-FED9-4004-882E-3EAB8798A4CB}.Debug|Any CPU.Build.0 = Debug|Any CPU 93 | {31BFBAD4-FED9-4004-882E-3EAB8798A4CB}.Release|Any CPU.ActiveCfg = Release|Any CPU 94 | {31BFBAD4-FED9-4004-882E-3EAB8798A4CB}.Release|Any CPU.Build.0 = Release|Any CPU 95 | {283FB71D-FBFA-4502-91C2-D866ABF2194D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 96 | {283FB71D-FBFA-4502-91C2-D866ABF2194D}.Debug|Any CPU.Build.0 = Debug|Any CPU 97 | {283FB71D-FBFA-4502-91C2-D866ABF2194D}.Release|Any CPU.ActiveCfg = Release|Any CPU 98 | {283FB71D-FBFA-4502-91C2-D866ABF2194D}.Release|Any CPU.Build.0 = Release|Any CPU 99 | {CB011875-E267-4E0E-8901-BEDB433E2603}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 100 | {CB011875-E267-4E0E-8901-BEDB433E2603}.Debug|Any CPU.Build.0 = Debug|Any CPU 101 | {CB011875-E267-4E0E-8901-BEDB433E2603}.Release|Any CPU.ActiveCfg = Release|Any CPU 102 | {CB011875-E267-4E0E-8901-BEDB433E2603}.Release|Any CPU.Build.0 = Release|Any CPU 103 | {E8853DE6-584D-494E-ABC0-8BEDC937532C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 104 | {E8853DE6-584D-494E-ABC0-8BEDC937532C}.Debug|Any CPU.Build.0 = Debug|Any CPU 105 | {E8853DE6-584D-494E-ABC0-8BEDC937532C}.Release|Any CPU.ActiveCfg = Release|Any CPU 106 | {E8853DE6-584D-494E-ABC0-8BEDC937532C}.Release|Any CPU.Build.0 = Release|Any CPU 107 | {1137DDF2-FBEF-4192-B911-D3D60592D453}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 108 | {1137DDF2-FBEF-4192-B911-D3D60592D453}.Debug|Any CPU.Build.0 = Debug|Any CPU 109 | {1137DDF2-FBEF-4192-B911-D3D60592D453}.Release|Any CPU.ActiveCfg = Release|Any CPU 110 | {1137DDF2-FBEF-4192-B911-D3D60592D453}.Release|Any CPU.Build.0 = Release|Any CPU 111 | {13B70208-AE21-44FE-98A6-FA42852445A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 112 | {13B70208-AE21-44FE-98A6-FA42852445A7}.Debug|Any CPU.Build.0 = Debug|Any CPU 113 | {13B70208-AE21-44FE-98A6-FA42852445A7}.Release|Any CPU.ActiveCfg = Release|Any CPU 114 | {13B70208-AE21-44FE-98A6-FA42852445A7}.Release|Any CPU.Build.0 = Release|Any CPU 115 | {492CF046-3282-4097-9B5C-C740706A8CBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 116 | {492CF046-3282-4097-9B5C-C740706A8CBE}.Debug|Any CPU.Build.0 = Debug|Any CPU 117 | {492CF046-3282-4097-9B5C-C740706A8CBE}.Release|Any CPU.ActiveCfg = Release|Any CPU 118 | {492CF046-3282-4097-9B5C-C740706A8CBE}.Release|Any CPU.Build.0 = Release|Any CPU 119 | EndGlobalSection 120 | GlobalSection(SolutionProperties) = preSolution 121 | HideSolutionNode = FALSE 122 | EndGlobalSection 123 | GlobalSection(NestedProjects) = preSolution 124 | {6C8E9658-82D0-400E-AFAE-606B237F521D} = {5E0BADCE-71FC-4950-ACFE-1340DD435402} 125 | {7CAB5C8E-DC76-422F-9C4F-F02FC155526B} = {5E0BADCE-71FC-4950-ACFE-1340DD435402} 126 | {342CDFFB-A85F-4081-8B50-88A34243DFAD} = {C7655257-3E2F-40BD-B738-C445652C83A5} 127 | {96708E9A-2B62-49A8-904C-26E142EDD6E3} = {C7655257-3E2F-40BD-B738-C445652C83A5} 128 | {9AC2BB39-6734-4A91-98D0-D5A28FD97270} = {5E0BADCE-71FC-4950-ACFE-1340DD435402} 129 | {E7F26141-34CE-428E-8AEC-BD211CE272EF} = {5E0BADCE-71FC-4950-ACFE-1340DD435402} 130 | {43D64D3E-CDA0-488B-ACC8-F051504B8E42} = {5E0BADCE-71FC-4950-ACFE-1340DD435402} 131 | {7BC01184-5F31-40E5-AE3E-65C007E9DC31} = {5E0BADCE-71FC-4950-ACFE-1340DD435402} 132 | {6B0E24A4-AF46-4FCB-B6F7-BF968B776140} = {C7655257-3E2F-40BD-B738-C445652C83A5} 133 | {C7655257-3E2F-40BD-B738-C445652C83A5} = {5E0BADCE-71FC-4950-ACFE-1340DD435402} 134 | {31BFBAD4-FED9-4004-882E-3EAB8798A4CB} = {E813619C-7632-46E8-B571-30D642D13D99} 135 | {283FB71D-FBFA-4502-91C2-D866ABF2194D} = {E813619C-7632-46E8-B571-30D642D13D99} 136 | {CB011875-E267-4E0E-8901-BEDB433E2603} = {E813619C-7632-46E8-B571-30D642D13D99} 137 | {E8853DE6-584D-494E-ABC0-8BEDC937532C} = {E813619C-7632-46E8-B571-30D642D13D99} 138 | {13B70208-AE21-44FE-98A6-FA42852445A7} = {5E0BADCE-71FC-4950-ACFE-1340DD435402} 139 | {492CF046-3282-4097-9B5C-C740706A8CBE} = {5E0BADCE-71FC-4950-ACFE-1340DD435402} 140 | EndGlobalSection 141 | GlobalSection(ExtensibilityGlobals) = postSolution 142 | SolutionGuid = {83624657-71E1-4209-8A42-056014F3D0C5} 143 | EndGlobalSection 144 | EndGlobal 145 | -------------------------------------------------------------------------------- /icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sd797994/Oxygen/70e4b7cf4335cbd7bd5d30fb2fe43c3eda558e59/icon/icon.png -------------------------------------------------------------------------------- /k8s/.dockerignore: -------------------------------------------------------------------------------- 1 | *.yaml 2 | *.bat 3 | *.sh -------------------------------------------------------------------------------- /k8s/ApiGateway.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 2 | WORKDIR /app 3 | COPY ApiGateWay/. . 4 | CMD ["dotnet","ApiGateWay.dll"] -------------------------------------------------------------------------------- /k8s/ClientSample.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 2 | WORKDIR /app 3 | COPY ClientSample/. . 4 | CMD ["dotnet","Server.ClientSample.dll"] -------------------------------------------------------------------------------- /k8s/ServerSample.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 2 | WORKDIR /app 3 | COPY ServerSample/. . 4 | CMD ["dotnet","Server.Sample.dll"] -------------------------------------------------------------------------------- /k8s/build.bat: -------------------------------------------------------------------------------- 1 | cd ../test/Server.Sample 2 | dotnet publish -c debug -o ../../k8s/ServerSample 3 | cd ../Client.Sample 4 | dotnet publish -c debug -o ../../k8s/ClientSample 5 | cd ../../ApiGateWay 6 | dotnet publish -c debug -o ../k8s/ApiGateWay 7 | cd ../k8s 8 | docker build -f E:/dotnet_project/Oxygen/k8s/ServerSample.Dockerfile . -t oxygen-service-sample:latest 9 | docker build -f E:/dotnet_project/Oxygen/k8s/ClientSample.Dockerfile . -t oxygen-client-sample:latest 10 | docker build -f E:/dotnet_project/Oxygen/k8s/ApiGateway.Dockerfile . -t oxygen-apigateway-sample:latest -------------------------------------------------------------------------------- /k8s/k8sSample.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: serversample-dep 5 | labels: 6 | app: serversample-dep 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: serversample-pod 12 | minReadySeconds: 5 13 | strategy: 14 | type: RollingUpdate 15 | rollingUpdate: 16 | maxUnavailable: 1 17 | maxSurge: 1 18 | template: 19 | metadata: 20 | labels: 21 | app: serversample-pod 22 | annotations: 23 | dapr.io/enabled: "true" 24 | dapr.io/id: "serversample" 25 | dapr.io/port: "80" 26 | spec: 27 | containers: 28 | - name: web 29 | image: oxygen-service-sample:latest 30 | imagePullPolicy: Never 31 | ports: 32 | - containerPort: 80 33 | volumeMounts: 34 | - mountPath: /app 35 | name: v1 36 | - mountPath: /remote_debugger:rw 37 | name: v2 38 | - mountPath: /root/.vs-debugger 39 | name: v3 40 | volumes: 41 | - name: v1 42 | hostPath: 43 | path: /host_mnt/e/dotnet_project/Oxygen/k8s/ServerSample 44 | - name: v2 45 | hostPath: 46 | path: /host_mnt/c/Users/Administrator/vsdbg/vs2017u5 47 | - name: v3 48 | hostPath: 49 | path: /host_mnt/e/dockerdebugger/.vs-debugger 50 | --- 51 | apiVersion: v1 52 | kind: Service 53 | metadata: 54 | name: serversample 55 | spec: 56 | selector: 57 | app: serversample-pod 58 | ports: 59 | - protocol: TCP 60 | port: 80 61 | targetPort: 80 62 | --- 63 | apiVersion: apps/v1 64 | kind: Deployment 65 | metadata: 66 | name: apigateway-dep 67 | labels: 68 | app: apigateway-dep 69 | spec: 70 | replicas: 1 71 | selector: 72 | matchLabels: 73 | app: apigateway-pod 74 | minReadySeconds: 5 75 | strategy: 76 | type: RollingUpdate 77 | rollingUpdate: 78 | maxUnavailable: 1 79 | maxSurge: 1 80 | template: 81 | metadata: 82 | labels: 83 | app: apigateway-pod 84 | annotations: 85 | dapr.io/enabled: "true" 86 | dapr.io/id: "ApiGateWay" 87 | dapr.io/port: "80" 88 | spec: 89 | containers: 90 | - name: web 91 | image: oxygen-apigateway-sample:latest 92 | imagePullPolicy: Never 93 | ports: 94 | - containerPort: 80 95 | volumeMounts: 96 | - mountPath: /app 97 | name: v1 98 | - mountPath: /remote_debugger:rw 99 | name: v2 100 | - mountPath: /root/.vs-debugger 101 | name: v3 102 | volumes: 103 | - name: v1 104 | hostPath: 105 | path: /host_mnt/e/dotnet_project/Oxygen/k8s/ApiGateWay 106 | - name: v2 107 | hostPath: 108 | path: /host_mnt/c/Users/Administrator/vsdbg/vs2017u5 109 | - name: v3 110 | hostPath: 111 | path: /host_mnt/e/dockerdebugger/.vs-debugger 112 | --- 113 | apiVersion: v1 114 | kind: Service 115 | metadata: 116 | name: apigateway-svr 117 | spec: 118 | selector: 119 | app: apigateway-pod 120 | ports: 121 | - protocol: TCP 122 | port: 80 123 | targetPort: 80 124 | nodePort: 30000 125 | type: NodePort -------------------------------------------------------------------------------- /k8s/start-istio.sh: -------------------------------------------------------------------------------- 1 | istioctl kube-inject -f k8sSample.yaml | kubectl apply -f - -------------------------------------------------------------------------------- /k8s/start.bat: -------------------------------------------------------------------------------- 1 | kubectl apply -f k8sSample.yaml -------------------------------------------------------------------------------- /k8s/start.sh: -------------------------------------------------------------------------------- 1 | kubectl create -f k8sSample.yaml -------------------------------------------------------------------------------- /k8s/stop.bat: -------------------------------------------------------------------------------- 1 | kubectl delete -f k8sSample.yaml -------------------------------------------------------------------------------- /k8s/stop.sh: -------------------------------------------------------------------------------- 1 | kubectl delete -f k8sSample.yaml -------------------------------------------------------------------------------- /nugetpack/License/License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 sd797994 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. -------------------------------------------------------------------------------- /nugetpack/Oxygen.CsharpClientAgent.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Oxygen.CsharpClientAgent 5 | 0.3.4 6 | Oxygen.CsharpClientAgent 7 | yuping zeng 8 | yuping zeng 9 | https://www.github.com/sd797994/oxygen 10 | https://www.github.com/sd797994/oxygen 11 | https://www.github.com/sd797994/oxygen 12 | false 13 | a sample rpc framework from .net core + k8s 14 | Summary of changes made in this release of the package. 15 | Copyright 2019 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /nugetpack/Oxygen.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Oxygen 5 | 0.3.4 6 | Oxygen 7 | yuping zeng 8 | yuping zeng 9 | https://www.github.com/sd797994/oxygen 10 | https://www.github.com/sd797994/oxygen 11 | https://www.github.com/sd797994/oxygen 12 | false 13 | a sample rpc framework from .net core + k8s 14 | Summary of changes made in this release of the package. 15 | Copyright 2019 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /nugetpack/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sd797994/Oxygen/70e4b7cf4335cbd7bd5d30fb2fe43c3eda558e59/nugetpack/images/icon.png -------------------------------------------------------------------------------- /nugetpack/nugetpack.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sd797994/Oxygen/70e4b7cf4335cbd7bd5d30fb2fe43c3eda558e59/nugetpack/nugetpack.exe -------------------------------------------------------------------------------- /nugetpack/pack.bat: -------------------------------------------------------------------------------- 1 | cd ..\src\Oxygen 2 | dotnet build -c release -o ..\..\nugetpack\lib 3 | cd ..\..\nugetpack\lib\ 4 | del /a /f /s /q "*.pdb" "*.json" 5 | copy Oxygen.CsharpClientAgent.dll ..\lib2 6 | cd ..\ 7 | .\nugetpack.exe 8 | nuget pack Oxygen.nuspec 9 | nuget pack Oxygen.CsharpClientAgent.nuspec -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![Image text](https://raw.githubusercontent.com/sd797994/Oxygen/dev-k8s/icon/icon.png) 2 | 3 | Oxygen 是一款基于.netcore3.1 的针对k8s平台的分布式服务框架,目前支持基于istio的服务网格实现微服务,这里可以下载[简易案例][1] 4 | ## 系统要求 5 | 6 | * window10 / centos7.5 + 7 | 8 | * docker for windows 19.03 + / linux docker-ce 9 | 10 | * kubernetes 1.14.8 + (docker for windows) /linux kubernetes 11 | * dotnetcore3.1 + vs2019 + nuget 12 | ## 特色 13 | * 基于dotnetty/kestrel实现的高性能远程过程调用代理(RPC),支持多种主流协议(tcp/http1.1/http2.0) 14 | * 基于Messagepack实现的类型序列化/反序列化 15 | * 采用k8s自带的dns服务实现服务注册发现,集成istio默认的追踪头实现分布式链路追踪以及自定义追踪头实现金丝雀灰度发布等等(必须选择http协议) 16 | ## 安装 17 | * 创建两个默认的控制台程序,并通过nuget安装oxygen: 18 | 19 | ```bash 20 | Install-Package Oxygen -Version 0.1.4(可登录nuget获取最新版本) 21 | ``` 22 | 23 | * 在根目录创建oxygen.json并注入oxygen需要的端口配置(用于服务间通讯) 24 | 25 | ```bash 26 | { 27 | "Oxygen": { 28 | "ServerPort": 80, //服务间通讯端口号,在k8s环境下必须所有容器一致,以确保调用 29 | "ProtocolType": 2, //通讯协议目前支持tcp/http1.1/http2.0分别对应0/1/2 30 | "CustomHeader": "canaryver" //自定义追踪头,可用于实现金丝雀发布 31 | } 32 | } 33 | ``` 34 | * 在Program.cs引入下面的通用主机代码 35 | ```bash 36 | 客户端(多用于网关等不需要提供rpc服务的节点): 37 | private static IConfiguration Configuration { get; set; } 38 | static async Task Main(string[] args) 39 | { 40 | await CreateDefaultHost(args).Build().RunAsync(); 41 | } 42 | static IHostBuilder CreateDefaultHost(string[] args) => new HostBuilder() 43 | .ConfigureAppConfiguration((hostContext, config) => 44 | { 45 | config.SetBasePath(Directory.GetCurrentDirectory()); 46 | //获取oxygen配置节 47 | config.AddJsonFile("oxygen.json"); 48 | Configuration = config.Build(); 49 | }) 50 | .ConfigureContainer(builder => 51 | { 52 | //注入oxygen依赖 53 | builder.RegisterOxygen(); 54 | }) 55 | .ConfigureServices(services => 56 | { 57 | //注册oxygen配置节 58 | services.ConfigureOxygen(Configuration); 59 | services.AddLogging(configure => 60 | { 61 | configure.AddConsole(); 62 | }); 63 | services.AddHttpClient(); 64 | }) 65 | .UseServiceProviderFactory(new AutofacServiceProviderFactory()); 66 | 服务端(提供rpc服务): 67 | static IHostBuilder CreateDefaultHost(string[] args) => new HostBuilder() 68 | .ConfigureAppConfiguration((hostContext, config) => 69 | { 70 | config.SetBasePath(Directory.GetCurrentDirectory()); 71 | //获取oxygen配置节 72 | config.AddJsonFile("oxygen.json"); 73 | Configuration = config.Build(); 74 | }) 75 | .ConfigureContainer(builder => 76 | { 77 | //注入oxygen依赖 78 | builder.RegisterOxygen(); 79 | //注入本地业务依赖 80 | builder.RegisterType().As().InstancePerDependency(); 81 | }) 82 | //注册成为oxygen服务节点 83 | .UseOxygenService((context, services) => { 84 | //注册oxygen配置 85 | services.ConfigureOxygen(Configuration); 86 | services.AddLogging(configure => 87 | { 88 | configure.AddConsole(); 89 | }); 90 | services.AddAutofac(); 91 | }) 92 | .UseServiceProviderFactory(new AutofacServiceProviderFactory()); 93 | ``` 94 | 95 | * 创建一个接口项目,Oxygen.CsharpClientAgent 96 | ```bash 97 | Install-Package Oxygen.CsharpClientAgent -Version 0.0.2(可登录nuget获取最新版本) 98 | ``` 99 | * 定义对应的服务接口,并打上暴露rpc服务的标记 100 | ```bash 101 | [RemoteService("myserver")]//暴露到k8s的服务名 102 | interface IXXXServices 103 | Task GetResult(RequestModel model); 104 | ``` 105 | * 创建一个服务项目,引用上面的接口项目并实现它 106 | ```bash 107 | public class XXXServices : IXXXServices 108 | public asyn Task GetResult(RequestModel model) 109 | { 110 | await Task.Dealy(0); //推荐使用async/await异步编程 111 | return new ResponseDto(){ Message = $"hello world,{model.Name}" }; 112 | } 113 | ``` 114 | * 客户端只需要引入接口项目,服务端则需要引入接口项目以及对应的实现,在客户端项目里使用如下方式即可远程调用服务 115 | 116 | ```bash 117 | private readonly IServerProxyFactory _proxyFactory; 118 | public 构造函数(IServerProxyFactory proxyFactory) 119 | { 120 | _proxyFactory = proxyFactory; 121 | } 122 | public async Task StartAsync(CancellationToken cancellationToken) 123 | { 124 | var xxxserver = _proxyFactory.CreateProxy();//直接通过引用接口类型创建代理 125 | var result = await xxxserver.GetResult(new RequestModel() { Name = "admin" }); 126 | //var remoteProxy = _proxyFactory.CreateProxy("/api/myserver/XXXServices/GetResult"); //通过url的方式创建代理 127 | //var result = await remoteProxy.SendAsync(new { Name = "admin" }); 128 | } 129 | ``` 130 | * 项目整体服务注册基于k8s平台,所以需要提前准备k8s环境,可参考[简易案例][1] 131 | ## License 132 | 133 | MIT 134 | 135 | [1]: https://github.com/sd797994/Oxygen-EshopSample "简易案例" 136 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/CustomerIp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | 5 | namespace Oxygen.CommonTool 6 | { 7 | /// 8 | /// 客户端信息 9 | /// 10 | public class CustomerInfo 11 | { 12 | /// 13 | /// 客户端IP 14 | /// 15 | public IPEndPoint Ip { get; set; } 16 | /// 17 | /// 客户端追踪http头 18 | /// 19 | public Dictionary TraceHeaders { get; private set; } 20 | 21 | public void SetTraceHeader(Dictionary traceHeaders) 22 | { 23 | TraceHeaders = traceHeaders; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/EnumMeshType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Oxygen.CommonTool 6 | { 7 | public enum EnumMeshType 8 | { 9 | None = 0, 10 | Istio = 1, 11 | Dapr = 2 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/EnumProtocolType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Oxygen.CommonTool 6 | { 7 | public enum EnumProtocolType 8 | { 9 | TCP = 0, 10 | HTTP11 = 1, 11 | HTTP2 = 2 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/GlobalCommon.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.CommonTool.Logger; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.NetworkInformation; 8 | using System.Security.Cryptography; 9 | using System.Text; 10 | 11 | namespace Oxygen.CommonTool 12 | { 13 | /// 14 | /// 全局通用工具类 15 | /// 16 | public class GlobalCommon 17 | { 18 | /// 19 | /// 获取可用端口号 20 | /// 21 | /// 22 | public static int GetFreePort(params int[] ingorePort) 23 | { 24 | //检查指定端口是否已用 25 | bool PortIsAvailable(int port) 26 | { 27 | bool isAvailable = true; 28 | IList portUsed = PortIsUsed(); 29 | foreach (int p in portUsed) 30 | { 31 | if (p == port) 32 | { 33 | isAvailable = false; break; 34 | } 35 | } 36 | return isAvailable; 37 | } 38 | //获取操作系统已用的端口号 39 | IList PortIsUsed() 40 | { 41 | //获取本地计算机的网络连接和通信统计数据的信息 42 | IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); 43 | //返回本地计算机上的所有Tcp监听程序 44 | IPEndPoint[] ipsTCP = ipGlobalProperties.GetActiveTcpListeners(); 45 | //返回本地计算机上的所有UDP监听程序 46 | IPEndPoint[] ipsUDP = ipGlobalProperties.GetActiveUdpListeners(); 47 | //返回本地计算机上的Internet协议版本4(IPV4 传输控制协议(TCP)连接的信息。 48 | TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections(); 49 | IList allPorts = new ArrayList(); 50 | foreach (IPEndPoint ep in ipsTCP) allPorts.Add(ep.Port); 51 | foreach (IPEndPoint ep in ipsUDP) allPorts.Add(ep.Port); 52 | foreach (TcpConnectionInformation conn in tcpConnInfoArray) allPorts.Add(conn.LocalEndPoint.Port); 53 | return allPorts; 54 | } 55 | int MAX_PORT = 65535; //系统tcp/udp端口数最大是65535 56 | int BEGIN_PORT = 5000;//从这个端口开始检测 57 | var usePort = new List(); 58 | while (true) 59 | { 60 | var randomPort = new Random(Guid.NewGuid().GetHashCode()).Next(BEGIN_PORT, MAX_PORT); 61 | if (ingorePort != null && ingorePort.ToList().Contains(randomPort)) 62 | { 63 | usePort.Add(randomPort); 64 | } 65 | else if (PortIsAvailable(randomPort)) 66 | { 67 | return randomPort; 68 | } 69 | else 70 | { 71 | usePort.Add(randomPort); 72 | } 73 | if (usePort.Count == MAX_PORT - BEGIN_PORT) 74 | { 75 | break; 76 | } 77 | } 78 | throw new Exception("没有找到可用端口号"); 79 | } 80 | /// 81 | /// 获取本机在局域网内的ip 82 | /// 83 | /// 84 | public static IPAddress GetMachineIp() 85 | { 86 | IPAddress result = default(IPAddress); 87 | NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); 88 | foreach (NetworkInterface adapter in nics) 89 | { 90 | if (adapter.NetworkInterfaceType == NetworkInterfaceType.Ethernet) 91 | { 92 | IPInterfaceProperties ipxx = adapter.GetIPProperties(); 93 | UnicastIPAddressInformationCollection ipCollection = ipxx.UnicastAddresses; 94 | foreach (UnicastIPAddressInformation ipadd in ipCollection) 95 | { 96 | if (ipadd.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) 97 | { 98 | result = ipadd.Address; 99 | } 100 | } 101 | } 102 | } 103 | return result; 104 | } 105 | public static string SHA256Encrypt(string StrIn) 106 | { 107 | var tmpByte = Encoding.UTF8.GetBytes(StrIn); 108 | var EncryptBytes = new SHA256Managed().ComputeHash(tmpByte); 109 | return Convert.ToBase64String(EncryptBytes); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/Logger/IOxygenLogger.cs: -------------------------------------------------------------------------------- 1 | namespace Oxygen.CommonTool.Logger 2 | { 3 | /// 4 | /// 日志接口 5 | /// 6 | public interface IOxygenLogger 7 | { 8 | /// 9 | /// 异常日志 10 | /// 11 | /// 12 | void LogError(string message); 13 | /// 14 | /// 信息日志 15 | /// 16 | /// 17 | void LogInfo(string message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/Logger/OxygenConsoleLogger.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Microsoft.Extensions.Logging.Console; 3 | using System; 4 | 5 | namespace Oxygen.CommonTool.Logger 6 | { 7 | /// 8 | /// 控制台日志 9 | /// 10 | public class OxygenConsoleLogger: IOxygenLogger 11 | { 12 | 13 | private readonly ILogger _logger; 14 | public OxygenConsoleLogger(ILogger logger 15 | ) 16 | { 17 | _logger = logger; 18 | } 19 | /// 20 | /// 普通信息 21 | /// 22 | /// 23 | public void LogInfo(string message) 24 | { 25 | _logger.LogInformation($"|{DateTime.Now}|OXYGEN_INFO|{message}"); 26 | } 27 | /// 28 | /// 异常信息 29 | /// 30 | /// 31 | public void LogError(string message) 32 | { 33 | _logger.LogError($"|{DateTime.Now}|OXYGEN_ERROR|{message}"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/Module.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | 3 | namespace Oxygen.CommonTool 4 | { 5 | public class Module : Autofac.Module 6 | { 7 | protected override void Load(ContainerBuilder builder) 8 | { 9 | builder.RegisterAssemblyTypes(ThisAssembly) 10 | .AsImplementedInterfaces() 11 | .InstancePerLifetimeScope(); 12 | //注入scope作用域的CustomerInfo用于传递客户端请求信息 13 | builder.RegisterType().As().InstancePerLifetimeScope(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/Oxygen.CommonTool.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 0.0.4.0 6 | 0.0.4.0 7 | 0.0.4.0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/OxygenIocContainer.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading; 6 | 7 | namespace Oxygen.CommonTool 8 | { 9 | public class OxygenIocContainer 10 | { 11 | private static AsyncLocal Current = new AsyncLocal(); 12 | public static void BuilderIocContainer(ILifetimeScope container) 13 | { 14 | Current.Value = container; 15 | } 16 | public static void DisposeIocContainer() 17 | { 18 | Current.Value = null; 19 | } 20 | public static T Resolve() 21 | { 22 | try 23 | { 24 | if (Current == null) 25 | { 26 | throw new Exception("IOC实例化出错!"); 27 | } 28 | else 29 | { 30 | return Current.Value.Resolve(); 31 | } 32 | } 33 | catch (Exception ex) 34 | { 35 | throw ex; 36 | } 37 | } 38 | public static object Resolve(Type type) 39 | { 40 | try 41 | { 42 | if (Current == null) 43 | { 44 | throw new Exception("IOC实例化出错!"); 45 | } 46 | else 47 | { 48 | return Current.Value.Resolve(type); 49 | } 50 | } 51 | catch (Exception ex) 52 | { 53 | throw ex; 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/OxygenSetting.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | 6 | namespace Oxygen.CommonTool 7 | { 8 | /// 9 | /// Oxygen本地配置 10 | /// 11 | public class OxygenSetting 12 | { 13 | public OxygenSetting(IConfiguration configuration) 14 | { 15 | if (!string.IsNullOrWhiteSpace(configuration["Oxygen:ServerPort"])) 16 | { 17 | ServerPort = int.Parse(configuration["Oxygen:ServerPort"]); 18 | } 19 | else 20 | { 21 | ServerPort = 80; 22 | } 23 | var mesh = new MeshInfo(configuration); 24 | ProtocolType = mesh.Info.ProtocolType; 25 | MeshType = mesh.Info.MeshType; 26 | switch (MeshType) 27 | { 28 | case EnumMeshType.None: 29 | default: 30 | break; 31 | case EnumMeshType.Dapr: 32 | OpenActor = ((DaprMeshInfo)mesh.Info).OpenActor; 33 | break; 34 | case EnumMeshType.Istio: 35 | CustomHeader = ((IstioMeshInfo)mesh.Info).CustomHeader; 36 | break; 37 | } 38 | } 39 | /// 40 | /// 服务端口 41 | /// 42 | public static int ServerPort { get; set; } = 80; 43 | /// 44 | /// 协议0 = tcp 1 = http1.1 2 = http/2 45 | /// 46 | public static EnumProtocolType ProtocolType { get; set; } = EnumProtocolType.TCP; 47 | /// 48 | /// 追踪头(金丝雀) 49 | /// 50 | public static List CustomHeader { get; set; } = new List(); 51 | /// 52 | /// 网格类型none istio dapr 53 | /// 54 | public static EnumMeshType MeshType { get; set; } = EnumMeshType.None; 55 | /// 56 | /// 开启actor(仅限于dapr网格) 57 | /// 58 | public static bool OpenActor { get; set; } = false; 59 | } 60 | class MeshInfo 61 | { 62 | internal MeshInfo(IConfiguration configuration) 63 | { 64 | if (bool.TryParse(configuration["Oxygen:Mesh:None:Open"], out bool noneOpen) && noneOpen) 65 | { 66 | Info = new NoneMeshInfo(configuration); 67 | } 68 | else if (bool.TryParse(configuration["Oxygen:Mesh:Dapr:Open"], out bool daprOpen) && daprOpen) 69 | { 70 | Info = new DaprMeshInfo(configuration); 71 | } 72 | else if (bool.TryParse(configuration["Oxygen:Mesh:Istio:Open"], out bool istioOpen) && istioOpen) 73 | { 74 | Info = new IstioMeshInfo(configuration); 75 | } 76 | } 77 | internal MeshBase Info { get; set; } 78 | } 79 | class MeshBase 80 | { 81 | internal EnumMeshType MeshType; 82 | internal EnumProtocolType ProtocolType; 83 | } 84 | class NoneMeshInfo : MeshBase 85 | { 86 | internal NoneMeshInfo(IConfiguration configuration) 87 | { 88 | base.MeshType = EnumMeshType.None; 89 | if (int.TryParse(configuration["Oxygen:Mesh:None:ProtocolType"], out int Protocol)) 90 | { 91 | base.ProtocolType = (EnumProtocolType)Protocol; 92 | } 93 | } 94 | } 95 | class DaprMeshInfo : MeshBase 96 | { 97 | internal DaprMeshInfo(IConfiguration configuration) 98 | { 99 | base.MeshType = EnumMeshType.Dapr; 100 | if (int.TryParse(configuration["Oxygen:Mesh:Dapr:ProtocolType"], out int Protocol)) 101 | { 102 | base.ProtocolType = (EnumProtocolType)Protocol; 103 | } 104 | if (bool.TryParse(configuration["Oxygen:Mesh:Dapr:OpenActor"], out bool OpenActor)) 105 | { 106 | this.OpenActor = OpenActor; 107 | } 108 | } 109 | internal bool OpenActor { get; set; } 110 | } 111 | class IstioMeshInfo : MeshBase 112 | { 113 | internal IstioMeshInfo(IConfiguration configuration) 114 | { 115 | base.MeshType = EnumMeshType.Istio; 116 | if (int.TryParse(configuration["Oxygen:Mesh:Istio:ProtocolType"], out int Protocol)) 117 | { 118 | base.ProtocolType = (EnumProtocolType)Protocol; 119 | } 120 | if (!string.IsNullOrWhiteSpace(configuration["Oxygen:Mesh:Istio:CustomHeader"])) 121 | { 122 | CustomHeader = configuration["Oxygen:Mesh:Istio:CustomHeader"].Split(',').ToList(); 123 | } 124 | } 125 | internal List CustomHeader; 126 | } 127 | } -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/RpcInterfaceType.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyModel; 2 | using Oxygen.CsharpClientAgent; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Runtime.Loader; 8 | using System.Text; 9 | 10 | namespace Oxygen.CommonTool 11 | { 12 | public class RpcInterfaceType 13 | { 14 | public static Lazy> Types = new Lazy>(() => 15 | { 16 | var list = new List(); 17 | var deps = DependencyContext.Default; 18 | var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包 19 | foreach (var lib in libs) 20 | { 21 | try 22 | { 23 | var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); 24 | list.Add(assembly); 25 | } 26 | catch (Exception) 27 | { 28 | // ignored 29 | } 30 | } 31 | return list.SelectMany(a => a.GetTypes().Where(t => t.GetCustomAttributes(typeof(RemoteServiceAttribute)).Any() && t.IsInterface)).ToArray(); 32 | }); 33 | public static Lazy> RemoteTypes = new Lazy>(() => 34 | { 35 | var list = new List(); 36 | var deps = DependencyContext.Default; 37 | var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包 38 | foreach (var lib in libs) 39 | { 40 | try 41 | { 42 | var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); 43 | list.Add(assembly); 44 | } 45 | catch (Exception) 46 | { 47 | // ignored 48 | } 49 | } 50 | var interfaces = list.SelectMany(a => a.GetTypes().Where(t => t.GetCustomAttributes(typeof(RemoteServiceAttribute)).Any() && t.IsInterface)).ToArray(); 51 | var localclass = list.SelectMany(x => x.GetTypes().Where(t => t.GetInterfaces().Any() && interfaces.Contains(t.GetInterfaces().FirstOrDefault()))).Select(x => x.GetInterfaces().FirstOrDefault()).ToList(); 52 | return interfaces.Where(x => !localclass.Contains(x)); 53 | }); 54 | public static Lazy> LocalTypes = new Lazy>(() => 55 | { 56 | var list = new List(); 57 | var deps = DependencyContext.Default; 58 | var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包 59 | foreach (var lib in libs) 60 | { 61 | try 62 | { 63 | var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); 64 | list.Add(assembly); 65 | } 66 | catch (Exception) 67 | { 68 | // ignored 69 | } 70 | } 71 | var interfaces = list.SelectMany(a => a.GetTypes().Where(t => t.GetCustomAttributes(typeof(RemoteServiceAttribute)).Any() && t.IsInterface)).ToArray(); 72 | return list.SelectMany(x => x.GetTypes().Where(t => t.GetInterfaces().Any() && interfaces.Contains(t.GetInterfaces().FirstOrDefault()))).Select(x => x.GetInterfaces().FirstOrDefault()).ToList(); 73 | }); 74 | 75 | 76 | public static Lazy> ActorTypes = new Lazy>(() => 77 | { 78 | var result = new List<(Type interfaceType, Type? classType, bool autoSave)>(); 79 | var list = new List(); 80 | var deps = DependencyContext.Default; 81 | var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包 82 | foreach (var lib in libs) 83 | { 84 | try 85 | { 86 | var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); 87 | list.Add(assembly); 88 | } 89 | catch (Exception) 90 | { 91 | // ignored 92 | } 93 | } 94 | var interfaces = list.SelectMany(a => a.GetTypes().Where(t => t.GetCustomAttributes(typeof(ActorServiceAttribute)).Any() && t.IsInterface)).ToList(); 95 | var actortypes = list.SelectMany(x => x.GetTypes().Where(t => t.GetInterfaces().Any() && interfaces.Contains(t.GetInterfaces().FirstOrDefault()))).ToList(); 96 | if (interfaces.Any()) 97 | { 98 | interfaces.ForEach(x => 99 | { 100 | Type? _class = actortypes.FirstOrDefault(y => y.GetInterface(x.Name) != null) ?? null; 101 | var autosave = x.GetCustomAttribute().AutoSave; 102 | result.Add((x, _class, autosave)); 103 | }); 104 | } 105 | return result; 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/TaskExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Oxygen.CommonTool 5 | { 6 | /// 7 | /// task类型扩展 8 | /// 9 | public static class TaskExtension 10 | { 11 | /// 12 | /// 超时等待回调 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | public static async Task WaitAsync(this TaskCompletionSource tcs, CancellationToken ctok) 19 | { 20 | CancellationTokenSource linkedCts = null; 21 | try 22 | { 23 | var cts = new CancellationTokenSource(); 24 | linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, ctok); 25 | var exitTok = linkedCts.Token; 26 | async Task ListenForCancelTaskFnc() 27 | { 28 | await Task.Delay(-1, exitTok).ConfigureAwait(false); 29 | } 30 | var cancelTask = ListenForCancelTaskFnc(); 31 | await Task.WhenAny(tcs.Task, cancelTask).ConfigureAwait(false); 32 | cts.Cancel(); 33 | } 34 | finally 35 | { 36 | linkedCts?.Dispose(); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Oxygen.CommonTool/TraceHeaderHelper.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using DotNetty.Codecs.Http; 3 | using DotNetty.Common.Utilities; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Primitives; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Oxygen.CommonTool 12 | { 13 | public class TraceHeaderHelper 14 | { 15 | static string[] traceHeader = { "x-request-id", "x-b3-traceid", "x-b3-spanid", "x-b3-parentspanid", "x-b3-sampled", "x-b3-flags", "x-ot-span-context" }; 16 | public static Dictionary GetTraceHeaders(HttpHeaders headers) 17 | { 18 | var result = new Dictionary(); 19 | foreach (var trace in traceHeader) 20 | { 21 | if (headers.TryGetAsString(new AsciiString(trace), out string val)) 22 | { 23 | result.Add(trace, val); 24 | } 25 | } 26 | foreach(var customer in OxygenSetting.CustomHeader) 27 | { 28 | if (headers.TryGetAsString(new AsciiString(customer), out string val)) 29 | { 30 | result.Add(customer, val); 31 | } 32 | } 33 | return result; 34 | } 35 | public static Dictionary GetTraceHeaders(IHeaderDictionary headers) 36 | { 37 | var result = new Dictionary(); 38 | foreach (var trace in traceHeader) 39 | { 40 | if (headers.TryGetValue(trace, out StringValues val)) 41 | { 42 | result.Add(trace, val.ToString()); 43 | } 44 | } 45 | foreach (var customer in OxygenSetting.CustomHeader) 46 | { 47 | if (headers.TryGetValue(customer, out StringValues val)) 48 | { 49 | result.Add(customer, val); 50 | } 51 | } 52 | return result; 53 | } 54 | 55 | 56 | public static void BuildTraceHeader(HttpHeaders headers, Dictionary traceObj = null) 57 | { 58 | if (traceObj != null && traceObj.Count > 0) 59 | { 60 | foreach (var obj in traceObj) 61 | { 62 | headers.Set(new AsciiString(obj.Key), obj.Value); 63 | } 64 | } 65 | } 66 | public static void BuildTraceHeader(System.Net.Http.Headers.HttpHeaders headers, Dictionary traceObj = null) 67 | { 68 | if (traceObj != null && traceObj.Count > 0) 69 | { 70 | foreach (var obj in traceObj) 71 | { 72 | headers.Add(obj.Key, obj.Value); 73 | } 74 | } 75 | } 76 | } 77 | /// 78 | /// 追踪头中间件 79 | /// 80 | public class RequestMiddleware 81 | { 82 | private readonly RequestDelegate _next; 83 | private readonly ILifetimeScope container; 84 | public RequestMiddleware( 85 | RequestDelegate next, ILifetimeScope container) 86 | { 87 | _next = next; 88 | this.container = container; 89 | } 90 | 91 | public async Task Invoke(HttpContext httpContext) 92 | { 93 | //每次请求将重新初始化全局容器确保容器唯一 94 | OxygenIocContainer.BuilderIocContainer(container); 95 | //为客户端信息添加追踪头 96 | OxygenIocContainer.Resolve().SetTraceHeader(TraceHeaderHelper.GetTraceHeaders(httpContext.Request.Headers)); 97 | await _next(httpContext); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Oxygen.CsharpClientAgent/ActorServiceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | 7 | namespace Oxygen.CsharpClientAgent 8 | { 9 | /// 10 | /// actor服务标记物 11 | /// 12 | [AttributeUsage(AttributeTargets.Interface)] 13 | public class ActorServiceAttribute : Attribute 14 | { 15 | /// 16 | /// 自动保存 17 | /// 18 | public bool AutoSave { get; set; } 19 | public ActorServiceAttribute(bool AutoSave) 20 | { 21 | this.AutoSave = AutoSave; 22 | } 23 | } 24 | public abstract class ActorModel 25 | { 26 | public ActorModel() 27 | { 28 | bool hashKey = false; 29 | foreach (var property in this.GetType().GetProperties()) 30 | { 31 | if (property.GetCustomAttribute() != null) 32 | { 33 | hashKey = true; 34 | break; 35 | } 36 | } 37 | if (!hashKey) 38 | throw new ArgumentException("actormodel must have one actorkey!"); 39 | } 40 | public string GetKey() { 41 | return (string)this.GetType().GetRuntimeProperties().First(x => x.GetCustomAttribute(typeof(ActorKeyAttribute)) != null).GetValue(this); 42 | } 43 | public bool SaveChanges = false; 44 | } 45 | [AttributeUsage(AttributeTargets.Property)] 46 | public class ActorKeyAttribute : Attribute 47 | { 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Oxygen.CsharpClientAgent/Oxygen.CsharpClientAgent.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 0.0.4.0 6 | 0.0.4.0 7 | 0.0.4.0 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Oxygen.CsharpClientAgent/RemoteServiceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Oxygen.CsharpClientAgent 4 | { 5 | /// 6 | /// 远程服务标记物 7 | /// 8 | [AttributeUsage(AttributeTargets.Interface)] 9 | public class RemoteServiceAttribute : Attribute 10 | { 11 | public RemoteServiceAttribute(string serverName) 12 | { 13 | ServerName = serverName; 14 | } 15 | public string ServerName { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Oxygen.DaprActorProvider/ActorServiceBuilder.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Dapr.Actors; 3 | using Dapr.Actors.AspNetCore; 4 | using Dapr.Actors.Runtime; 5 | using MediatR; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Oxygen.CommonTool; 9 | using Oxygen.CommonTool.Logger; 10 | using Oxygen.DaprActorProvider.StateManage; 11 | using System; 12 | using System.Collections.Concurrent; 13 | using System.Collections.Generic; 14 | using System.Diagnostics.Contracts; 15 | using System.Linq; 16 | using System.Linq.Expressions; 17 | using System.Reflection; 18 | using System.Text; 19 | using System.Threading.Tasks; 20 | 21 | namespace Oxygen.DaprActorProvider 22 | { 23 | public class ActorServiceBuilder 24 | { 25 | /// 26 | /// 注册actor到容器并提供中介者用于自动保存 27 | /// 28 | /// 29 | public static void RegisterActorToContainer(ContainerBuilder builder) 30 | { 31 | builder.RegisterType().As().InstancePerLifetimeScope(); 32 | builder.RegisterAssemblyTypes(typeof(ActorStateSubscriber).GetTypeInfo().Assembly).AsClosedTypesOf(typeof(INotificationHandler<>)).AsImplementedInterfaces(); 33 | builder.Register(ctx => 34 | { 35 | var c = ctx.Resolve(); 36 | return t => c.Resolve(t); 37 | }); 38 | } 39 | /// 40 | /// 通过actorruntime 注册actor 41 | /// 42 | /// 43 | public static void RegisterActorMiddleware(object builder, ILifetimeScope container) 44 | { 45 | ((IWebHostBuilder)builder).UseActors(x => RegisterActor(x, container)); 46 | } 47 | /// 48 | /// 注册所有的actor服务 49 | /// 50 | /// 51 | static void RegisterActor(ActorRuntime runtime, ILifetimeScope container) 52 | { 53 | var remote = RpcInterfaceType.ActorTypes.Value; 54 | if (remote != null && remote.Any()) 55 | { 56 | foreach (var type in remote.Where(x => x.classType != null)) 57 | { 58 | Func createFunc = (info) => new ActorService(info, (actorService, actorId) => 59 | { 60 | var actorInstance = ActorDelegateDir[type.classType](new object[] { actorService, actorId, container }) as OxygenActorBase; 61 | actorInstance.AutoSave = type.autoSave; 62 | return actorInstance; 63 | }); 64 | ActorDelegateDir.Add(type.classType, GetActorDelegate(type.classType)); 65 | typeof(ActorRuntime).GetMethod("RegisterActor").MakeGenericMethod(type.classType).Invoke(runtime, new object[] { createFunc }); 66 | } 67 | } 68 | } 69 | #region actor委托字典创建actor对象 70 | static Dictionary> ActorDelegateDir = new Dictionary>(); 71 | static Func GetActorDelegate(Type type) 72 | { 73 | var ctor = type.GetConstructors()[0]; 74 | ParameterInfo[] paramsInfo = ctor.GetParameters(); 75 | ParameterExpression param = 76 | Expression.Parameter(typeof(object[]), "args"); 77 | 78 | Expression[] argsExp = 79 | new Expression[paramsInfo.Length]; 80 | for (int i = 0; i < paramsInfo.Length; i++) 81 | { 82 | Expression index = Expression.Constant(i); 83 | Type paramType = paramsInfo[i].ParameterType; 84 | 85 | Expression paramAccessorExp = 86 | Expression.ArrayIndex(param, index); 87 | 88 | Expression paramCastExp = 89 | Expression.Convert(paramAccessorExp, paramType); 90 | 91 | argsExp[i] = paramCastExp; 92 | } 93 | NewExpression newExp = Expression.New(ctor, argsExp); 94 | var lambda = 95 | Expression.Lambda>(newExp, param); 96 | return lambda.Compile(); 97 | } 98 | #endregion 99 | } 100 | } -------------------------------------------------------------------------------- /src/Oxygen.DaprActorProvider/Module.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | 3 | namespace Oxygen.DaprActorProvider 4 | { 5 | public class Module : Autofac.Module 6 | { 7 | protected override void Load(ContainerBuilder builder) 8 | { 9 | //创建actor服务代理 10 | ActorServiceBuilder.RegisterActorToContainer(builder); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Oxygen.DaprActorProvider/Oxygen.DaprActorProvider.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Oxygen.DaprActorProvider/OxygenActor.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Dapr.Actors; 3 | using Dapr.Actors.Runtime; 4 | using MediatR; 5 | using Oxygen.DaprActorProvider.StateManage; 6 | using System.Threading.Tasks; 7 | 8 | namespace Oxygen.DaprActorProvider 9 | { 10 | public abstract class OxygenActorBase : Actor 11 | { 12 | protected object baseinstance { get; set; } 13 | public bool AutoSave { get; set; } 14 | public OxygenActorBase(ActorService actorService, ActorId actorId) 15 | : base(actorService, actorId) 16 | { 17 | 18 | } 19 | protected abstract Task SaveInstance(); 20 | protected abstract Task DeleteInstance(); 21 | protected abstract Task SaveAll(bool? autoSave); 22 | 23 | } 24 | public abstract class OxygenActor : OxygenActorBase 25 | { 26 | private ActorId actorId; 27 | private readonly ILifetimeScope container; 28 | public OxygenActor(ActorService actorService, ActorId actorId, ILifetimeScope container) 29 | : base(actorService, actorId) 30 | { 31 | this.actorId = actorId; 32 | this.container = container; 33 | } 34 | private T _instance; 35 | public T instance { get { return _instance; } protected set { _instance = value; baseinstance = value; } } 36 | 37 | protected override async Task DeleteInstance() 38 | { 39 | await StateManager.TryRemoveStateAsync(actorId.GetId()); 40 | } 41 | /// 42 | /// actor被创建,需要从持久化设备恢复之前的状态 43 | /// 44 | /// 45 | protected override async Task OnActivateAsync() 46 | { 47 | var result = await StateManager.TryGetStateAsync(actorId.GetId()); 48 | if (result.HasValue) 49 | { 50 | instance = result.Value; 51 | } 52 | await base.OnActivateAsync(); 53 | } 54 | /// 55 | /// actor被显式的释放时应该持久化状态 56 | /// 57 | /// 58 | protected override async Task OnDeactivateAsync() 59 | { 60 | if (instance != null) 61 | { 62 | await StateManager.TryAddStateAsync(actorId.GetId(), instance); 63 | } 64 | await base.OnDeactivateAsync(); 65 | } 66 | /// 67 | /// 官方AOP实现,用于异步发布消息到订阅器进行持久化 68 | /// 69 | /// 70 | /// 71 | protected override Task OnPostActorMethodAsync(ActorMethodContext actorMethodContext) 72 | { 73 | return SaveAll(AutoSave); 74 | } 75 | /// 76 | /// 手动保存 77 | /// 78 | /// 79 | protected override Task SaveAll(bool? autoSave) 80 | { 81 | //异步发消息持久化 82 | return Task.Run(async () => await container.Resolve().Publish(new ActorStateMessage(this) { AutoSave = autoSave ?? AutoSave })); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Oxygen.DaprActorProvider/ServerProxyFactoryExtension.cs: -------------------------------------------------------------------------------- 1 | using Dapr.Actors; 2 | using Dapr.Actors.Client; 3 | using Oxygen.CommonTool; 4 | using Oxygen.ISerializeService; 5 | using Oxygen.IServerProxyFactory; 6 | using System; 7 | using System.Collections.Concurrent; 8 | using System.Net.Http; 9 | using System.Text.Json; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace Oxygen.DaprActorProvider 14 | { 15 | public static class ServerProxyFactoryExtension 16 | { 17 | static Lazy> ActorDir = new Lazy>(() => new ConcurrentDictionary()); 18 | static Lazy> ActorProxyDir = new Lazy>(() => new ConcurrentDictionary()); 19 | /// 20 | /// 通过强类型创建代理 21 | /// 22 | /// 23 | /// 24 | public static T CreateProxy(this IServerProxyFactory.IServerProxyFactory factory, object key = null) where T : IActor 25 | { 26 | var name = typeof(T).Name[1..]; 27 | if (ActorDir.Value.TryGetValue($"{name}{key}", out IActor instance)) 28 | { 29 | return (T)instance; 30 | } 31 | else 32 | { 33 | var actorProxy = ActorProxy.Create(new ActorId(key.ToString()), name); 34 | ActorDir.Value.TryAdd($"{name}{key}", actorProxy); 35 | return actorProxy; 36 | } 37 | } 38 | public static IVirtualProxyServer CreateProxy(this IServerProxyFactory.IServerProxyFactory factory, string path, object key = null) 39 | { 40 | if (key == null) 41 | return factory.CreateProxy(path); 42 | if (path.ToLower().StartsWith("/api")) 43 | { 44 | path = path.Replace("/api", "api"); 45 | if (ActorProxyDir.Value.TryGetValue($"{path.ToLower()}", out VirtualActorProxyServer instance)) 46 | { 47 | return instance; 48 | } 49 | else 50 | { 51 | var names = path.Split('/'); 52 | if (names.Length == 4) 53 | { 54 | if (names[0].ToLower().Equals("api")) 55 | { 56 | var ActorType = names[2]; 57 | var Method = names[3]; 58 | var actorProxy = new VirtualActorProxyServer(Method, ActorType, key.ToString()); 59 | if (actorProxy.CreateSuccess) 60 | ActorProxyDir.Value.TryAdd($"{path.ToLower()}", actorProxy); 61 | else 62 | return default; 63 | return actorProxy; 64 | } 65 | } 66 | } 67 | } 68 | return default; 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/Oxygen.DaprActorProvider/StateManage/ActorStateMessage.cs: -------------------------------------------------------------------------------- 1 | using Dapr.Actors.Runtime; 2 | using MediatR; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace Oxygen.DaprActorProvider.StateManage 8 | { 9 | public class ActorStateMessage : INotification 10 | { 11 | public ActorStateMessage(object actor) 12 | { 13 | Actor = (Actor)actor; 14 | } 15 | public bool AutoSave { get; set; } 16 | public Actor Actor { get; set; } 17 | public IActorStateManager StateManager { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Oxygen.DaprActorProvider/StateManage/ActorStateSubscriber.cs: -------------------------------------------------------------------------------- 1 | using Dapr.Actors.Runtime; 2 | using MediatR; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Oxygen.DaprActorProvider.StateManage 11 | { 12 | /// 13 | /// 状态保存订阅器 14 | /// 15 | public class ActorStateSubscriber : INotificationHandler 16 | { 17 | public static Lazy StateManagerProperty = new Lazy(() => typeof(Actor).GetProperty("StateManager", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); 18 | public static Lazy ActorInstanceProperty = new Lazy(() => typeof(OxygenActorBase).GetProperty("baseinstance", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); 19 | public static Lazy InstanceSaveMethod = new Lazy(() => typeof(OxygenActorBase).GetMethod("SaveInstance", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); 20 | Func localfunc; 21 | public async Task Handle(ActorStateMessage request, CancellationToken cancellationToken) 22 | { 23 | var actor = request.Actor; 24 | var stateManager = StateManagerProperty.Value.GetValue(actor); 25 | var instance = ActorInstanceProperty.Value.GetValue(actor); 26 | await ((IActorStateManager)stateManager).AddOrUpdateStateAsync(actor.Id.GetId(), instance, (x, y) => y); 27 | if (request.AutoSave) 28 | { 29 | if (localfunc == null) 30 | localfunc = (Func)InstanceSaveMethod.Value.CreateDelegate(typeof(Func), actor); 31 | await localfunc(); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Oxygen.DaprActorProvider/VirtualActorProxyServer.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.CommonTool; 2 | using Oxygen.CommonTool.Logger; 3 | using Oxygen.CsharpClientAgent; 4 | using Oxygen.ISerializeService; 5 | using Oxygen.IServerProxyFactory; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Net; 10 | using System.Net.Http; 11 | using System.Net.Http.Headers; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace Oxygen.DaprActorProvider 16 | { 17 | 18 | public class VirtualActorProxyServer : IVirtualProxyServer 19 | { 20 | static Lazy serialize = new Lazy(() => OxygenIocContainer.Resolve()); 21 | static Lazy logger = new Lazy(() => OxygenIocContainer.Resolve()); 22 | string method { get; set; } 23 | string actorType { get; set; } 24 | string actorId { get; set; } 25 | public bool CreateSuccess = false; 26 | HttpClient client; 27 | public VirtualActorProxyServer(string method, string actorType, string actorId) 28 | { 29 | this.method = method; 30 | this.actorType = actorType; 31 | this.actorId = actorId; 32 | try 33 | { 34 | this.InputType = RpcInterfaceType.ActorTypes.Value.FirstOrDefault(x => x.interfaceType.Name.Equals($"I{actorType}")).interfaceType.GetMethod(method).GetParameters()[0].ParameterType; 35 | this.client = new HttpClient(); 36 | CreateSuccess = true; 37 | } 38 | catch(Exception) 39 | { 40 | 41 | } 42 | } 43 | public Type InputType { get; set; } 44 | 45 | public void Init(string serverName, string pathName, Type inputType, Type returnType) 46 | { 47 | 48 | } 49 | 50 | public async Task SendAsync(object input) 51 | { 52 | var responseMessage = await client.SendAsync(GetClientSendMessage(input, actorType, actorId, method)); 53 | if (responseMessage != null && responseMessage.StatusCode == HttpStatusCode.OK) 54 | { 55 | return serialize.Value.DeserializesJson(await responseMessage.Content.ReadAsStringAsync()); 56 | } 57 | else 58 | { 59 | logger.Value.LogError($"客户端调用actor请求异常,状态码:{responseMessage?.StatusCode}"); 60 | } 61 | return default; 62 | } 63 | 64 | HttpRequestMessage GetClientSendMessage(object input,string actorType,string actorId,string method) 65 | { 66 | var json = serialize.Value.SerializesJson(input); 67 | actorId = string.IsNullOrEmpty(actorId) ? (serialize.Value.DeserializesJson(InputType, json) as ActorModel).GetKey() : actorId; 68 | var url = $"http://localhost:3500/v1.0/actors/{actorType}/{actorId}/method/{method}"; 69 | var request = new HttpRequestMessage(HttpMethod.Post, url) { Version = new Version(1, 1) }; 70 | request.Content = new StringContent(json); 71 | request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); 72 | return request; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Oxygen.DotNettyRpcProviderService/BootstrapFactory.cs: -------------------------------------------------------------------------------- 1 | using DotNetty.Buffers; 2 | using DotNetty.Codecs; 3 | using DotNetty.Codecs.Http; 4 | using DotNetty.Transport.Bootstrapping; 5 | using DotNetty.Transport.Channels; 6 | using DotNetty.Transport.Libuv; 7 | using Oxygen.CommonTool; 8 | using Oxygen.CommonTool.Logger; 9 | using Oxygen.IRpcProviderService; 10 | using Oxygen.ISerializeService; 11 | using Oxygen.IServerProxyFactory; 12 | using System; 13 | using System.Collections.Generic; 14 | using System.Runtime.InteropServices; 15 | using System.Text; 16 | 17 | namespace Oxygen.DotNettyRpcProviderService 18 | { 19 | public class BootstrapFactory 20 | { 21 | private readonly IOxygenLogger _logger; 22 | private readonly ISerialize _serialize; 23 | public BootstrapFactory(IOxygenLogger logger, ISerialize serialize) 24 | { 25 | _logger = logger; 26 | _serialize = serialize; 27 | } 28 | /// 29 | /// 创建客户端启动器 30 | /// 31 | /// 32 | /// 33 | public Bootstrap CreateClientBootstrap(ReceiveHander receiveHander) 34 | { 35 | IEventLoopGroup group; 36 | var bootstrap = new Bootstrap(); 37 | group = new EventLoopGroup(); 38 | switch (OxygenSetting.ProtocolType) 39 | { 40 | case EnumProtocolType.HTTP11: 41 | bootstrap.Channel(); 42 | bootstrap 43 | .Group(group) 44 | .Option(ChannelOption.SoBacklog, 8192) 45 | .Handler(new ActionChannelInitializer(channel => 46 | { 47 | IChannelPipeline pipeline = channel.Pipeline; 48 | pipeline.AddLast(new HttpClientCodec()); 49 | pipeline.AddLast(new HttpObjectAggregator(1024 * 10 * 1024)); 50 | pipeline.AddLast(new HttpContentDecompressor()); 51 | pipeline.AddLast("handler", new RpcClientHandler(_logger, _serialize, receiveHander)); 52 | })); 53 | break; 54 | case EnumProtocolType.TCP: 55 | default: 56 | bootstrap.Channel(); 57 | bootstrap 58 | .Group(group) 59 | .Option(ChannelOption.TcpNodelay, true) 60 | .Option(ChannelOption.Allocator, PooledByteBufferAllocator.Default) 61 | .Option(ChannelOption.ConnectTimeout, new TimeSpan(0, 0, 5)) 62 | .Handler(new ActionChannelInitializer(ch => 63 | { 64 | var pipeline = ch.Pipeline; 65 | pipeline.AddLast(new LengthFieldPrepender(4)); 66 | pipeline.AddLast(new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 4, 0, 4)); 67 | pipeline.AddLast(new MessageDecoder>(_serialize)); 68 | pipeline.AddLast(new MessageEncoder(_serialize)); 69 | pipeline.AddLast(new RpcClientHandler(_logger, _serialize, receiveHander)); 70 | })); 71 | break; 72 | } 73 | return bootstrap; 74 | } 75 | /// 76 | /// 创建服务端启动器 77 | /// 78 | /// 79 | /// 80 | public ServerBootstrap CreateServerBootstrap(ILocalProxyGenerator localProxyGenerator) 81 | { 82 | var dispatcher = new DispatcherEventLoopGroup(); 83 | var _bossGroup = dispatcher; 84 | var _workerGroup = new WorkerEventLoopGroup(dispatcher); 85 | var _bootstrap = new ServerBootstrap().Channel() 86 | .Group(_bossGroup, _workerGroup); 87 | switch (OxygenSetting.ProtocolType) 88 | { 89 | case EnumProtocolType.HTTP11: 90 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) 91 | || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) 92 | { 93 | _bootstrap.Option(ChannelOption.SoReuseport, true) 94 | .ChildOption(ChannelOption.SoReuseaddr, true); 95 | } 96 | _bootstrap 97 | .Option(ChannelOption.SoBacklog, 8192) 98 | .ChildHandler(new ActionChannelInitializer(channel => 99 | { 100 | IChannelPipeline pipeline = channel.Pipeline; 101 | pipeline.AddLast("codec", new HttpServerCodec()); 102 | pipeline.AddLast(new HttpObjectAggregator(1024 * 10 * 1024)); 103 | pipeline.AddLast(new HttpContentCompressor()); 104 | pipeline.AddLast("handler", new RpcServerHandler(_logger, localProxyGenerator, _serialize)); 105 | })); 106 | break; 107 | case EnumProtocolType.TCP: 108 | default: 109 | _bootstrap 110 | .Option(ChannelOption.SoBacklog, 100) 111 | .ChildOption(ChannelOption.Allocator, PooledByteBufferAllocator.Default) 112 | .ChildOption(ChannelOption.ConnectTimeout, new TimeSpan(0, 0, 5)) 113 | .ChildHandler(new ActionChannelInitializer(channel => 114 | { 115 | var pipeline = channel.Pipeline; 116 | pipeline.AddLast(new LengthFieldPrepender(4)); 117 | pipeline.AddLast(new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 4, 0, 4)); 118 | pipeline.AddLast(new MessageDecoder>(_serialize)); 119 | pipeline.AddLast(new MessageEncoder(_serialize)); 120 | pipeline.AddLast(new RpcServerHandler(_logger, localProxyGenerator, _serialize)); 121 | })); 122 | break; 123 | } 124 | return _bootstrap; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Oxygen.DotNettyRpcProviderService/MessageCoder.cs: -------------------------------------------------------------------------------- 1 | using DotNetty.Buffers; 2 | using DotNetty.Codecs; 3 | using DotNetty.Transport.Channels; 4 | using Oxygen.IRpcProviderService; 5 | using Oxygen.ISerializeService; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | 10 | namespace Oxygen.DotNettyRpcProviderService 11 | { 12 | public class MessageDecoder : MessageToMessageDecoder 13 | { 14 | private ISerialize _serialize { get; } 15 | 16 | 17 | public MessageDecoder(ISerialize serialize) 18 | { 19 | _serialize = serialize; 20 | } 21 | 22 | protected override void Decode(IChannelHandlerContext context, IByteBuffer message, List output) 23 | { 24 | var len = message.ReadableBytes; 25 | var array = new byte[len]; 26 | message.GetBytes(message.ReaderIndex, array, 0, len); 27 | output.Add(_serialize.Deserializes(array)); 28 | } 29 | } 30 | public class MessageEncoder : MessageToByteEncoder> 31 | { 32 | private ISerialize _serialize { get; } 33 | 34 | public MessageEncoder(ISerialize serialize) 35 | { 36 | _serialize = serialize; 37 | } 38 | protected override void Encode(IChannelHandlerContext context, RpcGlobalMessageBase message, IByteBuffer output) 39 | { 40 | output.WriteBytes(_serialize.Serializes(message)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Oxygen.DotNettyRpcProviderService/Module.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Oxygen.IRpcProviderService; 3 | 4 | namespace Oxygen.DotNettyRpcProviderService 5 | { 6 | public class Module : Autofac.Module 7 | { 8 | protected override void Load(ContainerBuilder builder) 9 | { 10 | builder.RegisterType().As().SingleInstance(); 11 | builder.RegisterType().As().SingleInstance(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Oxygen.DotNettyRpcProviderService/Oxygen.DotNettyRpcProviderService.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 0.0.4.0 6 | 0.0.4.0 7 | 0.0.4.0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Oxygen.DotNettyRpcProviderService/ProtocolMessageBuilder.cs: -------------------------------------------------------------------------------- 1 | using DotNetty.Buffers; 2 | using DotNetty.Codecs.Http; 3 | using DotNetty.Common.Utilities; 4 | using Oxygen.CommonTool; 5 | using Oxygen.IRpcProviderService; 6 | using Oxygen.ISerializeService; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace Oxygen.DotNettyRpcProviderService 12 | { 13 | /// 14 | /// 协议消息体构造器 15 | /// 16 | public class ProtocolMessageBuilder 17 | { 18 | private readonly ISerialize _serialize; 19 | public ProtocolMessageBuilder(ISerialize serialize) 20 | { 21 | _serialize = serialize; 22 | } 23 | /// 24 | /// 构造客户端请求消息体 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | 31 | public object GetClientSendMessage(Guid taskId, string serverName, string pathName, object input, Dictionary traceHeaders = null) 32 | { 33 | var sendMessage = new RpcGlobalMessageBase 34 | { 35 | TaskId = taskId, 36 | Path = pathName, 37 | Message = input is string ? _serialize.Deserializes(_serialize.SerializesJsonString((string)input)) : input 38 | }; 39 | switch (OxygenSetting.ProtocolType) 40 | { 41 | case EnumProtocolType.HTTP11: 42 | byte[] json = _serialize.Serializes(sendMessage); 43 | var request = new DefaultFullHttpRequest(HttpVersion.Http11, HttpMethod.Post, $"http://{serverName}", Unpooled.WrappedBuffer(json), false); 44 | HttpHeaders headers = request.Headers; 45 | headers.Set(HttpHeaderNames.ContentType, AsciiString.Cached("application/x-msgpack")); 46 | headers.Set(HttpHeaderNames.ContentLength, AsciiString.Cached($"{json.Length}")); 47 | TraceHeaderHelper.BuildTraceHeader(headers, traceHeaders); 48 | return request; 49 | case EnumProtocolType.TCP: 50 | default: 51 | return sendMessage; 52 | } 53 | } 54 | /// 55 | /// 构造服务端发送消息体 56 | /// 57 | /// 58 | /// 59 | public object GetServerSendMessage(RpcGlobalMessageBase message, Dictionary traceHeaders = null) 60 | { 61 | switch (OxygenSetting.ProtocolType) 62 | { 63 | case EnumProtocolType.HTTP11: 64 | byte[] json = _serialize.Serializes(message); 65 | var response = new DefaultFullHttpResponse(HttpVersion.Http11, HttpResponseStatus.OK, Unpooled.WrappedBuffer(json), false); 66 | HttpHeaders headers = response.Headers; 67 | headers.Set(HttpHeaderNames.ContentType, AsciiString.Cached("application/x-msgpack")); 68 | headers.Set(HttpHeaderNames.Server, "dotnetty"); 69 | headers.Set(HttpHeaderNames.Date, AsciiString.Cached($"{DateTime.UtcNow.DayOfWeek}, {DateTime.UtcNow:dd MMM yyyy HH:mm:ss z}")); 70 | headers.Set(HttpHeaderNames.ContentLength, AsciiString.Cached($"{json.Length}")); 71 | TraceHeaderHelper.BuildTraceHeader(headers, traceHeaders); 72 | return response; 73 | case EnumProtocolType.TCP: 74 | default: 75 | return message; 76 | } 77 | } 78 | /// 79 | /// 构造接收消息体 80 | /// 81 | /// 82 | public (RpcGlobalMessageBase messageBase, Dictionary traceHeaders) GetReceiveMessage(object message) 83 | { 84 | if (message is IHttpContent) 85 | { 86 | var buf = ((IHttpContent)message).Content; 87 | byte[] array; 88 | int length = buf.ReadableBytes; 89 | array = new byte[length]; 90 | buf.GetBytes(buf.ReaderIndex, array); 91 | Dictionary traceHeaders = default; 92 | if (message is IFullHttpRequest) 93 | { 94 | var headers = ((IFullHttpRequest)message).Headers; 95 | traceHeaders = TraceHeaderHelper.GetTraceHeaders(headers); 96 | } 97 | return (_serialize.Deserializes>(array), traceHeaders); 98 | } 99 | else if (message is RpcGlobalMessageBase) 100 | { 101 | return ((RpcGlobalMessageBase)message,null); 102 | } 103 | return default; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Oxygen.DotNettyRpcProviderService/RpcClientHandler.cs: -------------------------------------------------------------------------------- 1 | using DotNetty.Buffers; 2 | using DotNetty.Codecs.Http; 3 | using DotNetty.Transport.Channels; 4 | using Oxygen.CommonTool; 5 | using Oxygen.CommonTool.Logger; 6 | using Oxygen.IRpcProviderService; 7 | using Oxygen.ISerializeService; 8 | using System; 9 | using System.Text; 10 | 11 | namespace Oxygen.DotNettyRpcProviderService 12 | { 13 | /// 14 | /// 客户端回调处理类 15 | /// 16 | public class RpcClientHandler : ChannelHandlerAdapter 17 | { 18 | private event ReceiveHander _hander; 19 | private readonly IOxygenLogger _logger; 20 | private readonly ISerialize _serialize; 21 | private readonly ProtocolMessageBuilder protocolMessageBuilder; 22 | public RpcClientHandler(IOxygenLogger logger, ISerialize serialize, ReceiveHander hander) 23 | { 24 | _logger = logger; 25 | _serialize = serialize; 26 | _hander = hander; 27 | protocolMessageBuilder = new ProtocolMessageBuilder(_serialize); 28 | } 29 | /// 30 | /// 从tcp管道接受消息 31 | /// 32 | /// 33 | /// 34 | public override void ChannelRead(IChannelHandlerContext context, object message) 35 | { 36 | try 37 | { 38 | _hander?.Invoke(protocolMessageBuilder.GetReceiveMessage(message).messageBase); 39 | } 40 | catch (Exception e) 41 | { 42 | _logger.LogError("客户端回调处理异常: " + e.Message); 43 | } 44 | } 45 | /// 46 | /// tcp管道异常处理 47 | /// 48 | /// 49 | /// 50 | public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) 51 | { 52 | _logger.LogError("客户端回调异常: " + exception.Message); 53 | context.CloseAsync(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Oxygen.DotNettyRpcProviderService/RpcClientProvider.cs: -------------------------------------------------------------------------------- 1 | using DotNetty.Buffers; 2 | using DotNetty.Codecs; 3 | using DotNetty.Codecs.Http; 4 | using DotNetty.Common.Utilities; 5 | using DotNetty.Transport.Bootstrapping; 6 | using DotNetty.Transport.Channels; 7 | using DotNetty.Transport.Libuv; 8 | using Oxygen.CommonTool; 9 | using Oxygen.CommonTool.Logger; 10 | using Oxygen.IRpcProviderService; 11 | using Oxygen.ISerializeService; 12 | using System; 13 | using System.Collections.Concurrent; 14 | using System.Collections.Generic; 15 | using System.Linq; 16 | using System.Net; 17 | using System.Reflection.Emit; 18 | using System.Text; 19 | using System.Threading; 20 | using System.Threading.Tasks; 21 | namespace Oxygen.DotNettyRpcProviderService 22 | { 23 | public delegate void ReceiveHander(RpcGlobalMessageBase message); 24 | 25 | /// 26 | /// 客户端消息服务类 27 | /// 28 | public class RpcClientProvider : IRpcClientProvider 29 | { 30 | private readonly IOxygenLogger _logger; 31 | private readonly ISerialize _serialize; 32 | public static readonly ConcurrentDictionary> TaskHookInfos = 33 | new ConcurrentDictionary>(); 34 | private readonly ProtocolMessageBuilder protocolMessageBuilder; 35 | #region dotnetty相关 36 | static Bootstrap _bootstrap; 37 | static readonly ConcurrentDictionary Channels = new ConcurrentDictionary(); 38 | #endregion 39 | 40 | public RpcClientProvider(IOxygenLogger logger, ISerialize serialize) 41 | { 42 | _logger = logger; 43 | _serialize = serialize; 44 | _bootstrap = new BootstrapFactory(logger, serialize).CreateClientBootstrap(ReceiveMessage); 45 | protocolMessageBuilder = new ProtocolMessageBuilder(serialize); 46 | } 47 | 48 | /// 49 | /// 创建客户端实例 50 | /// 51 | /// 52 | /// 53 | /// 54 | /// 55 | public async Task CreateClient(string serverName) 56 | { 57 | try 58 | { 59 | if (Channels.TryGetValue(serverName, out var channel)) 60 | { 61 | if (!channel.Active) 62 | { 63 | await CloseChannel(channel); 64 | Channels.TryRemove(serverName, out channel); 65 | return await CreateNewChannel(serverName); 66 | } 67 | else 68 | { 69 | return true; 70 | } 71 | } 72 | else 73 | { 74 | return await CreateNewChannel(serverName); 75 | } 76 | } 77 | catch (Exception) 78 | { 79 | return false; 80 | } 81 | 82 | } 83 | /// 84 | /// 创建新的通道 85 | /// 86 | /// 87 | /// 88 | private async Task CreateNewChannel(string serverName) 89 | { 90 | var newChannel = await _bootstrap.ConnectAsync(serverName, OxygenSetting.ServerPort); 91 | //var newChannel = await _bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse("127.0.0.1"), OxygenSetting.ServerPort)); 92 | if (newChannel.Active) 93 | { 94 | Channels.TryAdd(serverName, newChannel); 95 | return true; 96 | } 97 | else 98 | { 99 | await CloseChannel(newChannel); 100 | Channels.TryRemove(serverName, out newChannel); 101 | return false; 102 | } 103 | } 104 | 105 | /// 106 | /// 发送消息 107 | /// 108 | /// 109 | /// 110 | /// 111 | /// 112 | /// 113 | public async Task SendMessage(string serverName, string pathName, object input, Dictionary traceHeaders = null) where T : class 114 | { 115 | return await SendMessage(serverName, pathName, input, null, traceHeaders); 116 | } 117 | public async Task SendMessage(string serverName, string pathName, object input, Type returnType, Dictionary traceHeaders = null) 118 | { 119 | return await SendMessage(serverName, pathName, input, returnType, traceHeaders); 120 | } 121 | #region 私有方法 122 | /// 123 | /// 发送消息到远程服务器 124 | /// 125 | /// 126 | /// 127 | /// 128 | /// 129 | /// 130 | /// 131 | private async Task SendMessage(string serverName, string pathName, object input, Type returnType, Dictionary traceHeaders = null) where T : class 132 | { 133 | T result = default; 134 | if (Channels.TryGetValue(serverName, out var _channel)) 135 | { 136 | try 137 | { 138 | var taskId = Guid.NewGuid(); 139 | var sendMessage = protocolMessageBuilder.GetClientSendMessage(taskId, serverName, pathName, input, traceHeaders); 140 | var resultTask = RegisterResultCallbackAsync(taskId); 141 | await _channel.WriteAndFlushAsync(sendMessage); 142 | var resultBt = await resultTask; 143 | if (resultBt != null && resultBt.Any()) 144 | { 145 | if (returnType == null) 146 | return _serialize.Deserializes(resultBt); 147 | else 148 | return _serialize.Deserializes(returnType, resultBt) as T; 149 | } 150 | return default; 151 | } 152 | catch (Exception e) 153 | { 154 | _logger.LogError($"调用异常:{e.Message},调用堆栈{e.StackTrace.ToString()}"); 155 | } 156 | } 157 | return result; 158 | } 159 | /// 160 | /// 消息回调处理 161 | /// 162 | /// 163 | void ReceiveMessage(RpcGlobalMessageBase message) 164 | { 165 | if (message != null) 166 | { 167 | switch (message.code) 168 | { 169 | case HttpStatusCode.OK: 170 | var task = GetHook(message.TaskId); 171 | task?.TrySetResult(_serialize.Serializes(message.Message)); 172 | break; 173 | case HttpStatusCode.NotFound: 174 | _logger.LogError("RPC调用失败,未找到对应的消费者应用程序!"); 175 | task = GetHook(message.TaskId); 176 | task?.TrySetResult(null); 177 | break; 178 | case HttpStatusCode.Unauthorized: 179 | _logger.LogError("RPC调用失败,数字签名验签不通过!"); 180 | task = GetHook(message.TaskId); 181 | task?.TrySetResult(null); 182 | break; 183 | } 184 | } 185 | } 186 | /// 187 | /// 消息钩子 188 | /// 189 | /// 190 | /// 191 | async Task RegisterResultCallbackAsync(Guid id) 192 | { 193 | var task = new TaskCompletionSource(); 194 | SetHook(id, task); 195 | try 196 | { 197 | var result = await task.Task; 198 | return result; 199 | } 200 | finally 201 | { 202 | RemoveHook(id); 203 | task.TrySetCanceled(); 204 | } 205 | } 206 | 207 | /// 208 | /// 删除通道消息 209 | /// 210 | /// 211 | async Task CloseChannel(IChannel channel) 212 | { 213 | await channel.CloseAsync(); 214 | _bootstrap.RemoteAddress(channel.RemoteAddress); 215 | } 216 | 217 | 218 | /// 219 | /// 获取钩子 220 | /// 221 | /// 222 | TaskCompletionSource GetHook(Guid id) 223 | { 224 | TaskHookInfos.TryGetValue(id, out var value); 225 | return value; 226 | } 227 | /// 228 | /// 删除钩子 229 | /// 230 | /// 231 | void RemoveHook(Guid id) 232 | { 233 | TaskHookInfos.TryRemove(id, out _); 234 | } 235 | 236 | /// 237 | /// 设置钩子 238 | /// 239 | void SetHook(Guid id, TaskCompletionSource message) 240 | { 241 | TaskHookInfos.TryAdd(id, message); 242 | } 243 | #endregion 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/Oxygen.DotNettyRpcProviderService/RpcServerHandler.cs: -------------------------------------------------------------------------------- 1 | using DotNetty.Buffers; 2 | using DotNetty.Codecs.Http; 3 | using DotNetty.Codecs.Http.Multipart; 4 | using DotNetty.Common; 5 | using DotNetty.Common.Utilities; 6 | using DotNetty.Transport.Channels; 7 | using Oxygen.CommonTool; 8 | using Oxygen.CommonTool.Logger; 9 | using Oxygen.IRpcProviderService; 10 | using Oxygen.ISerializeService; 11 | using Oxygen.IServerProxyFactory; 12 | using System; 13 | using System.Linq; 14 | using System.Text; 15 | 16 | namespace Oxygen.DotNettyRpcProviderService 17 | { 18 | /// 19 | /// 服务端消息处理类 20 | /// 21 | public class RpcServerHandler : ChannelHandlerAdapter 22 | { 23 | private readonly IOxygenLogger _logger; 24 | private readonly ILocalProxyGenerator _localProxyGenerator; 25 | private readonly ISerialize _serialize; 26 | private readonly ProtocolMessageBuilder protocolMessageBuilder; 27 | public RpcServerHandler(IOxygenLogger logger, ILocalProxyGenerator localProxyGenerator, ISerialize serialize) 28 | { 29 | _logger = logger; 30 | _localProxyGenerator = localProxyGenerator; 31 | _serialize = serialize; 32 | protocolMessageBuilder = new ProtocolMessageBuilder(_serialize); 33 | } 34 | /// 35 | /// 从tcp管道接受消息 36 | /// 37 | /// 38 | /// 39 | public override async void ChannelRead(IChannelHandlerContext context, object message) 40 | { 41 | try 42 | { 43 | var messageobj = protocolMessageBuilder.GetReceiveMessage(message); 44 | var localHanderResult = await _localProxyGenerator.Invoke(messageobj); 45 | if (localHanderResult != null) 46 | { 47 | await context.WriteAndFlushAsync(protocolMessageBuilder.GetServerSendMessage(localHanderResult)); 48 | } 49 | } 50 | catch (Exception e) 51 | { 52 | _logger.LogError("服务端消息处理异常: " + e.Message); 53 | } 54 | } 55 | 56 | /// 57 | /// tcp管道异常处理 58 | /// 59 | /// 60 | /// 61 | public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) 62 | { 63 | _logger.LogError("服务端消息处理异常: " + exception.Message); 64 | context.CloseAsync(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Oxygen.DotNettyRpcProviderService/RpcServerProvider.cs: -------------------------------------------------------------------------------- 1 | using DotNetty.Buffers; 2 | using DotNetty.Codecs; 3 | using DotNetty.Codecs.Http; 4 | using DotNetty.Transport.Bootstrapping; 5 | using DotNetty.Transport.Channels; 6 | using DotNetty.Transport.Libuv; 7 | using Oxygen.CommonTool; 8 | using Oxygen.CommonTool.Logger; 9 | using Oxygen.IRpcProviderService; 10 | using Oxygen.ISerializeService; 11 | using Oxygen.IServerProxyFactory; 12 | using System; 13 | using System.Net; 14 | using System.Runtime.InteropServices; 15 | using System.Threading.Tasks; 16 | 17 | namespace Oxygen.DotNettyRpcProviderService 18 | { 19 | /// 20 | /// 服务端消息服务 21 | /// 22 | public class RpcServerProvider : IRpcServerProvider 23 | { 24 | private readonly IOxygenLogger _logger; 25 | #region dotnetty相关 26 | IEventLoopGroup _bossGroup; 27 | IEventLoopGroup _workerGroup; 28 | ServerBootstrap _bootstrap; 29 | IChannel boundChannel; 30 | #endregion 31 | 32 | public RpcServerProvider(IOxygenLogger logger,ILocalProxyGenerator localProxyGenerator, ISerialize serialize) 33 | { 34 | _logger = logger; 35 | _bootstrap = new BootstrapFactory(logger, serialize).CreateServerBootstrap(localProxyGenerator); 36 | } 37 | 38 | /// 39 | /// 启动tcp服务 40 | /// 41 | /// 42 | public async Task OpenServer(Action action) 43 | { 44 | var port = OxygenSetting.ServerPort; 45 | boundChannel = await _bootstrap.BindAsync(port); 46 | _logger.LogInfo($"bind tcp 0.0.0.0:{port} to listen"); 47 | return new IPEndPoint(GlobalCommon.GetMachineIp(), port); 48 | } 49 | /// 50 | /// 关闭tcp服务 51 | /// 52 | /// 53 | public async Task CloseServer() 54 | { 55 | await boundChannel.CloseAsync(); 56 | await _bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)); 57 | await _workerGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Oxygen.IRpcProviderService/IRpcClientProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Threading.Tasks; 5 | 6 | namespace Oxygen.IRpcProviderService 7 | { 8 | /// 9 | /// 客户端消息服务接口 10 | /// 11 | public interface IRpcClientProvider 12 | { 13 | /// 14 | /// 创建客户端实例 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | Task CreateClient(string serverName); 21 | 22 | /// 23 | /// 发送消息 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | /// 31 | /// 32 | Task SendMessage(string serverName, string pathName, object input, Dictionary traceHeaders = null) where T : class; 33 | Task SendMessage(string serverName, string pathName, object input, Type returnType, Dictionary traceHeaders = null); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Oxygen.IRpcProviderService/IRpcServerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | 5 | namespace Oxygen.IRpcProviderService 6 | { 7 | /// 8 | /// 服务端消息服务接口 9 | /// 10 | public interface IRpcServerProvider 11 | { 12 | /// 13 | /// 启动tcp服务 14 | /// 15 | /// 16 | Task OpenServer(Action middlewarebuilder = null); 17 | 18 | /// 19 | /// 关闭tcp服务 20 | /// 21 | /// 22 | Task CloseServer(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Oxygen.IRpcProviderService/Oxygen.IRpcProviderService.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 0.0.4.0 6 | 0.0.4.0 7 | 0.0.4.0 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Oxygen.IRpcProviderService/RpcGlobalMessageBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace Oxygen.IRpcProviderService 5 | { 6 | /// 7 | /// RPC通用消息父类 8 | /// 9 | public class RpcGlobalMessageBase 10 | { 11 | /// 12 | /// 客户端IP 13 | /// 14 | public IPEndPoint CustomerIp { get; set; } 15 | /// 16 | /// 任务ID 17 | /// 18 | public Guid TaskId { get; set; } 19 | /// 20 | /// 请求服务路径 21 | /// 22 | public string Path { get; set; } 23 | /// 24 | /// 请求消息体 25 | /// 26 | public T Message { get; set; } 27 | 28 | public HttpStatusCode code { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Oxygen.ISerializeService/ISerialize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Oxygen.ISerializeService 4 | { 5 | /// 6 | /// 序列化接口 7 | /// 8 | public interface ISerialize 9 | { 10 | /// 11 | /// 序列化 12 | /// 13 | /// 14 | /// 15 | /// 16 | byte[] Serializes(T input); 17 | 18 | /// 19 | /// 序列化T为JSON字符串 20 | /// 21 | /// 22 | /// 23 | /// 24 | string SerializesJson(T input); 25 | /// 26 | /// 序列化json字符串为Byte[] 27 | /// 28 | /// 29 | /// 30 | byte[] SerializesJsonString(string jsonStr); 31 | 32 | /// 33 | /// 反序列化 34 | /// 35 | /// 36 | /// 37 | /// 38 | T Deserializes(byte[] input); 39 | 40 | /// 41 | /// 反序列化JSON字符串为T 42 | /// 43 | /// 44 | /// 45 | /// 46 | T DeserializesJson(string input); 47 | /// 48 | /// 反序列化JSON字符串为object 49 | /// 50 | /// 51 | /// 52 | /// 53 | object DeserializesJson(Type type,string input); 54 | 55 | /// 56 | /// 序列化 57 | /// 58 | /// 59 | /// 60 | /// 61 | byte[] Serializes(Type type, object input); 62 | /// 63 | /// 反序列化 64 | /// 65 | /// 66 | /// 67 | /// 68 | object Deserializes(Type type, byte[] input); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Oxygen.ISerializeService/Oxygen.ISerializeService.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 0.0.4.0 6 | 0.0.4.0 7 | 0.0.4.0 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Oxygen.IServerProxyFactory/ILocalMethodDelegate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Oxygen.IServerProxyFactory 7 | { 8 | public interface ILocalMethodDelegate 9 | { 10 | Type Type { get; set; } 11 | Type ParmterType { get; set; } 12 | void Build(object obj); 13 | Task Excute(object val); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Oxygen.IServerProxyFactory/ILocalProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.IRpcProviderService; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Oxygen.IServerProxyFactory 6 | { 7 | /// 8 | /// 本地代理消息分发处理接口 9 | /// 10 | public interface ILocalProxyGenerator 11 | { 12 | /// 13 | /// 消息分发处理 14 | /// 15 | /// 16 | /// 17 | Task> Invoke((RpcGlobalMessageBase messageBase, Dictionary traceHeaders) message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Oxygen.IServerProxyFactory/IRemoteMethodDelegate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Oxygen.IServerProxyFactory 6 | { 7 | public interface IRemoteMethodDelegate 8 | { 9 | object Excute(object val, string serviceName, string pathName); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Oxygen.IServerProxyFactory/IRemoteProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Oxygen.IServerProxyFactory 6 | { 7 | /// 8 | /// 远程代理服务生成器 9 | /// 10 | public interface IRemoteProxyGenerator 11 | { 12 | /// 13 | /// 通过代理类发送消息到远程服务器 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | Task SendAsync(TIn input, Dictionary traceHeaders, string serviceName, string pathName) where TOut : class; 23 | Task SendObjAsync(TIn input, Type OutType, string serviceName, string pathName); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Oxygen.IServerProxyFactory/IServerProxyFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Oxygen.IServerProxyFactory 4 | { 5 | /// 6 | /// 服务代理工厂接口 7 | /// 8 | public interface IServerProxyFactory 9 | { 10 | /// 11 | /// 通过强类型创建代理 12 | /// 13 | /// 14 | /// 15 | T CreateProxy() where T : class; 16 | 17 | /// 18 | /// 通过路径创建代理 19 | /// 20 | /// 21 | /// 22 | IVirtualProxyServer CreateProxy(string path); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Oxygen.IServerProxyFactory/IVirtualProxyServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Reflection; 4 | using System.Threading.Tasks; 5 | 6 | namespace Oxygen.IServerProxyFactory 7 | { 8 | /// 9 | /// 虚拟代理接口 10 | /// 11 | public interface IVirtualProxyServer 12 | { 13 | /// 14 | /// 初始化代理 15 | /// 16 | /// 17 | /// 18 | /// 19 | void Init(string serverName, string pathName, Type inputType, Type returnType); 20 | /// 21 | /// 通过虚拟代理发送请求 22 | /// 23 | /// 24 | /// 25 | Task SendAsync(object input); 26 | Type InputType { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Oxygen.IServerProxyFactory/MethodDelegateInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text; 5 | 6 | namespace Oxygen.IServerProxyFactory 7 | { 8 | public class MethodDelegateInfo 9 | { 10 | public string ServiceName { get; set; } 11 | public string PathName { get; set; } 12 | public MethodInfo MethodInfo { get; set; } 13 | public IRemoteMethodDelegate MethodDelegate { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Oxygen.IServerProxyFactory/Oxygen.IServerProxyFactory.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 0.0.4.0 6 | 0.0.4.0 7 | 0.0.4.0 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Oxygen.KestrelRpcProviderService/Module.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Oxygen.IRpcProviderService; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Net.Http; 6 | using System.Text; 7 | 8 | namespace Oxygen.KestrelRpcProviderService 9 | { 10 | public class Module : Autofac.Module 11 | { 12 | protected override void Load(ContainerBuilder builder) 13 | { 14 | builder.RegisterType().As().SingleInstance(); 15 | builder.RegisterType().As().SingleInstance(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Oxygen.KestrelRpcProviderService/Oxygen.KestrelRpcProviderService.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 0.0.4.0 6 | 0.0.4.0 7 | 0.0.4.0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Oxygen.KestrelRpcProviderService/ProtocolMessageBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Oxygen.CommonTool; 3 | using Oxygen.IRpcProviderService; 4 | using Oxygen.ISerializeService; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Net; 10 | using System.Net.Http; 11 | using System.Net.Http.Headers; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace Oxygen.KestrelRpcProviderService 16 | { 17 | /// 18 | /// 协议消息体构造器 19 | /// 20 | public class ProtocolMessageBuilder 21 | { 22 | private readonly ISerialize _serialize; 23 | public ProtocolMessageBuilder(ISerialize serialize) 24 | { 25 | _serialize = serialize; 26 | } 27 | /// 28 | /// 构造客户端请求消息体 29 | /// 30 | /// 31 | /// 32 | /// 33 | /// 34 | 35 | public HttpRequestMessage GetClientSendMessage(Guid taskId, string serverName, string pathName, object input, Dictionary traceHeaders = null) 36 | { 37 | var sendMessage = new RpcGlobalMessageBase 38 | { 39 | TaskId = taskId, 40 | Path = pathName, 41 | Message = input is string ? _serialize.Deserializes(_serialize.SerializesJsonString((string)input)) : input 42 | }; 43 | byte[] json = _serialize.Serializes(sendMessage); 44 | Version clientVersion = default; 45 | switch (OxygenSetting.ProtocolType) 46 | { 47 | case EnumProtocolType.HTTP11: 48 | clientVersion = new Version(1, 1); 49 | break; 50 | case EnumProtocolType.HTTP2: 51 | clientVersion = new Version(2, 0); 52 | break; 53 | } 54 | string url; 55 | switch (OxygenSetting.MeshType) 56 | { 57 | case EnumMeshType.Dapr: 58 | url = $"http://localhost:3500/v1.0/invoke/{serverName}/method{pathName}"; 59 | break; 60 | default: 61 | url = $"http://{serverName}:{OxygenSetting.ServerPort}"; 62 | break; 63 | } 64 | var request = new HttpRequestMessage(HttpMethod.Post, url) { Version = clientVersion }; 65 | request.Content = new ByteArrayContent(json); 66 | request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-msgpack"); 67 | request.Content.Headers.ContentLength = json.Length; 68 | TraceHeaderHelper.BuildTraceHeader(request.Content.Headers, traceHeaders); 69 | return request; 70 | } 71 | public async Task<(RpcGlobalMessageBase messageBase, Dictionary traceHeaders)> GetReceiveMessage(HttpContext message) 72 | { 73 | using (var buffer = new MemoryStream()) 74 | { 75 | await message.Request.Body.CopyToAsync(buffer); 76 | byte[] bytes = buffer.ToArray(); 77 | buffer.Write(bytes, 0, Convert.ToInt32(buffer.Length)); 78 | var result = new RpcGlobalMessageBase(); 79 | //根据header contenttype选择是反序列化json还是反序列化byte 80 | if (message.Request.ContentType != "application/json") 81 | result = _serialize.Deserializes>(bytes); 82 | else 83 | { 84 | result.Path = message.Request.Path.Value; 85 | result.Message = _serialize.DeserializesJson(Encoding.Default.GetString(bytes)); 86 | } 87 | Dictionary traceHeaders = default; 88 | if (message.Request.Headers.Any()) 89 | { 90 | traceHeaders = TraceHeaderHelper.GetTraceHeaders(message.Request.Headers); 91 | } 92 | return (result, traceHeaders); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Oxygen.KestrelRpcProviderService/RpcClientProvider.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.CommonTool; 2 | using Oxygen.CommonTool.Logger; 3 | using Oxygen.IRpcProviderService; 4 | using Oxygen.ISerializeService; 5 | using System; 6 | using System.Collections.Concurrent; 7 | using System.Collections.Generic; 8 | using System.Net; 9 | using System.Net.Http; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace Oxygen.KestrelRpcProviderService 14 | { 15 | public class RpcClientProvider : IRpcClientProvider 16 | { 17 | private readonly IOxygenLogger _logger; 18 | private readonly ISerialize _serialize; 19 | private readonly ProtocolMessageBuilder protocolMessageBuilder; 20 | private ConcurrentDictionary DicClient =new ConcurrentDictionary(); 21 | public RpcClientProvider(IOxygenLogger logger, ISerialize serialize) 22 | { 23 | _logger = logger; 24 | _serialize = serialize; 25 | protocolMessageBuilder = new ProtocolMessageBuilder(serialize); 26 | if (OxygenSetting.ProtocolType == EnumProtocolType.HTTP2) 27 | AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);//非https环境下设置上下文支持http2连接 28 | } 29 | public async Task CreateClient(string serverName) 30 | { 31 | DicClient.TryAdd(serverName, new HttpClient()); 32 | return await Task.FromResult(true); 33 | } 34 | 35 | public async Task SendMessage(string serverName, string pathName, object input, Dictionary traceHeaders = null) where T : class 36 | { 37 | return await SendMessage(serverName, pathName, input, null, traceHeaders); 38 | } 39 | 40 | public async Task SendMessage(string serverName, string pathName, object input, Type returnType, Dictionary traceHeaders = null) 41 | { 42 | return await SendMessage(serverName, pathName, input, returnType, traceHeaders); 43 | } 44 | private async Task SendMessage(string serverName, string pathName, object input, Type returnType, Dictionary traceHeaders = null) where T : class 45 | { 46 | T result = default; 47 | if (DicClient.TryGetValue(serverName, out var _httpclient)) 48 | { 49 | try 50 | { 51 | var taskId = Guid.NewGuid(); 52 | var sendMessage = protocolMessageBuilder.GetClientSendMessage(taskId, serverName, pathName, input, traceHeaders); 53 | var responseMessage = await _httpclient.SendAsync(sendMessage); 54 | if (responseMessage != null && responseMessage.StatusCode == HttpStatusCode.OK) 55 | { 56 | var resultBt = ReceiveMessage(await responseMessage.Content.ReadAsByteArrayAsync()); 57 | if (returnType == null) 58 | return _serialize.Deserializes(resultBt); 59 | else 60 | return _serialize.Deserializes(returnType, resultBt) as T; 61 | } 62 | else 63 | { 64 | _logger.LogError($"客户端调用http请求异常,状态码:{responseMessage?.StatusCode}"); 65 | } 66 | return default; 67 | } 68 | catch (Exception e) 69 | { 70 | _logger.LogError($"客户端调用异常:{e.Message},接口地址:http://{serverName}:{OxygenSetting.ServerPort}/{pathName},调用堆栈{e.StackTrace.ToString()}"); 71 | } 72 | } 73 | return result; 74 | } 75 | 76 | /// 77 | /// 消息回调处理 78 | /// 79 | /// 80 | byte[] ReceiveMessage(byte[] resultBt) 81 | { 82 | var message= _serialize.Deserializes>(resultBt); 83 | if (message != null) 84 | { 85 | switch (message.code) 86 | { 87 | case HttpStatusCode.OK: 88 | return _serialize.Serializes(message.Message); 89 | case HttpStatusCode.NotFound: 90 | _logger.LogError("RPC调用失败,未找到对应的消费者应用程序!"); 91 | return default; 92 | case HttpStatusCode.Unauthorized: 93 | _logger.LogError("RPC调用失败,数字签名验签不通过!"); 94 | return default; 95 | } 96 | } 97 | return default; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Oxygen.KestrelRpcProviderService/RpcServerHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.WebUtilities; 4 | using Microsoft.Extensions.Logging; 5 | using Oxygen.CommonTool; 6 | using Oxygen.CommonTool.Logger; 7 | using Oxygen.IRpcProviderService; 8 | using Oxygen.ISerializeService; 9 | using Oxygen.IServerProxyFactory; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.IO; 13 | using System.Net; 14 | using System.Text; 15 | using System.Threading.Tasks; 16 | 17 | namespace Oxygen.KestrelRpcProviderService 18 | { 19 | public class RpcServerHandler 20 | { 21 | private ISerialize _serialize; 22 | private IOxygenLogger _logger; 23 | private readonly ProtocolMessageBuilder protocolMessageBuilder; 24 | private readonly ILocalProxyGenerator _localProxyGenerator; 25 | public RpcServerHandler(ISerialize serialize, ILocalProxyGenerator localProxyGenerator, IOxygenLogger logger) 26 | { 27 | _serialize = serialize; 28 | _localProxyGenerator = localProxyGenerator; 29 | _logger = logger; 30 | protocolMessageBuilder = new ProtocolMessageBuilder(_serialize); 31 | } 32 | public void BuildHandler(IApplicationBuilder app) 33 | { 34 | app.MapWhen(contxt => contxt.Request.Method == "POST", appbuilder => appbuilder.Run(async http => await handle(http))); 35 | } 36 | private async Task handle(HttpContext http) 37 | { 38 | try 39 | { 40 | var messageobj = await protocolMessageBuilder.GetReceiveMessage(http); 41 | var localHanderResult = await _localProxyGenerator.Invoke(messageobj); 42 | if (localHanderResult.Message != null) 43 | { 44 | byte[] json = default; 45 | if (http.Request.ContentType != "application/json") 46 | { 47 | json = _serialize.Serializes(localHanderResult); 48 | await http.Response.Body.WriteAsync(json, 0, json.Length); 49 | } 50 | else 51 | { 52 | http.Response.ContentType = "application/json"; 53 | await http.Response.WriteAsync(_serialize.SerializesJson(localHanderResult.Message)); 54 | } 55 | } 56 | else 57 | { 58 | http.Response.StatusCode = 404; 59 | await http.Response.Body.WriteAsync(new byte[] { }, 0, 0); 60 | } 61 | } 62 | catch (Exception e) 63 | { 64 | _logger.LogError("服务端消息处理异常: " + e.Message); 65 | http.Response.StatusCode = 502; 66 | await http.Response.Body.WriteAsync(new byte[] { }, 0, 0); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Oxygen.KestrelRpcProviderService/RpcServerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Server.Kestrel.Core; 5 | using Oxygen.CommonTool; 6 | using Oxygen.CommonTool.Logger; 7 | using Oxygen.IRpcProviderService; 8 | using Oxygen.ISerializeService; 9 | using Oxygen.IServerProxyFactory; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Net; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | 16 | namespace Oxygen.KestrelRpcProviderService 17 | { 18 | /// 19 | /// kestrel服务端提供者 20 | /// 21 | public class RpcServerProvider : IRpcServerProvider 22 | { 23 | public IWebHost Host { get; private set; } 24 | private readonly IOxygenLogger _logger; 25 | private readonly ISerialize _serialize; 26 | private readonly ILocalProxyGenerator _localProxyGenerator; 27 | public RpcServerProvider(IOxygenLogger logger,ISerialize serialize, ILocalProxyGenerator localProxyGenerator) 28 | { 29 | _logger = logger; 30 | _serialize = serialize; 31 | _localProxyGenerator = localProxyGenerator; 32 | } 33 | 34 | /// 35 | /// 启动kestrel服务器 36 | /// 37 | /// 38 | public async Task OpenServer(Action middlewarebuilder = null) 39 | { 40 | var port = OxygenSetting.ServerPort; 41 | var builder = new WebHostBuilder() 42 | .UseKestrel(options => 43 | { 44 | options.Listen(IPAddress.Any, port, listenOptions => 45 | { 46 | listenOptions.Protocols = OxygenSetting.ProtocolType == EnumProtocolType.HTTP11 ? HttpProtocols.Http1 : HttpProtocols.Http2; 47 | }); 48 | }) 49 | .Configure(app =>new RpcServerHandler(_serialize, _localProxyGenerator, _logger).BuildHandler(app)); 50 | middlewarebuilder?.Invoke(builder); 51 | Host = builder.Build(); 52 | await Host.StartAsync(); 53 | _logger.LogInfo($"bind tcp 0.0.0.0:{port} to listen"); 54 | return new IPEndPoint(GlobalCommon.GetMachineIp(), port); 55 | } 56 | /// 57 | /// 关闭kestrel服务器 58 | /// 59 | /// 60 | public async Task CloseServer() 61 | { 62 | await Host.StopAsync(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Oxygen.MessagePackSerializeService/Module.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | 3 | namespace Oxygen.MessagePackSerializeService 4 | { 5 | 6 | public class Module : Autofac.Module 7 | { 8 | protected override void Load(ContainerBuilder builder) 9 | { 10 | builder.RegisterAssemblyTypes(ThisAssembly) 11 | .AsImplementedInterfaces() 12 | .InstancePerLifetimeScope(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Oxygen.MessagePackSerializeService/Oxygen.MessagePackSerializeService.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 0.0.4.0 6 | 0.0.4.0 7 | 0.0.4.0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Oxygen.MessagePackSerializeService/Serialize.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | using MessagePack.Resolvers; 3 | using Oxygen.CommonTool.Logger; 4 | using Oxygen.ISerializeService; 5 | using System; 6 | using System.Linq; 7 | 8 | namespace Oxygen.MessagePackSerializeService 9 | { 10 | /// 11 | /// 序列化服务 12 | /// 13 | public class Serialize : ISerialize 14 | { 15 | private readonly IOxygenLogger _logger; 16 | public static Lazy loadConfig = new Lazy(() => { 17 | StaticCompositeResolver.Instance.Register(NativeDateTimeResolver.Instance, ContractlessStandardResolverAllowPrivate.Instance); 18 | var options = MessagePackSerializerOptions.Standard.WithResolver(StaticCompositeResolver.Instance); 19 | options.WithCompression(MessagePackCompression.Lz4BlockArray); 20 | MessagePackSerializer.DefaultOptions = options; 21 | return true; 22 | }); 23 | public Serialize(IOxygenLogger logger) 24 | { 25 | _ = loadConfig.Value; 26 | _logger = logger; 27 | } 28 | /// 29 | /// 序列化 30 | /// 31 | /// 32 | /// 33 | /// 34 | public byte[] Serializes(T input) 35 | { 36 | if (input == null) 37 | return default(byte[]); 38 | try 39 | { 40 | return MessagePackSerializer.Serialize(input); 41 | } 42 | catch (Exception e) 43 | { 44 | _logger.LogError($"序列化对象失败:{e.Message}"); 45 | } 46 | return default(byte[]); 47 | } 48 | /// 49 | /// 序列化T为JSON字符串 50 | /// 51 | /// 52 | /// 53 | /// 54 | public string SerializesJson(T input) 55 | { 56 | if (input == null) 57 | return default(string); 58 | try 59 | { 60 | return MessagePackSerializer.SerializeToJson(input); 61 | } 62 | catch (Exception e) 63 | { 64 | _logger.LogError($"序列化对象失败:{e.Message}"); 65 | } 66 | return default(string); 67 | } 68 | /// 69 | /// 序列化json字符串为Byte[] 70 | /// 71 | /// 72 | /// 73 | public byte[] SerializesJsonString(string jsonStr) 74 | { 75 | if (jsonStr == null) 76 | return default(byte[]); 77 | try 78 | { 79 | return MessagePackSerializer.ConvertFromJson(jsonStr); 80 | } 81 | catch (Exception e) 82 | { 83 | _logger.LogError($"序列化对象失败:{e.Message}"); 84 | } 85 | return default(byte[]); 86 | } 87 | 88 | /// 89 | /// 反序列化 90 | /// 91 | /// 92 | /// 93 | /// 94 | public T Deserializes(byte[] input) 95 | { 96 | if (input == null || !input.Any()) 97 | return default(T); 98 | try 99 | { 100 | return MessagePackSerializer.Deserialize(input); 101 | } 102 | catch (Exception e) 103 | { 104 | _logger.LogError($"反序化对象失败:{e.Message}"); 105 | } 106 | return default(T); 107 | } 108 | 109 | /// 110 | /// 反序列化JSON字符串为T 111 | /// 112 | /// 113 | /// 114 | /// 115 | public T DeserializesJson(string input) 116 | { 117 | if (input == null || !input.Any()) 118 | return default(T); 119 | try 120 | { 121 | return MessagePackSerializer.Deserialize(SerializesJsonString(input)); 122 | } 123 | catch (Exception e) 124 | { 125 | _logger.LogError($"反序化对象失败:{e.Message},消息体:{input}"); 126 | } 127 | return default(T); 128 | } 129 | /// 130 | /// 序列化JSON字符串为object 131 | /// 132 | /// 133 | /// 134 | /// 135 | public object DeserializesJson(Type type, string input) 136 | { 137 | if (input == null || !input.Any()) 138 | return default; 139 | try 140 | { 141 | return MessagePackSerializer.Deserialize(type, SerializesJsonString(input)); 142 | } 143 | catch (Exception e) 144 | { 145 | _logger.LogError($"反序化对象失败:{e.Message},消息体:{input}"); 146 | } 147 | return default; 148 | } 149 | /// 150 | /// 序列化 151 | /// 152 | /// 153 | /// 154 | /// 155 | public byte[] Serializes(Type type, object input) 156 | { 157 | if (input == null) 158 | return default(byte[]); 159 | try 160 | { 161 | return MessagePackSerializer.Serialize(type, input); 162 | } 163 | catch (Exception e) 164 | { 165 | _logger.LogError($"序列化对象失败:{e.Message}"); 166 | } 167 | return default(byte[]); 168 | } 169 | 170 | public object Deserializes(Type type, byte[] input) 171 | { 172 | if (input == null || !input.Any()) 173 | return null; 174 | try 175 | { 176 | return MessagePackSerializer.Deserialize(type, input); 177 | } 178 | catch (Exception e) 179 | { 180 | _logger.LogError($"反序化对象失败:{e.Message}"); 181 | } 182 | return null; 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/LocalMethodAopProvider.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.IRpcProviderService; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Oxygen.ServerProxyFactory 8 | { 9 | /// 10 | /// 本地管道AOP提供者 11 | /// 12 | public class LocalMethodAopProvider 13 | { 14 | static Func BeforeFunc; 15 | static Func AfterFunc; 16 | static Func> ExceptionFunc; 17 | /// 18 | /// 为管道注册匿名委托 19 | /// 20 | /// 21 | /// 22 | /// 23 | public static void RegisterPipelineHandler(Func beforeFunc = null, Func afterFunc = null, Func> exceptionFunc = null) 24 | { 25 | if (beforeFunc != null) 26 | BeforeFunc = beforeFunc; 27 | if (afterFunc != null) 28 | AfterFunc = afterFunc; 29 | if (exceptionFunc != null) 30 | ExceptionFunc = exceptionFunc; 31 | } 32 | /// 33 | /// 调用方法前后异常匿名委托 34 | /// 35 | /// 36 | /// 37 | /// 38 | /// 39 | /// 40 | public static async Task UsePipelineHandler(Tin param, Func> method) where Tout : class 41 | { 42 | try 43 | { 44 | Tout result = default; 45 | if (BeforeFunc != null) 46 | await BeforeFunc(param); 47 | result = await method(param); 48 | if (AfterFunc != null) 49 | await AfterFunc(result); 50 | return result; 51 | } 52 | catch (Exception e) 53 | { 54 | if (ExceptionFunc != null) 55 | return await ExceptionFunc(e); 56 | } 57 | return default; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/LocalMethodDelegate.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.IProxyClientBuilder; 2 | using Oxygen.IServerProxyFactory; 3 | using Oxygen.ServerProxyFactory; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Oxygen.IProxyClientBuilder 11 | { 12 | /// 13 | /// 代理委托 14 | /// 15 | /// 16 | /// 17 | public class LocalMethodDelegate : ILocalMethodDelegate where Tout : class 18 | { 19 | private Func> localfunc; 20 | public Type Type { get; set; } 21 | public Type ParmterType { get; set; } 22 | public MethodInfo Method { get; set; } 23 | public LocalMethodDelegate(MethodInfo method, Type type) 24 | { 25 | Method = method; 26 | Type = type; 27 | ParmterType = typeof(Tin); 28 | } 29 | public void Build(object obj) 30 | { 31 | localfunc = (Func>)Method.CreateDelegate(typeof(Func>), obj); 32 | } 33 | public async Task Excute(object val) 34 | { 35 | return await LocalMethodAopProvider.UsePipelineHandler((Tin)val, localfunc); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/LocalProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.Loader; 7 | using System.Threading.Tasks; 8 | using Autofac; 9 | using Microsoft.Extensions.DependencyModel; 10 | using Oxygen.CommonTool; 11 | using Oxygen.CommonTool.Logger; 12 | using Oxygen.CsharpClientAgent; 13 | using Oxygen.IProxyClientBuilder; 14 | using Oxygen.IRpcProviderService; 15 | using Oxygen.ISerializeService; 16 | using Oxygen.IServerProxyFactory; 17 | 18 | namespace Oxygen.ServerProxyFactory 19 | { 20 | /// 21 | /// 本地代理消息分发处理类 22 | /// 23 | public class LocalProxyGenerator : ILocalProxyGenerator 24 | { 25 | private readonly IOxygenLogger _logger; 26 | private readonly ISerialize _serialize; 27 | private readonly ILifetimeScope container; 28 | private static readonly ConcurrentDictionary InstanceDictionary = new ConcurrentDictionary(); 29 | public LocalProxyGenerator(IOxygenLogger logger, ISerialize serialize, ILifetimeScope container) 30 | { 31 | _logger = logger; 32 | _serialize = serialize; 33 | this.container = container; 34 | } 35 | 36 | 37 | /// 38 | /// 消息分发处理 39 | /// 40 | /// 41 | /// 42 | public async Task> Invoke((RpcGlobalMessageBase messageBase, Dictionary traceHeaders) body) 43 | { 44 | if (body.messageBase == null) 45 | { 46 | _logger.LogError($"消息分发不能为空消息"); 47 | return default; 48 | } 49 | else 50 | { 51 | using (var scope = container.BeginLifetimeScope()) 52 | { 53 | OxygenIocContainer.BuilderIocContainer(scope);//仅在当前请求内创建上下文模型 54 | body.messageBase.Message = await ExcutePath(body.messageBase.Path, _serialize.Serializes(body.messageBase.Message), body.traceHeaders); 55 | OxygenIocContainer.DisposeIocContainer();//注销上下文 56 | } 57 | body.messageBase.code = System.Net.HttpStatusCode.OK; 58 | return body.messageBase; 59 | } 60 | } 61 | 62 | /// 63 | /// 执行本地方法 64 | /// 65 | /// 66 | /// 67 | /// 68 | public async Task ExcutePath(string pathname, byte[] input, Dictionary traceHeaders) 69 | { 70 | if (InstanceDictionary.TryGetValue(pathname.ToLower(), out ILocalMethodDelegate methodDelegate)) 71 | { 72 | OxygenIocContainer.Resolve().SetTraceHeader(traceHeaders); 73 | methodDelegate.Build(OxygenIocContainer.Resolve(methodDelegate.Type)); 74 | return await methodDelegate.Excute(_serialize.Deserializes(methodDelegate.ParmterType, input)); 75 | } 76 | else 77 | { 78 | _logger.LogError($"没有找到目标地址:{pathname.ToLower()}"); 79 | } 80 | return null; 81 | } 82 | /// 83 | /// 缓存本地方法类型信息 84 | /// 85 | /// 86 | /// 87 | public static void LoadMethodDelegate() 88 | { 89 | foreach (var type in RpcInterfaceType.LocalTypes.Value) 90 | { 91 | foreach(var method in type.GetMethods()) 92 | { 93 | var delegateObj = CreateMethodDelegate(type, method, out string key); 94 | InstanceDictionary.TryAdd(key.ToLower(), delegateObj); 95 | } 96 | } 97 | } 98 | 99 | /// 100 | /// 创建本地方法委托 101 | /// 102 | /// 103 | private static ILocalMethodDelegate CreateMethodDelegate(Type localType, MethodInfo method, out string pathname) 104 | { 105 | pathname = "/" + localType.Name + "/" + method.Name; 106 | return (ILocalMethodDelegate)Activator.CreateInstance(typeof(LocalMethodDelegate<,>).MakeGenericType(method.GetParameters()[0].ParameterType, method.ReturnType.GetGenericArguments().FirstOrDefault()), method, localType); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/Module.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | 3 | namespace Oxygen.ServerProxyFactory 4 | { 5 | 6 | public class Module : Autofac.Module 7 | { 8 | protected override void Load(ContainerBuilder builder) 9 | { 10 | builder.RegisterAssemblyTypes(ThisAssembly) 11 | .AsImplementedInterfaces() 12 | .InstancePerLifetimeScope(); 13 | //注入远程代理服务 14 | ProxyClientBuilder.CreateRemoteProxyClientInstance(builder); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/Oxygen.ServerProxyFactory.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 0.0.4.0 6 | 0.0.4.0 7 | 0.0.4.0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/ProxyClientBuilder.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Microsoft.Extensions.DependencyModel; 3 | using Oxygen.CommonTool; 4 | using Oxygen.CsharpClientAgent; 5 | using Oxygen.IServerProxyFactory; 6 | using System; 7 | using System.Collections.Concurrent; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Reflection; 11 | using System.Runtime.Loader; 12 | 13 | namespace Oxygen.ServerProxyFactory 14 | { 15 | /// 16 | /// 代理服务创建类 17 | /// 18 | public class ProxyClientBuilder 19 | { 20 | /// 21 | /// 为远程服务构建代理类 22 | /// 23 | /// 24 | public static void CreateRemoteProxyClientInstance(ContainerBuilder builder) 25 | { 26 | var remote = RpcInterfaceType.RemoteTypes.Value; 27 | if (remote != null && remote.Any()) 28 | { 29 | foreach (var type in remote) 30 | { 31 | RemoteProxyDecoratorBuilder.RegisterProxyInDic(type); 32 | builder.RegisterInstance(CreateTypeInstance(type)).As(type); 33 | } 34 | } 35 | } 36 | public static object CreateTypeInstance(Type interfaceType) 37 | { 38 | var targetType = typeof(RemoteProxyDecoratorBuilder); 39 | return targetType.GetMethod("CreateProxyInstance").MakeGenericMethod(interfaceType).Invoke(Activator.CreateInstance(targetType), null); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/RemoteMethodDelegate.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.CommonTool; 2 | using Oxygen.IProxyClientBuilder; 3 | using Oxygen.IServerProxyFactory; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Oxygen.ServerProxyFactory 11 | { 12 | /// 13 | /// 代理委托 14 | /// 15 | /// 16 | /// 17 | public class RemoteMethodDelegate : IRemoteMethodDelegate where Tout : Task 18 | { 19 | private Func, string, string, Tout> proxyfunc; 20 | public RemoteMethodDelegate(MethodInfo method, object instence) 21 | { 22 | proxyfunc = (Func, string, string, Tout>)method.CreateDelegate(typeof(Func, string, string, Tout>), instence); 23 | } 24 | public object Excute(object val, string serviceName, string pathName) 25 | { 26 | CustomerInfo customer = OxygenIocContainer.Resolve(); 27 | return proxyfunc((Tin)val, customer.TraceHeaders, serviceName, pathName); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/RemoteProxyDecorator.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.CommonTool; 2 | using Oxygen.IServerProxyFactory; 3 | using System; 4 | using System.Collections.Concurrent; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | namespace Oxygen.ServerProxyFactory 9 | { 10 | public class RemoteProxyDecorator : DispatchProxy 11 | { 12 | IRemoteMethodDelegate GetProxy(string key, out string ServiceName, out string PathName) 13 | { 14 | var remotemethod = RemoteProxyDecoratorBuilder.Remotemethods.First(x => x.Key.Equals(key)).Value; 15 | if (remotemethod.MethodDelegate == null) 16 | remotemethod.MethodDelegate = (IRemoteMethodDelegate)Activator.CreateInstance(typeof(RemoteMethodDelegate<,>).MakeGenericType(remotemethod.MethodInfo.GetParameters()[0].ParameterType, remotemethod.MethodInfo.ReturnType), remotemethod.MethodInfo, 17 | OxygenIocContainer.Resolve() 18 | ); 19 | ServiceName = remotemethod.ServiceName; 20 | PathName = remotemethod.PathName; 21 | return remotemethod.MethodDelegate; 22 | } 23 | protected override object Invoke(MethodInfo targetMethod, object[] args) 24 | { 25 | return GetProxy($"/{targetMethod.DeclaringType.Name}/{targetMethod.Name}", out string ServiceName, out string PathName).Excute(args[0], ServiceName, PathName); 26 | 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/RemoteProxyDecoratorBuilder.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.CommonTool; 2 | using Oxygen.CommonTool.Logger; 3 | using Oxygen.CsharpClientAgent; 4 | using Oxygen.IServerProxyFactory; 5 | using System; 6 | using System.Collections.Concurrent; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Reflection; 10 | using System.Text; 11 | 12 | namespace Oxygen.ServerProxyFactory 13 | { 14 | public class RemoteProxyDecoratorBuilder 15 | { 16 | private static readonly Lazy logger = new Lazy(() => OxygenIocContainer.Resolve()); 17 | public static ConcurrentDictionary Remotemethods = new ConcurrentDictionary(); 18 | public T CreateProxyInstance() 19 | { 20 | return DispatchProxy.Create>(); 21 | } 22 | public static void RegisterProxyInDic(Type type) 23 | { 24 | var serviceName = (string)typeof(RemoteServiceAttribute).GetProperty("ServerName") 25 | ?.GetValue(type.GetCustomAttribute(typeof(RemoteServiceAttribute))); 26 | foreach (var method in type.GetMethods()) 27 | { 28 | var key = method.Name + string.Join("", method.GetParameters().Select(x => x.Name)); 29 | var tmpmod = new MethodDelegateInfo() 30 | { 31 | ServiceName = serviceName, 32 | PathName = $"/{type.Name}/{method.Name}", 33 | MethodInfo = typeof(IRemoteProxyGenerator).GetMethod("SendAsync").MakeGenericMethod(method.GetParameters()[0].ParameterType, method.ReturnParameter.ParameterType.GenericTypeArguments[0]) 34 | }; 35 | if (!Remotemethods.TryAdd($"{tmpmod.PathName}", tmpmod)) 36 | { 37 | logger.Value.LogError($"无法为远程代理添加同名服务{tmpmod.PathName},请确保服务名全局唯一"); 38 | } 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/RemoteProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Oxygen.CommonTool; 3 | using Oxygen.CommonTool.Logger; 4 | using Oxygen.IRpcProviderService; 5 | using Oxygen.IServerProxyFactory; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | namespace Oxygen.ServerProxyFactory 11 | { 12 | /// 13 | /// 远程代理服务生成器 14 | /// 15 | public class RemoteProxyGenerator: IRemoteProxyGenerator 16 | { 17 | private readonly IRpcClientProvider _clientProvider; 18 | private readonly IOxygenLogger _oxygenLogger; 19 | public RemoteProxyGenerator(IRpcClientProvider clientProvider, IOxygenLogger oxygenLogger) 20 | { 21 | _clientProvider = clientProvider; 22 | _oxygenLogger = oxygenLogger; 23 | } 24 | 25 | /// 26 | /// 通过代理类发送消息到远程服务器 27 | /// 28 | /// 29 | /// 30 | /// 31 | /// 32 | /// 33 | /// 34 | /// 35 | public async Task SendAsync(TIn input, Dictionary traceHeaders, string serviceName, string pathName) where TOut : class 36 | { 37 | return await SendAsync(input, serviceName, pathName, null, traceHeaders); 38 | } 39 | public async Task SendObjAsync(TIn input, Type OutType, string serviceName, string pathName) 40 | { 41 | return await SendAsync(input, serviceName, pathName, OutType, null); 42 | } 43 | private async Task SendAsync(TIn input, string serviceName, string pathName, Type OutType = null, Dictionary traceHeaders = null) where TOut : class 44 | { 45 | try 46 | { 47 | if (await _clientProvider.CreateClient(serviceName)) 48 | { 49 | if (OutType == null) 50 | return await _clientProvider.SendMessage(serviceName, pathName, input, traceHeaders ?? OxygenIocContainer.Resolve().TraceHeaders); 51 | else 52 | return await _clientProvider.SendMessage(serviceName, pathName, input, OutType, traceHeaders ?? OxygenIocContainer.Resolve().TraceHeaders) as TOut; 53 | } 54 | else 55 | { 56 | _oxygenLogger.LogError($"远程调用通道创建失败,服务名:{serviceName}"); 57 | } 58 | } 59 | catch (Exception e) 60 | { 61 | _oxygenLogger.LogError($"远程调用失败:{e.Message},堆栈跟踪:{e.StackTrace.ToString()}"); 62 | } 63 | return await Task.FromResult(default(TOut)); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/ServerProxyFactory.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Oxygen.CommonTool; 3 | using Oxygen.CommonTool.Logger; 4 | using Oxygen.CsharpClientAgent; 5 | using Oxygen.IServerProxyFactory; 6 | using System; 7 | using System.Collections.Concurrent; 8 | using System.Linq; 9 | using System.Reflection; 10 | using System.Threading.Tasks; 11 | 12 | namespace Oxygen.ServerProxyFactory 13 | { 14 | /// 15 | /// 服务代理工厂类 16 | /// 17 | public class ServerProxyFactory : IServerProxyFactory.IServerProxyFactory 18 | { 19 | private readonly ILifetimeScope _container; 20 | private static readonly ConcurrentDictionary InstanceDictionary = new ConcurrentDictionary(); 21 | private static readonly ConcurrentDictionary InstanceParmDictionary = 22 | new ConcurrentDictionary(); 23 | public ServerProxyFactory(ILifetimeScope container) 24 | { 25 | _container = container; 26 | } 27 | 28 | /// 29 | /// 通过强类型创建代理 30 | /// 31 | /// 32 | /// 33 | public T CreateProxy() where T : class 34 | { 35 | if (_container.TryResolve(typeof(T), out var instance)) 36 | { 37 | return instance as T; 38 | } 39 | return default(T); 40 | } 41 | 42 | /// 43 | /// 通过路径创建代理 44 | /// 45 | /// 46 | /// 47 | public IVirtualProxyServer CreateProxy(string path) 48 | { 49 | if (!string.IsNullOrEmpty(path)) 50 | { 51 | if (path.ToLower().StartsWith("/api")) 52 | { 53 | path = path.Replace("/api", "api"); 54 | if (_container.TryResolve(typeof(IVirtualProxyServer), out var instance)) 55 | { 56 | var vitual = instance as VirtualProxyServer; 57 | if (vitual != null) 58 | { 59 | if (InstanceParmDictionary.TryGetValue(path.ToLower(), out var messageType)) 60 | { 61 | vitual.Init(messageType.Item1[0], messageType.Item1[1], messageType.Item2, messageType.Item3); 62 | return vitual; 63 | } 64 | else 65 | { 66 | var names = path.Split('/'); 67 | if (names.Length == 4) 68 | { 69 | if (names[0].ToLower().Equals("api")) 70 | { 71 | var className = $"{names[2]}"; 72 | var type = GetProxyClient(className); 73 | if (type != null) 74 | { 75 | //_container.TryResolve(type, out object remoteProxyDecorator); 76 | var serviceName = (string)typeof(RemoteServiceAttribute).GetProperty("ServerName") 77 | ?.GetValue(type.GetCustomAttribute(typeof(RemoteServiceAttribute))); 78 | var method = type.GetMethods().FirstOrDefault(x => x.Name.ToLower().Equals(names[3].ToLower())); 79 | if (method != null) 80 | { 81 | var parmType = method.GetParameters().FirstOrDefault().ParameterType; 82 | var returnType = method.ReturnType.GenericTypeArguments[0]; 83 | InstanceParmDictionary.TryAdd(path.ToLower(), (new[] { serviceName, $"{type.Name}/{method.Name}" }, parmType, returnType)); 84 | vitual.Init(serviceName, $"{type.Name}/{method.Name}", parmType, returnType); 85 | return vitual; 86 | } 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | return default; 96 | } 97 | 98 | /// 99 | /// 通过代理程序集获取类型 100 | /// 101 | /// 102 | /// 103 | Type GetProxyClient(string className) 104 | { 105 | if (!InstanceDictionary.TryGetValue(className, out var messageType)) 106 | { 107 | messageType = RpcInterfaceType.Types.Value.FirstOrDefault(x => x.Name.ToLower().Contains(className.ToLower())); 108 | if (messageType != null) 109 | { 110 | InstanceDictionary.TryAdd(className, messageType); 111 | } 112 | } 113 | if (messageType != null) 114 | { 115 | return messageType; 116 | } 117 | return null; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Oxygen.ServerProxyFactory/VirtualProxyServer.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.IServerProxyFactory; 2 | using System; 3 | using System.Reflection; 4 | using System.Threading.Tasks; 5 | 6 | namespace Oxygen.ServerProxyFactory 7 | { 8 | /// 9 | /// 虚拟代理类 10 | /// 11 | public class VirtualProxyServer: IVirtualProxyServer 12 | { 13 | private readonly IRemoteProxyGenerator _proxyGenerator; 14 | 15 | public VirtualProxyServer(IRemoteProxyGenerator proxyGenerator) 16 | { 17 | _proxyGenerator = proxyGenerator; 18 | } 19 | /// 20 | /// 初始化代理 21 | /// 22 | /// 23 | /// 24 | /// 25 | public void Init(string serverName, string pathName, Type inputType, Type returnType) 26 | { 27 | ServerName = serverName; 28 | PathName = pathName; 29 | InputType = inputType; 30 | ReturnType = returnType; 31 | } 32 | public string ServerName { get; set; } 33 | public string PathName { get; set; } 34 | public Type InputType { get; set; } 35 | public Type ReturnType { get; set; } 36 | /// 37 | /// 通过虚拟代理发送请求 38 | /// 39 | /// 40 | /// 41 | public async Task SendAsync(object input) 42 | { 43 | return await _proxyGenerator.SendObjAsync(input, ReturnType, ServerName, PathName); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Oxygen/Oxygen.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 0.0.3.0 6 | 0.0.3.0 7 | 0.0.3.0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Oxygen/OxygenClientService.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Microsoft.Extensions.Hosting; 3 | using Oxygen.CommonTool; 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Oxygen 9 | { 10 | /// 11 | /// OxygenClient服务 12 | /// 13 | public class OxygenClientService : IHostedService 14 | { 15 | private static bool _stopFlag = false; 16 | public OxygenClientService(ILifetimeScope container) 17 | { 18 | OxygenIocContainer.BuilderIocContainer(container); 19 | AppDomain.CurrentDomain.ProcessExit += ProcessExit; 20 | } 21 | 22 | private Task _executingTask; 23 | 24 | public async Task StartAsync(CancellationToken cancellationToken) 25 | { 26 | _executingTask = ExecuteAsync(); 27 | if (_executingTask.IsCompleted) 28 | { 29 | await _executingTask; 30 | } 31 | } 32 | 33 | public async Task StopAsync(CancellationToken cancellationToken) 34 | { 35 | OxygenIocContainer.DisposeIocContainer(); 36 | await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken)); 37 | _stopFlag = true; 38 | } 39 | 40 | public async void ProcessExit(object s, EventArgs e) 41 | { 42 | if (!_stopFlag) 43 | { 44 | await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite)); 45 | } 46 | } 47 | 48 | public async Task ExecuteAsync() 49 | { 50 | await Task.Delay(5000); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Oxygen/OxygenHostService.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using MediatR; 3 | using Microsoft.Extensions.Hosting; 4 | using Oxygen.CommonTool; 5 | using Oxygen.DaprActorProvider; 6 | using Oxygen.IRpcProviderService; 7 | using Oxygen.ServerProxyFactory; 8 | using System; 9 | using System.Runtime.Loader; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace Oxygen 14 | { 15 | /// 16 | /// OxygenHost服务 17 | /// 18 | public class OxygenHostService : IHostedService 19 | { 20 | private readonly IRpcServerProvider _rpcServerProvider; 21 | private static bool _stopFlag = false; 22 | private readonly ILifetimeScope _container; 23 | public OxygenHostService(IRpcServerProvider rpcServerProvider, ILifetimeScope container) 24 | { 25 | OxygenIocContainer.BuilderIocContainer(container); 26 | _container = container; 27 | _rpcServerProvider = rpcServerProvider; 28 | } 29 | 30 | private Task _executingTask; 31 | 32 | public async Task StartAsync(CancellationToken cancellationToken) 33 | { 34 | _executingTask = ExecuteAsync(); 35 | if (_executingTask.IsCompleted) 36 | { 37 | await _executingTask; 38 | } 39 | if (OxygenSetting.OpenActor) 40 | await _rpcServerProvider.OpenServer((x) => ActorServiceBuilder.RegisterActorMiddleware(x, _container)); 41 | else 42 | await _rpcServerProvider.OpenServer(); 43 | LocalProxyGenerator.LoadMethodDelegate(); 44 | } 45 | 46 | public async Task StopAsync(CancellationToken cancellationToken) 47 | { 48 | await CloseOxygenService(); 49 | OxygenIocContainer.DisposeIocContainer(); 50 | await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken)); 51 | _stopFlag = true; 52 | } 53 | 54 | public async void ProcessExit(object s, EventArgs e) 55 | { 56 | if (!_stopFlag) 57 | { 58 | await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite)); 59 | } 60 | } 61 | 62 | public async Task ExecuteAsync() 63 | { 64 | await Task.Delay(5000); 65 | } 66 | 67 | public async Task CloseOxygenService() 68 | { 69 | try 70 | { 71 | await _rpcServerProvider.CloseServer(); 72 | } 73 | catch (Exception) 74 | { 75 | 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /src/Oxygen/RpcConfigurationModule.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Oxygen.CommonTool; 7 | using Oxygen.IRpcProviderService; 8 | using Oxygen.ServerProxyFactory; 9 | using System; 10 | using System.Threading.Tasks; 11 | 12 | namespace Oxygen 13 | { 14 | /// 15 | /// 配置中心 16 | /// 17 | public static class RpcConfigurationModule 18 | { 19 | private static bool CONFIGSERVICE = false; 20 | /// 21 | /// 依赖注入Oxygen服务 22 | /// 23 | /// 24 | /// 25 | public static ContainerBuilder RegisterOxygen(this ContainerBuilder builder) 26 | { 27 | //注入rpc服务 28 | switch (OxygenSetting.MeshType) 29 | { 30 | case EnumMeshType.None: 31 | case EnumMeshType.Istio: 32 | switch (OxygenSetting.ProtocolType) 33 | { 34 | case EnumProtocolType.TCP: 35 | case EnumProtocolType.HTTP11: 36 | default: 37 | builder.RegisterModule(new DotNettyRpcProviderService.Module()); 38 | break; 39 | case EnumProtocolType.HTTP2: 40 | builder.RegisterModule(new KestrelRpcProviderService.Module()); 41 | break; 42 | } 43 | break; 44 | case EnumMeshType.Dapr: 45 | builder.RegisterModule(new KestrelRpcProviderService.Module());//dapr目前仅支持kestrel 46 | builder.RegisterModule(new DaprActorProvider.Module());//加载dapr actor 47 | break; 48 | } 49 | //注入序列化服务 50 | builder.RegisterModule(new MessagePackSerializeService.Module()); 51 | //注入代理工厂 52 | builder.RegisterModule(new ServerProxyFactory.Module()); 53 | //注入通用服务 54 | builder.RegisterModule(new CommonTool.Module()); 55 | return builder; 56 | } 57 | /// 58 | /// 注册成为Oxygen服务节点 59 | /// 60 | /// 61 | /// 62 | /// 63 | public static IHostBuilder UseOxygenService(this IHostBuilder hostBuilder, Action collection) 64 | { 65 | CONFIGSERVICE = true; 66 | //注入线程同步服务 67 | hostBuilder.ConfigureServices((a, b) => collection(a, b)); 68 | return hostBuilder; 69 | } 70 | /// 71 | /// 注册oxygen配置节 72 | /// 73 | /// 74 | /// 75 | /// 76 | public static IServiceCollection ConfigureOxygen(this IServiceCollection services, IConfiguration configuration) 77 | { 78 | //注入默认配置节 79 | new OxygenSetting(configuration); 80 | if (CONFIGSERVICE) 81 | { 82 | //注入Host启动类 83 | services.AddHostedService(); 84 | } 85 | else 86 | { 87 | //注入Client启动类 88 | services.AddHostedService(); 89 | } 90 | return services; 91 | } 92 | //注入方法前、方法后、异常的处理程序 93 | public static IServiceCollection RegisterPipelineHandler(this IServiceCollection services, Func beforeFunc = null, Func afterFunc = null, Func> exceptionFunc = null) 94 | { 95 | LocalMethodAopProvider.RegisterPipelineHandler(beforeFunc, afterFunc, exceptionFunc); 96 | return services; 97 | } 98 | /// 99 | /// 注册oxygen mesh追踪头管道 100 | /// 101 | /// 102 | /// 103 | 104 | public static IApplicationBuilder UseOxygenTrace(this IApplicationBuilder builder) 105 | { 106 | builder.UseMiddleware(); 107 | return builder; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /test/Application.Interface/Application.Interface.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/Application.Interface/ApplicationBaseResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Application.Interface 6 | { 7 | public class ApplicationBaseResult 8 | { 9 | public void SetResult(int code, string message, object data = null) 10 | { 11 | Code = code; 12 | Message = message; 13 | Data = data; 14 | } 15 | 16 | public int Code { get; set; } 17 | public string Message { get; set; } 18 | 19 | public object Data { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/Application.Interface/Interfaces/IUserActorService.cs: -------------------------------------------------------------------------------- 1 | using Application.Interface.UseCase.Dto; 2 | using Dapr.Actors; 3 | using Oxygen.CsharpClientAgent; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Application.Interface.Interfaces 10 | { 11 | 12 | [ActorService(false)] 13 | public interface IUserActorService : IActor 14 | { 15 | Task Login(LoginInput input); 16 | Task Register(RegisterInput input); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/Application.Interface/Interfaces/IUserLoginUseCase.cs: -------------------------------------------------------------------------------- 1 | using Application.Interface.UseCase.Dto; 2 | using Oxygen.CsharpClientAgent; 3 | using System.Threading.Tasks; 4 | 5 | namespace Application.Interface 6 | { 7 | [RemoteService("serversample")] 8 | public interface IUserLoginUseCase 9 | { 10 | Task Login(LoginInput input); 11 | Task Register(RegisterInput input); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/Application.Interface/UseCase/Dto/LoginInput.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.CsharpClientAgent; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Application.Interface 7 | { 8 | public class LoginInput : ActorModel 9 | { 10 | [ActorKey] 11 | public string UserName { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/Application.Interface/UseCase/Dto/Register.cs: -------------------------------------------------------------------------------- 1 | using Oxygen.CsharpClientAgent; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Application.Interface.UseCase.Dto 7 | { 8 | public class RegisterInput: ActorModel 9 | { 10 | [ActorKey] 11 | public string UserName { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/Application.Service/Actor/UserActorService.cs: -------------------------------------------------------------------------------- 1 | using Application.Interface; 2 | using Application.Interface.Interfaces; 3 | using Application.Interface.UseCase.Dto; 4 | using Autofac; 5 | using Dapr.Actors; 6 | using Dapr.Actors.Runtime; 7 | using Oxygen.DaprActorProvider; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace Application.Service.Actor 15 | { 16 | public class UserActorService : ActorBase, IUserActorService 17 | { 18 | public UserActorService(ActorService service, ActorId id, ILifetimeScope container) : base(service, id, container) { } 19 | public async Task Login(LoginInput input) 20 | { 21 | return await DoAsync(input.SaveChanges, async (x) => 22 | { 23 | await Task.Delay(0); 24 | if (instance != null && instance.UserName.Equals(input.UserName)) 25 | { 26 | x.Code = 0; 27 | x.Message = "登录成功"; 28 | } 29 | else 30 | { 31 | x.Code = 500; 32 | x.Message = "登录失败"; 33 | } 34 | }); 35 | } 36 | 37 | public async Task Register(RegisterInput input) 38 | { 39 | return await DoAsync(input.SaveChanges, async (x) => 40 | { 41 | await Task.Delay(0); 42 | if (string.IsNullOrEmpty(input.UserName)) 43 | { 44 | x.Code = -1; 45 | x.Message = "请输入用户名"; 46 | } 47 | if (instance != null && instance.UserName.Equals(input.UserName)) 48 | { 49 | x.Code = -1; 50 | x.Message = "该用户注册过了"; 51 | } 52 | else 53 | { 54 | instance = new LoginInput() { UserName = input.UserName }; 55 | x.Code = 0; 56 | x.Message = "注册成功"; 57 | } 58 | }); 59 | } 60 | /// 61 | /// 提供抽象方法SaveInstance的实现用于自动保存时调用仓储进行持久化 62 | /// 63 | /// 64 | protected override async Task SaveInstance() 65 | { 66 | //在真实环境中调用repo进行数据持久化 67 | Console.WriteLine("SaveInstance success"); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /test/Application.Service/ActorBase.cs: -------------------------------------------------------------------------------- 1 | using Application.Interface; 2 | using Autofac; 3 | using Dapr.Actors; 4 | using Dapr.Actors.Runtime; 5 | using Oxygen.DaprActorProvider; 6 | using System; 7 | using System.Threading.Tasks; 8 | 9 | namespace Application.Service 10 | { 11 | public abstract class ActorBase : OxygenActor 12 | { 13 | public ActorBase(ActorService service, ActorId id, ILifetimeScope container) : base(service, id, container) { } 14 | 15 | public async Task DoAsync(bool saveChanges, Func runMethod) 16 | { 17 | var result = new ApplicationBaseResult(); 18 | try 19 | { 20 | await runMethod(result); 21 | result.Code = 0; 22 | if (saveChanges) 23 | await base.SaveAll(true); 24 | } 25 | catch (Exception e) 26 | { 27 | result.Message = "出错了,请稍后再试"; 28 | } 29 | return result; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/Application.Service/Application.Service.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/Application.Service/UseCase/UserLoginUseCase.cs: -------------------------------------------------------------------------------- 1 | using Application.Interface; 2 | using Application.Interface.UseCase.Dto; 3 | using Dapr.Actors.Runtime; 4 | using Oxygen.IServerProxyFactory; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using Application.Interface.Interfaces; 9 | using Oxygen.DaprActorProvider; 10 | using Oxygen.CommonTool; 11 | 12 | namespace Application.Service 13 | { 14 | public class UserLoginUseCase : UseCaseBase, IUserLoginUseCase 15 | { 16 | private readonly IServerProxyFactory serverProxyFactory; 17 | public static List UserNames = new List(); 18 | public UserLoginUseCase(IServerProxyFactory serverProxyFactory) 19 | { 20 | this.serverProxyFactory = serverProxyFactory; 21 | } 22 | public async Task Login(LoginInput input) 23 | { 24 | if (OxygenSetting.OpenActor) 25 | { 26 | var actorService = serverProxyFactory.CreateProxy("1"); 27 | return await actorService.Login(input); 28 | } 29 | else 30 | { 31 | return await DoAsync(async (x) => 32 | { 33 | if (UserNames.Any(y => y.Equals(input.UserName))) 34 | { 35 | x.Code = 0; 36 | x.Message = "登录成功"; 37 | } 38 | else 39 | { 40 | x.Code = 500; 41 | x.Message = "登录失败"; 42 | } 43 | }); 44 | } 45 | } 46 | public async Task Register(RegisterInput input) 47 | { 48 | if (OxygenSetting.OpenActor) 49 | { 50 | var actorService = serverProxyFactory.CreateProxy(input.UserName); 51 | input.SaveChanges = true; 52 | return await actorService.Register(input); 53 | } 54 | else 55 | { 56 | return await DoAsync(async (x) => 57 | { 58 | await Task.Delay(0); 59 | if (string.IsNullOrEmpty(input.UserName)) 60 | { 61 | x.Code = -1; 62 | x.Message = "请输入用户名"; 63 | } 64 | if (UserNames.Any(y => y.Equals(input.UserName))) 65 | { 66 | x.Code = -1; 67 | x.Message = "该用户注册过了"; 68 | } 69 | else 70 | { 71 | UserNames.Add(input.UserName); 72 | x.Code = 0; 73 | x.Message = "注册成功"; 74 | } 75 | }); 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/Application.Service/UseCaseBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using Application.Interface; 6 | 7 | namespace Application.Service 8 | { 9 | public class UseCaseBase 10 | { 11 | public async Task DoAsync(Func runMethod) 12 | { 13 | var result = new ApplicationBaseResult(); 14 | try 15 | { 16 | await runMethod(result); 17 | result.Code = 0; 18 | } 19 | catch (Exception e) 20 | { 21 | result.Message = "出错了,请稍后再试"; 22 | } 23 | return result; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/Client.Sample/Client.Sample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7.1 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Always 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/Client.Sample/CustomHostService.cs: -------------------------------------------------------------------------------- 1 | using Application.Interface; 2 | using Application.Interface.Interfaces; 3 | using Application.Interface.UseCase.Dto; 4 | using Microsoft.Extensions.Hosting; 5 | using Oxygen.CommonTool; 6 | using Oxygen.DaprActorProvider; 7 | using Oxygen.ISerializeService; 8 | using Oxygen.IServerProxyFactory; 9 | using System; 10 | using System.Collections; 11 | using System.Collections.Generic; 12 | using System.Diagnostics; 13 | using System.Linq; 14 | using System.Net; 15 | using System.Net.Http; 16 | using System.Threading; 17 | using System.Threading.Tasks; 18 | 19 | namespace Client.Sample 20 | { 21 | public class CustomHostService : IHostedService 22 | { 23 | private readonly IServerProxyFactory _proxyFactory; 24 | public CustomHostService(IServerProxyFactory proxyFactory) 25 | { 26 | _proxyFactory = proxyFactory; 27 | } 28 | static int succ = 0; 29 | static int fail = 0; 30 | public async Task StartAsync(CancellationToken cancellationToken) 31 | { 32 | //测试调用 33 | Console.WriteLine("按任意键开始测试...."); 34 | Console.ReadLine(); 35 | EventWaitHandle _event = new AutoResetEvent(false); 36 | var callCount = 1; 37 | while (true) 38 | { 39 | Stopwatch sw = new Stopwatch(); 40 | sw.Start(); 41 | succ = 0; 42 | fail = 0; 43 | await fortest(1, callCount, async i => 44 | { 45 | var userserver = _proxyFactory.CreateProxy(); 46 | var result1 = await userserver.Register(new RegisterInput() { UserName = "admin" }); 47 | //var remoteProxy = _proxyFactory.CreateProxy("/api/serversample/UserLoginUseCase/register"); 48 | //var result1 = await remoteProxy.SendAsync(new RegisterInput() { UserName = "admin" }); 49 | if (result1 == null) 50 | { 51 | Interlocked.Increment(ref fail); 52 | } 53 | else 54 | { 55 | Interlocked.Increment(ref succ); 56 | } 57 | if (fail + succ == callCount) 58 | { 59 | _event.Set(); 60 | sw.Stop(); 61 | } 62 | }); 63 | _event.WaitOne(); 64 | Console.WriteLine($"RPC调用{callCount}次,成功{succ}次,失败{fail}次,累计耗时{sw.ElapsedMilliseconds}ms"); 65 | Console.WriteLine("按任意键继续按q退出...."); 66 | var returncode = Console.ReadLine(); 67 | if (returncode == "q") 68 | { 69 | break; 70 | } 71 | else if (int.TryParse(returncode, out int newcount)) 72 | { 73 | callCount = newcount; 74 | } 75 | else 76 | { 77 | callCount = 1; 78 | } 79 | } 80 | } 81 | async Task fortest(int type, int callCount, Func action) 82 | { 83 | if (type == 0) 84 | { 85 | for (var i = 0; i < callCount; i++) 86 | { 87 | await action.Invoke(i); 88 | } 89 | } 90 | else if (type == 1) 91 | { 92 | Parallel.For(0, callCount, async i => 93 | { 94 | await action.Invoke(i); 95 | }); 96 | } 97 | } 98 | public async Task StopAsync(CancellationToken cancellationToken) 99 | { 100 | await Task.CompletedTask; 101 | } 102 | } 103 | public class BaseDto 104 | { 105 | public Guid ID { get; set; } 106 | } 107 | public class AAA : BaseDto 108 | { 109 | 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /test/Client.Sample/Program.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Autofac.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Microsoft.Extensions.Logging; 7 | using Oxygen; 8 | using System.IO; 9 | using System.Threading.Tasks; 10 | 11 | namespace Client.Sample 12 | { 13 | class Program 14 | { 15 | private static IConfiguration Configuration { get; set; } 16 | static async Task Main(string[] args) 17 | { 18 | await CreateDefaultHost(args).Build().RunAsync(); 19 | } 20 | static IHostBuilder CreateDefaultHost(string[] args) => new HostBuilder() 21 | .ConfigureAppConfiguration((hostContext, config) => 22 | { 23 | config.SetBasePath(Directory.GetCurrentDirectory()); 24 | //获取oxygen配置节 25 | config.AddJsonFile("oxygen.json"); 26 | Configuration = config.Build(); 27 | }) 28 | .ConfigureContainer(builder => 29 | { 30 | //注入oxygen依赖 31 | builder.RegisterOxygen(); 32 | }) 33 | .ConfigureServices(services => 34 | { 35 | //注册oxygen配置节 36 | services.ConfigureOxygen(Configuration); 37 | services.AddLogging(configure => 38 | { 39 | configure.AddConsole(); 40 | }); 41 | services.AddHostedService(); 42 | services.AddHttpClient(); 43 | }) 44 | .UseServiceProviderFactory(new AutofacServiceProviderFactory()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/Client.Sample/oxygen.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sd797994/Oxygen/70e4b7cf4335cbd7bd5d30fb2fe43c3eda558e59/test/Client.Sample/oxygen.json -------------------------------------------------------------------------------- /test/Server.Sample/Program.cs: -------------------------------------------------------------------------------- 1 | using Application.Interface; 2 | using Application.Service; 3 | using Autofac; 4 | using Autofac.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | using Oxygen; 10 | using System; 11 | using System.IO; 12 | using System.Text.Json; 13 | using System.Text.Json.Serialization; 14 | using System.Threading.Tasks; 15 | 16 | namespace Server.Sample 17 | { 18 | class Program 19 | { 20 | private static IConfiguration Configuration { get; set; } 21 | static async Task Main(string[] args) 22 | { 23 | await CreateDefaultHost(args).Build().RunAsync(); 24 | } 25 | static IHostBuilder CreateDefaultHost(string[] args) => new HostBuilder() 26 | .ConfigureAppConfiguration((hostContext, config) => 27 | { 28 | config.SetBasePath(Directory.GetCurrentDirectory()); 29 | //获取oxygen配置节 30 | config.AddJsonFile("oxygen.json"); 31 | Configuration = config.Build(); 32 | }) 33 | .ConfigureContainer(builder => 34 | { 35 | //注入oxygen依赖 36 | builder.RegisterOxygen(); 37 | //注入本地业务依赖 38 | builder.RegisterType().As().InstancePerLifetimeScope(); 39 | 40 | }) 41 | //注册成为oxygen服务节点 42 | .UseOxygenService((context, services) => 43 | { 44 | //注册oxygen配置 45 | services.ConfigureOxygen(Configuration); 46 | services.RegisterPipelineHandler(async (obj) => 47 | { 48 | Console.WriteLine($"这里是方法前拦截器,拦截到参数:{JsonSerializer.Serialize(obj)}"); 49 | await Task.CompletedTask; 50 | }, async (result) => 51 | { 52 | Console.WriteLine($"这里是方法后拦截器,拦截到方法结果:{JsonSerializer.Serialize(result)}"); 53 | await Task.CompletedTask; 54 | }, async (exp) => 55 | { 56 | Console.WriteLine($"这里是方法异常拦截器,拦截到异常:{exp.Message}"); 57 | return await Task.FromResult(new ApplicationBaseResult() { Message = exp.Message }); 58 | }); 59 | services.AddLogging(configure => 60 | { 61 | configure.AddConsole(); 62 | }); 63 | services.AddAutofac(); 64 | }) 65 | .UseServiceProviderFactory(new AutofacServiceProviderFactory()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/Server.Sample/Server.Sample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7.1 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Always 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /test/Server.Sample/oxygen.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sd797994/Oxygen/70e4b7cf4335cbd7bd5d30fb2fe43c3eda558e59/test/Server.Sample/oxygen.json --------------------------------------------------------------------------------