+ @Html.TextArea("CustomHeaders", new { @class = "form-control" })
+
diff --git a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Pages/Invoke.cshtml.cs b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Pages/Invoke.cshtml.cs
index af7966f..2e40fe6 100644
--- a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Pages/Invoke.cshtml.cs
+++ b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Pages/Invoke.cshtml.cs
@@ -5,6 +5,9 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Grpc;
+using FM.GrpcDashboard.Services;
+using Microsoft.AspNetCore.Mvc.Rendering;
+using Microsoft.Extensions.Configuration;
namespace FM.GrpcDashboard.Pages
{
@@ -21,14 +24,18 @@ public class InvokeModel : PageModel
[BindProperty]
public string RequestJson { get; set; }
- GrpcService _grpcSrv;
+ [BindProperty]
+ public string CustomHeaders { get; set; }
- public InvokeModel(GrpcService grpcSrv)
+ GrpcServiceProxy _grpcSrv;
+ private IConfiguration _conf;
+ public InvokeModel(GrpcServiceProxy grpcSrv, IConfiguration conf)
{
_grpcSrv = grpcSrv;
+ _conf = conf;
}
- public IActionResult OnGet(string endpoint, string methodName)
+ public async Task
OnGet(string endpoint, string methodName)
{
Endpoint = endpoint?.Trim();
MethodName = methodName?.Trim();
@@ -36,13 +43,25 @@ public IActionResult OnGet(string endpoint, string methodName)
{
return RedirectToPage("Error", new { msg = "服务地址和要调用的服务方法名称不能为空" });
}
- MethodInfoRS = _grpcSrv.GetMethodInfo(Endpoint, MethodName);
+ MethodInfoRS = await _grpcSrv.GetMethodInfo(Endpoint, MethodName);
return Page();
}
- public IActionResult OnPost()
+ public async Task OnPost()
{
- var res = _grpcSrv.MethodInvoke(Endpoint, MethodName, RequestJson);
+ var dicCustomHeaders = new Dictionary();
+ if (!string.IsNullOrWhiteSpace(CustomHeaders))
+ {
+ try
+ {
+ dicCustomHeaders = CustomHeaders.FromJson>();
+ }
+ catch (Exception ex)
+ {
+ return RedirectToPage("Error", new { msg = $"自定义头json格式错误:{ex}" });
+ }
+ }
+ var res = await _grpcSrv.MethodInvoke(Endpoint, MethodName, RequestJson, dicCustomHeaders);
return new JsonResult(new { respJson = res });
}
}
diff --git a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Pages/Unregister.cshtml.cs b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Pages/Unregister.cshtml.cs
index 290c1ba..f41ccde 100644
--- a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Pages/Unregister.cshtml.cs
+++ b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Pages/Unregister.cshtml.cs
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using System.Threading.Tasks;
namespace FM.GrpcDashboard.Pages
{
@@ -18,11 +19,11 @@ public IActionResult OnGetAsync()
///
/// 服务反注册
///
- public IActionResult OnPostAsync(string serviceName)
+ public async Task OnPostAsync(string serviceName)
{
ViewData["HttpMethod"] = "post";
ViewData["ServiceName"] = serviceName;
- _consulSrv.UnRegService(serviceName).GetAwaiter().GetResult();
+ await _consulSrv.UnRegService(serviceName);
return Page();
}
}
diff --git a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/ConsulService.cs b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/ConsulService.cs
index c7a6784..6af7bed 100644
--- a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/ConsulService.cs
+++ b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/ConsulService.cs
@@ -73,7 +73,7 @@ public async Task UnRegService(string serviceName)
var sids = services.Select(q => q.Service.ID).ToList();
foreach (var sid in sids)
{
- var res = client.Agent.ServiceDeregister(sid).Result;
+ var res = await client.Agent.ServiceDeregister(sid);
}
}
}
diff --git a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/GrpcService.cs b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/GrpcService.cs
index 1b4e414..70933a9 100644
--- a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/GrpcService.cs
+++ b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/GrpcService.cs
@@ -3,22 +3,26 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
using static Grpc.BaseService;
-namespace FM.GrpcDashboard
+namespace FM.GrpcDashboard.Services
{
- public class GrpcService
+ public class GrpcService : IGrpcReflection
{
ILogger _logger;
IConfiguration _config;
ConsulService _consulSrv;
+ int _grpcTimeout;
public GrpcService(ILogger logger, IConfiguration config, ConsulService consulSrv)
{
_logger = logger;
_config = config;
_consulSrv = consulSrv;
+ _grpcTimeout = _config.GetValue("GrpcTimeout");
}
///
/// 获取服务基本信息
@@ -29,12 +33,13 @@ public async Task GetInfo(string address, int port)
try
{
var client = new BaseServiceClient(channel);
- return client.Info(new InfoRQ { MethodName = "" }, deadline: DateTime.UtcNow.AddSeconds(_config.GetValue("GrpcTimeout")));
+ return await client.InfoAsync(new InfoRQ { MethodName = "" },
+ deadline: DateTime.UtcNow.AddSeconds(_grpcTimeout));
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
- return null;
+ throw ex;
}
finally
{
@@ -120,41 +125,49 @@ public Tuple SaveResponse(string serviceName, string methodName, b
///
/// 获取方法信息
///
- public MethodInfoRS GetMethodInfo(string endpoint, string methodName)
+ public async Task GetMethodInfo(string endpoint, string methodName)
{
var channel = new Channel(endpoint, ChannelCredentials.Insecure);
try
{
var client = new BaseServiceClient(channel);
- return client.MethodInfo(new MethodInfoRQ
+ return await client.MethodInfoAsync(new MethodInfoRQ
{
FullName = methodName
- }, deadline: DateTime.UtcNow.AddSeconds(_config.GetValue("GrpcTimeout")));
+ }, deadline: DateTime.UtcNow.AddSeconds(_grpcTimeout));
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
- return null;
+ throw ex;
}
finally
{
- channel.ShutdownAsync().Wait();
+ await channel.ShutdownAsync();
}
}
///
/// grpc方法调用
///
- public string MethodInvoke(string endpoint, string methodName, string requestJson)
+ public async Task MethodInvoke(string endpoint, string methodName, string requestJson, Dictionary customHeaders)
{
var channel = new Channel(endpoint, ChannelCredentials.Insecure);
try
{
var client = new BaseServiceClient(channel);
- return client.MethodInvoke(new MethodInvokeRQ
+ var metadata = new Metadata();
+ foreach (var item in customHeaders)
+ {
+ if (!metadata.Any(p => p.Key == item.Key))
+ metadata.Add(new Metadata.Entry(item.Key, item.Value));
+ }
+ return (await client.MethodInvokeAsync(new MethodInvokeRQ
{
FullName = methodName,
RequestJson = requestJson
- }/*, deadline: DateTime.UtcNow.AddSeconds(_config.GetValue("GrpcTimeout"))*/).ResponseJson;
+ }, metadata
+ // , deadline: DateTime.UtcNow.AddSeconds(_grpcTimeout)
+ )).ResponseJson;
}
catch (Exception ex)
{
@@ -163,7 +176,7 @@ public string MethodInvoke(string endpoint, string methodName, string requestJso
}
finally
{
- channel.ShutdownAsync().Wait();
+ await channel.ShutdownAsync();
}
}
}
diff --git a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/GrpcServiceProxy.cs b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/GrpcServiceProxy.cs
new file mode 100644
index 0000000..a6f6269
--- /dev/null
+++ b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/GrpcServiceProxy.cs
@@ -0,0 +1,98 @@
+using Grpc;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace FM.GrpcDashboard.Services
+{
+ public class GrpcServiceProxy : IGrpcReflection
+ {
+ private readonly IEnumerable grpcServices;
+ private ConcurrentDictionary dicGrpcType = new ConcurrentDictionary();
+
+ public GrpcServiceProxy(IEnumerable grpcServices)
+ {
+ this.grpcServices = grpcServices;
+ }
+
+ public async Task GetInfo(string address, int port)
+ {
+ var endpoint = $"{address}:{port}";
+ foreach (var grpcService in getGrpcSevices(endpoint))
+ {
+ try
+ {
+ var result = await grpcService.GetInfo(address, port);
+ var typeName = grpcService.GetType().Name;
+ dicGrpcType.AddOrUpdate(endpoint, typeName, (k,v) => typeName);
+ return result;
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+ }
+ return null;
+ }
+
+ public async Task GetMethodInfo(string endpoint, string methodName)
+ {
+ foreach (var grpcService in getGrpcSevices(endpoint))
+ {
+ try
+ {
+ return await grpcService.GetMethodInfo(endpoint, methodName);
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+ }
+ return null;
+ }
+
+ public async Task MethodInvoke(string endpoint, string methodName, string requestJson, Dictionary customHeaders)
+ {
+ Exception lastException = new Exception();
+ foreach (var grpcService in getGrpcSevices(endpoint))
+ {
+ try
+ {
+ return await grpcService.MethodInvoke(endpoint, methodName, requestJson, customHeaders);
+ }
+ catch (Exception ex)
+ {
+ lastException = ex;
+ continue;
+ }
+ }
+ return lastException.ToString();
+ }
+
+ private IEnumerable getGrpcSevices(string endpoint)
+ {
+
+ var grpcType = dicGrpcType.GetValueOrDefault(endpoint);
+ if (string.IsNullOrEmpty(grpcType))
+ {
+ return grpcServices;
+ }
+ else
+ {
+ return grpcServices.Where(p => p.GetType().Name == grpcType);
+ }
+ }
+
+ public Tuple SaveResponse(string serviceName, string methodName, bool isSaveResponse)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Tuple Throttle(string serviceName, string methodName, bool isThrottle)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/GrpcServiceV2.cs b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/GrpcServiceV2.cs
new file mode 100644
index 0000000..5893247
--- /dev/null
+++ b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/GrpcServiceV2.cs
@@ -0,0 +1,138 @@
+using Google.Protobuf.Reflection;
+using Grpc;
+using Grpc.Core;
+using Grpc.Core.Utils;
+using Grpc.Reflection.V1Alpha;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FM.GrpcDashboard.Services
+{
+ public class GrpcServiceV2 : IGrpcReflection
+ {
+ private static ConcurrentDictionary dicInfoMethods = new ConcurrentDictionary();
+ private readonly ILogger _logger;
+
+ public GrpcServiceV2(ILogger logger)
+ {
+ this._logger = logger;
+ }
+ public async Task GetInfo(string address, int port)
+ {
+ var result = new InfoRS();
+ var channel = new Channel(address, port, ChannelCredentials.Insecure);
+ try
+ {
+ var client = new ServerReflection.ServerReflectionClient(channel);
+ var resp = client.ServerReflectionInfo();
+ await resp.RequestStream.WriteAsync(new ServerReflectionRequest() { ListServices = "" });
+ await resp.ResponseStream.ForEachAsync(async res =>
+ {
+ switch (res.MessageResponseCase.ToString())
+ {
+ case "ListServicesResponse":
+ await ListServices(resp.RequestStream, res.ListServicesResponse);
+ break;
+ case "FileDescriptorResponse":
+ ListMethods(res.FileDescriptorResponse, result);
+ break;
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, ex.Message);
+ throw ex;
+ }
+ finally
+ {
+ await channel.ShutdownAsync();
+ }
+ return result;
+ }
+
+ private async Task ListServices(IClientStreamWriter req, ListServiceResponse resp)
+ {
+ var services = resp.Service.Where(p => !p.Name.StartsWith("grpc."));
+ foreach (var service in services)
+ {
+ await req.WriteAsync(new ServerReflectionRequest() { FileContainingSymbol = service.Name });
+ }
+ await req.CompleteAsync();
+ }
+
+ private void ListMethods(FileDescriptorResponse resp, InfoRS infoRs)
+ {
+ byte[] bytes = new byte[resp.FileDescriptorProto.First().Length];
+ resp.FileDescriptorProto.First().CopyTo(bytes, 0);
+ var fdp = ProtobufExtensions.Deserialize(bytes);
+ foreach (var service in fdp.Services)
+ {
+ foreach (var method in service.Methods)
+ {
+ var name = $"{fdp.Package}.{service.Name}/{method.Name}";
+ infoRs.MethodInfos.Add(new MethodInfo()
+ {
+ Name = name
+ });
+ var inputTypeName = method.InputType.Split('.').Last();
+ var outputTypeName = method.OutputType.Split('.').Last();
+ var intputType = fdp.MessageTypes.First(p => p.Name == inputTypeName);
+ var outputType = fdp.MessageTypes.First(p => p.Name == outputTypeName);
+ var infoMethodRS = new MethodInfoRS()
+ {
+ RequestJson = intputType.Fields.ToDictionary(p => p.Name, p => p.DefaultValue).ToJson(),
+ ResponseJson = outputType.Fields.ToDictionary(p => p.Name, p => p.DefaultValue).ToJson()
+ };
+ dicInfoMethods.AddOrUpdate(name, infoMethodRS, (k,v) => infoMethodRS);
+ }
+ }
+ }
+
+ public async Task GetMethodInfo(string endpoint, string methodName)
+ {
+ if (!dicInfoMethods.TryGetValue(methodName, out var result))
+ {
+ var endpoints = endpoint.Split(':');
+ await GetInfo(endpoints[0], endpoint[1]);
+ dicInfoMethods.TryGetValue(methodName, out result);
+ }
+ return result;
+ }
+
+ public Task MethodInvoke(string endpoint, string methodName, string requestJson, Dictionary customHeaders)
+ {
+ var request = requestJson.Replace("\r\n", "");
+ var metadata = new Metadata();
+ foreach (var item in customHeaders)
+ {
+ if (!metadata.Any(p => p.Key == item.Key))
+ metadata.Add(new Metadata.Entry(item.Key, item.Value));
+ }
+ var sbHeader = new StringBuilder();
+ foreach (var item in metadata)
+ {
+ sbHeader.Append($" -H '{item.Key}:{item.Value}'");
+ }
+ var cmd = $"grpcurl -d '{request}' {sbHeader} -plaintext {endpoint} {methodName}";
+ _logger.LogInformation(cmd);
+ var result = cmd.Bash();
+ return Task.FromResult(result);
+ }
+
+ public Tuple SaveResponse(string serviceName, string methodName, bool isSaveResponse)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Tuple Throttle(string serviceName, string methodName, bool isThrottle)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/IGrpcReflection.cs b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/IGrpcReflection.cs
new file mode 100644
index 0000000..95120fe
--- /dev/null
+++ b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Services/IGrpcReflection.cs
@@ -0,0 +1,22 @@
+using Grpc;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace FM.GrpcDashboard.Services
+{
+ public interface IGrpcReflection
+ {
+ Task GetInfo(string address, int port);
+
+ Task GetMethodInfo(string endpoint, string methodName);
+
+ Task MethodInvoke(string endpoint, string methodName, string requestJson, Dictionary customHeaders);
+
+ Tuple Throttle(string serviceName, string methodName, bool isThrottle);
+
+ Tuple SaveResponse(string serviceName, string methodName, bool isSaveResponse);
+
+ }
+}
diff --git a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Startup.cs b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Startup.cs
index 044b44e..63490b3 100644
--- a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Startup.cs
+++ b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/Startup.cs
@@ -1,3 +1,4 @@
+using FM.GrpcDashboard.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
@@ -35,7 +36,9 @@ public void ConfigureServices(IServiceCollection services)
services.AddMvc();
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
services.TryAddSingleton();
- services.TryAddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
diff --git a/src/FM.GrpcDashboard/src/FM.GrpcDashboard/grpcurl b/src/FM.GrpcDashboard/src/FM.GrpcDashboard/grpcurl
new file mode 100644
index 0000000000000000000000000000000000000000..d7edc261f893da72cc063ad3b1ac42bd53e8c620
GIT binary patch
literal 22626304
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