您现在的位置是:网站首页> 编程资料编程资料
基于.net core微服务的另一种实现方法_实用技巧_
2023-05-24
362人已围观
简介 基于.net core微服务的另一种实现方法_实用技巧_
前言
基于.net core 的微服务,网上很多介绍都是千篇一律基于类似webapi,通过http请求形式进行访问,但这并不符合大家使用习惯.如何像形如[ GetService的方式, 调用远程的服务,如果你正在为此苦恼, 本文或许是一种参考.
背景
原项目基于传统三层模式组织代码逻辑,随着时间的推移,项目内各模块逻辑互相交织,互相依赖,维护起来较为困难.为此我们需要引入一种新的机制来尝试改变这个现状,在考察了 Java spring cloud/doubbo, c# wcf/webapi/asp.net core 等一些微服务框架后,我们最终选择了基于 .net core + Ocelot 微服务方式. 经过讨论大家最终期望的项目结果大致如下所示.

但原项目团队成员已经习惯了基于接口服务的这种编码形式, 让大家将需要定义的接口全部以http 接口形式重写定义一遍, 同时客户端调用的时候, 需要将原来熟悉的形如 XXService.YYMethod(args1, args2) 直接使用通过 "."出内部成员,替换为让其直接写 HttpClient.Post("url/XX/YY",”args1=11&args2=22”)的形式访问远程接口,确实是一件十分痛苦的事情.
问题提出
基于以上, 如何通过一种模式来简化这种调用形式, 继而使大家在调用的时候不需要关心该服务是在本地(本地类库依赖)还是远程, 只需要按照常规方式使用即可, 至于是直接使用本地服务还是通过http发送远程请求,这个都交给框架处理.为了方便叙述, 本文假定以销售订单和用户服务为例. 销售订单服务对外提供一个创建订单的接口.订单创建成功后, 调用用户服务更新用户积分.UML参考如下.

问题转化
- 在客户端,通过微服务对外公开的接口,生成接口代理, 即将接口需要的信息[接口名/方法名及该方法需要的参数]包装成http请求向远程服务发起请求.
- 在微服务http接入段, 我们可以定义一个统一的入口,当服务端收到请求后,解析出接口名/方法名及参数信息,并创建对应的实现类,从而执行接口请求,并将返回值通过http返回给客户端.
- 最后,客户端通过类似
AppRuntims.Instance.GetService形式访问远程服务创建订单.().SaveOrder(orderInfo) - 数据以json格式传输.
解决方案及实现
为了便于处理,我们定义了一个空接口IApiService,用来标识服务接口.
远程服务客户端代理
public class RemoteServiceProxy : IApiService { public string Address { get; set; } //服务地址private ApiActionResult PostHttpRequest(string interfaceId, string methodId, params object[] p) { ApiActionResult apiRetult = null; using (var httpClient = new HttpClient()) { var param = new ArrayList(); //包装参数 foreach (var t in p) { if (t == null) { param.Add(null); } else { var ns = t.GetType().Namespace; param.Add(ns != null && ns.Equals("System") ? t : JsonConvert.SerializeObject(t)); } } var postContentStr = JsonConvert.SerializeObject(param); HttpContent httpContent = new StringContent(postContentStr); if (CurrentUserId != Guid.Empty) { httpContent.Headers.Add("UserId", CurrentUserId.ToString()); } httpContent.Headers.Add("EnterpriseId", EnterpriseId.ToString()); httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); var url = Address.TrimEnd('/') + $"/{interfaceId}/{methodId}"; AppRuntimes.Instance.Loger.Debug($"httpRequest:{url},data:{postContentStr}"); var response = httpClient.PostAsync(url, httpContent).Result; //提交请求 if (!response.IsSuccessStatusCode) { AppRuntimes.Instance.Loger.Error($"httpRequest error:{url},statuscode:{response.StatusCode}"); throw new ICVIPException("网络异常或服务响应失败"); } var responseStr = response.Content.ReadAsStringAsync().Result; AppRuntimes.Instance.Loger.Debug($"httpRequest response:{responseStr}"); apiRetult = JsonConvert.DeserializeObject(responseStr); } if (!apiRetult.IsSuccess) { throw new BusinessException(apiRetult.Message ?? "服务请求失败"); } return apiRetult; } //有返回值的方法代理 public T Invoke(string interfaceId, string methodId, params object[] param) { T rs = default(T); var apiRetult = PostHttpRequest(interfaceId, methodId, param); try { if (typeof(T).Namespace == "System") { rs = (T)TypeConvertUtil.BasicTypeConvert(typeof(T), apiRetult.Data); } else { rs = JsonConvert.DeserializeObject(Convert.ToString(apiRetult.Data)); } } catch (Exception ex) { AppRuntimes.Instance.Loger.Error("数据转化失败", ex); throw; } return rs; } //没有返回值的代理 public void InvokeWithoutReturn(string interfaceId, string methodId, params object[] param) { PostHttpRequest(interfaceId, methodId, param); } } 远程服务端http接入段统一入口
[Route("api/svc/{interfaceId}/{methodId}"), Produces("application/json")] public async Task Process(string interfaceId, string methodId) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); ApiActionResult result = null; string reqParam = string.Empty; try { using (var reader = new StreamReader(Request.Body, Encoding.UTF8)) { reqParam = await reader.ReadToEndAsync(); } AppRuntimes.Instance.Loger.Debug($"recive client request:api/svc/{interfaceId}/{methodId},data:{reqParam}"); ArrayList param = null; if (!string.IsNullOrWhiteSpace(reqParam)) { //解析参数 param = JsonConvert.DeserializeObject(reqParam); } //转交本地服务处理中心处理 var data = LocalServiceExector.Exec(interfaceId, methodId, param); result = ApiActionResult.Success(data); } catch BusinessException ex) //业务异常 { result = ApiActionResult.Error(ex.Message); } catch (Exception ex) { //业务异常 if (ex.InnerException is BusinessException) { result = ApiActionResult.Error(ex.InnerException.Message); } else { AppRuntimes.Instance.Loger.Error($"调用服务发生异常{interfaceId}-{methodId},data:{reqParam}", ex); result = ApiActionResult.Fail("服务发生异常"); } } finally { stopwatch.Stop(); AppRuntimes.Instance.Loger.Debug($"process client request end:api/svc/{interfaceId}/{methodId},耗时[ {stopwatch.ElapsedMilliseconds} ]毫秒"); } //result.Message = AppRuntimes.Instance.GetCfgVal("ServerName") + " " + result.Message; result.Message = result.Message; return result; } 本地服务中心通过接口名和方法名,找出具体的实现类的方法,并使用传递的参数执行,ps:因为涉及到反射获取具体的方法,暂不支持相同参数个数的方法重载.仅支持不同参数个数的方法重载.
public static object Exec(string interfaceId, string methodId, ArrayList param) { var svcMethodInfo = GetInstanceAndMethod(interfaceId, methodId, param.Count); var currentMethodParameters = new ArrayList(); for (var i = 0; i < svcMethodInfo.Paramters.Length; i++) { var tempParamter = svcMethodInfo.Paramters[i]; if (param[i] == null) { currentMethodParameters.Add(null); } else { if (!tempParamter.ParameterType.Namespace.Equals("System") || tempParamter.ParameterType.Name == "Byte[]") { currentMethodParameters.Add(JsonConvert.DeserializeObject(Convert.ToString(param[i]), tempParamter.ParameterType) } else { currentMethodParameters.Add(TypeConvertUtil.BasicTypeConvert(tempParamter.ParameterType, param[i])); } } } return svcMethodInfo.Invoke(currentMethodParameters.ToArray()); } private static InstanceMethodInfo GetInstanceAndMethod(string interfaceId, string methodId, int paramCount) { var methodKey = $"{interfaceId}_{methodId}_{paramCount}"; if (methodCache.ContainsKey(methodKey)) { return methodCache[methodKey]; } InstanceMethodInfo temp = null; var svcType = ServiceFactory.GetSvcType(interfaceId, true); if (svcType == null) { throw new ICVIPException($"找不到API接口的服务实现:{interfaceId}"); } var methods = svcType.GetMethods().Where(t => t.Name == methodId).ToList(); if (methods.IsNullEmpty()) { throw new BusinessException($"在API接口[{interfaceId}]的服务实现中[{svcType.FullName}]找不到指定的方法:{methodId}"); } var method = methods.FirstOrDefault(t => t.GetParameters().Length == paramCount); if (method == null) { throw new ICVIPException($"在API接口中[{interfaceId}]的服务实现[{svcType.FullName}]中,方法[{methodId}]参数个数不匹配"); } var paramtersTypes = method.GetParameters(); object instance = null; try { instance = Activator.CreateInstance(svcType); } catch (Exception ex) { throw new BusinessException($"在实例化服务[{svcType}]发生异常,请确认其是否包含一个无参的构造函数", ex); } temp = new InstanceMethodInfo() { Instance = instance, InstanceType = svcType, Key = methodKey, Method = method, Paramters = paramtersTypes }; if (!methodCache.ContainsKey(methodKey)) { lock (_syncAddMethodCacheLocker) { if (!methodCache.ContainsKey(methodKey)) { methodCache.Add(methodKey, temp); } } } return temp;服务配置,指示具体的服务的远程地址,当未配置的服务默认为本地服务.
[ { "ServiceId": "XZL.Api.IOrderService", "Address": "http://localhost:8801/api/svc" }, { "ServiceId": "XZL.Api.IUserService", "Address": "http://localhost:8802/api/svc" } ]AppRuntime.Instance.GetService的实现.
private static List<(string typeName, Type svcType)> svcTypeDic; private static ConcurrentDictionarysvcInstance = new ConcurrentDictionary (); public static TService GetService () { var serviceId = typeof(TService).FullName; //读取服务配置 var serviceInfo = ServiceConfonfig.Instance.GetServiceInfo(serviceId); if (serviceInfo == null) { return (TService)Activator.CreateInstance(GetSvcType(serviceId)); } else { var rs = GetService (serviceId + (serviceInfo.IsRemote ? "|Remote" : ""), serviceInfo.IsSingle); if (rs != null && rs is RemoteServiceProxy) { var temp = rs as RemoteServiceProxy; temp.Address = serviceInfo.Address; //指定服务地址 } return rs; } } public static TService GetService (string interfaceId, bool isSingle) { //服务非单例模式 if (!isSingle) { return (TService)Activator.CreateInstance(GetSvcType(interfaceId)); } object obj = null; if (svcInstance.TryGetValue(interfaceId, out obj) && obj != null) { return (TService)obj; } var svcType = GetSvcType(interfaceId); if (svcType == null) { throw new ICVIPException($"系统中未找到[{interfaceId}]的代理类"); } obj = Activator.CreateInstance(svcType); svcInstance.TryAdd(interfaceId, obj); return (TService)obj; } //获取服务的实现类 public static Type GetSvcType(string interfaceId, bool? isLocal = null) { if (!_loaded) { LoadServiceType(); } Type rs = null; var tempKey = interfaceId; var temp = svcTypeDic.Where(x => x.typeName == tempKey).ToList(); if (temp == null || temp.Count == 0) { return rs; } if (isLocal.HasValue) { if (isLocal.Value) { rs = temp.FirstOrDefault(t => !typeof(RemoteServiceProxy).IsAssignableFrom(t.svcType)).svcType; } else { rs = temp.FirstOrDefault(t => typeof(RemoteServiceProxy).IsAssignabl
相关内容
- 基于.net standard 的动态编译实现代码_实用技巧_
- .Net Core项目如何添加日志功能详解_实用技巧_
- .Net Core Cors中间件的深入讲解_实用技巧_
- .net core xss攻击防御的方法_实用技巧_
- .Net Core2.1 WebAPI新增Swagger插件详解_实用技巧_
- .Net使用RabbitMQ即时发消息Demo_实用技巧_
- .NET core高性能对象转换示例代码_实用技巧_
- ASP.NET MVC如何使用Unity实现Ioc详解_实用技巧_
- jenkins部署.net平台自动化构建的方法步骤_实用技巧_
- .NET Core控制台应用程序如何使用异步(Async)Main方法详解_实用技巧_
点击排行
本栏推荐
