Compare commits

...

64 Commits
v1.3.2 ... dev

Author SHA1 Message Date
LiZhengHao 7afd0d2870
Fix UriTool to retrieve correct IP when preferredNetworks is null (#336) 2024-11-26 01:16:00 +00:00
catcherwong c02eca0a22 prepare v1.3.10
Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-11-03 15:38:26 +00:00
LiZhengHao c26496ee89
preferredNetworks supports multiple prefixes and regular expressions (#331) 2024-10-29 05:59:15 +00:00
memoyu d2918c37e3
Upgrade System.Text.Json version to 8.0.5 (#332) 2024-10-26 15:24:03 +00:00
ZUOXIANGE 86ba5d878b
fix method name and comment (#323) 2024-09-23 10:40:04 +00:00
Catcher Wong ded6426c8c
Merge pull request #321 from nacos-group/naming-redo
align java sdk for naming redo
2024-09-21 14:12:51 +08:00
catcherwong 237bddc703 align java sdk for naming redo
Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-09-21 06:08:48 +00:00
catcherwong a3a8022361 add package readme for Nacos.Microsoft.Extensions.ServiceDiscovery
Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-09-20 15:24:14 +00:00
catcherwong d65d967e85 update workflow to support Nacos.Microsoft.Extensions.ServiceDiscovery
Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-09-20 15:08:23 +00:00
ZUOXIANGE 60de3cb573
Microsoft.Extensions.ServiceDiscovery support (#317)
* Microsoft.Extensions.ServiceDiscovery  support

* comment fix

* comment fix
2024-09-20 02:54:42 +00:00
catcherwong 59b02d31a7 update version to 1.3.9
Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-09-17 06:28:55 +00:00
Catcher Wong 4adf6d87fc
rm netcoreapp31 and some changes (#316)
Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-09-17 06:24:21 +00:00
Catcher Wong 2f77ac59f2
replace ntjson with stjson (#315)
Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-09-17 06:03:38 +00:00
Catcher Wong 85719ccffc
replace Grpc.Core with Grpc.Net.Client (#314)
* replace Grpc.Core with Grpc.Net.Client

Signed-off-by: catcherwong <catcher_hwq@outlook.com>

* update github action

Signed-off-by: catcherwong <catcher_hwq@outlook.com>

* fix java 8 setup

Signed-off-by: catcherwong <catcher_hwq@outlook.com>

---------

Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-09-17 05:38:45 +00:00
Catcher Wong cb9f1d16ce
TFM RM net5 and ADD net8 (#313)
Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-08-23 00:54:43 +00:00
Catcher Wong 686e3bfdc8
Merge branch 'master' into dev 2024-08-20 10:06:20 +08:00
Catcher Wong 3ea0fdef46
update version to 1.3.8 (#311)
Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-08-20 02:00:45 +00:00
memoyu 5f14c1d7d4
fix:block under multiple listeners to re-listen #300 #306 #307 (#308) 2024-08-11 15:07:16 +00:00
memoyu e6cf34bbfe
upgrade deps packages #302 (#304) 2024-07-25 01:06:35 +00:00
Catcher Wong 185362b2af
Fix naming http signature (#297) (#298)
* fix namint http signature error



* update version to 1.3.7



---------

Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-07-11 02:09:28 +00:00
Catcher Wong c7a50c7764
Fix naming http signature (#297)
* fix namint http signature error

Signed-off-by: catcherwong <catcher_hwq@outlook.com>

* update version to 1.3.7

Signed-off-by: catcherwong <catcher_hwq@outlook.com>

---------

Signed-off-by: catcherwong <catcher_hwq@outlook.com>
2024-07-11 01:19:04 +00:00
Catcher Wong 42fb0b56cd
Merge pull request #294 from nacos-group/dev
release v1.3.6
2024-06-27 08:59:04 +08:00
Catcher Wong 4359f77eaa
Update version.props 2024-06-27 00:49:36 +00:00
memoyu d40db4ab61
fix: if the config key changes, re-listen (#293) 2024-06-26 00:36:20 +00:00
Memoyu 7664d2d36a
docs: update sample code #284 (#285) 2023-11-21 10:36:36 +00:00
Catcher Wong 81d54b2bbf
Merge pull request #247 from nacos-group/dev
release v1.3.5
2023-04-11 23:08:47 +08:00
Catcher Wong 290cc6d784
update version (#246)
* update version

* chore: back setup java
2023-04-11 23:02:29 +08:00
Lycoris 40afb63e45
修复使用ak,sk 连接nacos时,403验签失败问题 (#245) 2023-04-11 22:42:52 +08:00
Lycoris 27e92bd2c6
修复http模式下,生成签名问题 (#244) 2023-04-11 13:45:25 +08:00
inversionhourglass 9a700ad62e
Config support custom endpoint port. (#230)
Co-authored-by: taoye <taoye01@benlai.com>
2022-08-31 22:25:18 +08:00
Catcher Wong f12c66a3b2
Merge pull request #223 from nacos-group/dev
prepare v1.3.4
2022-08-10 22:03:19 +08:00
Catcher Wong 68df6f5351
Merge pull request #222 from nacos-group/update-action-and-docs
chore: update nacos version and docs
2022-08-10 09:44:16 +08:00
catcherwong a29ef5086e chore: update nacos version and docs 2022-08-10 07:59:33 +08:00
Catcher Wong ce3de526e1
Merge pull request #221 from nacos-group/iss-218
fix(aspnetcore): wrong ASPNETCORE_URLS lead to wrong ip
2022-08-07 14:38:58 +08:00
catcherwong 76463efd14 chore: unpass unit test for RegSvcBgTaskTests 2022-08-07 14:32:30 +08:00
catcherwong 22537b78b8 fix(aspnetcore): wrong ASPNETCORE_URLS lead to wrong ip 2022-08-07 14:17:42 +08:00
Catcher Wong 9597becbf4
Merge pull request #219 from nacos-group/OpenApi
Add some open api
2022-07-30 12:03:43 +08:00
catcherwong 30d9b1c2a4 feat(openapi): add query system metrics 2022-07-30 11:54:08 +08:00
Catcher Wong 8ebdb6b1cb
Merge pull request #215 from nacos-group/batchReg
feat(naming): add batchRegisterInstance
2022-07-13 22:24:59 +08:00
catcherwong 15994e4463 feat(naming): add batchRegisterInstance 2022-07-13 21:39:01 +08:00
catcherwong 4b11429f76 feat: add namespace operation in openapi 2022-07-06 23:11:23 +08:00
Catcher Wong cca6ba4921
Merge pull request #214 from nacos-group/dev
fix: NU5100 error when packing Nacos.System.Configuration
2022-07-03 10:15:45 +08:00
catcherwong f2d57d6640 fix: NU5100 error when packing Nacos.System.Configuration 2022-07-03 10:06:46 +08:00
Catcher Wong 7d67c1d8da
Merge pull request #213 from nacos-group/dev
update version to 1.3.4
2022-07-02 09:23:38 +08:00
catcherwong 31d4465e16 update version to 1.3.4 2022-07-02 09:05:45 +08:00
Catcher Wong 4e78c2d059
Merge pull request #212 from zlzforever/dev
upgrade deps packages
2022-07-02 00:02:48 +08:00
Lewis Zou 8184ce58e0 upgrade deps packages 2022-07-01 23:46:46 +08:00
Catcher Wong 1e995cdd76
Merge pull request #208 from nacos-group/dev
v1.3.3 release
2022-05-30 23:01:06 +08:00
catcherwong 66712bc22c docs: add v1.3.3 release note 2022-05-30 22:43:53 +08:00
Catcher Wong db995de99e
Merge pull request #207 from nacos-group/docs/package-readme-file
docs: package readme file
2022-05-30 22:01:36 +08:00
catcherwong 8c8e5879f7 docs: package readme file 2022-05-30 14:05:18 +08:00
Catcher Wong 17a7dadfa1
Merge pull request #206 from nacos-group/iss-205
feat: config cache from JM.SNAPSHOT.PATH at first (#205)
2022-05-29 23:51:40 +08:00
catcherwong b8cd831e97 feat: config cache from JM.SNAPSHOT.PATH at first (#205) 2022-05-29 23:34:03 +08:00
Catcher Wong e13041e8a3
Merge pull request #204 from nacos-group/update-ci-nacosversion
chore: update ci nacos version
2022-05-26 10:40:31 +08:00
Catcher Wong b728ca143d
Improve Microsoft.Extensions.Configuration Integration (#203)
* feat: INacosConfigService and ILoggerFactory from di

* test: add more ut for ms config

* feat: AddNacosV2Configuration parameter with useful things

* feat: add UseNacosConfig for hostbuilder extension

* style: remove some useless code and add some comments
2022-05-26 10:32:51 +08:00
catcherwong d88a74a6d4 chore: update ci nacos version 2022-05-25 23:07:40 +08:00
Catcher Wong 83eae087eb
Merge pull request #202 from nacos-group/dev
v1.3.3
2022-05-24 19:24:13 +08:00
Catcher Wong 28b66a6181
Merge pull request #201 from nacos-group/iss-200-startup-error
fix: ReceiveConfigInfo dict get item error in startup (#200)
2022-05-24 19:10:37 +08:00
catcherwong 4718fba1e2 fix: ReceiveConfigInfo dict get item error in startup (#200) 2022-05-24 17:02:02 +08:00
Catcher Wong 7936de782f
Merge pull request #197 from nacos-group/dev
fix IsSubscribed exception when NamingUseRpc is false
2022-04-09 09:32:06 +08:00
Catcher Wong 99575dde9d
Merge pull request #196 from nacos-group/iss-194
fix(naming): IsSubscribed exception when NamingUseRpc is false (#194)
2022-04-09 09:24:33 +08:00
catcherwong 671246e462 fix(naming): IsSubscribed exception when NamingUseRpc is false (#194) 2022-04-09 09:19:08 +08:00
Catcher Wong a5c22793ff
Merge pull request #193 from SpringHgui/patch-1
[fix] on empty config throw ArgumentNullException
2022-03-21 22:17:43 +08:00
Gui.H da0b20b68d
Update MyNacosConfigFilter.cs
[fix] on empty config throw ArgumentNullException
2022-03-21 20:07:26 +08:00
131 changed files with 2966 additions and 1224 deletions

View File

@ -11,11 +11,14 @@ jobs:
name: build on windows
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
dotnet-version: |
8.0.x
7.0.x
6.0.x
- name: Build with dotnet
run: |
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json src\Nacos\Nacos.csproj
@ -31,23 +34,27 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
dotnet-version: |
8.0.x
7.0.x
6.0.x
- name: Setup up JDK 1.8
uses: actions/setup-java@v1
uses: actions/setup-java@v4
with:
java-version: 1.8
distribution: 'temurin'
java-version: 8
- name: Show dotnet Version
run: dotnet --version
- name: Show java Version
run: java -version
- name: Setup Stable 1.x Nacos Server
run: |
wget https://github.com/alibaba/nacos/releases/download/1.4.2/nacos-server-1.4.2.tar.gz
tar -xvf nacos-server-1.4.2.tar.gz
wget https://github.com/alibaba/nacos/releases/download/1.4.4/nacos-server-1.4.4.tar.gz
tar -xvf nacos-server-1.4.4.tar.gz
cd nacos/bin
# sed -i 's/nacos.core.auth.enabled=false/nacos.core.auth.enabled=true/g' ../conf/application.properties
chmod 755 startup.sh
@ -70,7 +77,7 @@ jobs:
dotnet test tests/Nacos.Tests/Nacos.Tests.csproj --filter "Category=all|Category=1x" --no-restore
- name: Upload Nacos logs
if: always()
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: nacos-logs
path: /home/runner/work/nacos-sdk-csharp/nacos-sdk-csharp/nacos/logs

View File

@ -11,11 +11,14 @@ jobs:
name: build on windows
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
dotnet-version: |
8.0.x
7.0.x
6.0.x
- name: Build with dotnet
run: |
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json src\Nacos\Nacos.csproj
@ -31,23 +34,27 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
dotnet-version: |
8.0.x
7.0.x
6.0.x
- name: Setup up JDK 1.8
uses: actions/setup-java@v1
uses: actions/setup-java@v4
with:
java-version: 1.8
distribution: 'temurin'
java-version: 8
- name: Show dotnet Version
run: dotnet --version
- name: Show java Version
run: java -version
- name: Setup Stable 2.x Nacos Server
run: |
wget https://github.com/alibaba/nacos/releases/download/2.0.2/nacos-server-2.0.2.tar.gz
tar -xvf nacos-server-2.0.2.tar.gz
wget https://github.com/alibaba/nacos/releases/download/2.1.0/nacos-server-2.1.0.tar.gz
tar -xvf nacos-server-2.1.0.tar.gz
cd nacos/bin
# sed -i 's/nacos.core.auth.enabled=false/nacos.core.auth.enabled=true/g' ../conf/application.properties
chmod 755 startup.sh
@ -70,7 +77,7 @@ jobs:
dotnet test tests/Nacos.Tests/Nacos.Tests.csproj --filter "Category=all|Category=1x|Category=2x" --no-restore
- name: Upload Nacos logs
if: always()
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: nacos-logs
path: /home/runner/work/nacos-sdk-csharp/nacos-sdk-csharp/nacos/logs

View File

@ -13,23 +13,27 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
dotnet-version: |
8.0.x
7.0.x
6.0.x
- name: Setup up JDK 1.8
uses: actions/setup-java@v1
uses: actions/setup-java@v4
with:
java-version: 1.8
distribution: 'temurin'
java-version: 8
- name: Show dotnet Version
run: dotnet --version
- name: Show java Version
run: java -version
- name: Setup Unstable Nacos Server
run: |
wget https://github.com/alibaba/nacos/releases/download/2.0.3/nacos-server-2.0.3.tar.gz
tar -xvf nacos-server-2.0.3.tar.gz
wget https://github.com/alibaba/nacos/releases/download/2.1.1/nacos-server-2.1.1.tar.gz
tar -xvf nacos-server-2.1.1.tar.gz
cd nacos/bin
# sed -i 's/nacos.core.auth.enabled=false/nacos.core.auth.enabled=true/g' ../conf/application.properties
sed -i 's/Xms512m/Xms1024m/g' startup.sh
@ -55,7 +59,7 @@ jobs:
dotnet test tests/Nacos.Tests/Nacos.Tests.csproj --filter "Category=all|Category=1x|Category=2x" --no-restore
- name: Upload Nacos logs
if: always()
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: nacos-logs
path: /home/runner/work/nacos-sdk-csharp/nacos-sdk-csharp/nacos/logs

View File

@ -11,15 +11,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
dotnet-version: |
8.0.x
7.0.x
6.0.x
- name: Build with dotnet
run: |
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json src/Nacos/Nacos.csproj
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json src/Nacos.AspNetCore/Nacos.AspNetCore.csproj
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json src/Nacos.Microsoft.Extensions.ServiceDiscovery/Nacos.Microsoft.Extensions.ServiceDiscovery.csproj
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json src/Nacos.Microsoft.Extensions.Configuration/Nacos.Microsoft.Extensions.Configuration.csproj
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json parsers/Nacos.IniParser/Nacos.IniParser.csproj
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json parsers/Nacos.YamlParser/Nacos.YamlParser.csproj
@ -29,12 +33,13 @@ jobs:
ver=alpha`date +%Y%m%d%H%M%S`
dotnet pack src/Nacos/Nacos.csproj --version-suffix $ver -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet pack src/Nacos.AspNetCore/Nacos.AspNetCore.csproj --version-suffix $ver -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet pack src/Nacos.Microsoft.Extensions.ServiceDiscovery/Nacos.Microsoft.Extensions.ServiceDiscovery.csproj --version-suffix $ver -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet pack src/Nacos.Microsoft.Extensions.Configuration/Nacos.Microsoft.Extensions.Configuration.csproj --version-suffix $ver -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet pack parsers/Nacos.IniParser/Nacos.IniParser.csproj --version-suffix $ver -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet pack parsers/Nacos.YamlParser/Nacos.YamlParser.csproj --version-suffix $ver -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet msbuild -p:Configuration=Release -t:pack -p:PackageOutputPath=/home/runner/work/nugetpkgs -p:VersionSuffix=$ver src/Nacos.System.Configuration/Nacos.System.Configuration.csproj
- name: Upload artifact
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v3
with:
name: nugetpkgs
path: /home/runner/work/nugetpkgs
@ -46,9 +51,10 @@ jobs:
steps:
- name: Download build artifacts
uses: actions/download-artifact@v1
uses: actions/download-artifact@v3
with:
name: nugetpkgs
path: nugetpkgs
- name: list nugetpkgs
run: ls nugetpkgs
- name: Release

View File

@ -11,15 +11,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
dotnet-version: |
8.0.x
7.0.x
6.0.x
- name: Build with dotnet
run: |
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json src/Nacos/Nacos.csproj
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json src/Nacos.AspNetCore/Nacos.AspNetCore.csproj
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json src/Nacos.Microsoft.Extensions.ServiceDiscovery/Nacos.Microsoft.Extensions.ServiceDiscovery.csproj
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json src/Nacos.Microsoft.Extensions.Configuration/Nacos.Microsoft.Extensions.Configuration.csproj
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json parsers/Nacos.IniParser/Nacos.IniParser.csproj
dotnet build --configuration Release --source https://api.nuget.org/v3/index.json parsers/Nacos.YamlParser/Nacos.YamlParser.csproj
@ -28,12 +32,13 @@ jobs:
run: |
dotnet pack src/Nacos/Nacos.csproj -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet pack src/Nacos.AspNetCore/Nacos.AspNetCore.csproj -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet pack src/Nacos.Microsoft.Extensions.ServiceDiscovery/Nacos.Microsoft.Extensions.ServiceDiscovery.csproj -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet pack src/Nacos.Microsoft.Extensions.Configuration/Nacos.Microsoft.Extensions.Configuration.csproj -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet pack parsers/Nacos.IniParser/Nacos.IniParser.csproj -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet pack parsers/Nacos.YamlParser/Nacos.YamlParser.csproj -o /home/runner/work/nugetpkgs -c Release --no-build
dotnet msbuild -p:Configuration=Release -t:pack -p:PackageOutputPath=/home/runner/work/nugetpkgs src/Nacos.System.Configuration/Nacos.System.Configuration.csproj
- name: Upload artifact
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v3
with:
name: nugetpkgs
path: /home/runner/work/nugetpkgs
@ -45,9 +50,10 @@ jobs:
steps:
- name: Download build artifacts
uses: actions/download-artifact@v1
uses: actions/download-artifact@v3
with:
name: nugetpkgs
path: nugetpkgs
- name: list nugetpkgs
run: ls nugetpkgs
- name: Release

View File

@ -39,6 +39,18 @@ https://nacos-sdk-csharp.readthedocs.io/en/latest/
1. Configure in `Program.cs`
```cs
// after v1.3.3, we can use UseNacosConfig to simplify
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseNacosConfig(section: "NacosConfig", parser: null, logAction: null)
// .UseNacosConfig(section: "NacosConfig", parser: Nacos.YamlParser.YamlConfigurationStringParser.Instance logAction: null)
// .UseNacosConfig(section: "NacosConfig", parser: Nacos.IniParser.IniConfigurationStringParser.Instance logAction: null)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
// before v1.3.3
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, builder) =>
@ -55,7 +67,7 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
});
```
2. Modify `appsettings.json`

View File

@ -41,6 +41,18 @@ https://nacos-sdk-csharp.readthedocs.io/en/latest/
1. 在 `Program.cs` 进行如下配置
```cs
// v1.3.3 版本之后, 可以用 UseNacosConfig 来简化
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseNacosConfig(section: "NacosConfig", parser: null, logAction: null)
// .UseNacosConfig(section: "NacosConfig", parser: Nacos.YamlParser.YamlConfigurationStringParser.Instance logAction: null)
// .UseNacosConfig(section: "NacosConfig", parser: Nacos.IniParser.IniConfigurationStringParser.Instance logAction: null)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
// v1.3.3 版本之前
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, builder) =>
@ -57,7 +69,7 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
});
```
2. 修改 `appsettings.json`

View File

@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<NugetVersion>1.3.2</NugetVersion>
<NugetVersion>1.3.10</NugetVersion>
</PropertyGroup>
</Project>

View File

@ -4,6 +4,7 @@
2022
^^^^^^^^^^^^^
- `微服务 配置中心 Nacos .Net 5 <https://blog.csdn.net/i2blue/article/details/124827269>`_ 【2022-05-17】
- `待挖掘 <https://github.com/nacos-group/nacos-sdk-csharp>`_ 【2022-xx-xx】

View File

@ -28,3 +28,14 @@ SDK 在 v1.x 版本之后,就是默认用 grpc 的方式和 nacos server 对
针对 b 的情况,需要把 9848 暴露出来。
如果修改了默认端口或者是通过环境变量设置了偏移,自行调整对应端口,参考 https://nacos.io/zh-cn/docs/2.0.0-compatibility.html
3. nacos-sdk-csharp 版本与 nacos server 版本关系
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
nacos server 目前主要有 1.x 版本和 2.x 版本
nacos-sdk-csharp 有 0.x unofficial 版本 和 1.x 版本
nacos-sdk-csharp 0.x unofficial 版本 只能应用于 nacos server 1.x 版本
nacos-sdk-csharp 1.x 版本 可以同时应用于 nacos server 1.x 版本 和 2.x 版本

View File

@ -40,11 +40,11 @@
:hidden:
:caption: Release Note
releasenote/v1.2.1
releasenote/v1.2.2
releasenote/v1.3.0
releasenote/v1.3.1
releasenote/v1.3.2
releasenote/v1.3.3
.. toctree::
:maxdepth: 2

View File

@ -0,0 +1,18 @@
v1.3.3 (May 30th, 2022) 发布记录
=============================================
1. [NAMING] Fix IsSubscribed exception when NamingUseRpc is false (#194 #196)
#. [CONFIG] Fix ReceiveConfigInfo dict get item error in startup (#200 #201)
#. [CONFIG] Improve Microsoft.Extensions.Configuration Integration (#203 #204)
#. [CONFIG] Config cache path from JM.SNAPSHOT.PATH env at first (#205 #206)
#. [CORE] Add package readme file (#207)
#. [CI] Update version of Nacos Server
------------
1. [NAMING] 修复 NamingUseRpc 设置成 false 是 IsSubscribed 抛异常的问题 (#194 #196)
#. [CONFIG] 修复启动时 ReceiveConfigInfo 字典操作异常问题 (#200 #201)
#. [CONFIG] 优化 Microsoft.Extensions.Configuration 的集成 (#203 #204)
#. [CONFIG] 优化配置缓存的路径读取方式 (#205 #206)
#. [CORE] 添加nuget包说明文件 (#207)
#. [CI] 更新 Nacos Server 版本

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28803.156
# Visual Studio Version 17
VisualStudioVersion = 17.11.35303.130
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C473C3A7-1B44-4E1F-83C0-745AD0566FE9}"
EndProject
@ -47,6 +47,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.zh-cn.md = README.zh-cn.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nacos.Microsoft.Extensions.ServiceDiscovery", "src\Nacos.Microsoft.Extensions.ServiceDiscovery\Nacos.Microsoft.Extensions.ServiceDiscovery.csproj", "{46FDA099-A266-4E7B-9B5B-496575A54CCD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -109,6 +111,10 @@ Global
{CA0A661F-01D5-4DF7-9CD0-0399F89A8D47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA0A661F-01D5-4DF7-9CD0-0399F89A8D47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA0A661F-01D5-4DF7-9CD0-0399F89A8D47}.Release|Any CPU.Build.0 = Release|Any CPU
{46FDA099-A266-4E7B-9B5B-496575A54CCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46FDA099-A266-4E7B-9B5B-496575A54CCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46FDA099-A266-4E7B-9B5B-496575A54CCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46FDA099-A266-4E7B-9B5B-496575A54CCD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -128,6 +134,7 @@ Global
{CEE5E43E-F4E1-4E46-9B41-ABCBECDDEA63} = {CFFCAA8B-3562-420B-AA8B-C525CC1ECD78}
{7C20F5FB-33D4-460A-86F4-FC42122FA543} = {C473C3A7-1B44-4E1F-83C0-745AD0566FE9}
{CA0A661F-01D5-4DF7-9CD0-0399F89A8D47} = {25B1A184-1541-4F4E-A151-24A47CC08F34}
{46FDA099-A266-4E7B-9B5B-496575A54CCD} = {C473C3A7-1B44-4E1F-83C0-745AD0566FE9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A1C5215E-0E70-4C04-B21E-5209BCF32472}

View File

@ -2,7 +2,7 @@
<Import Project="../../build/version.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5.0;net6.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<PackageId>nacos-sdk-csharp.IniParser</PackageId>
<VersionPrefix>$(NugetVersion)</VersionPrefix>
<VersionSuffix></VersionSuffix>
@ -13,6 +13,7 @@
<RepositoryUrl>https://github.com/nacos-group/nacos-sdk-csharp</RepositoryUrl>
<ProjectUrl>https://github.com/nacos-group/nacos-sdk-csharp</ProjectUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>
</PackageReleaseNotes>
</PropertyGroup>
@ -24,14 +25,15 @@
<ItemGroup>
<None Include="../../LICENSE" Pack="true" Visible="false" PackagePath="" />
<None Include="README.md" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">

View File

@ -0,0 +1,21 @@
# nacos-sdk-csharp.IniParser
nacos-sdk-csharp.IniParser provides ini format config parser.
```csharp
builder.Host.UseNacosConfig(section: "NacosConfig", parser: Nacos.IniParser.IniConfigurationStringParser.Instance);
// or
builder.Host.ConfigureAppConfiguration((c, b) =>
{
var config = b.Build();
b.AddNacosV2Configuration(config.GetSection("NacosConfig"), parser: Nacos.IniParser.IniConfigurationStringParser.Instance);
});
```
## Links
* [Documentation](https://nacos-sdk-csharp.readthedocs.io/en/latest/)
* [nacos-sdk-csharp GitHub](https://github.com/nacos-group/nacos-sdk-csharp)

View File

@ -2,7 +2,7 @@
<Import Project="../../build/version.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5.0;net6.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<PackageId>nacos-sdk-csharp.YamlParser</PackageId>
<VersionPrefix>$(NugetVersion)</VersionPrefix>
<VersionSuffix></VersionSuffix>
@ -13,6 +13,7 @@
<RepositoryUrl>https://github.com/nacos-group/nacos-sdk-csharp</RepositoryUrl>
<ProjectUrl>https://github.com/nacos-group/nacos-sdk-csharp</ProjectUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>
</PackageReleaseNotes>
</PropertyGroup>
@ -24,14 +25,15 @@
<ItemGroup>
<None Include="../../LICENSE" Pack="true" Visible="false" PackagePath="" />
<None Include="README.md" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">

View File

@ -0,0 +1,21 @@
# nacos-sdk-csharp.YamlParser
nacos-sdk-csharp.YamlParser provides yaml/yml format config parser.
```csharp
builder.Host.UseNacosConfig(section: "NacosConfig", parser: Nacos.YamlParser.YamlConfigurationStringParser.Instance);
// or
builder.Host.ConfigureAppConfiguration((c, b) =>
{
var config = b.Build();
b.AddNacosV2Configuration(config.GetSection("NacosConfig"), parser: Nacos.YamlParser.YamlConfigurationStringParser.Instance);
});
```
## Links
* [Documentation](https://nacos-sdk-csharp.readthedocs.io/en/latest/)
* [nacos-sdk-csharp GitHub](https://github.com/nacos-group/nacos-sdk-csharp)

View File

@ -1,12 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Nacos.AspNetCore\Nacos.AspNetCore.csproj" />
<ProjectReference Include="..\..\src\Nacos.Microsoft.Extensions.ServiceDiscovery\Nacos.Microsoft.Extensions.ServiceDiscovery.csproj" />
</ItemGroup>
</Project>

View File

@ -83,6 +83,9 @@
public static string? AESDecrypt(string data, string key)
{
if (string.IsNullOrEmpty(data))
return null;
byte[] encryptedBytes = Convert.FromBase64String(data);
byte[] bKey = new byte[32];
Array.Copy(Encoding.UTF8.GetBytes(key.PadRight(bKey.Length)), bKey, bKey.Length);

View File

@ -9,10 +9,12 @@
public class NamingController : ControllerBase
{
private readonly Nacos.V2.INacosNamingService _client;
private readonly IHttpClientFactory _httpClientFactory;
public NamingController(Nacos.V2.INacosNamingService client)
public NamingController(Nacos.V2.INacosNamingService client, IHttpClientFactory httpClientFactory)
{
_client = client;
_httpClientFactory = httpClientFactory;
}
// GET n/g
@ -102,6 +104,17 @@
return res ?? "GetServicesOfServer";
}
// GET n/sd
[HttpGet("sd")]
public async Task<string> ServiceDiscovery()
{
var client = _httpClientFactory.CreateClient("app1");
var response = await client.GetAsync("/api/values").ConfigureAwait(false);
var res = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
return res;
}
// GET n/sub
[HttpGet("sub")]
public async Task<string> Subscribe()

View File

@ -0,0 +1,65 @@
namespace App3.Controllers
{
using Microsoft.AspNetCore.Mvc;
using Nacos.V2.Utils;
using System.Threading.Tasks;
[ApiController]
[Route("o")]
public class OpenApiController : ControllerBase
{
private readonly Nacos.OpenApi.INacosOpenApi _api;
public OpenApiController(Nacos.OpenApi.INacosOpenApi api)
{
_api = api;
}
// GET o/n-g
[HttpGet("n-g")]
public async Task<string> NamespaceGetAll()
{
var list = await _api.GetNamespacesAsync().ConfigureAwait(false);
var res = list.ToJsonString();
return res ?? "GetAllInstances";
}
// GET o/n-g
[HttpGet("n-c")]
public async Task<string> NamespaceCreate(string i, string n)
{
var flag = await _api.CreateNamespaceAsync(i, n, "").ConfigureAwait(false);
return flag.ToString();
}
// GET o/n-u
[HttpGet("n-u")]
public async Task<string> NamespaceUpdate(string i, string n)
{
var flag = await _api.UpdateNamespaceAsync(i, n, "").ConfigureAwait(false);
return flag.ToString();
}
// GET o/n-u
[HttpGet("n-d")]
public async Task<string> NamespaceDelete(string i)
{
var flag = await _api.DeleteNamespaceAsync(i).ConfigureAwait(false);
return flag.ToString();
}
// GET o/metrics
[HttpGet("metrics")]
public async Task<string> GetMetrics()
{
var flag = await _api.GetMetricsAsync(false).ConfigureAwait(false);
return flag.ToJsonString();
}
}
}

View File

@ -1,4 +1,5 @@
using Nacos.V2.DependencyInjection;
using Nacos.OpenApi;
var builder = WebApplication.CreateBuilder(args);
@ -27,6 +28,32 @@ builder.Services.AddNacosV2Naming(x =>
x.NamingUseRpc = true;
});
builder.Services.AddNacosOpenApi(x =>
{
x.ServerAddresses = new System.Collections.Generic.List<string> { "http://localhost:8848/" };
x.EndPoint = "";
x.Namespace = "cs";
});
// Microsoft.Extensions.ServiceDiscovery
builder.Services.AddServiceDiscovery(o =>
{
o.RefreshPeriod = TimeSpan.FromSeconds(60);
})
.AddConfigurationServiceEndpointProvider()
.AddNacosServiceEndpointProvider();
builder.Services.ConfigureHttpClientDefaults(static http =>
{
http.AddServiceDiscovery();
});
// use IHttpClientFactory
builder.Services.AddHttpClient("app1", cfg =>
{
cfg.BaseAddress = new Uri("http://app1");
});
builder.Services.AddControllers();
var app = builder.Build();
@ -38,9 +65,6 @@ if (app.Environment.IsDevelopment())
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.MapControllers();
app.Run("http://*:9632");

View File

@ -20,6 +20,17 @@ builder.Services.Configure<MsConfigApp.AppSettings>(builder.Configuration.GetSec
builder.Services.AddControllers();
// NOTE: after v1.3.3
// read configuration from config files
// default parser is json
builder.Host.UseNacosConfig(section: "NacosConfig");
// specify ini or yaml parser
// builder.Host.UseNacosConfig(section: "NacosConfig", parser: Nacos.IniParser.IniConfigurationStringParser.Instance);
// builder.Host.UseNacosConfig(section: "NacosConfig", parser: Nacos.YamlParser.YamlConfigurationStringParser.Instance);
/*
NOTE: before v1.3.3
builder.Host.ConfigureAppConfiguration((c, b) =>
{
var config = b.Build();
@ -27,13 +38,15 @@ builder.Host.ConfigureAppConfiguration((c, b) =>
// read configuration from config files
// default is json
// b.AddNacosV2Configuration(config.GetSection("NacosConfig"));
b.AddNacosV2Configuration(config.GetSection("NacosConfig"), logAction: x => x.AddSerilog(Log.Logger));
b.AddNacosV2Configuration(config.GetSection("NacosConfig"));
// specify ini or yaml
// b.AddNacosV2Configuration(config.GetSection("NacosConfig"), Nacos.IniParser.IniConfigurationStringParser.Instance);
// b.AddNacosV2Configuration(config.GetSection("NacosConfig"), Nacos.YamlParser.YamlConfigurationStringParser.Instance);
})
.UseSerilog();
b.AddNacosV2Configuration(config.GetSection("NacosConfig"), parser: Nacos.IniParser.IniConfigurationStringParser.Instance);
b.AddNacosV2Configuration(config.GetSection("NacosConfig"), parser: Nacos.YamlParser.YamlConfigurationStringParser.Instance);
});
*/
builder.Host.UseSerilog();
var app = builder.Build();

View File

@ -1,4 +1,4 @@
# MsConfigApp
# MsConfigApp
This sample shows how to integrate ASP.NET Core Configuration System.
@ -37,7 +37,9 @@ https://nacos.io/en-us/docs/quick-start-docker.html
"Password": "123456",
"AccessKey": "",
"SecretKey": "",
"EndPoint": "acm.aliyun.com"
"EndPoint": "acm.aliyun.com",
"ConfigFilterAssemblies": [ "MsConfigApp" ],
"ConfigFilterExtInfo": "{\"JsonPaths\":[\"ConnectionStrings.Default\"],\"Other\":\"xxxxxx\"}"
}
}
```
@ -69,10 +71,30 @@ The first one, its dataid is demo, group is DFAULT_GROUP and the value is as fol
}
```
> NOTE: If **ConfigFilterAssemblies** is not empty, using the following config value.
```JSON
{
"ConnectionStrings": {
"Default": "U2VydmVyPTEyNy4wLjAuMTtQb3J0PTMzMDY7RGF0YWJhc2U9ZGVtbztVc2VyIElkPXJvb3Q7UGFzc3dvcmQ9MTIzNDU2Ow=="
},
"version": "测试version",
"AppSettings": {
"Str": "val",
"num": 1,
"arr": [1, 2, 3],
"subobj": {
"a": "b"
}
}
}
```
The second one, its dataid is common, group is DFAULT_GROUP and the value is as following.
```JSON
{
"all": "test"
}
```
```

View File

@ -2,7 +2,7 @@
<Import Project="../../build/version.props" />
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<PackageId>nacos-sdk-csharp.AspNetCore</PackageId>
<VersionPrefix>$(NugetVersion)</VersionPrefix>
<VersionSuffix></VersionSuffix>
@ -13,7 +13,8 @@
<RepositoryUrl>https://github.com/nacos-group/nacos-sdk-csharp</RepositoryUrl>
<ProjectUrl>https://github.com/nacos-group/nacos-sdk-csharp</ProjectUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReleaseNotes>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>
</PackageReleaseNotes>
</PropertyGroup>
@ -28,6 +29,7 @@
<ItemGroup>
<None Include="../../LICENSE" Pack="true" Visible="false" PackagePath="" />
<None Include="README.md" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
@ -35,8 +37,8 @@
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

View File

@ -0,0 +1,31 @@
# nacos-sdk-csharp.AspNetCore
nacos-sdk-csharp.AspNetCore provides service registration and discovery With ASP.NET Core.
```csharp
builder.Services.AddNacosAspNet(x =>
{
x.ServerAddresses = new List<string> { "http://localhost:8848/" };
x.Namespace = "cs";
x.ServiceName = "App2";
x.GroupName = "DEFAULT_GROUP";
x.ClusterName = "DEFAULT";
x.Ip = "";
x.PreferredNetworks = "";
x.Port = 0;
x.Weight = 100;
x.RegisterEnabled = true;
x.InstanceEnabled = true;
x.Ephemeral = true;
x.Secure = false;
});
// or
builder.Services.AddNacosAspNet(builder.Configuration);
```
## Links
* [Documentation](https://nacos-sdk-csharp.readthedocs.io/en/latest/)
* [nacos-sdk-csharp GitHub](https://github.com/nacos-group/nacos-sdk-csharp)

View File

@ -1,147 +1,174 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Nacos.AspNetCore.Tests")]
namespace Nacos.AspNetCore
{
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
internal static class UriTool
{
public static IEnumerable<Uri> GetUri(IFeatureCollection features, string ip, int port, string preferredNetworks)
{
var splitChars = new char[] { ',', ';' };
var appPort = port <= 0 ? 80 : port;
// 1. config
if (!string.IsNullOrWhiteSpace(ip))
{
// it seems that nacos don't return the scheme
// so here use http only.
return new List<Uri> { new Uri($"http://{ip}:{appPort}") };
}
// 1.1. Ip is null && Port has value
if (string.IsNullOrWhiteSpace(ip) && appPort != 80)
{
return new List<Uri> { new Uri($"http://{GetCurrentIp(preferredNetworks)}:{appPort}") };
}
var address = string.Empty;
// 2. IServerAddressesFeature
if (features != null)
{
var addresses = features.Get<IServerAddressesFeature>();
var addressCollection = addresses?.Addresses;
if (addressCollection != null && addressCollection.Any())
{
var uris = new List<Uri>();
foreach (var item in addressCollection)
{
var url = ReplaceAddress(item, preferredNetworks);
uris.Add(new Uri(url));
}
return uris;
}
}
// 3. ASPNETCORE_URLS
address = Environment.GetEnvironmentVariable("ASPNETCORE_URLS");
if (!string.IsNullOrWhiteSpace(address))
{
var url = ReplaceAddress(address, preferredNetworks);
return url.Split(splitChars).Select(x => new Uri(x));
}
// 4. --urls
var cmdArgs = Environment.GetCommandLineArgs();
if (cmdArgs != null && cmdArgs.Any())
{
var cmd = cmdArgs.FirstOrDefault(x => x.StartsWith("--urls", StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrWhiteSpace(cmd))
{
address = cmd.Split('=')[1];
var url = ReplaceAddress(address, preferredNetworks);
return url.Split(splitChars).Select(x => new Uri(x));
}
}
// 5. current ip address third
address = $"http://{GetCurrentIp(preferredNetworks)}:{appPort}";
return new List<Uri> { new Uri(address) };
}
private static string ReplaceAddress(string address, string preferredNetworks)
{
var ip = GetCurrentIp(preferredNetworks);
if (address.Contains("*"))
{
address = address.Replace("*", ip);
}
else if (address.Contains("+"))
{
address = address.Replace("+", ip);
}
else if (address.Contains("localhost", StringComparison.OrdinalIgnoreCase))
{
address = address.Replace("localhost", ip, StringComparison.OrdinalIgnoreCase);
}
else if (address.Contains("0.0.0.0", StringComparison.OrdinalIgnoreCase))
{
address = address.Replace("0.0.0.0", ip, StringComparison.OrdinalIgnoreCase);
}
return address;
}
private static string GetCurrentIp(string preferredNetworks)
{
var instanceIp = "127.0.0.1";
try
{
// 获取可用网卡
var nics = NetworkInterface.GetAllNetworkInterfaces()?.Where(network => network.OperationalStatus == OperationalStatus.Up);
// 获取所有可用网卡IP信息
var ipCollection = nics?.Select(x => x.GetIPProperties())?.SelectMany(x => x.UnicastAddresses);
foreach (var ipadd in ipCollection)
{
if (!IPAddress.IsLoopback(ipadd.Address) && ipadd.Address.AddressFamily == AddressFamily.InterNetwork)
{
if (string.IsNullOrEmpty(preferredNetworks))
{
instanceIp = ipadd.Address.ToString();
break;
}
if (!ipadd.Address.ToString().StartsWith(preferredNetworks)) continue;
instanceIp = ipadd.Address.ToString();
break;
}
}
}
catch
{
// ignored
}
return instanceIp;
}
}
}
using System.Text.RegularExpressions;
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Nacos.AspNetCore.Tests")]
namespace Nacos.AspNetCore
{
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
internal static class UriTool
{
public static IEnumerable<Uri> GetUri(IFeatureCollection features, string ip, int port, string preferredNetworks)
{
var splitChars = new char[] { ',', ';' };
var appPort = port <= 0 ? 80 : port;
// 1. config
if (!string.IsNullOrWhiteSpace(ip))
{
// it seems that nacos don't return the scheme
// so here use http only.
return new List<Uri> { new Uri($"http://{ip}:{appPort}") };
}
// 1.1. Ip is null && Port has value
if (string.IsNullOrWhiteSpace(ip) && appPort != 80)
{
return new List<Uri> { new Uri($"http://{GetCurrentIp(preferredNetworks)}:{appPort}") };
}
var address = string.Empty;
// 2. IServerAddressesFeature
if (features != null)
{
var addresses = features.Get<IServerAddressesFeature>();
var addressCollection = addresses?.Addresses;
if (addressCollection != null && addressCollection.Any())
{
var uris = new List<Uri>();
foreach (var item in addressCollection)
{
var url = ReplaceAddress(item, preferredNetworks);
uris.Add(new Uri(url));
}
return uris;
}
}
// 3. ASPNETCORE_URLS
address = Environment.GetEnvironmentVariable("ASPNETCORE_URLS");
if (!string.IsNullOrWhiteSpace(address))
{
var url = ReplaceAddress(address, preferredNetworks);
var uris = url.Split(splitChars).Select(x => new Uri(x));
foreach (var item in uris)
{
if (!IPAddress.TryParse(item.Host, out _))
{
throw new Nacos.V2.Exceptions.NacosException("Invalid ip address from ASPNETCORE_URLS");
}
}
return uris;
}
// 4. --urls
var cmdArgs = Environment.GetCommandLineArgs();
if (cmdArgs != null && cmdArgs.Any())
{
var cmd = cmdArgs.FirstOrDefault(x => x.StartsWith("--urls", StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrWhiteSpace(cmd))
{
address = cmd.Split('=')[1];
var url = ReplaceAddress(address, preferredNetworks);
var uris = url.Split(splitChars).Select(x => new Uri(x));
foreach (var item in uris)
{
if (!IPAddress.TryParse(item.Host, out _))
{
throw new Nacos.V2.Exceptions.NacosException("Invalid ip address from --urls");
}
}
return uris;
}
}
// 5. current ip address third
address = $"http://{GetCurrentIp(preferredNetworks)}:{appPort}";
return new List<Uri> { new Uri(address) };
}
private static string ReplaceAddress(string address, string preferredNetworks)
{
var ip = GetCurrentIp(preferredNetworks);
if (address.Contains("*"))
{
address = address.Replace("*", ip);
}
else if (address.Contains("+"))
{
address = address.Replace("+", ip);
}
else if (address.Contains("localhost", StringComparison.OrdinalIgnoreCase))
{
address = address.Replace("localhost", ip, StringComparison.OrdinalIgnoreCase);
}
else if (address.Contains("0.0.0.0", StringComparison.OrdinalIgnoreCase))
{
address = address.Replace("0.0.0.0", ip, StringComparison.OrdinalIgnoreCase);
}
return address;
}
private static string GetCurrentIp(string preferredNetworks)
{
var instanceIp = "127.0.0.1";
try
{
// 获取可用网卡
var nics = NetworkInterface.GetAllNetworkInterfaces()?.Where(network => network.OperationalStatus == OperationalStatus.Up);
// 获取所有可用网卡IP信息
var ipCollection = nics?.Select(x => x.GetIPProperties())?.SelectMany(x => x.UnicastAddresses);
var preferredNetworksArr = string.IsNullOrEmpty(preferredNetworks)
? new string[0] : preferredNetworks.Split(",");
foreach (var ipadd in ipCollection)
{
if (!IPAddress.IsLoopback(ipadd.Address) &&
ipadd.Address.AddressFamily == AddressFamily.InterNetwork)
{
if (string.IsNullOrEmpty(preferredNetworks))
{
instanceIp = ipadd.Address.ToString();
break;
}
if (!preferredNetworksArr.Any(preferredNetwork =>
ipadd.Address.ToString().StartsWith(preferredNetwork)
|| Regex.IsMatch(ipadd.Address.ToString(), preferredNetwork))) continue;
instanceIp = ipadd.Address.ToString();
break;
}
}
}
catch
{
// ignored
}
return instanceIp;
}
}
}

View File

@ -16,10 +16,5 @@
/// Configuration group
/// </summary>
public string Group { get; set; }
/// <summary>
/// Tenant information. It corresponds to the Namespace field in Nacos.
/// </summary>
public string Tenant { get; set; }
}
}

View File

@ -1,6 +1,4 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Nacos.Microsoft.Extensions.Configuration.Tests")]
namespace Nacos.Microsoft.Extensions.Configuration
namespace Nacos.Microsoft.Extensions.Configuration
{
using global::Microsoft.Extensions.Configuration;
using Newtonsoft.Json;

View File

@ -2,7 +2,7 @@
<Import Project="../../build/version.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5.0;net6.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<PackageId>nacos-sdk-csharp.Extensions.Configuration</PackageId>
<VersionPrefix>$(NugetVersion)</VersionPrefix>
<VersionSuffix></VersionSuffix>
@ -13,6 +13,7 @@
<RepositoryUrl>https://github.com/nacos-group/nacos-sdk-csharp</RepositoryUrl>
<ProjectUrl>https://github.com/nacos-group/nacos-sdk-csharp</ProjectUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>
</PackageReleaseNotes>
</PropertyGroup>
@ -24,32 +25,42 @@
<ItemGroup>
<None Include="../../LICENSE" Pack="true" Visible="false" PackagePath="" />
<None Include="README.md" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Nacos\Nacos.csproj" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Nacos.Microsoft.Extensions.Configuration.Tests" />
</ItemGroup>
</Project>

View File

@ -1,8 +1,11 @@
namespace Microsoft.Extensions.Configuration
{
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Nacos.Microsoft.Extensions.Configuration;
using Nacos.V2;
using Nacos.V2.DependencyInjection;
using System;
public static class NacosConfigurationExtensions
@ -12,9 +15,18 @@
/// </summary>
/// <param name="builder">IConfigurationBuilder</param>
/// <param name="action">setup NacosConfigurationSource</param>
/// <param name="client">The nacos config client</param>
/// <param name="loggerFactory">The loggerFactory</param>
/// <param name="parser">The parser.</param>
/// <param name="logAction">The logAction.</param>
/// <returns>IConfigurationBuilder</returns>
public static IConfigurationBuilder AddNacosV2Configuration(
this IConfigurationBuilder builder, Action<NacosV2ConfigurationSource> action)
this IConfigurationBuilder builder,
Action<NacosV2ConfigurationSource> action,
INacosConfigService client = null,
ILoggerFactory loggerFactory = null,
INacosConfigurationParser parser = null,
Action<ILoggingBuilder> logAction = null)
{
if (builder == null)
{
@ -26,11 +38,11 @@
throw new ArgumentNullException(nameof(action));
}
var source = new NacosV2ConfigurationSource();
var source = new NacosV2ConfigurationSource(null, null);
action.Invoke(source);
source.NacosConfigurationParser ??= parser ?? DefaultJsonConfigurationStringParser.Instance;
action(source);
source.NacosConfigurationParser ??= DefaultJsonConfigurationStringParser.Instance;
BuildDISource(source, client, loggerFactory, logAction);
return builder.Add(source);
}
@ -40,12 +52,16 @@
/// </summary>
/// <param name="builder">IConfigurationBuilder</param>
/// <param name="configuration">Configuration binding nacos configuration source</param>
/// <param name="client">The nacos config client</param>
/// <param name="loggerFactory">The loggerFactory</param>
/// <param name="parser">The parser.</param>
/// <param name="logAction">The logging action.</param>
/// <param name="logAction">The logAction.</param>
/// <returns>IConfigurationBuilder</returns>
public static IConfigurationBuilder AddNacosV2Configuration(
this IConfigurationBuilder builder,
IConfiguration configuration,
INacosConfigService client = null,
ILoggerFactory loggerFactory = null,
INacosConfigurationParser parser = null,
Action<ILoggingBuilder> logAction = null)
{
@ -59,12 +75,79 @@
throw new ArgumentNullException(nameof(configuration));
}
var source = new NacosV2ConfigurationSource();
var source = new NacosV2ConfigurationSource(null, null);
configuration.Bind(source);
source.NacosConfigurationParser = parser ?? DefaultJsonConfigurationStringParser.Instance;
source.LoggingBuilder = logAction;
BuildDISource(source, client, loggerFactory, logAction);
return builder.Add(source);
}
/// <summary>
/// Use nacos config combine IHostBuilder and ConfigureAppConfiguration
/// </summary>
/// <param name="builder">host builder.</param>
/// <param name="section">basic nacos configuration section.</param>
/// <param name="parser">The parser.</param>
/// <param name="logAction">The logAction.</param>
/// <returns>IHostBuilder</returns>
public static IHostBuilder UseNacosConfig(this IHostBuilder builder, string section, INacosConfigurationParser parser = null, Action<ILoggingBuilder> logAction = null)
{
builder.ConfigureAppConfiguration((_, cfb) =>
{
var config = cfb.Build();
cfb.AddNacosV2Configuration(config.GetSection(section), parser: parser, logAction: logAction);
});
return builder;
}
#if NET5_0_OR_GREATER
/// <summary>
/// Use nacos config combine IWebHostBuilder and ConfigureAppConfiguration
/// </summary>
/// <param name="builder">host builder.</param>
/// <param name="section">basic nacos configuration section.</param>
/// <param name="parser">The parser.</param>
/// <param name="logAction">The logAction.</param>
/// <returns>IHostBuilder</returns>
public static AspNetCore.Hosting.IWebHostBuilder UseNacosConfig(this AspNetCore.Hosting.IWebHostBuilder builder, string section, INacosConfigurationParser parser = null, Action<ILoggingBuilder> logAction = null)
{
builder.ConfigureAppConfiguration((_, cfb) =>
{
var config = cfb.Build();
cfb.AddNacosV2Configuration(config.GetSection(section), parser: parser, logAction: logAction);
});
return builder;
}
#endif
private static void BuildDISource(
NacosV2ConfigurationSource source,
INacosConfigService client,
ILoggerFactory logFactory,
Action<ILoggingBuilder> logAction)
{
if (client == null)
{
IServiceCollection serviceCollection = new ServiceCollection();
var sdkAction = source.GetNacosSdkOptions();
serviceCollection.AddNacosV2Config(sdkAction);
serviceCollection.AddLogging(logAction ?? (x => x.AddConsole()));
var serviceProvider = serviceCollection.BuildServiceProvider();
client = serviceProvider.GetService<INacosConfigService>();
logFactory = serviceProvider.GetService<ILoggerFactory>();
}
source.Client = client ?? throw new Nacos.V2.Exceptions.NacosException("Can't get INacosConfigService instance from DI Container");
source.LoggerFactory = logFactory;
}
}
}

View File

@ -1,54 +0,0 @@
namespace Nacos.Microsoft.Extensions.Configuration.NacosLog
{
using global::Microsoft.Extensions.DependencyInjection;
using global::Microsoft.Extensions.Logging;
using System;
public class NacosLoggerFactory : ILoggerFactory
{
public ILogger CreateLogger(string name)
{
return _factory.CreateLogger(name);
}
public void AddProvider(ILoggerProvider provider)
{
}
public void Dispose()
{
_factory?.Dispose();
}
public static NacosLoggerFactory GetInstance(Action<ILoggingBuilder> builder = null)
{
if (_loggingFactory != null) return _loggingFactory;
_factory = GetLoggerFactory(builder);
var obj = new NacosLoggerFactory();
return _loggingFactory = obj;
}
private static ILoggerFactory _factory;
private static NacosLoggerFactory _loggingFactory;
private static ILoggerFactory GetLoggerFactory(Action<ILoggingBuilder> builder = null)
{
if (_factory != null) return _factory;
var serviceCollection = new ServiceCollection();
if (builder != null)
{
serviceCollection.AddLogging(builder);
}
else
{
serviceCollection.AddLogging(x => x.AddConsole());
}
ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
ILoggerFactory loggerFactory = serviceProvider.GetService<ILoggerFactory>();
return loggerFactory;
}
}
}

View File

@ -1,239 +1,192 @@
namespace Nacos.Microsoft.Extensions.Configuration
{
using global::Microsoft.Extensions.Configuration;
using global::Microsoft.Extensions.Logging;
using global::Microsoft.Extensions.Options;
using Nacos.V2;
using Nacos.V2.Config;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
internal class NacosV2ConfigurationProvider : ConfigurationProvider, IDisposable
{
private readonly NacosV2ConfigurationSource _configurationSource;
private readonly INacosConfigurationParser _parser;
private readonly INacosConfigService _client;
private readonly ConcurrentDictionary<string, string> _configDict;
private readonly Dictionary<string, MsConfigListener> _listenerDict;
private readonly ILogger _logger;
public NacosV2ConfigurationProvider(NacosV2ConfigurationSource configurationSource)
{
_configurationSource = configurationSource;
_parser = configurationSource.NacosConfigurationParser;
_configDict = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
_listenerDict = new Dictionary<string, MsConfigListener>();
var options = Options.Create(new NacosSdkOptions()
{
ServerAddresses = configurationSource.ServerAddresses,
Namespace = configurationSource.GetNamespace(),
AccessKey = configurationSource.AccessKey,
ContextPath = configurationSource.ContextPath,
EndPoint = configurationSource.EndPoint,
DefaultTimeOut = configurationSource.DefaultTimeOut,
SecretKey = configurationSource.SecretKey,
Password = configurationSource.Password,
UserName = configurationSource.UserName,
ListenInterval = 20000,
ConfigUseRpc = configurationSource.ConfigUseRpc,
ConfigFilterAssemblies = configurationSource.ConfigFilterAssemblies,
ConfigFilterExtInfo = configurationSource.ConfigFilterExtInfo,
});
var nacosLoggerFactory = Nacos.Microsoft.Extensions.Configuration.NacosLog.NacosLoggerFactory.GetInstance(configurationSource.LoggingBuilder);
_logger = nacosLoggerFactory.CreateLogger<NacosV2ConfigurationProvider>();
_client = new NacosConfigService(nacosLoggerFactory, options);
if (configurationSource.Listeners != null && configurationSource.Listeners.Any())
{
var tasks = new List<Task>();
foreach (var item in configurationSource.Listeners)
{
var listener = new MsConfigListener(item.DataId, item.Group, item.Optional, this, _logger);
tasks.Add(_client.AddListener(item.DataId, item.Group, listener));
_listenerDict.Add($"{item.DataId}#{item.Group}", listener);
}
Task.WaitAll(tasks.ToArray());
}
else
{
// after remove old v1 code, Listeners must be not empty
throw new Nacos.V2.Exceptions.NacosException("Listeners is empty!!");
}
}
namespace Nacos.Microsoft.Extensions.Configuration
{
using global::Microsoft.Extensions.Configuration;
using global::Microsoft.Extensions.Logging;
using Nacos.V2;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
// for test
internal NacosV2ConfigurationProvider(NacosV2ConfigurationSource configurationSource, ILogger logger, INacosConfigService client, INacosConfigurationParser parser)
internal class NacosV2ConfigurationProvider : ConfigurationProvider, IDisposable
{
private readonly NacosV2ConfigurationSource _configurationSource;
private readonly INacosConfigurationParser _parser;
private readonly INacosConfigService _client;
private readonly ConcurrentDictionary<string, string> _configDict;
private readonly Dictionary<string, MsConfigListener> _listenerDict;
private readonly ILogger _logger;
public NacosV2ConfigurationProvider(NacosV2ConfigurationSource configurationSource, INacosConfigService client, ILoggerFactory loggerFactory)
{
_configurationSource = configurationSource;
_parser = parser;
_configDict = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
_listenerDict = new Dictionary<string, MsConfigListener>();
_logger = logger;
_client = client;
if (configurationSource.Listeners != null && configurationSource.Listeners.Any())
{
var tasks = new List<Task>();
foreach (var item in configurationSource.Listeners)
{
var listener = new MsConfigListener(item.DataId, item.Group, item.Optional, this, _logger);
tasks.Add(_client.AddListener(item.DataId, item.Group, listener));
_listenerDict.Add($"{item.DataId}#{item.Group}", listener);
}
Task.WaitAll(tasks.ToArray());
}
else
{
// after remove old v1 code, Listeners must be not empty
throw new Nacos.V2.Exceptions.NacosException("Listeners is empty!!");
_configurationSource = configurationSource;
_parser = configurationSource.NacosConfigurationParser;
_configDict = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
_listenerDict = new Dictionary<string, MsConfigListener>();
_client = client;
_logger = loggerFactory?.CreateLogger<NacosV2ConfigurationProvider>();
if (configurationSource.Listeners != null && configurationSource.Listeners.Any())
{
var tasks = new List<Task>();
foreach (var item in configurationSource.Listeners)
{
var listener = new MsConfigListener(item.DataId, item.Group, item.Optional, this, _logger);
tasks.Add(_client.AddListener(item.DataId, item.Group, listener));
_listenerDict.Add($"{item.DataId}#{item.Group}", listener);
}
Task.WaitAll(tasks.ToArray());
}
else
{
// after remove old v1 code, Listeners must be not empty
throw new Nacos.V2.Exceptions.NacosException("Listeners is empty!!");
}
}
public void Dispose()
{
var tasks = new List<Task>();
foreach (var item in _listenerDict)
{
var arr = item.Key.Split('#');
var dataId = arr[0];
var group = arr[1];
tasks.Add(_client.RemoveListener(dataId, group, item.Value));
}
Task.WaitAll(tasks.ToArray());
_logger?.LogInformation($"Remove All Listeners");
}
public override void Load()
{
try
{
if (_configurationSource.Listeners != null && _configurationSource.Listeners.Any())
{
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var listener in _configurationSource.Listeners)
{
try
{
var config = _client.GetConfig(listener.DataId, listener.Group, 3000)
.ConfigureAwait(false).GetAwaiter().GetResult();
_configDict.AddOrUpdate($"{_configurationSource.GetNamespace()}#{listener.Group}#{listener.DataId}", config, (x, y) => config);
var data = _parser.Parse(config);
foreach (var item in data)
{
dict[item.Key] = item.Value;
}
}
catch (Exception ex)
{
_logger?.LogWarning(ex, "MS Config Query config error, dataid={0}, group={1}, tenant={2}", listener.DataId, listener.Group, listener.Tenant);
if (!listener.Optional)
{
throw;
}
}
}
Data = dict;
}
else
{
// after remove old v1 code, Listeners must be not empty
throw new Nacos.V2.Exceptions.NacosException("Listeners is empty!!");
}
}
catch (Exception ex)
{
_logger?.LogError(ex, "Load config error");
}
internal IDictionary<string, string> GetData() => Data;
public void Dispose()
{
var tasks = new List<Task>();
foreach (var item in _listenerDict)
{
var arr = item.Key.Split('#');
var dataId = arr[0];
var group = arr[1];
tasks.Add(_client.RemoveListener(dataId, group, item.Value));
}
Task.WaitAll(tasks.ToArray());
_logger?.LogInformation($"Remove All Listeners");
}
public override void Load()
{
try
{
if (_configurationSource.Listeners != null && _configurationSource.Listeners.Any())
{
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var listener in _configurationSource.Listeners)
{
try
{
var config = _client.GetConfig(listener.DataId, listener.Group, 3000)
.ConfigureAwait(false).GetAwaiter().GetResult();
_configDict.AddOrUpdate($"{_configurationSource.GetNamespace()}#{listener.Group}#{listener.DataId}", config, (x, y) => config);
var data = _parser.Parse(config);
foreach (var item in data)
{
dict[item.Key] = item.Value;
}
}
catch (Exception ex)
{
_logger?.LogWarning(ex, "MS Config Query config error, dataid={0}, group={1}, tenant={2}", listener.DataId, listener.Group, _configurationSource.GetNamespace());
if (!listener.Optional)
{
throw;
}
}
}
Data = dict;
}
else
{
// after remove old v1 code, Listeners must be not empty
throw new Nacos.V2.Exceptions.NacosException("Listeners is empty!!");
}
}
catch (Exception ex)
{
_logger?.LogError(ex, "Load config error");
}
}
// for test
internal void SetListener(string key, MsConfigListener listener)
{
_listenerDict[key] = listener;
}
internal class MsConfigListener : IListener
{
private string _dataId;
private string _group;
private bool _optional;
private NacosV2ConfigurationProvider _provider;
private string _key;
private ILogger _logger;
internal MsConfigListener(string dataId, string group, bool optional, NacosV2ConfigurationProvider provider, ILogger logger)
{
this._dataId = dataId;
this._group = group;
this._optional = optional;
this._provider = provider;
this._logger = logger;
_key = $"{provider._configurationSource.GetNamespace()}#{_group}#{_dataId}";
}
public void ReceiveConfigInfo(string configInfo)
{
_logger?.LogDebug("MsConfigListener Receive ConfigInfo 【{0}】", configInfo);
try
{
_provider._configDict[_key] = configInfo;
}
internal class MsConfigListener : IListener
{
private string _dataId;
private string _group;
private bool _optional;
private NacosV2ConfigurationProvider _provider;
private string _key;
private ILogger _logger;
internal MsConfigListener(string dataId, string group, bool optional, NacosV2ConfigurationProvider provider, ILogger logger)
{
this._dataId = dataId;
this._group = group;
this._optional = optional;
this._provider = provider;
this._logger = logger;
_key = $"{provider._configurationSource.GetNamespace()}#{_group}#{_dataId}";
}
public void ReceiveConfigInfo(string configInfo)
{
_logger?.LogDebug("MsConfigListener Receive ConfigInfo 【{0}】", configInfo);
try
{
_provider._configDict.AddOrUpdate(_key, configInfo, (x, y) => configInfo);
var nData = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var listener in _provider._configurationSource.Listeners)
{
var key = $"{_provider._configurationSource.GetNamespace()}#{listener.Group}#{listener.DataId}";
if (_provider._configDict[key] == null)
{
continue;
foreach (var listener in _provider._configurationSource.Listeners)
{
var key = $"{_provider._configurationSource.GetNamespace()}#{listener.Group}#{listener.DataId}";
if (!_provider._configDict.TryGetValue(key, out var config))
{
continue;
}
var data = _provider._parser.Parse(config);
foreach (var item in data)
{
nData[item.Key] = item.Value;
}
var data = _provider._parser.Parse(_provider._configDict[key]);
foreach (var item in data)
{
nData[item.Key] = item.Value;
}
}
_provider.Data = nData;
_provider.OnReload();
}
catch (Exception ex)
{
_logger?.LogWarning(ex, $"call back reload config error");
if (!_optional)
{
throw;
}
}
}
}
}
}
_provider.Data = nData;
_provider.OnReload();
}
catch (Exception ex)
{
_logger?.LogWarning(ex, $"call back reload config error");
if (!_optional)
{
throw;
}
}
}
}
}
}

View File

@ -9,26 +9,31 @@
public class NacosV2ConfigurationSource : Nacos.V2.NacosSdkOptions, IConfigurationSource
{
/// <summary>
/// The INacosConfigService.
/// </summary>
internal INacosConfigService Client;
/// <summary>
/// The ILoggerFactory.
/// </summary>
internal ILoggerFactory LoggerFactory;
/// <summary>
/// The configuration listeners
/// </summary>
public List<ConfigListener> Listeners { get; set; }
/// <summary>
/// Tenant information. It corresponds to the Namespace field in Nacos.
/// </summary>
[Obsolete("please use Namespace to configure")]
public string Tenant { get; set; }
/// <summary>
/// The configuration parser, default is json
/// </summary>
public INacosConfigurationParser NacosConfigurationParser { get; set; }
/// <summary>
/// The logging builder, default will use AddConsole
/// </summary>
public Action<ILoggingBuilder> LoggingBuilder { get; set; }
public NacosV2ConfigurationSource(INacosConfigService client, ILoggerFactory loggerFactory)
{
Client = client;
LoggerFactory = loggerFactory;
}
/// <summary>
/// Build the provider
@ -37,25 +42,43 @@
/// <returns>IConfigurationProvider</returns>
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new NacosV2ConfigurationProvider(this);
return new NacosV2ConfigurationProvider(this, Client, LoggerFactory);
}
public string GetNamespace()
{
// breaking change here after 1.3.3 release
// do not use tenant any more!!!!
if (Namespace.IsNotNullOrWhiteSpace())
{
return Namespace;
}
#pragma warning disable CS0618
else if (Tenant.IsNotNullOrWhiteSpace())
{
return Tenant;
}
#pragma warning restore CS0618
else
{
return string.Empty;
}
}
internal Action<NacosSdkOptions> GetNacosSdkOptions()
{
Action<NacosSdkOptions> action = (x) =>
{
x.ServerAddresses = this.ServerAddresses;
x.Namespace = this.Namespace;
x.AccessKey = this.AccessKey;
x.ContextPath = this.ContextPath;
x.EndPoint = this.EndPoint;
x.DefaultTimeOut = this.DefaultTimeOut;
x.SecretKey = this.SecretKey;
x.Password = this.Password;
x.UserName = this.UserName;
x.ListenInterval = this.ListenInterval;
x.ConfigUseRpc = this.ConfigUseRpc;
x.ConfigFilterAssemblies = this.ConfigFilterAssemblies;
x.ConfigFilterExtInfo = this.ConfigFilterExtInfo;
};
return action;
}
}
}

View File

@ -0,0 +1,28 @@
# nacos-sdk-csharp.Extensions.Configuration
nacos-sdk-csharp.Extensions.Configuration provides integration with ASP.NET Core Configuration System.
```csharp
builder.Host.UseNacosConfig(section: "NacosConfig");
// or
builder.Host.ConfigureAppConfiguration((c, b) =>
{
var config = b.Build();
// read configuration from config files
// default is json
// b.AddNacosV2Configuration(config.GetSection("NacosConfig"));
b.AddNacosV2Configuration(config.GetSection("NacosConfig"));
// specify ini or yaml
b.AddNacosV2Configuration(config.GetSection("NacosConfig"), parser: Nacos.IniParser.IniConfigurationStringParser.Instance);
b.AddNacosV2Configuration(config.GetSection("NacosConfig"), parser: Nacos.YamlParser.YamlConfigurationStringParser.Instance);
});
```
## Links
* [Documentation](https://nacos-sdk-csharp.readthedocs.io/en/latest/)
* [nacos-sdk-csharp GitHub](https://github.com/nacos-group/nacos-sdk-csharp)

View File

@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../build/version.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>nacos-sdk-csharp.Extensions.ServiceDiscovery</PackageId>
<VersionPrefix>$(NugetVersion)</VersionPrefix>
<VersionSuffix></VersionSuffix>
<Authors>nacos-sdk-csharp Contributors</Authors>
<Description>nacos csharp sdk</Description>
<PackageTags>nacos,csharp,sdk,servicediscovery</PackageTags>
<PackageProjectUrl>https://github.com/nacos-group/nacos-sdk-csharp</PackageProjectUrl>
<RepositoryUrl>https://github.com/nacos-group/nacos-sdk-csharp</RepositoryUrl>
<ProjectUrl>https://github.com/nacos-group/nacos-sdk-csharp</ProjectUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>
</PackageReleaseNotes>
</PropertyGroup>
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<None Include="../../LICENSE" Pack="true" Visible="false" PackagePath="" />
<None Include="README.md" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Nacos\Nacos.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,24 @@
#pragma warning disable SA1200
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.ServiceDiscovery;
#pragma warning restore SA1200
// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.Hosting;
public static class NacosServiceDiscoveryExtensions
{
/// <summary>
/// Configures a service discovery endpoint provider which uses <see cref="T:Nacos.V2.INacosNamingService" /> to resolve endpoints.
/// </summary>
/// <param name="services">The service collection.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddNacosServiceEndpointProvider(this IServiceCollection services)
{
ArgumentNullException.ThrowIfNull(services);
services.AddServiceDiscoveryCore();
services.AddSingleton<IServiceEndpointProviderFactory, NacosServiceEndPointProviderFactory>();
return services;
}
}

View File

@ -0,0 +1,71 @@
#pragma warning disable SA1200
using Nacos.Microsoft.Extensions.ServiceDiscovery;
using System.Net;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.ServiceDiscovery;
using Nacos.V2;
#pragma warning restore SA1200
// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.Hosting;
/// <summary>
/// NacosServiceEndpointProvider
/// </summary>
/// <param name="query">query</param>
/// <param name="hostName">hostName</param>
/// <param name="options">options</param>
/// <param name="logger">logger</param>
/// <param name="namingService">namingService</param>
/// <param name="timeProvider">timeProvider</param>
internal sealed partial class NacosServiceEndpointProvider(
ServiceEndpointQuery query,
string hostName,
IOptionsMonitor<NacosServiceEndpointProviderOptions> options,
ILogger<NacosServiceEndpointProvider> logger,
INacosNamingService namingService,
TimeProvider timeProvider) : NacosServiceEndpointProviderBase(query, logger, timeProvider), IHostNameFeature
{
protected override double RetryBackOffFactor => options.CurrentValue.RetryBackOffFactor;
protected override TimeSpan MinRetryPeriod => options.CurrentValue.MinRetryPeriod;
protected override TimeSpan MaxRetryPeriod => options.CurrentValue.MaxRetryPeriod;
protected override TimeSpan DefaultRefreshPeriod => options.CurrentValue.DefaultRefreshPeriod;
string IHostNameFeature.HostName => hostName;
/// <inheritdoc/>
public override string ToString() => "Nacos";
protected override async Task ResolveAsyncCore()
{
var endpoints = new List<ServiceEndpoint>();
var ttl = DefaultRefreshPeriod;
Log.AddressQuery(logger, ServiceName, hostName);
var selectInstances = await namingService.SelectInstances(hostName, true).ConfigureAwait(false);
foreach (var instance in selectInstances)
{
var ipAddress = new IPAddress(instance.Ip.Split('.').Select(a => Convert.ToByte(a)).ToArray());
var ipPoint = new IPEndPoint(ipAddress, instance.Port);
var serviceEndpoint = ServiceEndpoint.Create(ipPoint);
serviceEndpoint.Features.Set<IServiceEndpointProvider>(this);
if (options.CurrentValue.ShouldApplyHostNameMetadata(serviceEndpoint))
{
serviceEndpoint.Features.Set<IHostNameFeature>(this);
}
endpoints.Add(serviceEndpoint);
}
if (endpoints.Count == 0)
{
throw new InvalidOperationException($"No records were found for service '{ServiceName}' ( name: '{hostName}').");
}
SetResult(endpoints, ttl);
}
}

View File

@ -0,0 +1,38 @@
#pragma warning disable SA1200
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.ServiceDiscovery;
using Nacos.Microsoft.Extensions.ServiceDiscovery;
using Nacos.V2;
#pragma warning restore SA1200
// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.Hosting;
/// <summary>
/// NacosServiceEndPointProviderFactory
/// </summary>
/// <param name="options">options</param>
/// <param name="logger">logger</param>
/// <param name="timeProvider">timeProvider</param>
/// <param name="namingService">nacos namingService</param>
internal sealed partial class NacosServiceEndPointProviderFactory(
IOptionsMonitor<NacosServiceEndpointProviderOptions> options,
ILogger<NacosServiceEndpointProvider> logger,
TimeProvider timeProvider,
INacosNamingService namingService) : IServiceEndpointProviderFactory
{
/// <summary>
/// Tries to create an <see cref="T:Microsoft.Extensions.ServiceDiscovery.IServiceEndpointProvider" /> instance for the specified <paramref name="query" />.
/// </summary>
/// <param name="query">The service to create the provider for.</param>
/// <param name="provider">The provider.</param>
/// <returns><see langword="true" /> if the provider was created, <see langword="false" /> otherwise.</returns>
public bool TryCreateProvider(ServiceEndpointQuery query, [NotNullWhen(true)] out IServiceEndpointProvider? provider)
{
provider = new NacosServiceEndpointProvider(query, query.ServiceName, options, logger, namingService, timeProvider);
return true;
}
}

View File

@ -0,0 +1,23 @@
#pragma warning disable SA1200
using Microsoft.Extensions.Logging;
#pragma warning restore SA1200
namespace Nacos.Microsoft.Extensions.ServiceDiscovery;
/// <summary>
/// log base
/// </summary>
internal partial class NacosServiceEndpointProviderBase
{
/// <summary>
/// log
/// </summary>
internal static partial class Log
{
[LoggerMessage(1, LogLevel.Trace, "Resolving endpoints for service '{ServiceName}' using host lookup for name '{RecordName}'.", EventName = "AddressQuery")]
public static partial void AddressQuery(ILogger logger, string serviceName, string recordName);
[LoggerMessage(2, LogLevel.Debug, "Skipping endpoint resolution for service '{ServiceName}': '{Reason}'.", EventName = "SkippedResolution")]
public static partial void SkippedResolution(ILogger logger, string serviceName, string reason);
}
}

View File

@ -0,0 +1,159 @@
#pragma warning disable SA1200
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Microsoft.Extensions.ServiceDiscovery;
#pragma warning restore SA1200
namespace Nacos.Microsoft.Extensions.ServiceDiscovery;
/// <summary>
/// A service end point provider that uses Nacos to resolve the service end points.
/// </summary>
internal abstract partial class NacosServiceEndpointProviderBase : IServiceEndpointProvider
{
private readonly object _lock = new();
private readonly ILogger _logger;
private readonly CancellationTokenSource _disposeCancellation = new();
private readonly TimeProvider _timeProvider;
private long _lastRefreshTimeStamp;
private Task _resolveTask = Task.CompletedTask;
private bool _hasEndpoints;
private CancellationChangeToken _lastChangeToken;
private CancellationTokenSource _lastCollectionCancellation;
private List<ServiceEndpoint>? _lastEndpointCollection;
private TimeSpan _nextRefreshPeriod;
protected NacosServiceEndpointProviderBase(
ServiceEndpointQuery query,
ILogger logger,
TimeProvider timeProvider)
{
ServiceName = query.ToString()!;
_logger = logger;
_lastEndpointCollection = null;
_timeProvider = timeProvider;
_lastRefreshTimeStamp = _timeProvider.GetTimestamp();
var cancellation = _lastCollectionCancellation = new CancellationTokenSource();
_lastChangeToken = new CancellationChangeToken(cancellation.Token);
}
private TimeSpan ElapsedSinceRefresh => _timeProvider.GetElapsedTime(_lastRefreshTimeStamp);
protected string ServiceName { get; }
protected abstract double RetryBackOffFactor { get; }
protected abstract TimeSpan MinRetryPeriod { get; }
protected abstract TimeSpan MaxRetryPeriod { get; }
protected abstract TimeSpan DefaultRefreshPeriod { get; }
protected CancellationToken ShutdownToken => _disposeCancellation.Token;
/// <inheritdoc/>
public async ValueTask PopulateAsync(IServiceEndpointBuilder endpoints, CancellationToken cancellationToken)
{
// Only add endpoints to the collection if a previous provider (eg, a configuration override) did not add them.
if (endpoints.Endpoints.Count != 0)
{
Log.SkippedResolution(_logger, ServiceName, "Collection has existing endpoints");
return;
}
if (ShouldRefresh())
{
Task resolveTask;
lock (_lock)
{
if (_resolveTask.IsCompleted && ShouldRefresh())
{
_resolveTask = ResolveAsyncCore();
}
resolveTask = _resolveTask;
}
await resolveTask.WaitAsync(cancellationToken).ConfigureAwait(false);
}
lock (_lock)
{
if (_lastEndpointCollection is { Count: > 0 } eps)
{
foreach (var ep in eps)
{
endpoints.Endpoints.Add(ep);
}
}
endpoints.AddChangeToken(_lastChangeToken);
}
}
private bool ShouldRefresh() => _lastEndpointCollection is null || _lastChangeToken is { HasChanged: true } || ElapsedSinceRefresh >= _nextRefreshPeriod;
protected abstract Task ResolveAsyncCore();
protected void SetResult(List<ServiceEndpoint> endpoints, TimeSpan validityPeriod)
{
lock (_lock)
{
if (endpoints is { Count: > 0 })
{
_lastRefreshTimeStamp = _timeProvider.GetTimestamp();
_nextRefreshPeriod = DefaultRefreshPeriod;
_hasEndpoints = true;
}
else
{
_nextRefreshPeriod = GetRefreshPeriod();
validityPeriod = TimeSpan.Zero;
_hasEndpoints = false;
}
if (validityPeriod <= TimeSpan.Zero)
{
validityPeriod = _nextRefreshPeriod;
}
else if (validityPeriod > _nextRefreshPeriod)
{
validityPeriod = _nextRefreshPeriod;
}
_lastCollectionCancellation.Cancel();
var cancellation = _lastCollectionCancellation = new CancellationTokenSource(validityPeriod, _timeProvider);
_lastChangeToken = new CancellationChangeToken(cancellation.Token);
_lastEndpointCollection = endpoints;
}
TimeSpan GetRefreshPeriod()
{
if (_hasEndpoints)
{
return MinRetryPeriod;
}
var nextTicks = (long)(_nextRefreshPeriod.Ticks * RetryBackOffFactor);
if (nextTicks <= 0 || nextTicks > MaxRetryPeriod.Ticks)
{
return MaxRetryPeriod;
}
return TimeSpan.FromTicks(nextTicks);
}
}
/// <inheritdoc/>
public async ValueTask DisposeAsync()
{
_disposeCancellation.Cancel();
if (_resolveTask is { } task)
{
#pragma warning disable CAC002
await task.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
#pragma warning restore CAC002
}
}
}

View File

@ -0,0 +1,37 @@
#pragma warning disable SA1200
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.ServiceDiscovery;
#pragma warning restore SA1200
namespace Nacos.Microsoft.Extensions.ServiceDiscovery;
/// <summary>
/// Options for configuring <see cref="NacosServiceEndpointProvider"/>.
/// </summary>
public class NacosServiceEndpointProviderOptions
{
/// <summary>
/// Gets or sets the default refresh period for endpoints resolved from Nacos.
/// </summary>
public TimeSpan DefaultRefreshPeriod { get; set; } = TimeSpan.FromMinutes(1);
/// <summary>
/// Gets or sets the initial period between retries.
/// </summary>
public TimeSpan MinRetryPeriod { get; set; } = TimeSpan.FromSeconds(1);
/// <summary>
/// Gets or sets the maximum period between retries.
/// </summary>
public TimeSpan MaxRetryPeriod { get; set; } = TimeSpan.FromSeconds(30);
/// <summary>
/// Gets or sets the retry period growth factor.
/// </summary>
public double RetryBackOffFactor { get; set; } = 2;
/// <summary>
/// Gets or sets a delegate used to determine whether to apply host name metadata to each resolved endpoint. Defaults to <c>false</c>.
/// </summary>
public Func<ServiceEndpoint, bool> ShouldApplyHostNameMetadata { get; set; } = _ => false;
}

View File

@ -0,0 +1,17 @@
# nacos-sdk-csharp.Extensions.ServiceDiscovery
nacos-sdk-csharp.Extensions.ServiceDiscovery provides service registration and discovery With Microsoft.Extensions.ServiceDiscovery.
```csharp
builder.Services.AddServiceDiscovery(o =>
{
o.RefreshPeriod = TimeSpan.FromSeconds(60);
})
.AddConfigurationServiceEndpointProvider()
.AddNacosServiceEndpointProvider();
```
## Links
* [Documentation](https://nacos-sdk-csharp.readthedocs.io/en/latest/)
* [nacos-sdk-csharp GitHub](https://github.com/nacos-group/nacos-sdk-csharp)

View File

@ -13,17 +13,19 @@
<RepositoryUrl>https://github.com/nacos-group/nacos-sdk-csharp</RepositoryUrl>
<ProjectUrl>https://github.com/nacos-group/nacos-sdk-csharp</ProjectUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>
</PackageReleaseNotes>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
<NoWarn>$(NoWarn);1591;NU5100</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Include="../Nacos.Microsoft.Extensions.Configuration/DefaultJsonConfigurationStringParser.cs" />
<None Include="../../LICENSE" Pack="true" Visible="false" PackagePath="" />
<None Include="README.md" Pack="true" Visible="false" PackagePath="" />
<ProjectReference Include="..\Nacos\Nacos.csproj" />

View File

@ -1,56 +1,58 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../build/version.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5.0;net6.0</TargetFrameworks>
<PackageId>nacos-sdk-csharp</PackageId>
<VersionPrefix>$(NugetVersion)</VersionPrefix>
<VersionSuffix></VersionSuffix>
<Authors>nacos-sdk-csharp Contributors</Authors>
<Description>nacos csharp sdk</Description>
<PackageTags>nacos,csharp,sdk</PackageTags>
<PackageProjectUrl>https://github.com/nacos-group/nacos-sdk-csharp</PackageProjectUrl>
<RepositoryUrl>https://github.com/nacos-group/nacos-sdk-csharp</RepositoryUrl>
<ProjectUrl>https://github.com/nacos-group/nacos-sdk-csharp</ProjectUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReleaseNotes>
</PackageReleaseNotes>
</PropertyGroup>
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<None Include="../../LICENSE" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="5.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Google.Protobuf" Version="3.14.0" />
<PackageReference Include="Grpc.Core" Version="2.34.1" />
<!--<PackageReference Include="Grpc.Net.Client" Version="2.33.1" />-->
<!--<Protobuf Include="V2\protos\nacos_grpc_service.proto" GrpcServices="Client" />-->
<!--<PackageReference Include="Grpc.Tools" Version="2.36.1" PrivateAssets="All" />-->
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../build/version.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<PackageId>nacos-sdk-csharp</PackageId>
<VersionPrefix>$(NugetVersion)</VersionPrefix>
<VersionSuffix></VersionSuffix>
<Authors>nacos-sdk-csharp Contributors</Authors>
<Description>nacos csharp sdk</Description>
<PackageTags>nacos,csharp,sdk</PackageTags>
<PackageProjectUrl>https://github.com/nacos-group/nacos-sdk-csharp</PackageProjectUrl>
<RepositoryUrl>https://github.com/nacos-group/nacos-sdk-csharp</RepositoryUrl>
<ProjectUrl>https://github.com/nacos-group/nacos-sdk-csharp</ProjectUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>
</PackageReleaseNotes>
</PropertyGroup>
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<None Include="../../LICENSE" Pack="true" Visible="false" PackagePath="" />
<None Include="README.md" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<PackageReference Include="Google.Protobuf" Version="3.27.2" />
<PackageReference Include="Grpc.Net.Client" Version="2.65.0" />
<!--<PackageReference Include="Grpc.Net.Client" Version="2.33.1" />-->
<!--<Protobuf Include="V2\protos\nacos_grpc_service.proto" GrpcServices="Client" />-->
<!--<PackageReference Include="Grpc.Tools" Version="2.65.0" PrivateAssets="All" />-->
</ItemGroup>
</Project>

View File

@ -0,0 +1,7 @@
namespace Nacos.OpenApi
{
internal class Constants
{
public static readonly string HttpClientName = "OpenApi";
}
}

View File

@ -0,0 +1,140 @@
namespace Nacos.OpenApi
{
using Microsoft.Extensions.Options;
using Nacos.V2.Utils;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
public class DefaultNacosOpenApi : INacosOpenApi
{
private const string _namespacePath = "nacos/v1/console/namespaces";
private const string _metricsPath = "nacos/v1/ns/operator/metrics";
private readonly IHttpClientFactory _httpClientFactory;
private readonly Nacos.V2.NacosSdkOptions _options;
public DefaultNacosOpenApi(IHttpClientFactory httpClientFactory, IOptions<Nacos.V2.NacosSdkOptions> optionsAccs)
{
this._httpClientFactory = httpClientFactory;
this._options = optionsAccs.Value;
}
public async Task<bool> CreateNamespaceAsync(string customNamespaceId, string namespaceName, string namespaceDesc)
{
var client = _httpClientFactory.CreateClient(Constants.HttpClientName);
var content = new StringContent($"customNamespaceId={customNamespaceId}&namespaceName={namespaceName}&namespaceDesc={namespaceDesc}");
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
var req = new HttpRequestMessage(HttpMethod.Post, $"{_options.ServerAddresses.First().TrimEnd('/')}/{_namespacePath}");
req.Content = content;
var resp = await client.SendAsync(req).ConfigureAwait(false);
if (resp.IsSuccessStatusCode)
{
var res = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
bool.TryParse(res, out bool result);
return result;
}
else
{
throw new Nacos.V2.Exceptions.NacosException((int)resp.StatusCode, "CreateNamespaceAsync exception");
}
}
public async Task<bool> DeleteNamespaceAsync(string namespaceId)
{
var client = _httpClientFactory.CreateClient(Constants.HttpClientName);
var content = new StringContent($"namespaceId={namespaceId}");
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
var req = new HttpRequestMessage(HttpMethod.Delete, $"{_options.ServerAddresses.First().TrimEnd('/')}/{_namespacePath}");
req.Content = content;
var resp = await client.SendAsync(req).ConfigureAwait(false);
if (resp.IsSuccessStatusCode)
{
var res = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
bool.TryParse(res, out bool result);
return result;
}
else
{
throw new Nacos.V2.Exceptions.NacosException((int)resp.StatusCode, "DeleteNamespaceAsync exception");
}
}
public async Task<NacosMetrics> GetMetricsAsync(bool onlyStatus)
{
var client = _httpClientFactory.CreateClient(Constants.HttpClientName);
var req = new HttpRequestMessage(HttpMethod.Get, $"{_options.ServerAddresses.First().TrimEnd('/')}/{_metricsPath}?onlyStatus={onlyStatus.ToString().ToLower()}");
var resp = await client.SendAsync(req).ConfigureAwait(false);
if (resp.IsSuccessStatusCode)
{
var res = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
return res.ToObj<NacosMetrics>();
}
else
{
throw new Nacos.V2.Exceptions.NacosException((int)resp.StatusCode, "GetMetricsAsync exception");
}
}
public async Task<List<NacosNamespace>> GetNamespacesAsync()
{
var client = _httpClientFactory.CreateClient(Constants.HttpClientName);
var req = new HttpRequestMessage(HttpMethod.Get, $"{_options.ServerAddresses.First().TrimEnd('/')}/{_namespacePath}");
var resp = await client.SendAsync(req).ConfigureAwait(false);
if (resp.IsSuccessStatusCode)
{
var res = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
var jobj = System.Text.Json.Nodes.JsonNode.Parse(res).AsObject();
if (jobj.TryGetPropertyValue("data", out var val))
{
return val.ToString().ToObj<List<NacosNamespace>>();
}
else
{
return new List<NacosNamespace>();
}
}
else
{
throw new Nacos.V2.Exceptions.NacosException((int)resp.StatusCode, "GetNamespacesAsync exception");
}
}
public async Task<bool> UpdateNamespaceAsync(string namespaceId, string namespaceName, string namespaceDesc)
{
var client = _httpClientFactory.CreateClient(Constants.HttpClientName);
var content = new StringContent($"customNamespaceId={namespaceId}&namespaceName={namespaceName}&namespaceDesc={namespaceDesc}");
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
var req = new HttpRequestMessage(HttpMethod.Post, $"{_options.ServerAddresses.First().TrimEnd('/')}/{_namespacePath}");
req.Content = content;
var resp = await client.SendAsync(req).ConfigureAwait(false);
if (resp.IsSuccessStatusCode)
{
var res = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
bool.TryParse(res, out bool result);
return result;
}
else
{
throw new Nacos.V2.Exceptions.NacosException((int)resp.StatusCode, "UpdateNamespaceAsync exception");
}
}
}
}

View File

@ -0,0 +1,30 @@
namespace Nacos.OpenApi
{
using Microsoft.Extensions.DependencyInjection;
using Nacos.V2;
using System;
using System.Net.Http;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddNacosOpenApi(this IServiceCollection services, Action<NacosSdkOptions> configure, Action<HttpClient> httpClientAction = null)
{
if (services == null) throw new ArgumentNullException(nameof(services));
services.AddOptions();
services.Configure(configure);
var clientBuilder = services.AddHttpClient(Constants.HttpClientName)
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { UseProxy = false, AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate });
if (httpClientAction != null)
{
clientBuilder.ConfigureHttpClient(httpClientAction);
}
services.AddSingleton<INacosOpenApi, DefaultNacosOpenApi>();
return services;
}
}
}

View File

@ -0,0 +1,46 @@
namespace Nacos.OpenApi
{
using System.Collections.Generic;
using System.Threading.Tasks;
public interface INacosOpenApi
{
/// <summary>
/// Get namespace list.
/// </summary>
/// <returns>namespace list</returns>
Task<List<NacosNamespace>> GetNamespacesAsync();
/// <summary>
/// Create namespace
/// </summary>
/// <param name="customNamespaceId">ID of namespace</param>
/// <param name="namespaceName">Name of namespace</param>
/// <param name="namespaceDesc">Description of namespace</param>
/// <returns>Created or not</returns>
Task<bool> CreateNamespaceAsync(string customNamespaceId, string namespaceName, string namespaceDesc);
/// <summary>
/// Update namespace
/// </summary>
/// <param name="namespaceId">ID of namespace</param>
/// <param name="namespaceName">Name of namespace</param>
/// <param name="namespaceDesc">Description of namespace</param>
/// <returns>Updated or not</returns>
Task<bool> UpdateNamespaceAsync(string namespaceId, string namespaceName, string namespaceDesc);
/// <summary>
/// Delete namespace
/// </summary>
/// <param name="namespaceId">ID of namespace</param>
/// <returns>Deleted or not</returns>
Task<bool> DeleteNamespaceAsync(string namespaceId);
/// <summary>
/// Query system metrics
/// </summary>
/// <param name="onlyStatus">only status info or not</param>
/// <returns>system metrics</returns>
Task<NacosMetrics> GetMetricsAsync(bool onlyStatus);
}
}

View File

@ -0,0 +1,50 @@
namespace Nacos.OpenApi
{
public class NacosMetrics
{
[System.Text.Json.Serialization.JsonPropertyName("status")]
public string Status { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("serviceCount")]
public int ServiceCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("instanceCount")]
public int InstanceCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("subscribeCount")]
public int SubscribeCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("raftNotifyTaskCount")]
public int RaftNotifyTaskCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("responsibleServiceCount")]
public int ResponsibleServiceCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("responsibleInstanceCount")]
public int ResponsibleInstanceCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("clientCount")]
public int ClientCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("connectionBasedClientCount")]
public int ConnectionBasedClientCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("ephemeralIpPortClientCount")]
public int EphemeralIpPortClientCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("persistentIpPortClientCount")]
public int PersistentIpPortClientCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("responsibleClientCount")]
public int ResponsibleClientCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("cpu")]
public float Cpu { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("load")]
public float Load { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("mem")]
public float Mem { get; set; }
}
}

View File

@ -0,0 +1,23 @@
namespace Nacos.OpenApi
{
public class NacosNamespace
{
[System.Text.Json.Serialization.JsonPropertyName("namespace")]
public string Namespace { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("namespaceShowName")]
public string NamespaceShowName { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("namespaceDesc")]
public string NamespaceDesc { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("quota")]
public int Quota { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("configCount")]
public int ConfigCount { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("type")]
public int Type { get; set; }
}
}

45
src/Nacos/README.md Normal file
View File

@ -0,0 +1,45 @@
# nacos-sdk-csharp
nacos-sdk-csharp provides basic nacos operation.
```csharp
// config
builder.Services.AddNacosV2Config(x =>
{
x.ServerAddresses = new List<string> { "http://localhost:8848/" };
x.Namespace = "cs";
x.UserName = "nacos";
x.Password = "nacos";
});
// naming
builder.Services.AddNacosV2Naming(x =>
{
x.ServerAddresses = new List<string> { "http://localhost:8848/" };
x.Namespace = "cs";
x.UserName = "nacos";
x.Password = "nacos";
});
// or
builder.Services.AddNacosV2Config(builder.Configuration);
builder.Services.AddNacosV2Naming(builder.Configuration);
```
```csharp
private readonly INacosConfigService _config;
private readonly INacosNamingService _naming;
public TheCtor(INacosConfigService config, INacosNamingService naming)
{
_config = config;
_naming = naming;
}
```
## Links
* [Documentation](https://nacos-sdk-csharp.readthedocs.io/en/latest/)
* [nacos-sdk-csharp GitHub](https://github.com/nacos-group/nacos-sdk-csharp)

View File

@ -2,7 +2,7 @@
{
public class Constants
{
public static string CLIENT_VERSION = "Nacos-CSharp-Client:v1.3.2";
public static string CLIENT_VERSION = "Nacos-CSharp-Client:v1.3.10";
public const string ClientName = "NacosClient";
@ -182,5 +182,7 @@
public static string AMORY_TAG = "Amory-Tag";
public static string LOCATION_TAG = "Location-Tag";
public static string CLUSTER_NAME_PATTERN_STRING = "^[0-9a-zA-Z-]+$";
}
}

View File

@ -225,7 +225,7 @@
newlabels["taskId"] = taskId;
RpcClient rpcClient = RpcClientFactory
.CreateClient($"{uuid}_config-{taskId}", RemoteConnectionType.GRPC, newlabels);
.CreateClient($"{uuid}_config-{taskId}", RemoteConnectionType.GRPC, newlabels, _options.TLSConfig);
if (rpcClient.IsWaitInited())
{
@ -334,6 +334,7 @@
{
var listenCachesMap = new Dictionary<string, List<CacheData>>();
var removeListenCachesMap = new Dictionary<string, List<CacheData>>();
var hasChangedKeys = false;
// TODO: should update logic here.....
foreach (var item in _cacheMap.Values)
@ -392,6 +393,7 @@
if (configChangeBatchListenResponse.ChangedConfigs != null && configChangeBatchListenResponse.ChangedConfigs.Any())
{
hasChangedKeys = true;
foreach (var item in configChangeBatchListenResponse.ChangedConfigs)
{
var changeKey = GroupKey.GetKeyTenant(item.DataId, item.Group, item.Tenant);
@ -450,6 +452,9 @@
}
}
}
if (hasChangedKeys)
await NotifyListenConfig().ConfigureAwait(false);
}
private async Task RefreshContentAndCheck(string groupKey, bool notify)
@ -496,8 +501,13 @@
protected override Task NotifyListenConfig()
{
_listenExecutebell.Add(_bellItem);
return Task.CompletedTask;
return Task.Factory.StartNew(async () =>
{
while (!_listenExecutebell.TryAdd(_bellItem))
{
await Task.Delay(500).ConfigureAwait(false);
}
});
}
}
}

View File

@ -7,7 +7,13 @@
public static class FileLocalConfigInfoProcessor
{
private static readonly string LOCAL_SNAPSHOT_PATH = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "nacos", "config");
private static readonly string LOCAL_SNAPSHOT_PATH = string.Empty;
static FileLocalConfigInfoProcessor()
{
var basePath = Nacos.V2.Utils.EnvUtil.GetEnvValue("JM.SNAPSHOT.PATH", Environment.GetFolderPath(Environment.SpecialFolder.Personal));
LOCAL_SNAPSHOT_PATH = Path.Combine(basePath, "nacos", "config");
}
public static async Task<string> GetFailoverAsync(string serverName, string dataId, string group, string tenant)
{

View File

@ -79,10 +79,11 @@
_isFixed = false;
var endpoint = _options.EndPoint.IndexOf(':') == -1 ? $"{_options.EndPoint}:{_endpointPort}" : _options.EndPoint;
if (@namespace.IsNullOrWhiteSpace())
{
_name = _options.EndPoint;
_addressServerUrl = $"http://{_options.EndPoint}:{_endpointPort}/{_contentPath}/{_defaultNodesPath}";
_addressServerUrl = $"http://{endpoint}/{_contentPath}/{_defaultNodesPath}";
}
else
{
@ -90,7 +91,7 @@
_tenant = $"{_options.EndPoint}-{@namespace}";
_name = $"{FIXED_NAME}-{GetFixedNameSuffix(_serverUrls)}-{@namespace}";
_addressServerUrl = $"http://{_options.EndPoint}:{_endpointPort}/{_contentPath}/{_defaultNodesPath}?namespace={@namespace}";
_addressServerUrl = $"http://{endpoint}/{_contentPath}/{_defaultNodesPath}?namespace={@namespace}";
}
_refreshSvcListTimer = new Timer(

View File

@ -58,6 +58,15 @@
/// <param name="instance">instance to register</param>
Task RegisterInstance(string serviceName, string groupName, Instance instance);
/// <summary>
/// batch register instance to service with specified instance properties.
/// since nacos server 2.1.1
/// </summary>
/// <param name="serviceName">name of service</param>
/// <param name="groupName">group of service</param>
/// <param name="instances">instances to register</param>
Task BatchRegisterInstance(string serviceName, string groupName, List<Instance> instances);
/// <summary>
/// deregister instance from a service.
/// </summary>
@ -438,4 +447,4 @@
/// </summary>
Task ShutDown();
}
}
}

View File

@ -72,5 +72,10 @@
/// Specify some extension info of IConfigFilter.
/// </summary>
public string ConfigFilterExtInfo { get; set; }
/// <summary>
/// TLS config
/// </summary>
public TLSConfig TLSConfig { get; set; }
}
}

View File

@ -5,31 +5,31 @@
public class BeatInfo
{
[Newtonsoft.Json.JsonProperty("port")]
[System.Text.Json.Serialization.JsonPropertyName("port")]
public int Port { get; set; }
[Newtonsoft.Json.JsonProperty("ip")]
[System.Text.Json.Serialization.JsonPropertyName("ip")]
public string Ip { get; set; }
[Newtonsoft.Json.JsonProperty("weight")]
[System.Text.Json.Serialization.JsonPropertyName("weight")]
public double? Weight { get; set; }
[Newtonsoft.Json.JsonProperty("serviceName")]
[System.Text.Json.Serialization.JsonPropertyName("serviceName")]
public string ServiceName { get; set; }
[Newtonsoft.Json.JsonProperty("cluster")]
[System.Text.Json.Serialization.JsonPropertyName("cluster")]
public string Cluster { get; set; }
[Newtonsoft.Json.JsonProperty("metadata")]
[System.Text.Json.Serialization.JsonPropertyName("metadata")]
public Dictionary<string, string> Metadata { get; set; } = new Dictionary<string, string>();
[Newtonsoft.Json.JsonProperty("scheduled")]
[System.Text.Json.Serialization.JsonPropertyName("scheduled")]
public bool Scheduled { get; set; }
[Newtonsoft.Json.JsonProperty("period")]
[System.Text.Json.Serialization.JsonPropertyName("period")]
public long Period { get; set; }
[Newtonsoft.Json.JsonProperty("stopped")]
[System.Text.Json.Serialization.JsonPropertyName("stopped")]
public bool Stopped { get; set; }
public override string ToString()

View File

@ -65,22 +65,22 @@
try
{
Newtonsoft.Json.Linq.JObject result = await _serverProxy.SendBeat(beatInfo, false).ConfigureAwait(false);
System.Text.Json.Nodes.JsonObject result = await _serverProxy.SendBeat(beatInfo, false).ConfigureAwait(false);
_logger?.LogDebug("[CLIENT-BEAT] sendbeat result = {0}", result.ToString());
long interval = result.GetValue(CLIENT_BEAT_INTERVAL_FIELD).ToObject<long>();
var interval = result[CLIENT_BEAT_INTERVAL_FIELD].GetValue<long>();
bool lightBeatEnabled = false;
if (result.ContainsKey(CommonParams.LIGHT_BEAT_ENABLED))
{
lightBeatEnabled = result.GetValue(CommonParams.LIGHT_BEAT_ENABLED).ToObject<bool>();
lightBeatEnabled = result[CommonParams.LIGHT_BEAT_ENABLED].GetValue<bool>();
}
if (interval > 0) nextTime = interval;
int code = OK;
if (result.ContainsKey(CommonParams.CODE)) code = result.GetValue(CommonParams.CODE).ToObject<int>();
if (result.ContainsKey(CommonParams.CODE)) code = result[CommonParams.CODE].GetValue<int>();
if (code == RESOURCE_NOT_FOUND)
{

View File

@ -11,61 +11,61 @@
/// <summary>
/// unique id of this instance.
/// </summary>
[Newtonsoft.Json.JsonProperty("instanceId")]
[System.Text.Json.Serialization.JsonPropertyName("instanceId")]
public string InstanceId { get; set; }
/// <summary>
/// instance ip.
/// </summary>
[Newtonsoft.Json.JsonProperty("ip")]
[System.Text.Json.Serialization.JsonPropertyName("ip")]
public string Ip { get; set; }
/// <summary>
/// instance port.
/// </summary>
[Newtonsoft.Json.JsonProperty("port")]
[System.Text.Json.Serialization.JsonPropertyName("port")]
public int Port { get; set; }
/// <summary>
/// instance weight.
/// </summary>
[Newtonsoft.Json.JsonProperty("weight")]
[System.Text.Json.Serialization.JsonPropertyName("weight")]
public double Weight { get; set; } = 1.0D;
/// <summary>
/// instance health status.
/// </summary>
[Newtonsoft.Json.JsonProperty("healthy")]
[System.Text.Json.Serialization.JsonPropertyName("healthy")]
public bool Healthy { get; set; } = true;
/// <summary>
/// If instance is enabled to accept request.
/// </summary>
[Newtonsoft.Json.JsonProperty("enabled")]
[System.Text.Json.Serialization.JsonPropertyName("enabled")]
public bool Enabled { get; set; } = true;
/// <summary>
/// If instance is ephemeral.
/// </summary>
[Newtonsoft.Json.JsonProperty("ephemeral")]
[System.Text.Json.Serialization.JsonPropertyName("ephemeral")]
public bool Ephemeral { get; set; } = true;
/// <summary>
/// cluster information of instance.
/// </summary>
[Newtonsoft.Json.JsonProperty("clusterName")]
[System.Text.Json.Serialization.JsonPropertyName("clusterName")]
public string ClusterName { get; set; }
/// <summary>
/// Service information of instance.
/// </summary>
[Newtonsoft.Json.JsonProperty("serviceName")]
[System.Text.Json.Serialization.JsonPropertyName("serviceName")]
public string ServiceName { get; set; }
/// <summary>
/// user extended attributes.
/// </summary>
[Newtonsoft.Json.JsonProperty("metadata")]
[System.Text.Json.Serialization.JsonPropertyName("metadata")]
public Dictionary<string, string> Metadata { get; set; } = new Dictionary<string, string>();
public void AddMetadata(string key, string value)
@ -110,5 +110,16 @@
{
return $"Instance{{instanceId='{InstanceId}', ip='{Ip}', port={Port}, weight={Weight}, healthy={Healthy}, enabled={Enabled}, ephemeral={Ephemeral}, clusterName='{ClusterName}', serviceName='{ServiceName}', metadata={Metadata.ToJsonString()}}}";
}
public override bool Equals(object obj)
{
if (obj is not Instance) return false;
var host = (Instance)obj;
return ToString().Equals(host.ToString());
}
public override int GetHashCode() => ToString().GetHashCode();
}
}

View File

@ -8,28 +8,28 @@
public class ServiceInfo
{
[Newtonsoft.Json.JsonProperty("name")]
[System.Text.Json.Serialization.JsonPropertyName("name")]
public string Name { get; set; }
[Newtonsoft.Json.JsonProperty("groupName")]
[System.Text.Json.Serialization.JsonPropertyName("groupName")]
public string GroupName { get; set; }
[Newtonsoft.Json.JsonProperty("cacheMillis")]
[System.Text.Json.Serialization.JsonPropertyName("cacheMillis")]
public long CacheMillis { get; set; } = 1000L;
[Newtonsoft.Json.JsonProperty("lastRefTime")]
[System.Text.Json.Serialization.JsonPropertyName("lastRefTime")]
public long LastRefTime { get; set; } = 0L;
[Newtonsoft.Json.JsonProperty("checksum")]
[System.Text.Json.Serialization.JsonPropertyName("checksum")]
public string Checksum { get; set; } = "";
[Newtonsoft.Json.JsonProperty("hosts")]
[System.Text.Json.Serialization.JsonPropertyName("hosts")]
public List<Instance> Hosts { get; set; } = new List<Instance>();
[Newtonsoft.Json.JsonProperty("metallIPsadata")]
[System.Text.Json.Serialization.JsonPropertyName("metallIPsadata")]
public bool AllIPs { get; set; } = false;
[Newtonsoft.Json.JsonProperty("clusters")]
[System.Text.Json.Serialization.JsonPropertyName("clusters")]
public string Clusters { get; set; }
public ServiceInfo()
@ -76,7 +76,7 @@
=> !string.IsNullOrEmpty(clusters) ? name + Constants.SERVICE_INFO_SPLITER + clusters : name;
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string JsonFromServer { get; set; }
public string GetKeyEncoded() => GetKey(System.Net.WebUtility.UrlEncode(GetGroupedServiceName()), Clusters);

View File

@ -5,16 +5,16 @@
public class InstancesChangeEvent : IEvent
{
[Newtonsoft.Json.JsonProperty("serviceName")]
[System.Text.Json.Serialization.JsonPropertyName("serviceName")]
public string ServiceName { get; set; }
[Newtonsoft.Json.JsonProperty("groupName")]
[System.Text.Json.Serialization.JsonPropertyName("groupName")]
public string GroupName { get; set; }
[Newtonsoft.Json.JsonProperty("clusters")]
[System.Text.Json.Serialization.JsonPropertyName("clusters")]
public string Clusters { get; set; }
[Newtonsoft.Json.JsonProperty("hosts")]
[System.Text.Json.Serialization.JsonPropertyName("hosts")]
public List<Instance> Hosts { get; set; }
public InstancesChangeEvent(string serviceName, string groupName, string clusters, List<Instance> hosts)

View File

@ -1,301 +1,308 @@
namespace Nacos.V2.Naming
{
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Nacos.V2.Common;
using Nacos.V2.Naming.Cache;
using Nacos.V2.Naming.Core;
using Nacos.V2.Naming.Dtos;
using Nacos.V2.Naming.Event;
using Nacos.V2.Naming.Remote;
using Nacos.V2.Remote;
using System.Collections.Generic;
namespace Nacos.V2.Naming
{
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Nacos.V2.Common;
using Nacos.V2.Naming.Cache;
using Nacos.V2.Naming.Core;
using Nacos.V2.Naming.Dtos;
using Nacos.V2.Naming.Event;
using Nacos.V2.Naming.Remote;
using Nacos.V2.Remote;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
public class NacosNamingService : INacosNamingService
{
private static readonly string UP = "UP";
private static readonly string DOWN = "DOWN";
private readonly ILogger _logger;
private readonly NacosSdkOptions _options;
private string _namespace;
private ServiceInfoHolder _serviceInfoHolder;
private InstancesChangeNotifier _changeNotifier;
private INamingClientProxy _clientProxy;
public NacosNamingService(
ILoggerFactory loggerFactory,
using System.Threading.Tasks;
public class NacosNamingService : INacosNamingService
{
private static readonly string UP = "UP";
private static readonly string DOWN = "DOWN";
private readonly ILogger _logger;
private readonly NacosSdkOptions _options;
private string _namespace;
private ServiceInfoHolder _serviceInfoHolder;
private InstancesChangeNotifier _changeNotifier;
private INamingClientProxy _clientProxy;
public NacosNamingService(
ILoggerFactory loggerFactory,
IOptions<NacosSdkOptions> optionAccs,
IHttpClientFactory clientFactory)
{
_logger = loggerFactory.CreateLogger<NacosNamingService>();
_options = optionAccs.Value;
_namespace = string.IsNullOrWhiteSpace(_options.Namespace) ? Utils.UtilAndComs.DEFAULT_NAMESPACE_ID : _options.Namespace;
this._changeNotifier = new InstancesChangeNotifier();
this._serviceInfoHolder = new ServiceInfoHolder(_logger, _namespace, _options, _changeNotifier);
this._clientProxy = new NamingClientProxyDelegate(_logger, _namespace, _serviceInfoHolder, _options, _changeNotifier, clientFactory);
}
public async Task DeregisterInstance(string serviceName, string ip, int port)
=> await DeregisterInstance(serviceName, ip, port, Constants.DEFAULT_CLUSTER_NAME).ConfigureAwait(false);
public async Task DeregisterInstance(string serviceName, string groupName, string ip, int port)
=> await DeregisterInstance(serviceName, groupName, ip, port, Constants.DEFAULT_CLUSTER_NAME).ConfigureAwait(false);
public async Task DeregisterInstance(string serviceName, string ip, int port, string clusterName)
=> await DeregisterInstance(serviceName, Constants.DEFAULT_GROUP, ip, port, clusterName).ConfigureAwait(false);
public async Task DeregisterInstance(string serviceName, string groupName, string ip, int port, string clusterName)
{
var instance = new Instance()
{
Ip = ip,
Port = port,
ClusterName = clusterName
};
await DeregisterInstance(serviceName, groupName, instance).ConfigureAwait(false);
}
public async Task DeregisterInstance(string serviceName, Instance instance)
=> await DeregisterInstance(serviceName, Constants.DEFAULT_GROUP, instance).ConfigureAwait(false);
public async Task DeregisterInstance(string serviceName, string groupName, Instance instance)
=> await _clientProxy.DeregisterService(serviceName, groupName, instance).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName)
=> await GetAllInstances(serviceName, new List<string>()).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, string groupName)
=> await GetAllInstances(serviceName, groupName, new List<string>()).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, bool subscribe)
=> await GetAllInstances(serviceName, new List<string>(), subscribe).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, string groupName, bool subscribe)
=> await GetAllInstances(serviceName, groupName, new List<string>(), subscribe).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, List<string> clusters)
=> await GetAllInstances(serviceName, Constants.DEFAULT_GROUP, clusters, true).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, string groupName, List<string> clusters)
=> await GetAllInstances(serviceName, groupName, clusters, true).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, List<string> clusters, bool subscribe)
=> await GetAllInstances(serviceName, Constants.DEFAULT_GROUP, clusters, subscribe).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, string groupName, List<string> clusters, bool subscribe)
{
ServiceInfo serviceInfo;
string clusterString = string.Join(",", clusters);
if (subscribe)
{
serviceInfo = _serviceInfoHolder.GetServiceInfo(serviceName, groupName, clusterString);
if (serviceInfo == null || !await _clientProxy.IsSubscribed(serviceName, groupName, clusterString).ConfigureAwait(false))
{
serviceInfo = await _clientProxy.Subscribe(serviceName, groupName, clusterString).ConfigureAwait(false);
}
}
else
{
serviceInfo = await _clientProxy.QueryInstancesOfService(serviceName, groupName, clusterString, 0, false).ConfigureAwait(false);
}
List<Instance> list = serviceInfo.Hosts;
if (serviceInfo == null || serviceInfo.Hosts == null || !serviceInfo.Hosts.Any())
{
return new List<Instance>();
}
return list;
}
public Task<string> GetServerStatus()
=> Task.FromResult(_clientProxy.ServerHealthy() ? UP : DOWN);
public async Task<ListView<string>> GetServicesOfServer(int pageNo, int pageSize)
=> await GetServicesOfServer(pageNo, pageSize, Constants.DEFAULT_GROUP).ConfigureAwait(false);
public async Task<ListView<string>> GetServicesOfServer(int pageNo, int pageSize, string groupName)
=> await GetServicesOfServer(pageNo, pageSize, groupName, null).ConfigureAwait(false);
public async Task<ListView<string>> GetServicesOfServer(int pageNo, int pageSize, AbstractSelector selector)
=> await GetServicesOfServer(pageNo, pageSize, Constants.DEFAULT_GROUP, selector).ConfigureAwait(false);
public async Task<ListView<string>> GetServicesOfServer(int pageNo, int pageSize, string groupName, AbstractSelector selector)
=> await _clientProxy.GetServiceList(pageNo, pageSize, groupName, selector).ConfigureAwait(false);
public Task<List<ServiceInfo>> GetSubscribeServices()
=> Task.FromResult(_changeNotifier.GetSubscribeServices());
public async Task RegisterInstance(string serviceName, string ip, int port)
=> await RegisterInstance(serviceName, ip, port, Constants.DEFAULT_CLUSTER_NAME).ConfigureAwait(false);
public async Task RegisterInstance(string serviceName, string groupName, string ip, int port)
=> await RegisterInstance(serviceName, groupName, ip, port, Constants.DEFAULT_CLUSTER_NAME).ConfigureAwait(false);
public async Task RegisterInstance(string serviceName, string ip, int port, string clusterName)
=> await RegisterInstance(serviceName, Constants.DEFAULT_GROUP, ip, port, clusterName).ConfigureAwait(false);
public async Task RegisterInstance(string serviceName, string groupName, string ip, int port, string clusterName)
{
var instance = new Instance()
{
Ip = ip,
Port = port,
Weight = 1.0d,
ClusterName = clusterName
};
await RegisterInstance(serviceName, groupName, instance).ConfigureAwait(false);
}
public async Task RegisterInstance(string serviceName, Instance instance)
=> await RegisterInstance(serviceName, Constants.DEFAULT_GROUP, instance).ConfigureAwait(false);
public async Task RegisterInstance(string serviceName, string groupName, Instance instance)
=> await _clientProxy.RegisterServiceAsync(serviceName, groupName, instance).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, bool healthy)
=> await SelectInstances(serviceName, new List<string>(), healthy).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, string groupName, bool healthy)
=> await SelectInstances(serviceName, groupName, healthy, true).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, bool healthy, bool subscribe)
=> await SelectInstances(serviceName, new List<string>(), healthy, subscribe).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, string groupName, bool healthy, bool subscribe)
=> await SelectInstances(serviceName, groupName, new List<string>(), healthy, subscribe).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, List<string> clusters, bool healthy)
=> await SelectInstances(serviceName, clusters, healthy, true).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, string groupName, List<string> clusters, bool healthy)
=> await SelectInstances(serviceName, groupName, clusters, healthy, true).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, List<string> clusters, bool healthy, bool subscribe)
=> await SelectInstances(serviceName, Constants.DEFAULT_GROUP, clusters, healthy, subscribe).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, string groupName, List<string> clusters, bool healthy, bool subscribe)
{
ServiceInfo serviceInfo;
string clusterString = string.Join(",", clusters);
if (subscribe)
{
serviceInfo = _serviceInfoHolder.GetServiceInfo(serviceName, groupName, clusterString);
if (serviceInfo == null)
{
serviceInfo = await _clientProxy.Subscribe(serviceName, groupName, clusterString).ConfigureAwait(false);
}
}
else
{
serviceInfo = await _clientProxy.QueryInstancesOfService(serviceName, groupName, clusterString, 0, false).ConfigureAwait(false);
}
return SelectInstances(serviceInfo, healthy);
}
private List<Instance> SelectInstances(ServiceInfo serviceInfo, bool healthy)
{
List<Instance> list = serviceInfo.Hosts;
if (serviceInfo == null || list == null || !list.Any()) return new List<Instance>();
return list.Where(x => x.Healthy.Equals(healthy) && x.Enabled && x.Weight > 0).ToList();
}
public async Task<Instance> SelectOneHealthyInstance(string serviceName)
=> await SelectOneHealthyInstance(serviceName, new List<string>()).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, string groupName)
=> await SelectOneHealthyInstance(serviceName, groupName, true).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, bool subscribe)
=> await SelectOneHealthyInstance(serviceName, new List<string>(), subscribe).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, string groupName, bool subscribe)
=> await SelectOneHealthyInstance(serviceName, groupName, new List<string>(), subscribe).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, List<string> clusters)
=> await SelectOneHealthyInstance(serviceName, clusters, true).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, string groupName, List<string> clusters)
=> await SelectOneHealthyInstance(serviceName, groupName, clusters, true).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, List<string> clusters, bool subscribe)
=> await SelectOneHealthyInstance(serviceName, Constants.DEFAULT_GROUP, clusters, subscribe).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, string groupName, List<string> clusters, bool subscribe)
{
string clusterString = string.Join(",", clusters);
if (subscribe)
{
ServiceInfo serviceInfo = _serviceInfoHolder.GetServiceInfo(serviceName, groupName, clusterString);
if (serviceInfo == null)
{
serviceInfo = await _clientProxy.Subscribe(serviceName, groupName, clusterString).ConfigureAwait(false);
}
return Balancer.GetHostByRandom(serviceInfo?.Hosts);
}
else
{
ServiceInfo serviceInfo = await _clientProxy
.QueryInstancesOfService(serviceName, groupName, clusterString, 0, false).ConfigureAwait(false);
return Balancer.GetHostByRandom(serviceInfo?.Hosts);
}
}
public Task ShutDown() => Task.CompletedTask;
public async Task Subscribe(string serviceName, IEventListener listener)
=> await Subscribe(serviceName, new List<string>(), listener).ConfigureAwait(false);
public async Task Subscribe(string serviceName, string groupName, IEventListener listener)
=> await Subscribe(serviceName, groupName, new List<string>(), listener).ConfigureAwait(false);
public async Task Subscribe(string serviceName, List<string> clusters, IEventListener listener)
=> await Subscribe(serviceName, Constants.DEFAULT_GROUP, clusters, listener).ConfigureAwait(false);
public async Task Subscribe(string serviceName, string groupName, List<string> clusters, IEventListener listener)
{
if (listener == null) return;
string clusterString = string.Join(",", clusters);
_changeNotifier.RegisterListener(groupName, serviceName, clusterString, listener);
await _clientProxy.Subscribe(serviceName, groupName, clusterString).ConfigureAwait(false);
}
public async Task Unsubscribe(string serviceName, IEventListener listener)
=> await Unsubscribe(serviceName, new List<string>(), listener).ConfigureAwait(false);
public async Task Unsubscribe(string serviceName, string groupName, IEventListener listener)
=> await Unsubscribe(serviceName, groupName, new List<string>(), listener).ConfigureAwait(false);
public async Task Unsubscribe(string serviceName, List<string> clusters, IEventListener listener)
=> await Unsubscribe(serviceName, Constants.DEFAULT_GROUP, clusters, listener).ConfigureAwait(false);
public async Task Unsubscribe(string serviceName, string groupName, List<string> clusters, IEventListener listener)
{
string clustersString = string.Join(",", clusters);
_changeNotifier.DeregisterListener(groupName, serviceName, clustersString, listener);
if (!_changeNotifier.IsSubscribed(groupName, serviceName, clustersString))
{
await _clientProxy.Unsubscribe(serviceName, groupName, clustersString).ConfigureAwait(false);
}
}
}
}
IHttpClientFactory clientFactory)
{
_logger = loggerFactory.CreateLogger<NacosNamingService>();
_options = optionAccs.Value;
_namespace = string.IsNullOrWhiteSpace(_options.Namespace) ? Utils.UtilAndComs.DEFAULT_NAMESPACE_ID : _options.Namespace;
this._changeNotifier = new InstancesChangeNotifier();
this._serviceInfoHolder = new ServiceInfoHolder(_logger, _namespace, _options, _changeNotifier);
this._clientProxy = new NamingClientProxyDelegate(_logger, _namespace, _serviceInfoHolder, _options, _changeNotifier, clientFactory);
}
public async Task DeregisterInstance(string serviceName, string ip, int port)
=> await DeregisterInstance(serviceName, ip, port, Constants.DEFAULT_CLUSTER_NAME).ConfigureAwait(false);
public async Task DeregisterInstance(string serviceName, string groupName, string ip, int port)
=> await DeregisterInstance(serviceName, groupName, ip, port, Constants.DEFAULT_CLUSTER_NAME).ConfigureAwait(false);
public async Task DeregisterInstance(string serviceName, string ip, int port, string clusterName)
=> await DeregisterInstance(serviceName, Constants.DEFAULT_GROUP, ip, port, clusterName).ConfigureAwait(false);
public async Task DeregisterInstance(string serviceName, string groupName, string ip, int port, string clusterName)
{
var instance = new Instance()
{
Ip = ip,
Port = port,
ClusterName = clusterName
};
await DeregisterInstance(serviceName, groupName, instance).ConfigureAwait(false);
}
public async Task DeregisterInstance(string serviceName, Instance instance)
=> await DeregisterInstance(serviceName, Constants.DEFAULT_GROUP, instance).ConfigureAwait(false);
public async Task DeregisterInstance(string serviceName, string groupName, Instance instance)
=> await _clientProxy.DeregisterService(serviceName, groupName, instance).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName)
=> await GetAllInstances(serviceName, new List<string>()).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, string groupName)
=> await GetAllInstances(serviceName, groupName, new List<string>()).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, bool subscribe)
=> await GetAllInstances(serviceName, new List<string>(), subscribe).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, string groupName, bool subscribe)
=> await GetAllInstances(serviceName, groupName, new List<string>(), subscribe).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, List<string> clusters)
=> await GetAllInstances(serviceName, Constants.DEFAULT_GROUP, clusters, true).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, string groupName, List<string> clusters)
=> await GetAllInstances(serviceName, groupName, clusters, true).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, List<string> clusters, bool subscribe)
=> await GetAllInstances(serviceName, Constants.DEFAULT_GROUP, clusters, subscribe).ConfigureAwait(false);
public async Task<List<Instance>> GetAllInstances(string serviceName, string groupName, List<string> clusters, bool subscribe)
{
ServiceInfo serviceInfo;
string clusterString = string.Join(",", clusters);
if (subscribe)
{
serviceInfo = _serviceInfoHolder.GetServiceInfo(serviceName, groupName, clusterString);
if (serviceInfo == null || !await _clientProxy.IsSubscribed(serviceName, groupName, clusterString).ConfigureAwait(false))
{
serviceInfo = await _clientProxy.Subscribe(serviceName, groupName, clusterString).ConfigureAwait(false);
}
}
else
{
serviceInfo = await _clientProxy.QueryInstancesOfService(serviceName, groupName, clusterString, 0, false).ConfigureAwait(false);
}
List<Instance> list = serviceInfo.Hosts;
if (serviceInfo == null || serviceInfo.Hosts == null || !serviceInfo.Hosts.Any())
{
return new List<Instance>();
}
return list;
}
public Task<string> GetServerStatus()
=> Task.FromResult(_clientProxy.ServerHealthy() ? UP : DOWN);
public async Task<ListView<string>> GetServicesOfServer(int pageNo, int pageSize)
=> await GetServicesOfServer(pageNo, pageSize, Constants.DEFAULT_GROUP).ConfigureAwait(false);
public async Task<ListView<string>> GetServicesOfServer(int pageNo, int pageSize, string groupName)
=> await GetServicesOfServer(pageNo, pageSize, groupName, null).ConfigureAwait(false);
public async Task<ListView<string>> GetServicesOfServer(int pageNo, int pageSize, AbstractSelector selector)
=> await GetServicesOfServer(pageNo, pageSize, Constants.DEFAULT_GROUP, selector).ConfigureAwait(false);
public async Task<ListView<string>> GetServicesOfServer(int pageNo, int pageSize, string groupName, AbstractSelector selector)
=> await _clientProxy.GetServiceList(pageNo, pageSize, groupName, selector).ConfigureAwait(false);
public Task<List<ServiceInfo>> GetSubscribeServices()
=> Task.FromResult(_changeNotifier.GetSubscribeServices());
public async Task RegisterInstance(string serviceName, string ip, int port)
=> await RegisterInstance(serviceName, ip, port, Constants.DEFAULT_CLUSTER_NAME).ConfigureAwait(false);
public async Task RegisterInstance(string serviceName, string groupName, string ip, int port)
=> await RegisterInstance(serviceName, groupName, ip, port, Constants.DEFAULT_CLUSTER_NAME).ConfigureAwait(false);
public async Task RegisterInstance(string serviceName, string ip, int port, string clusterName)
=> await RegisterInstance(serviceName, Constants.DEFAULT_GROUP, ip, port, clusterName).ConfigureAwait(false);
public async Task RegisterInstance(string serviceName, string groupName, string ip, int port, string clusterName)
{
var instance = new Instance()
{
Ip = ip,
Port = port,
Weight = 1.0d,
ClusterName = clusterName
};
await RegisterInstance(serviceName, groupName, instance).ConfigureAwait(false);
}
public async Task RegisterInstance(string serviceName, Instance instance)
=> await RegisterInstance(serviceName, Constants.DEFAULT_GROUP, instance).ConfigureAwait(false);
public async Task RegisterInstance(string serviceName, string groupName, Instance instance)
=> await _clientProxy.RegisterServiceAsync(serviceName, groupName, instance).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, bool healthy)
=> await SelectInstances(serviceName, new List<string>(), healthy).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, string groupName, bool healthy)
=> await SelectInstances(serviceName, groupName, healthy, true).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, bool healthy, bool subscribe)
=> await SelectInstances(serviceName, new List<string>(), healthy, subscribe).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, string groupName, bool healthy, bool subscribe)
=> await SelectInstances(serviceName, groupName, new List<string>(), healthy, subscribe).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, List<string> clusters, bool healthy)
=> await SelectInstances(serviceName, clusters, healthy, true).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, string groupName, List<string> clusters, bool healthy)
=> await SelectInstances(serviceName, groupName, clusters, healthy, true).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, List<string> clusters, bool healthy, bool subscribe)
=> await SelectInstances(serviceName, Constants.DEFAULT_GROUP, clusters, healthy, subscribe).ConfigureAwait(false);
public async Task<List<Instance>> SelectInstances(string serviceName, string groupName, List<string> clusters, bool healthy, bool subscribe)
{
ServiceInfo serviceInfo;
string clusterString = string.Join(",", clusters);
if (subscribe)
{
serviceInfo = _serviceInfoHolder.GetServiceInfo(serviceName, groupName, clusterString);
if (serviceInfo == null)
{
serviceInfo = await _clientProxy.Subscribe(serviceName, groupName, clusterString).ConfigureAwait(false);
}
}
else
{
serviceInfo = await _clientProxy.QueryInstancesOfService(serviceName, groupName, clusterString, 0, false).ConfigureAwait(false);
}
return SelectInstances(serviceInfo, healthy);
}
private List<Instance> SelectInstances(ServiceInfo serviceInfo, bool healthy)
{
List<Instance> list = serviceInfo.Hosts;
if (serviceInfo == null || list == null || !list.Any()) return new List<Instance>();
return list.Where(x => x.Healthy.Equals(healthy) && x.Enabled && x.Weight > 0).ToList();
}
public async Task<Instance> SelectOneHealthyInstance(string serviceName)
=> await SelectOneHealthyInstance(serviceName, new List<string>()).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, string groupName)
=> await SelectOneHealthyInstance(serviceName, groupName, true).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, bool subscribe)
=> await SelectOneHealthyInstance(serviceName, new List<string>(), subscribe).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, string groupName, bool subscribe)
=> await SelectOneHealthyInstance(serviceName, groupName, new List<string>(), subscribe).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, List<string> clusters)
=> await SelectOneHealthyInstance(serviceName, clusters, true).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, string groupName, List<string> clusters)
=> await SelectOneHealthyInstance(serviceName, groupName, clusters, true).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, List<string> clusters, bool subscribe)
=> await SelectOneHealthyInstance(serviceName, Constants.DEFAULT_GROUP, clusters, subscribe).ConfigureAwait(false);
public async Task<Instance> SelectOneHealthyInstance(string serviceName, string groupName, List<string> clusters, bool subscribe)
{
string clusterString = string.Join(",", clusters);
if (subscribe)
{
ServiceInfo serviceInfo = _serviceInfoHolder.GetServiceInfo(serviceName, groupName, clusterString);
if (serviceInfo == null)
{
serviceInfo = await _clientProxy.Subscribe(serviceName, groupName, clusterString).ConfigureAwait(false);
}
return Balancer.GetHostByRandom(serviceInfo?.Hosts);
}
else
{
ServiceInfo serviceInfo = await _clientProxy
.QueryInstancesOfService(serviceName, groupName, clusterString, 0, false).ConfigureAwait(false);
return Balancer.GetHostByRandom(serviceInfo?.Hosts);
}
}
public Task ShutDown() => Task.CompletedTask;
public async Task Subscribe(string serviceName, IEventListener listener)
=> await Subscribe(serviceName, new List<string>(), listener).ConfigureAwait(false);
public async Task Subscribe(string serviceName, string groupName, IEventListener listener)
=> await Subscribe(serviceName, groupName, new List<string>(), listener).ConfigureAwait(false);
public async Task Subscribe(string serviceName, List<string> clusters, IEventListener listener)
=> await Subscribe(serviceName, Constants.DEFAULT_GROUP, clusters, listener).ConfigureAwait(false);
public async Task Subscribe(string serviceName, string groupName, List<string> clusters, IEventListener listener)
{
if (listener == null) return;
string clusterString = string.Join(",", clusters);
_changeNotifier.RegisterListener(groupName, serviceName, clusterString, listener);
await _clientProxy.Subscribe(serviceName, groupName, clusterString).ConfigureAwait(false);
}
public async Task Unsubscribe(string serviceName, IEventListener listener)
=> await Unsubscribe(serviceName, new List<string>(), listener).ConfigureAwait(false);
public async Task Unsubscribe(string serviceName, string groupName, IEventListener listener)
=> await Unsubscribe(serviceName, groupName, new List<string>(), listener).ConfigureAwait(false);
public async Task Unsubscribe(string serviceName, List<string> clusters, IEventListener listener)
=> await Unsubscribe(serviceName, Constants.DEFAULT_GROUP, clusters, listener).ConfigureAwait(false);
public async Task Unsubscribe(string serviceName, string groupName, List<string> clusters, IEventListener listener)
{
string clustersString = string.Join(",", clusters);
_changeNotifier.DeregisterListener(groupName, serviceName, clustersString, listener);
if (!_changeNotifier.IsSubscribed(groupName, serviceName, clustersString))
{
await _clientProxy.Unsubscribe(serviceName, groupName, clustersString).ConfigureAwait(false);
}
}
public async Task BatchRegisterInstance(string serviceName, string groupName, List<Instance> instances)
{
Naming.Utils.NamingUtils.BatchCheckInstanceIsLegal(instances);
await _clientProxy.BatchRegisterServiceAsync(serviceName, groupName, instances).ConfigureAwait(false);
}
}
}

View File

@ -55,7 +55,7 @@
{ RemoteConstants.LABEL_MODULE, RemoteConstants.LABEL_MODULE_NAMING },
};
this.rpcClient = RpcClientFactory.CreateClient(uuid, RemoteConnectionType.GRPC, labels);
this.rpcClient = RpcClientFactory.CreateClient(uuid, RemoteConnectionType.GRPC, labels, _options.TLSConfig);
this._redoService = new NamingGrpcRedoService(_logger, this);
Start(serverListFactory, serviceInfoHolder);
@ -123,6 +123,19 @@
await DoRegisterService(serviceName, groupName, instance).ConfigureAwait(false);
}
public async Task BatchRegisterServiceAsync(string serviceName, string groupName, List<Instance> instances)
{
_redoService.CacheInstanceForRedo(serviceName, groupName, instances);
await DoBatchRegisterService(serviceName, groupName, instances).ConfigureAwait(false);
}
private async Task DoBatchRegisterService(string serviceName, string groupName, List<Instance> instances)
{
var request = new BatchInstanceRequest(namespaceId, serviceName, groupName, NamingRemoteConstants.BATCH_REGISTER_INSTANCE, instances);
await RequestToServer<CommonResponse>(request).ConfigureAwait(false);
_redoService.InstanceRegistered(serviceName, groupName);
}
public async Task DoRegisterService(string serviceName, string groupName, Instance instance)
{
var request = new InstanceRequest(namespaceId, serviceName, groupName, NamingRemoteConstants.REGISTER_INSTANCE, instance);
@ -235,8 +248,8 @@
return result;
string signData = !string.IsNullOrWhiteSpace(serviceName)
? DateTimeOffset.Now.ToUnixTimeSeconds().ToString() + CommonParams.SEPARATOR + serviceName
: DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
? DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString() + CommonParams.SEPARATOR + serviceName
: DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString();
string signature = HashUtil.GetHMACSHA1(signData, _options.SecretKey);
result[CommonParams.SIGNATURE_FILED] = signature;

View File

@ -0,0 +1,24 @@
namespace Nacos.V2.Naming.Remote.Grpc
{
using Nacos.V2.Naming.Dtos;
using System.Collections.Generic;
public class BatchInstanceRedoData : InstanceRedoData
{
public List<Instance> Instances { get; set; }
public BatchInstanceRedoData(string serviceName, string groupName)
: base(serviceName, groupName)
{
}
public static BatchInstanceRedoData Build(string serviceName, string groupName, List<Instance> instance)
{
var result = new BatchInstanceRedoData(serviceName, groupName)
{
Instances = instance
};
return result;
}
}
}

View File

@ -65,6 +65,14 @@
_registeredInstances.AddOrUpdate(key, redoData, (x, y) => redoData);
}
public void CacheInstanceForRedo(string serviceName, string groupName, List<Instance> instances)
{
string key = NamingUtils.GetGroupedName(serviceName, groupName);
var redoData = BatchInstanceRedoData.Build(serviceName, groupName, instances);
_registeredInstances.AddOrUpdate(key, redoData, (x, y) => redoData);
}
/// <summary>
/// Instance register successfully, mark registered status as true
/// </summary>
@ -92,6 +100,7 @@
if (_registeredInstances.TryGetValue(key, out var data))
{
data.Unregistering = true;
data.ExpectedRegistered = false;
}
}
@ -117,7 +126,10 @@
{
string key = NamingUtils.GetGroupedName(serviceName, groupName);
_registeredInstances.TryRemove(key, out _);
if (_registeredInstances.TryGetValue(key, out var data) && data != null && !data.ExpectedRegistered)
{
_registeredInstances.TryRemove(key, out _);
}
}
/// <summary>
@ -182,6 +194,7 @@
if (_subscribes.TryGetValue(key, out var data))
{
data.Unregistering = true;
data.ExpectedRegistered = false;
}
}
@ -195,7 +208,10 @@
{
string key = ServiceInfo.GetKey(NamingUtils.GetGroupedName(serviceName, groupName), cluster);
_subscribes.TryRemove(key, out _);
if (_subscribes.TryGetValue(key, out var data) && data != null && !data.ExpectedRegistered)
{
_subscribes.TryRemove(key, out _);
}
}
/// <summary>

View File

@ -7,12 +7,15 @@
{
this.ServiceName = serviceName;
this.GroupName = groupName;
this.ExpectedRegistered = true;
}
public string ServiceName { get; private set; }
public string GroupName { get; private set; }
public bool ExpectedRegistered { get; set; }
public T Data { get; set; }
public bool Registered { get; set; }

View File

@ -66,6 +66,11 @@
case RedoType.REGISTER:
if (IsClientDisabled()) return;
if (redoData is BatchInstanceRedoData batchInstanceRedoData)
{
await _clientProxy.BatchRegisterServiceAsync(serviceName, groupName, batchInstanceRedoData.Instances).ConfigureAwait(false);
}
await _clientProxy.DoRegisterService(serviceName, groupName, redoData.Data).ConfigureAwait(false);
break;
case RedoType.UNREGISTER:

View File

@ -72,7 +72,7 @@
this.serviceInfoHolder = serviceInfoHolder;
}
internal async Task<Newtonsoft.Json.Linq.JObject> SendBeat(BeatInfo beatInfo, bool lightBeatEnabled)
internal async Task<System.Text.Json.Nodes.JsonObject> SendBeat(BeatInfo beatInfo, bool lightBeatEnabled)
{
var parameters = new Dictionary<string, string>()
{
@ -88,7 +88,7 @@
if (!lightBeatEnabled) body["beat"] = beatInfo.ToJsonString();
var result = await ReqApi(UtilAndComs.NacosUrlBase + "/instance/beat", parameters, body, HttpMethod.Put).ConfigureAwait(false);
return Newtonsoft.Json.Linq.JObject.Parse(result);
return System.Text.Json.Nodes.JsonNode.Parse(result).AsObject();
}
private void SetServerPort(int serverPort)
@ -177,9 +177,9 @@
var result = await ReqApi(UtilAndComs.NacosUrlBase + "/service/list", paramters, HttpMethod.Get).ConfigureAwait(false);
var json = Newtonsoft.Json.Linq.JObject.Parse(result);
var count = json.GetValue("count")?.ToObject<int>() ?? 0;
var data = json.GetValue("doms")?.ToObject<List<string>>() ?? new List<string>();
var json = System.Text.Json.Nodes.JsonNode.Parse(result).AsObject();
var count = json["count"]?.GetValue<int>() ?? 0;
var data = json["doms"]?.GetValue<List<string>>() ?? new List<string>();
ListView<string> listView = new ListView<string>(count, data);
return listView;
@ -381,9 +381,9 @@
|| string.IsNullOrWhiteSpace(_options.SecretKey))
return;
string signData = !string.IsNullOrWhiteSpace(paramters[CommonParams.SERVICE_NAME_PARAM])
? DateTimeOffset.Now.ToUnixTimeSeconds().ToString() + CommonParams.SEPARATOR + paramters[CommonParams.SERVICE_NAME_PARAM]
: DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
string signData = paramters.ContainsKey(CommonParams.SERVICE_NAME_PARAM) && !string.IsNullOrWhiteSpace(paramters[CommonParams.SERVICE_NAME_PARAM])
? DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString() + CommonParams.SEPARATOR + paramters[CommonParams.SERVICE_NAME_PARAM]
: DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString();
string signature = HashUtil.GetHMACSHA1(signData, _options.SecretKey);
paramters[CommonParams.SIGNATURE_FILED] = signature;
@ -411,9 +411,9 @@
string result = ReqApi(UtilAndComs.NacosUrlBase + "/operator/metrics", new Dictionary<string, string>(),
HttpMethod.Get).ConfigureAwait(false).GetAwaiter().GetResult();
var json = Newtonsoft.Json.Linq.JObject.Parse(result);
var json = System.Text.Json.Nodes.JsonNode.Parse(result).AsObject();
string serverStatus = json.GetValue("status")?.ToString();
string serverStatus = json["status"]?.GetValue<string>();
return "UP".Equals(serverStatus);
}
catch
@ -493,7 +493,7 @@
{
foreach (var item in dict)
{
builder.Append($"{item.Key}={item.Value}&");
builder.Append($"{item.Key}={item.Value.UrlEncode()}&");
}
}
@ -501,7 +501,7 @@
{
foreach (var item in body)
{
builder.Append($"{item.Key}={item.Value}&");
builder.Append($"{item.Key}={item.Value.UrlEncode()}&");
}
}
@ -509,5 +509,10 @@
}
public Task<bool> IsSubscribed(string serviceName, string groupName, string clusters) => Task.FromResult(true);
public Task BatchRegisterServiceAsync(string serviceName, string groupName, List<Instance> instances)
{
throw new NotImplementedException("Do not support persistent instances to perform batch registration methods.");
}
}
}

View File

@ -15,6 +15,15 @@
/// <param name="instance">instance to register</param>
Task RegisterServiceAsync(string serviceName, string groupName, Instance instance);
/// <summary>
/// batch register instance to service with specified instance properties.
/// since nacos server 2.1.1
/// </summary>
/// <param name="serviceName">name of service</param>
/// <param name="groupName">group of service</param>
/// <param name="instances">instances to register</param>
Task BatchRegisterServiceAsync(string serviceName, string groupName, List<Instance> instances);
Task DeregisterService(string serviceName, string groupName, Instance instance);
Task UpdateInstance(string serviceName, string groupName, Instance instance);

View File

@ -12,6 +12,7 @@
using Nacos.V2.Security;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
@ -119,6 +120,13 @@
private INamingClientProxy GetExecuteClientProxy() => _options.NamingUseRpc ? grpcClientProxy : httpClientProxy;
public Task<bool> IsSubscribed(string serviceName, string groupName, string clusters)
=> grpcClientProxy.IsSubscribed(serviceName, groupName, clusters);
=> GetExecuteClientProxy().IsSubscribed(serviceName, groupName, clusters);
public async Task BatchRegisterServiceAsync(string serviceName, string groupName, List<Instance> instances)
{
if (instances == null || !instances.Any()) await Task.Yield();
await GetExecuteClientProxy().BatchRegisterServiceAsync(serviceName, groupName, instances).ConfigureAwait(false);
}
}
}

View File

@ -4,6 +4,8 @@
{
public static string REGISTER_INSTANCE = "registerInstance";
public static string BATCH_REGISTER_INSTANCE = "batchRegisterInstance";
public static string DE_REGISTER_INSTANCE = "deregisterInstance";
public static string QUERY_SERVICE = "queryService";

View File

@ -1,7 +1,11 @@
namespace Nacos.V2.Naming.Utils
{
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Nacos.V2.Common;
using Nacos.V2.Exceptions;
using Nacos.V2.Naming.Dtos;
using Nacos.V2.Utils;
public class NamingUtils
@ -94,5 +98,61 @@
{
return groupName + Constants.SERVICE_INFO_SPLITER + serviceName;
}
/// <summary>
/// Batch verify the validity of instances.
/// </summary>
/// <param name="instances">List of instances to be registered</param>
public static void BatchCheckInstanceIsLegal(List<Instance> instances)
{
HashSet<Instance> newInstanceSet = new HashSet<Instance>(instances);
foreach (var instance in newInstanceSet)
{
CheckInstanceIsEphemeral(instance);
CheckInstanceIsLegal(instance);
}
}
/// <summary>
/// check batch register is Ephemeral.
/// </summary>
/// <param name="instance">instance</param>
/// <exception cref="NacosException"></exception>
public static void CheckInstanceIsEphemeral(Instance instance)
{
if (!instance.Ephemeral)
{
throw new NacosException(
NacosException.INVALID_PARAM,
$"Batch registration does not allow persistent instance registration , Instance{instance}");
}
}
/// <summary>
/// Check instance param about keep alive.
/// heart beat timeout must > heart beat interval
/// ip delete timeout must > heart beat interval
/// </summary>
/// <param name="instance">need checked instance</param>
/// <exception cref="NacosException"></exception>
public static void CheckInstanceIsLegal(Instance instance)
{
if (instance.GetInstanceHeartBeatTimeOut() < instance.GetInstanceHeartBeatInterval()
|| instance.GetIpDeleteTimeout() < instance.GetInstanceHeartBeatInterval())
{
throw new NacosException(
NacosException.INVALID_PARAM,
"Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'.");
}
if (instance.ClusterName.IsNotNullOrWhiteSpace()
&& !Regex.IsMatch(instance.ClusterName, Common.Constants.CLUSTER_NAME_PATTERN_STRING))
{
throw new NacosException(
NacosException.INVALID_PARAM,
$"Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: {instance.ClusterName})");
}
}
}
}

View File

@ -4,10 +4,10 @@
public abstract class CommonRequest
{
[Newtonsoft.Json.JsonProperty("headers")]
[System.Text.Json.Serialization.JsonPropertyName("headers")]
public System.Collections.Generic.Dictionary<string, string> Headers { get; set; } = new System.Collections.Generic.Dictionary<string, string>();
[Newtonsoft.Json.JsonProperty("requestId")]
[System.Text.Json.Serialization.JsonPropertyName("requestId")]
public string RequestId { get; set; }
public void PutHeader(string key, string value)

View File

@ -2,22 +2,22 @@
{
public class CommonRequestMeta
{
[Newtonsoft.Json.JsonProperty("connectionId")]
[System.Text.Json.Serialization.JsonPropertyName("connectionId")]
public string ConnectionId { get; set; } = "";
[Newtonsoft.Json.JsonProperty("clientIp")]
[System.Text.Json.Serialization.JsonPropertyName("clientIp")]
public string ClientIp { get; set; } = "";
[Newtonsoft.Json.JsonProperty("clientPort")]
[System.Text.Json.Serialization.JsonPropertyName("clientPort")]
public int ClientPort { get; set; }
[Newtonsoft.Json.JsonProperty("clientVersion")]
[System.Text.Json.Serialization.JsonPropertyName("clientVersion")]
public string ClientVersion { get; set; } = "";
[Newtonsoft.Json.JsonProperty("type")]
[System.Text.Json.Serialization.JsonPropertyName("type")]
public string Type { get; set; } = "";
[Newtonsoft.Json.JsonProperty("labels")]
[System.Text.Json.Serialization.JsonPropertyName("labels")]
public System.Collections.Generic.Dictionary<string, string> Labels { get; set; } = new System.Collections.Generic.Dictionary<string, string>();
}
}

View File

@ -2,16 +2,16 @@
{
public abstract class CommonResponse
{
[Newtonsoft.Json.JsonProperty("resultCode")]
[System.Text.Json.Serialization.JsonPropertyName("resultCode")]
public int ResultCode { get; set; } = 200;
[Newtonsoft.Json.JsonProperty("errorCode")]
[System.Text.Json.Serialization.JsonPropertyName("errorCode")]
public int ErrorCode { get; set; }
[Newtonsoft.Json.JsonProperty("message")]
[System.Text.Json.Serialization.JsonPropertyName("message")]
public string Message { get; set; }
[Newtonsoft.Json.JsonProperty("requestId")]
[System.Text.Json.Serialization.JsonPropertyName("requestId")]
public string RequestId { get; set; }
public bool IsSuccess() => ResultCode == 200;

View File

@ -1,19 +1,22 @@
namespace Nacos.V2.Remote.GRpc
{
using Grpc.Core;
using Microsoft.Extensions.Logging;
using Nacos.V2.Common;
using Nacos.V2.Remote.Requests;
using Nacos.V2.Remote.Responses;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
public class GrpcClient : RpcClient
{
private static readonly string NACOS_SERVER_GRPC_PORT_OFFSET_KEY = "nacos.server.grpc.port.offset";
private static readonly string NACOS_SERVER_GRPC_PORT_DEFAULT_OFFSET = "1000";
public GrpcClient(string name)
: base(name)
public GrpcClient(string name, TLSConfig tlsConfig)
: base(name, tlsConfig)
{
}
@ -21,16 +24,41 @@
{
try
{
var options = new Grpc.Net.Client.GrpcChannelOptions();
var port = serverInfo.ServerPort + RpcPortOffset();
var address = string.Empty;
var channel = new Grpc.Core.Channel(
serverInfo.ServerIp,
port,
Grpc.Core.ChannelCredentials.Insecure,
/* keep config and naming using diff channel */
new List<Grpc.Core.ChannelOption> { new Grpc.Core.ChannelOption(GetName(), 1) });
if (_tlsConfig != null && _tlsConfig.Enabled)
{
var clientCertificate = new X509Certificate2(_tlsConfig.PfxFile, _tlsConfig.Password);
#if !NETSTANDARD2_0
var httpClientHandler = new SocketsHttpHandler();
httpClientHandler.UseProxy = false;
httpClientHandler.AllowAutoRedirect = false;
httpClientHandler.SslOptions.ClientCertificates = new X509CertificateCollection { clientCertificate };
httpClientHandler.SslOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => true;
#else
var httpClientHandler = new HttpClientHandler();
httpClientHandler.UseProxy = false;
httpClientHandler.AllowAutoRedirect = false;
httpClientHandler.ClientCertificates.Add(clientCertificate);
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
#endif
options.HttpHandler = httpClientHandler;
address = $"https://{serverInfo.ServerIp}:{port}";
}
else
{
options.Credentials = ChannelCredentials.Insecure;
address = $"http://{serverInfo.ServerIp}:{port}";
}
options.MaxRetryAttempts = 0;
var channel = Grpc.Net.Client.GrpcChannel.ForAddress(address, options);
// after nacos alpha2 server check response was changed!!
var response = ServerCheck(channel);
if (response == null || response is not ServerCheckResponse scResp)
{
@ -51,7 +79,6 @@
grpcConn.SetRequestClient(requestClient);
grpcConn.SetChannel(channel);
// after nacos alpha2 setup request was changed!!
ConnectionSetupRequest conSetupRequest = new ConnectionSetupRequest
{
ClientVersion = Constants.CLIENT_VERSION,
@ -82,7 +109,7 @@
}
}
private CommonResponse ServerCheck(Grpc.Core.ChannelBase channel)
private CommonResponse ServerCheck(Grpc.Net.Client.GrpcChannel channel)
{
try
{
@ -122,6 +149,12 @@
{
try
{
if (request is SetupAckRequest)
{
// there is no connection ready this time
return;
}
var response = HandleServerRequest(request);
response.RequestId = request.RequestId;
await call.RequestStream.WriteAsync(GrpcUtils.Convert(response)).ConfigureAwait(false);

View File

@ -1,60 +1,61 @@
namespace Nacos.V2.Remote.GRpc
{
using Nacos.V2.Exceptions;
using System;
using System.Threading.Tasks;
public class GrpcConnection : RemoteConnection
{
protected Grpc.Core.ChannelBase channel;
protected Nacos.Request.RequestClient reqClient;
protected Grpc.Core.AsyncDuplexStreamingCall<Nacos.Payload, Nacos.Payload> streamCall;
public void SetChannel(Grpc.Core.ChannelBase channel) => this.channel = channel;
public void SetRequestClient(Nacos.Request.RequestClient client) => this.reqClient = client;
public void SetBiRequestStreamClient(Grpc.Core.AsyncDuplexStreamingCall<Nacos.Payload, Nacos.Payload> streamCall) => this.streamCall = streamCall;
public GrpcConnection(RemoteServerInfo serverInfo) => this.ServerInfo = serverInfo;
protected override async Task Close()
{
if (reqClient != null) reqClient = null;
if (channel != null) await channel.ShutdownAsync().ConfigureAwait(false);
}
protected override Task<CommonResponse> Request(CommonRequest req, CommonRequestMeta meta) => Request(req, meta, 3000L);
protected override async Task<CommonResponse> Request(CommonRequest req, CommonRequestMeta meta, long timeoutMills)
{
// convert normal request to grpc request
Payload grpcRequest = GrpcUtils.Convert(req, meta);
Payload grpcResponse = null;
try
{
var callOptions = default(Grpc.Core.CallOptions).WithDeadline(DateTime.UtcNow.AddMilliseconds(timeoutMills));
grpcResponse = await reqClient.requestAsync(grpcRequest, callOptions);
}
catch (Exception ex)
{
throw new NacosException(NacosException.SERVER_ERROR, ex.Message);
}
var response = (CommonResponse)GrpcUtils.Parse(grpcResponse);
return response;
}
public void SendRequest(CommonRequest request, CommonRequestMeta meta)
{
Payload convert = GrpcUtils.Convert(request, meta);
streamCall.RequestStream.WriteAsync(convert);
}
}
}
namespace Nacos.V2.Remote.GRpc
{
using Nacos.V2.Exceptions;
using System;
using System.Threading.Tasks;
public class GrpcConnection : RemoteConnection
{
protected Grpc.Net.Client.GrpcChannel channel;
protected Nacos.Request.RequestClient reqClient;
protected Grpc.Core.AsyncDuplexStreamingCall<Nacos.Payload, Nacos.Payload> streamCall;
public void SetChannel(Grpc.Net.Client.GrpcChannel channel) => this.channel = channel;
public void SetRequestClient(Nacos.Request.RequestClient client) => this.reqClient = client;
public void SetBiRequestStreamClient(Grpc.Core.AsyncDuplexStreamingCall<Nacos.Payload, Nacos.Payload> streamCall) => this.streamCall = streamCall;
public GrpcConnection(RemoteServerInfo serverInfo) => this.ServerInfo = serverInfo;
protected override async Task Close()
{
if (reqClient != null) reqClient = null;
if (channel != null) await channel.ShutdownAsync().ConfigureAwait(false);
}
protected override Task<CommonResponse> Request(CommonRequest req, CommonRequestMeta meta) => Request(req, meta, 3000L);
protected override async Task<CommonResponse> Request(CommonRequest req, CommonRequestMeta meta, long timeoutMills)
{
// convert normal request to grpc request
Payload grpcRequest = GrpcUtils.Convert(req, meta);
Payload grpcResponse = null;
try
{
var callOptions = default(Grpc.Core.CallOptions).WithDeadline(DateTime.UtcNow.AddMilliseconds(timeoutMills));
grpcResponse = await reqClient.requestAsync(grpcRequest, callOptions).ResponseAsync
.ConfigureAwait(false);
}
catch (Exception ex)
{
throw new NacosException(NacosException.SERVER_ERROR, ex.Message);
}
var response = (CommonResponse)GrpcUtils.Parse(grpcResponse);
return response;
}
public void SendRequest(CommonRequest request, CommonRequestMeta meta)
{
Payload convert = GrpcUtils.Convert(request, meta);
streamCall.RequestStream.WriteAsync(convert);
}
}
}

View File

@ -10,10 +10,10 @@
this.Data = data;
}
[Newtonsoft.Json.JsonProperty("count")]
[System.Text.Json.Serialization.JsonPropertyName("count")]
public int Count { get; set; }
[Newtonsoft.Json.JsonProperty("data")]
[System.Text.Json.Serialization.JsonPropertyName("data")]
public List<T> Data { get; set; }
public override string ToString() => "ListView{" + "data=" + Data + ", count=" + Count + '}';

View File

@ -36,6 +36,8 @@
public static readonly string Req_ServerCheck = "ServerCheckRequest";
public static readonly string Req_SetupAck = "SetupAckRequest";
public static readonly string Req_Config_ReSync = "ConfigReSyncRequest";
public static readonly string Resp_Config_Pubish_Alpha2 = "ConfigPublishResponse";
@ -68,6 +70,8 @@
public static readonly string Resp_ServerCheck = "ServerCheckResponse";
public static readonly string Resp_SetupAck = "SetupAckResponse";
public static readonly string Req_PushAck = "PushAckRequest";
public static readonly string Req_ConnectReset = "ConnectResetRequest";
@ -84,6 +88,10 @@
public static readonly string Resp_Naming_NotifySubscriber = "NotifySubscriberResponse";
public static readonly string Req_Naming_BatchInstance = "BatchInstanceRequest";
public static readonly string Resp_Naming_BatchInstance = "BatchInstanceResponse";
public static Dictionary<string, Type> RemoteResponseTypeMapping = new Dictionary<string, Type>
{
{ Resp_Config_Pubish_Alpha1, typeof(ConfigPubishResponse) },
@ -107,6 +115,10 @@
{ Req_ConnectReset, typeof(ConnectResetRequest) },
{ Resp_HealthCheck, typeof(HealthCheckResponse) },
{ Resp_Naming_NotifySubscriber, typeof(NotifySubscriberResponse) },
{ Req_Naming_BatchInstance, typeof(BatchInstanceRequest) },
{ Resp_Naming_BatchInstance, typeof(BatchInstanceResponse) },
{ Req_SetupAck, typeof(SetupAckRequest) },
{ Resp_SetupAck, typeof(SetupAckResponse) },
};
}
}

View File

@ -2,13 +2,13 @@
{
public abstract class AbstractNamingRequest : CommonRequest
{
[Newtonsoft.Json.JsonProperty("namespace")]
[System.Text.Json.Serialization.JsonPropertyName("namespace")]
public string Namespace { get; set; }
[Newtonsoft.Json.JsonProperty("serviceName")]
[System.Text.Json.Serialization.JsonPropertyName("serviceName")]
public string ServiceName { get; set; }
[Newtonsoft.Json.JsonProperty("groupName")]
[System.Text.Json.Serialization.JsonPropertyName("groupName")]
public string GroupName { get; set; }
public AbstractNamingRequest(string @namespace, string serviceName, string groupName)

View File

@ -0,0 +1,23 @@
namespace Nacos.V2.Remote.Requests
{
using Nacos.V2.Naming.Dtos;
using System.Collections.Generic;
public class BatchInstanceRequest : AbstractNamingRequest
{
public BatchInstanceRequest(string @namespace, string serviceName, string groupName, string type, List<Instance> instances)
: base(@namespace, serviceName, groupName)
{
this.Type = type;
this.Instances = instances;
}
[System.Text.Json.Serialization.JsonPropertyName("type")]
public string Type { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("instances")]
public List<Instance> Instances { get; set; }
public override string GetRemoteType() => RemoteRequestType.Req_Naming_SubscribeService;
}
}

View File

@ -2,13 +2,13 @@
{
public class ClientAbilities
{
[Newtonsoft.Json.JsonProperty("remoteAbility")]
[System.Text.Json.Serialization.JsonPropertyName("remoteAbility")]
public ClientRemoteAbility RemoteAbility = new ClientRemoteAbility();
[Newtonsoft.Json.JsonProperty("configAbility")]
[System.Text.Json.Serialization.JsonPropertyName("configAbility")]
public ClientConfigAbility ConfigAbility = new ClientConfigAbility();
[Newtonsoft.Json.JsonProperty("namingAbility")]
[System.Text.Json.Serialization.JsonPropertyName("namingAbility")]
public ClientNamingAbility NamingAbility = new ClientNamingAbility();
}
}

View File

@ -2,7 +2,7 @@
{
public class ClientConfigAbility
{
[Newtonsoft.Json.JsonProperty("supportRemoteMetrics")]
[System.Text.Json.Serialization.JsonPropertyName("supportRemoteMetrics")]
public bool SupportRemoteMetrics { get; set; }
}
}

View File

@ -2,10 +2,10 @@
{
public class ClientNamingAbility
{
[Newtonsoft.Json.JsonProperty("supportDeltaPush")]
[System.Text.Json.Serialization.JsonPropertyName("supportDeltaPush")]
public bool SupportDeltaPush { get; set; }
[Newtonsoft.Json.JsonProperty("supportRemoteMetric")]
[System.Text.Json.Serialization.JsonPropertyName("supportRemoteMetric")]
public bool SupportRemoteMetric { get; set; }
}
}

View File

@ -2,7 +2,7 @@
{
public class ClientRemoteAbility
{
[Newtonsoft.Json.JsonProperty("supportRemoteConnection")]
[System.Text.Json.Serialization.JsonPropertyName("supportRemoteConnection")]
public bool SupportRemoteConnection { get; set; }
}
}

View File

@ -4,10 +4,10 @@
public class ConfigBatchListenRequest : CommonRequest
{
[Newtonsoft.Json.JsonProperty("configListenContexts")]
[System.Text.Json.Serialization.JsonPropertyName("configListenContexts")]
public List<ConfigListenContext> ConfigListenContexts { get; set; } = new List<ConfigListenContext>();
[Newtonsoft.Json.JsonProperty("listen")]
[System.Text.Json.Serialization.JsonPropertyName("listen")]
public bool Listen { get; set; } = true;
public void AddConfigListenContext(string tenant, string group, string dataId, string md5)

View File

@ -10,34 +10,34 @@
/// <summary>
/// The tenant, corresponding to the namespace field of Nacos
/// </summary>
[Newtonsoft.Json.JsonProperty("tenant")]
[System.Text.Json.Serialization.JsonPropertyName("tenant")]
public string Tenant { get; set; }
/// <summary>
/// Configuration ID
/// </summary>
[Newtonsoft.Json.JsonProperty("dataId")]
[System.Text.Json.Serialization.JsonPropertyName("dataId")]
public string DataId { get; set; }
/// <summary>
/// Configuration group
/// </summary>
[Newtonsoft.Json.JsonProperty("group")]
[System.Text.Json.Serialization.JsonPropertyName("group")]
public string Group { get; set; }
[Newtonsoft.Json.JsonProperty("beta")]
[System.Text.Json.Serialization.JsonPropertyName("beta")]
public bool Beta { get; set; }
[Newtonsoft.Json.JsonProperty("contentPush")]
[System.Text.Json.Serialization.JsonPropertyName("contentPush")]
public bool ContentPush { get; set; }
[Newtonsoft.Json.JsonProperty("lastModifiedTs")]
[System.Text.Json.Serialization.JsonPropertyName("lastModifiedTs")]
public long LastModifiedTs { get; set; }
[Newtonsoft.Json.JsonProperty("content")]
[System.Text.Json.Serialization.JsonPropertyName("content")]
public string Content { get; set; }
[Newtonsoft.Json.JsonProperty("type")]
[System.Text.Json.Serialization.JsonPropertyName("type")]
public string Type { get; set; }
public override string GetRemoteType() => RemoteRequestType.Resp_Config_ChangeNotify;

View File

@ -2,13 +2,13 @@
{
public class ConfigContext
{
[Newtonsoft.Json.JsonProperty("group")]
[System.Text.Json.Serialization.JsonPropertyName("group")]
public string Group { get; set; }
[Newtonsoft.Json.JsonProperty("dataId")]
[System.Text.Json.Serialization.JsonPropertyName("dataId")]
public string DataId { get; set; }
[Newtonsoft.Json.JsonProperty("tenant")]
[System.Text.Json.Serialization.JsonPropertyName("tenant")]
public string Tenant { get; set; }
}
}

View File

@ -10,16 +10,16 @@
this.Md5 = md5;
}
[Newtonsoft.Json.JsonProperty("group")]
[System.Text.Json.Serialization.JsonPropertyName("group")]
public string Group { get; private set; }
[Newtonsoft.Json.JsonProperty("md5")]
[System.Text.Json.Serialization.JsonPropertyName("md5")]
public string Md5 { get; private set; }
[Newtonsoft.Json.JsonProperty("dataId")]
[System.Text.Json.Serialization.JsonPropertyName("dataId")]
public string DataId { get; private set; }
[Newtonsoft.Json.JsonProperty("tenant")]
[System.Text.Json.Serialization.JsonPropertyName("tenant")]
public string Tenant { get; private set; }
}
}

View File

@ -15,29 +15,29 @@
/// <summary>
/// The tenant, corresponding to the namespace field of Nacos
/// </summary>
[Newtonsoft.Json.JsonProperty("tenant")]
[System.Text.Json.Serialization.JsonPropertyName("tenant")]
public string Tenant { get; private set; }
/// <summary>
/// Configuration ID
/// </summary>
[Newtonsoft.Json.JsonProperty("dataId")]
[System.Text.Json.Serialization.JsonPropertyName("dataId")]
public string DataId { get; private set; }
/// <summary>
/// Configuration group
/// </summary>
[Newtonsoft.Json.JsonProperty("group")]
[System.Text.Json.Serialization.JsonPropertyName("group")]
public string Group { get; private set; }
/// <summary>
/// Configuration content
/// </summary>
[Newtonsoft.Json.JsonProperty("content")]
[System.Text.Json.Serialization.JsonPropertyName("content")]
public string Content { get; private set; }
[Newtonsoft.Json.JsonProperty("additionMap")]
[System.Text.Json.Serialization.JsonPropertyName("additionMap")]
public Dictionary<string, string> AdditionMap { get; set; } = new Dictionary<string, string>();
public void PutAdditonalParam(string key, string value)

View File

@ -12,22 +12,22 @@
/// <summary>
/// The tenant, corresponding to the namespace field of Nacos
/// </summary>
[Newtonsoft.Json.JsonProperty("tenant")]
[System.Text.Json.Serialization.JsonPropertyName("tenant")]
public string Tenant { get; private set; }
/// <summary>
/// Configuration ID
/// </summary>
[Newtonsoft.Json.JsonProperty("dataId")]
[System.Text.Json.Serialization.JsonPropertyName("dataId")]
public string DataId { get; private set; }
/// <summary>
/// Configuration group
/// </summary>
[Newtonsoft.Json.JsonProperty("group")]
[System.Text.Json.Serialization.JsonPropertyName("group")]
public string Group { get; private set; }
[Newtonsoft.Json.JsonProperty("tag")]
[System.Text.Json.Serialization.JsonPropertyName("tag")]
public string Tag { get; set; }
public override string GetRemoteType() => RemoteRequestType.Req_Config_Get;

View File

@ -12,19 +12,19 @@
/// <summary>
/// The tenant, corresponding to the namespace field of Nacos
/// </summary>
[Newtonsoft.Json.JsonProperty("tenant")]
[System.Text.Json.Serialization.JsonPropertyName("tenant")]
public string Tenant { get; private set; }
/// <summary>
/// Configuration ID
/// </summary>
[Newtonsoft.Json.JsonProperty("dataId")]
[System.Text.Json.Serialization.JsonPropertyName("dataId")]
public string DataId { get; private set; }
/// <summary>
/// Configuration group
/// </summary>
[Newtonsoft.Json.JsonProperty("group")]
[System.Text.Json.Serialization.JsonPropertyName("group")]
public string Group { get; private set; }
public override string GetRemoteType() => RemoteRequestType.Req_Config_ReSync;

View File

@ -13,25 +13,25 @@
/// <summary>
/// The tenant, corresponding to the namespace field of Nacos
/// </summary>
[Newtonsoft.Json.JsonProperty("tenant")]
[System.Text.Json.Serialization.JsonPropertyName("tenant")]
public string Tenant { get; private set; }
/// <summary>
/// Configuration ID
/// </summary>
[Newtonsoft.Json.JsonProperty("dataId")]
[System.Text.Json.Serialization.JsonPropertyName("dataId")]
public string DataId { get; private set; }
/// <summary>
/// Configuration group
/// </summary>
[Newtonsoft.Json.JsonProperty("group")]
[System.Text.Json.Serialization.JsonPropertyName("group")]
public string Group { get; private set; }
/// <summary>
/// Configuration content
/// </summary>
[Newtonsoft.Json.JsonProperty("tag")]
[System.Text.Json.Serialization.JsonPropertyName("tag")]
public string Tag { get; private set; }
public override string GetRemoteType() => RemoteRequestType.Req_Config_Remove;

View File

@ -2,10 +2,10 @@
{
public class ConnectResetRequest : CommonRequest
{
[Newtonsoft.Json.JsonProperty("serverIp")]
[System.Text.Json.Serialization.JsonPropertyName("serverIp")]
public string ServerIp { get; set; }
[Newtonsoft.Json.JsonProperty("serverPort")]
[System.Text.Json.Serialization.JsonPropertyName("serverPort")]
public string ServerPort { get; set; }
public override string GetRemoteType() => RemoteRequestType.Req_ConnectReset;

View File

@ -4,16 +4,16 @@
public class ConnectionSetupRequest : CommonRequest
{
[Newtonsoft.Json.JsonProperty("clientVersion")]
[System.Text.Json.Serialization.JsonPropertyName("clientVersion")]
public string ClientVersion { get; set; }
[Newtonsoft.Json.JsonProperty("abilities")]
[System.Text.Json.Serialization.JsonPropertyName("abilities")]
public ClientAbilities Abilities { get; set; }
[Newtonsoft.Json.JsonProperty("tenant")]
[System.Text.Json.Serialization.JsonPropertyName("tenant")]
public string Tenant { get; set; }
[Newtonsoft.Json.JsonProperty("labels")]
[System.Text.Json.Serialization.JsonPropertyName("labels")]
public Dictionary<string, string> Labels = new Dictionary<string, string>();
public override string GetRemoteType() => RemoteRequestType.Req_ConnectionSetup;

View File

@ -2,10 +2,10 @@
{
public class InstanceRequest : AbstractNamingRequest
{
[Newtonsoft.Json.JsonProperty("type")]
[System.Text.Json.Serialization.JsonPropertyName("type")]
public string Type { get; set; }
[Newtonsoft.Json.JsonProperty("instance")]
[System.Text.Json.Serialization.JsonPropertyName("instance")]
public Nacos.V2.Naming.Dtos.Instance Instance { get; set; }
public InstanceRequest(string @namespace, string serviceName, string groupName, string type, Nacos.V2.Naming.Dtos.Instance instance)

View File

@ -2,16 +2,16 @@
{
public class NotifySubscriberRequest : CommonRequest
{
[Newtonsoft.Json.JsonProperty("namespace")]
[System.Text.Json.Serialization.JsonPropertyName("namespace")]
public string Namespace { get; set; }
[Newtonsoft.Json.JsonProperty("serviceName")]
[System.Text.Json.Serialization.JsonPropertyName("serviceName")]
public string ServiceName { get; set; }
[Newtonsoft.Json.JsonProperty("groupName")]
[System.Text.Json.Serialization.JsonPropertyName("groupName")]
public string GroupName { get; set; }
[Newtonsoft.Json.JsonProperty("serviceInfo")]
[System.Text.Json.Serialization.JsonPropertyName("serviceInfo")]
public Nacos.V2.Naming.Dtos.ServiceInfo ServiceInfo { get; set; }
public override string GetRemoteType() => RemoteRequestType.Req_Naming_NotifySubscriber;

View File

@ -14,13 +14,13 @@
this.PageSize = pageSize;
}
[Newtonsoft.Json.JsonProperty("pageNo")]
[System.Text.Json.Serialization.JsonPropertyName("pageNo")]
public int PageNo { get; set; }
[Newtonsoft.Json.JsonProperty("pageSize")]
[System.Text.Json.Serialization.JsonPropertyName("pageSize")]
public int PageSize { get; set; }
[Newtonsoft.Json.JsonProperty("selector")]
[System.Text.Json.Serialization.JsonPropertyName("selector")]
public string Selector { get; set; }
public override string GetRemoteType() => RemoteRequestType.Req_Naming_ServiceList;

Some files were not shown because too many files have changed in this diff Show More