当前位置: 首页 > news >正文

网站开发价格 北京/今日新闻摘抄十条

网站开发价格 北京,今日新闻摘抄十条,国家高新技术企业认定机构,网站seo优化方案背景 原项目基于传统三层模式组织代码逻辑,随着时间的推移,项目内各模块逻辑互相交织,互相依赖,维护起来较为困难.为此我们需要引入一种新的机制来尝试改变这个现状,在考察了 Java spring cloud/doubbo, c# wcf/webapi/asp.net core 等一些微服务框架后,我们最终选择…
  1. 背景
     

            原项目基于传统三层模式组织代码逻辑,随着时间的推移,项目内各模块逻辑互相交织,互相依赖,维护起来较为困难.为此我们需要引入一种新的机制来尝试改变这个现状,在考察了 Java spring cloud/doubbo, c# wcf/webapi/asp.net core 等一些微服务框架后,我们最终选择了基于 .net core + Ocelot 微服务方式. 经过讨论大家最终期望的项目结果大致如下所示.


    image

          但原项目团队成员已经习惯了基于接口服务的这种编码形式, 让大家将需要定义的接口全部以http 接口形式重写定义一遍, 同时客户端调用的时候, 需要将原来熟悉的形如 XXService.YYMethod(args1, args2) 直接使用通过 "."出内部成员,替换为让其直接写 HttpClient.Post("url/XX/YY",”args1=11&args2=22”)的形式访问远程接口,确实是一件十分痛苦的事情.

  2. 问题提出


         基于以上, 如何通过一种模式来简化这种调用形式, 继而使大家在调用的时候不需要关心该服务是在本地(本地类库依赖)还是远程, 只需要按照常规方式使用即可, 至于是直接使用本地服务还是通过http发送远程请求,这个都交给框架处理.为了方便叙述, 本文假定以销售订单和用户服务为例. 销售订单服务对外提供一个创建订单的接口.订单创建成功后, 调用用户服务更新用户积分.UML参考如下.
    image
  3. 问题转化

    1. 在客户端,通过微服务对外公开的接口,生成接口代理, 即将接口需要的信息[接口名/方法名及该方法需要的参数]包装成http请求向远程服务发起请求.
    2. 在微服务http接入段, 我们可以定义一个统一的入口,当服务端收到请求后,解析出接口名/方法名及参数信息,并创建对应的实现类,从而执行接口请求,并将返回值通过http返回给客户端.
    3. 最后,客户端通过类似 AppRuntims.Instance.GetService<IOrderService>().SaveOrder(orderInfo) 形式访问远程服务创建订单.
    4. 数据以json格式传输.
  4. 解决方案及实现

    1. 为了便于处理,我们定义了一个空接口IApiService,用来标识服务接口.

    2. 远程服务客户端代理

       

      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<ApiActionResult>(responseStr);}if (!apiRetult.IsSuccess){throw new BusinessException(apiRetult.Message ?? "服务请求失败");}return apiRetult;}//有返回值的方法代理public T Invoke<T>(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<T>(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);}
      }

       

    3. 远程服务端http接入段统一入口
       

       

      [Route("api/svc/{interfaceId}/{methodId}"), Produces("application/json")]
      public async Task<ApiActionResult> 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<ArrayList>(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;
      }

       

    4. 本地服务中心通过接口名和方法名,找出具体的实现类的方法,并使用传递的参数执行,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;

       

    5. 服务配置,指示具体的服务的远程地址,当未配置的服务默认为本地服务.
       

       

      [{"ServiceId": "XZL.Api.IOrderService","Address": "http://localhost:8801/api/svc"},{"ServiceId": "XZL.Api.IUserService","Address": "http://localhost:8802/api/svc"} 
      ]

       

    6. AppRuntime.Instance.GetService<TService>()的实现.
       

       

      private static List<(string typeName, Type svcType)> svcTypeDic;
      private static ConcurrentDictionary<string, Object> svcInstance = new ConcurrentDictionary<string, object>();public static TService GetService<TService>(){var serviceId = typeof(TService).FullName;//读取服务配置var serviceInfo = ServiceConfonfig.Instance.GetServiceInfo(serviceId);if (serviceInfo == null){return (TService)Activator.CreateInstance(GetSvcType(serviceId));}else{ var rs = GetService<TService>(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<TService>(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).IsAssignableFrom(t.svcType)).svcType;}}else{rs = temp[0].svcType;}return rs;
      }

       

    7. 为了性能影响,我们在程序启动的时候可以将当前所有的ApiService类型缓存.

       

      public static void LoadServiceType(){if (_loaded){return;}lock (_sync){if (_loaded){return;} try{svcTypeDic = new List<(string typeName, Type svcType)>();var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;var dir = new DirectoryInfo(path);var files = dir.GetFiles("XZL*.dll");foreach (var file in files){ var types = LoadAssemblyFromFile(file);svcTypeDic.AddRange(types);} _loaded = true;}catch{_loaded = false;}}}//加载指定文件中的ApiService实现
      private static List<(string typeName, Type svcType)> LoadAssemblyFromFile(FileInfo file)
      {var lst = new List<(string typeName, Type svcType)>();if (file.Extension != ".dll" && file.Extension != ".exe"){return lst;}try{var types = Assembly.Load(file.Name.Substring(0, file.Name.Length - 4)).GetTypes().Where(c => c.IsClass && !c.IsAbstract && c.IsPublic);foreach (Type type in types){//客户端代理基类if (type == typeof(RemoteServiceProxy)){continue;}if (!typeof(IApiService).IsAssignableFrom(type)){continue;}//绑定现类lst.Add((type.FullName, type));foreach (var interfaceType in type.GetInterfaces()){if (!typeof(IApiService).IsAssignableFrom(interfaceType)){continue;} //绑定接口与实际实现类lst.Add((interfaceType.FullName, type));  }}}catch{}return lst;
      }

       

    8. 具体api远程服务代理示例
       

       

      public class UserServiceProxy : RemoteServiceProxy, IUserService{private string serviceId = typeof(IUserService).FullName;public void IncreaseScore(int userId,int score){return InvokeWithoutReturn(serviceId, nameof(IncreaseScore), userId,score);}public UserInfo GetUserById(int userId){return Invoke<UserInfo >(serviceId, nameof(GetUserById),  userId);}
      }

       

  5. 结语
     

    经过以上改造后, 我们便可很方便的通过形如 AppRuntime.Instance.GetService<TService>().MethodXX()无感的访问远程服务, 服务是部署在远程还是在本地以dll依赖形式存在,这个便对调用者透明了.无缝的对接上了大家固有习惯.

http://www.lbrq.cn/news/1028629.html

相关文章:

  • 凤台县城乡建设委员会网站/天猫seo搜索优化
  • 做网站用中文路径/线上运营推广
  • 大方做网站/杭州网站建设 seo
  • 云顶科技做网站的/长沙建设网站制作
  • 阿里云网站建设素材/长沙seo关键词排名
  • 免费申请网站空间及域名/百度上看了不健康的内容犯法吗
  • 浙江做网站公司/今日十大热点新闻头条
  • 公司网站建设小江/凡科建站官网
  • 如何做网站嵌入腾讯地图/seo长尾关键词排名
  • 网站建设代码上传/成人短期技能培训学校
  • 做淘宝客需要网站吗/互联网项目
  • 广东网站开发软件/百度热榜实时热点
  • 网站建设一般字体多大/什么公司适合做seo优化
  • 网站设计作业多少钱/做一个简单的网站需要多少钱
  • 在线做插画的网站/如何免费注册网站
  • 怎样去同行网站做外连接/销售的三个核心点
  • 合肥哪家公司做网站/小程序搭建教程
  • 广东深圳网站建设服务/天津seo培训机构
  • 软件系统网站建设/网站模板下载
  • 杭州网站建设怎么样/百度竞价排名
  • 设计网站卖钱/竞价交易规则
  • html5网站建设企业论文/百度搜索关键词排名优化
  • 开发一款网站需要多少钱/竞价排名的优缺点
  • 海城区建设局网站/凡科建站靠谱吗
  • 商城网站都有什么功能模块/关键词优化报价查询
  • php做旅游网站/域名年龄对seo的影响
  • 吉林省建设集团有限公司网站/线上职业技能培训平台
  • 赣州晒房网门户网站/seo 的原理和作用
  • 网站版建设/提供seo顾问服务适合的对象是
  • 网络架构和管理/厦门seo外包
  • ZigBee入门与提高(3)—— ZigBee协议初识
  • [机器学习]08-基于逻辑回归模型的鸢尾花数据集分类
  • Java Stream API 中常用方法复习及项目实战示例
  • WPF 开发的瑞士军刀:Prism 框架从入门到精通指南
  • 大模型推理框架vLLM 中的Prompt缓存实现原理
  • 【论文阅读】RestorerID: Towards Tuning-Free Face Restoration with ID Preservation