This is an automated email from the ASF dual-hosted git repository.
xiaoyu pushed a commit to branch main
in repository
https://gitbox.apache.org/repos/asf/incubator-shenyu-client-dotnet.git
The following commit(s) were added to refs/heads/main by this push:
new c2e9175 support register client via zookeeper (#9)
c2e9175 is described below
commit c2e91756f0e3970c9f7528c4257b015c0d7f1ac2
Author: Han Gao <[email protected]>
AuthorDate: Thu May 12 19:38:21 2022 +0800
support register client via zookeeper (#9)
---
.../Services/ShenyuStartupService.cs | 4 +-
.../Utils/ShenyuCollectionExtensions.cs | 26 ++-
.../Apache.ShenYu.Client.csproj | 1 +
.../Apache.ShenYu.Client/Options/ShenyuOptions.cs | 11 +-
.../Registers/IShenyuRegister.cs | 2 +-
.../Registers/ShenyuHttpRegister.cs | 25 +--
.../Registers/ShenyuZookeeperRegister.cs | 196 +++++++++++++++++++++
.../Utils/CollectionExtentions.cs | 69 ++++++++
client/Apache.ShenYu.Client/Utils/Constants.cs | 31 +++-
.../Utils/RegisterTypeEnums.cs | 4 +-
10 files changed, 350 insertions(+), 19 deletions(-)
diff --git a/client/Apache.ShenYu.AspNetCore/Services/ShenyuStartupService.cs
b/client/Apache.ShenYu.AspNetCore/Services/ShenyuStartupService.cs
index 9801cac..2388afc 100644
--- a/client/Apache.ShenYu.AspNetCore/Services/ShenyuStartupService.cs
+++ b/client/Apache.ShenYu.AspNetCore/Services/ShenyuStartupService.cs
@@ -28,6 +28,8 @@ using Apache.ShenYu.Client.Options;
using Apache.ShenYu.Client.Registers;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
@@ -190,4 +192,4 @@ namespace Apache.ShenYu.AspNetCore.Services
return shenyuClientAttribute != null ? shenyuClientAttribute.Path
: routePath;
}
}
-}
+}
\ No newline at end of file
diff --git
a/client/Apache.ShenYu.AspNetCore/Utils/ShenyuCollectionExtensions.cs
b/client/Apache.ShenYu.AspNetCore/Utils/ShenyuCollectionExtensions.cs
index 2dbff1f..fc47a3b 100644
--- a/client/Apache.ShenYu.AspNetCore/Utils/ShenyuCollectionExtensions.cs
+++ b/client/Apache.ShenYu.AspNetCore/Utils/ShenyuCollectionExtensions.cs
@@ -19,6 +19,7 @@ using System;
using Apache.ShenYu.AspNetCore.Services;
using Apache.ShenYu.Client.Options;
using Apache.ShenYu.Client.Registers;
+using Apache.ShenYu.Client.Utils;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -40,9 +41,32 @@ namespace Apache.ShenYu.AspNetCore.Utils
throw new ArgumentNullException(nameof(services));
}
+ var shenyuOptions = new ShenyuOptions();
+ configuration.GetSection(ShenyuOptions.Shenyu).Bind(shenyuOptions);
+
services.Configure<ShenyuOptions>(configuration.GetSection(ShenyuOptions.Shenyu));
services.AddHostedService<ShenyuStartupService>();
- services.AddSingleton<IShenyuRegister, ShenyuHttpRegister>();
+
+ if (shenyuOptions.Register.RegisterType.Equals(""))
+ {
+ services.AddSingleton<IShenyuRegister, ShenyuHttpRegister>();
+ }
+
+ switch (shenyuOptions.Register.RegisterType)
+ {
+ case Constants.RegisterType.Http:
+ {
+ services.AddSingleton<IShenyuRegister,
ShenyuHttpRegister>();
+ break;
+ }
+ case Constants.RegisterType.Zookeeper:
+ {
+ services.AddSingleton<IShenyuRegister,
ShenyuZookeeperRegister>();
+ break;
+ }
+ default:
+ throw new Exception($"not supported type
{shenyuOptions.Register.RegisterType}");
+ }
}
}
}
diff --git a/client/Apache.ShenYu.Client/Apache.ShenYu.Client.csproj
b/client/Apache.ShenYu.Client/Apache.ShenYu.Client.csproj
index 1ccd434..1ac1d3b 100644
--- a/client/Apache.ShenYu.Client/Apache.ShenYu.Client.csproj
+++ b/client/Apache.ShenYu.Client/Apache.ShenYu.Client.csproj
@@ -46,5 +46,6 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions"
Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
+ <PackageReference Include="ZooKeeperNetEx" Version="3.4.12.4" />
</ItemGroup>
</Project>
diff --git a/client/Apache.ShenYu.Client/Options/ShenyuOptions.cs
b/client/Apache.ShenYu.Client/Options/ShenyuOptions.cs
index aa9504c..2d24bc5 100644
--- a/client/Apache.ShenYu.Client/Options/ShenyuOptions.cs
+++ b/client/Apache.ShenYu.Client/Options/ShenyuOptions.cs
@@ -15,6 +15,8 @@
* limitations under the License.
*/
+using System.Collections.Generic;
+
namespace Apache.ShenYu.Client.Options
{
public class ShenyuOptions
@@ -26,10 +28,15 @@ namespace Apache.ShenYu.Client.Options
public class RegisterOptions
{
+ /// <summary>
+ /// register types, including http, zookeeper.
+ /// </summary>
public string RegisterType { get; set; } = "http";
public string ServerList { get; set; }
- public string UserName { get; set; }
- public string Password { get; set; }
+
+ public Dictionary<string, string> Props { get; set; }
+
+
}
public class ClientOptions
diff --git a/client/Apache.ShenYu.Client/Registers/IShenyuRegister.cs
b/client/Apache.ShenYu.Client/Registers/IShenyuRegister.cs
index ddd01af..361597e 100644
--- a/client/Apache.ShenYu.Client/Registers/IShenyuRegister.cs
+++ b/client/Apache.ShenYu.Client/Registers/IShenyuRegister.cs
@@ -44,6 +44,6 @@ namespace Apache.ShenYu.Client.Registers
/// <summary>
/// Close.
/// </summary>
- void Close();
+ Task Close();
}
}
diff --git a/client/Apache.ShenYu.Client/Registers/ShenyuHttpRegister.cs
b/client/Apache.ShenYu.Client/Registers/ShenyuHttpRegister.cs
index 6a67a51..71786ec 100644
--- a/client/Apache.ShenYu.Client/Registers/ShenyuHttpRegister.cs
+++ b/client/Apache.ShenYu.Client/Registers/ShenyuHttpRegister.cs
@@ -32,9 +32,7 @@ namespace Apache.ShenYu.Client.Registers
{
public class ShenyuHttpRegister : IShenyuRegister
{
- public string UserName { get; set; }
- public string Password { get; set; }
- public List<string> ServerList { get; set; }
+ private List<string> ServerList { get; set; }
private Dictionary<string, string> AccessTokens { get; set; }
@@ -42,6 +40,8 @@ namespace Apache.ShenYu.Client.Registers
private URIRegisterDTO _uriRegisterDto;
private ShenyuOptions _shenyuOptions;
private readonly ILogger<ShenyuHttpRegister> _logger;
+ private string _userName;
+ private string _password;
public ShenyuHttpRegister(ILogger<ShenyuHttpRegister> logger)
{
@@ -51,8 +51,8 @@ namespace Apache.ShenYu.Client.Registers
public async Task Init(ShenyuOptions shenyuOptions)
{
this._shenyuOptions = shenyuOptions;
- this.UserName = this._shenyuOptions.Register.UserName;
- this.Password = this._shenyuOptions.Register.Password;
+
this._shenyuOptions.Register.Props.TryGetValue(Constants.RegisterConstants.UserName,
out this._userName);
+
this._shenyuOptions.Register.Props.TryGetValue(Constants.RegisterConstants.Password,
out this._password);
this.ServerList =
this._shenyuOptions.Register.ServerList?.Split(',').ToList();
this.AccessTokens = new Dictionary<string, string>();
this._client = new HttpClient();
@@ -61,16 +61,16 @@ namespace Apache.ShenYu.Client.Registers
public async Task PersistInterface(MetaDataRegisterDTO metadata)
{
- await this.DoRegister(metadata, Constants.MetaPath,
RegisterTypeEnums.MetaData);
+ await this.DoRegister(metadata, Constants.MetaPath,
Constants.MetaType);
}
public async Task PersistURI(URIRegisterDTO registerDTO)
{
- await this.DoRegister(registerDTO, Constants.URIPath,
RegisterTypeEnums.Uri);
+ await this.DoRegister(registerDTO, Constants.UriPath,
Constants.UriType);
this._uriRegisterDto = registerDTO;
}
- public void Close()
+ public Task Close()
{
throw new NotImplementedException();
}
@@ -95,7 +95,10 @@ namespace Apache.ShenYu.Client.Registers
private async Task SetAccessToken(string server)
{
var builder = new UriBuilder(server)
- { Path = Constants.LoginPath, Query =
$"userName={this.UserName}&password={this.Password}" };
+ {
+ Path = Constants.LoginPath,
+ Query = $"userName={this._userName}&password={this._password}"
+ };
var resp = await this._client.GetStringAsync(builder.ToString());
var jObject = JObject.Parse(resp);
var token = jObject["data"]?["token"].ToString();
@@ -103,7 +106,7 @@ namespace Apache.ShenYu.Client.Registers
this.AccessTokens.Add(server, token);
}
- private async Task DoRegister<T>(T t, string path, RegisterTypeEnums
type)
+ private async Task DoRegister<T>(T t, string path, string type)
{
foreach (var server in this.ServerList)
{
@@ -131,7 +134,7 @@ namespace Apache.ShenYu.Client.Registers
}
else
{
- this._logger.LogWarning("failed to register type: {},
content: {}", type, content);
+ this._logger.LogWarning("failed to register type: {},
content: {}", type, content, resp);
}
}
}
diff --git a/client/Apache.ShenYu.Client/Registers/ShenyuZookeeperRegister.cs
b/client/Apache.ShenYu.Client/Registers/ShenyuZookeeperRegister.cs
new file mode 100644
index 0000000..1539918
--- /dev/null
+++ b/client/Apache.ShenYu.Client/Registers/ShenyuZookeeperRegister.cs
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Apache.ShenYu.Client.Models.DTO;
+using Apache.ShenYu.Client.Options;
+using Apache.ShenYu.Client.Utils;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using org.apache.zookeeper;
+
+namespace Apache.ShenYu.Client.Registers
+{
+ public class ShenyuZookeeperRegister : IShenyuRegister
+ {
+ private readonly ILogger<ShenyuZookeeperRegister> _logger;
+ private string _serverList;
+ private ShenyuOptions _shenyuOptions;
+ private int _sessionTimeout;
+ private ZooKeeper _zkClient;
+ private Dictionary<string, string> _nodeDataMap = new
Dictionary<string, string>();
+
+ public ShenyuZookeeperRegister(ILogger<ShenyuZookeeperRegister> logger)
+ {
+ _logger = logger;
+ }
+
+ public Task Init(ShenyuOptions shenyuOptions)
+ {
+ this._shenyuOptions = shenyuOptions;
+ this._serverList = shenyuOptions.Register.ServerList;
+ this._sessionTimeout =
+ Convert.ToInt32(
+
this._shenyuOptions.Register.Props.GetValueOrDefault(Constants.RegisterConstants.SessionTimeout,
+ "3000"));
+ this._zkClient = CreateClient(this._serverList,
this._sessionTimeout, new StatWatcher(this));
+ return Task.CompletedTask;
+ }
+
+ public async Task PersistInterface(MetaDataRegisterDTO metadata)
+ {
+ // build metadata path
+ string rpcType = metadata.rpcType;
+ string contextPath = BuildContextNodePath(metadata.contextPath,
metadata.appName);
+
+ // create parent node
+ string parentPath =
$"/shenyu/register/metadata/{metadata.rpcType}/{contextPath}";
+ await _zkClient.CreateWithParentAsync(parentPath, null,
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+
+ // create or set metadata node
+ string nodeName = BuildMetadataNodeName(metadata);
+ string nodePath = $"{parentPath}/{nodeName}";
+
+ var existStat = await _zkClient.existsAsync(nodePath);
+ // create node
+ var metadataStr = JsonConvert.SerializeObject(metadata,
Formatting.None, new JsonSerializerSettings
+ {
+ NullValueHandling = NullValueHandling.Ignore
+ });
+ if (existStat == null)
+ {
+ await _zkClient.createAsync(nodePath,
Encoding.UTF8.GetBytes(metadataStr), ZooDefs.Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT);
+ }
+ else
+ {
+ // update node
+ await _zkClient.setDataAsync(nodePath,
Encoding.UTF8.GetBytes(metadataStr));
+ }
+ }
+
+ public async Task PersistURI(URIRegisterDTO registerDTO)
+ {
+ // build uri path
+ string contextPath = BuildContextNodePath(registerDTO.contextPath,
registerDTO.appName);
+ string parentPath =
+ $"/shenyu/register/uri/{registerDTO.rpcType}/{contextPath}";
+ await _zkClient.CreateWithParentAsync(parentPath, null,
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+
+ string nodePath =
$"{parentPath}/{registerDTO.host}:{registerDTO.port}";
+
+ // create ephemeral node
+ var existStat = await _zkClient.existsAsync(nodePath, null);
+ if (existStat == null)
+ {
+ var uriRegString = JsonConvert.SerializeObject(registerDTO,
Formatting.None, new JsonSerializerSettings
+ {
+ NullValueHandling = NullValueHandling.Ignore
+ });
+ _nodeDataMap[nodePath] = uriRegString;
+ await _zkClient.createAsync(nodePath,
Encoding.UTF8.GetBytes(uriRegString), ZooDefs.Ids.OPEN_ACL_UNSAFE,
+ CreateMode.EPHEMERAL);
+ }
+ }
+
+ public async Task Close()
+ {
+ await this._zkClient.closeAsync();
+ }
+
+ private ZooKeeper CreateClient(string connString, int sessionTimeout,
Watcher watcher, string chroot = null)
+ {
+ var zk = new ZooKeeper(connString, sessionTimeout, watcher);
+
+ // waiting for connection finished
+ Thread.Sleep(1000);
+ while (zk.getState() == ZooKeeper.States.CONNECTING)
+ {
+ Thread.Sleep(1000);
+ }
+
+ var state = zk.getState();
+ if (state != ZooKeeper.States.CONNECTED && state !=
ZooKeeper.States.CONNECTEDREADONLY)
+ {
+ throw new Exception($"failed to connect to zookeeper endpoint:
{connString}");
+ }
+
+ return zk;
+ }
+
+ private string BuildContextNodePath(string contextPath, string appName)
+ {
+ return string.IsNullOrEmpty(contextPath)
+ ? appName
+ : (contextPath.StartsWith("/")
+ ? contextPath.Substring(1)
+ : contextPath);
+ }
+
+ private string BuildMetadataNodeName(MetaDataRegisterDTO metadata)
+ {
+ string nodeName;
+ string rpcType = metadata.rpcType;
+
+ if (Constants.RegisterRpcType.Http.Equals(rpcType) ||
Constants.RegisterRpcType.SpringCloud.Equals(rpcType))
+ {
+ nodeName = string.Join(Constants.SelectorJoinRule,
metadata.contextPath,
+ metadata.ruleName.Replace(Constants.PathSeparator,
Constants.SelectorJoinRule));
+ }
+ else
+ {
+ nodeName = string.Join(".", metadata.serviceName,
metadata.methodName);
+ }
+
+ return nodeName.StartsWith(Constants.PathSeparator) ?
nodeName.Substring(1) : nodeName;
+ }
+
+ class StatWatcher : Watcher
+ {
+ private readonly ShenyuZookeeperRegister _register;
+
+ public StatWatcher(ShenyuZookeeperRegister register)
+ {
+ _register = register;
+ }
+
+ public override async Task process(WatchedEvent @event)
+ {
+ var state = @event.getState();
+
+ if (Event.KeeperState.SyncConnected.Equals(state))
+ {
+ foreach (var node in this._register._nodeDataMap)
+ {
+ var existStat = await
this._register._zkClient.existsAsync(node.Key);
+ if (existStat != null)
+ {
+ await
this._register._zkClient.CreateWithParentAsync(node.Key,
+ Encoding.UTF8.GetBytes(node.Value),
+ ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL);
+ this._register._logger.LogInformation("zookeeper
client register success: {}", node.Value);
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/Apache.ShenYu.Client/Utils/CollectionExtentions.cs
b/client/Apache.ShenYu.Client/Utils/CollectionExtentions.cs
new file mode 100644
index 0000000..e44c1d7
--- /dev/null
+++ b/client/Apache.ShenYu.Client/Utils/CollectionExtentions.cs
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using org.apache.zookeeper;
+using org.apache.zookeeper.data;
+
+namespace Apache.ShenYu.Client.Utils
+{
+ public static class CollectionExtentions
+ {
+ public static TValue GetValueOrDefault<TKey, TValue>(
+ this IDictionary<TKey, TValue> dictionary,
+ TKey key,
+ TValue defaultValue)
+ {
+ return dictionary.TryGetValue(key, out var value) ? value :
defaultValue;
+ }
+
+ public static async Task CreateWithParentAsync(
+ this ZooKeeper zooKeeper,
+ string path,
+ byte[] data,
+ List<ACL> acl,
+ CreateMode createMode
+ )
+ {
+ var paths = path.Split('/');
+ var cur = "";
+ foreach (var item in paths)
+ {
+ if (string.IsNullOrEmpty(item))
+ {
+ continue;
+ }
+ cur += $"/{item}";
+ var existStat = await zooKeeper.existsAsync(cur, null);
+ if (existStat != null)
+ {
+ continue;
+ }
+
+ if (cur.Equals(path))
+ {
+ await zooKeeper.createAsync(cur, data, acl, createMode);
+ }
+ else
+ {
+ await zooKeeper.createAsync(cur, null, acl, createMode);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/Apache.ShenYu.Client/Utils/Constants.cs
b/client/Apache.ShenYu.Client/Utils/Constants.cs
index f8e5885..684631a 100644
--- a/client/Apache.ShenYu.Client/Utils/Constants.cs
+++ b/client/Apache.ShenYu.Client/Utils/Constants.cs
@@ -21,7 +21,36 @@ namespace Apache.ShenYu.Client.Utils
{
public const string LoginPath = "/platform/login";
public const string XAccessToken = "X-Access-Token";
- public const string URIPath = "/shenyu-client/register-uri";
+
+ public const string UriPath = "/shenyu-client/register-uri";
public const string MetaPath = "/shenyu-client/register-metadata";
+
+ public const string MetaType = "metadata";
+ public const string UriType = "uri";
+
+ public const string SelectorJoinRule = "-";
+ public const string PathSeparator = "/";
+ public const string DotSeparator = ".";
+
+ public class RegisterType
+ {
+ public const string Http = "http";
+ public const string Zookeeper = "zookeeper";
+ }
+
+ public class RegisterRpcType
+ {
+ public const string Http = "http";
+ public const string SpringCloud = "springCloud";
+ }
+
+ public class RegisterConstants
+ {
+ public const string UserName = "UserName";
+ public const string Password = "Password";
+
+ public const string SessionTimeout = "SessionTimeout";
+ public const string ConnectionTimeout = "SessionTimeout";
+ }
}
}
diff --git a/client/Apache.ShenYu.Client/Utils/RegisterTypeEnums.cs
b/client/Apache.ShenYu.Client/Utils/RegisterTypeEnums.cs
index 9ac9325..395bdd2 100644
--- a/client/Apache.ShenYu.Client/Utils/RegisterTypeEnums.cs
+++ b/client/Apache.ShenYu.Client/Utils/RegisterTypeEnums.cs
@@ -19,7 +19,7 @@ namespace Apache.ShenYu.Client.Utils
{
public enum RegisterTypeEnums
{
- MetaData,
- Uri
+ Http,
+ Zookeeper,
}
}
\ No newline at end of file