.net core 微服务之Api网关(Api Gateway)

2021-06-19 10:07

阅读:535

微服务网关目录

      • 1、 微服务引子
      • 2、使用Nginx作为api网关
      • 3、自创api网关(重复轮子)
        • 3.1、构建初始化
        • 3.2、构建中间件
      • 4、结语
  • 引用链接

1、 微服务引子

首先恭喜你,进入微服务的开发世界。微服务属于架构演进中的一种阶段,其特点是根据业务模块水平划分服务种类,每个服务可以独立部署并互相隔离,并对外提供轻量的Api调用,服务具有高可用特性。

微服务应遵循的设计原则:

  • 单一职责原则: 每个微服务只需要实现自己的业务逻辑
  • 服务自治原则: 每个微服务都是独立的,不依赖其他模块
  • 轻量级通信原则:一般采用Http + Json方式
  • 接口明确原则:接口尽量做的更通用,更灵活,从而尽量避免接口参数的来回修改。

我从2017年12月开始接触微服务概念,并开始着手构建公司的微服务平台,系统架构采用 .net core webapi方式组织,随着微服务的增多,越来越需要一个统一入口管理这些微服务。

2、使用Nginx作为api网关

Nginx是由IgorSysoev为俄罗斯访问量第二的Rambler.ru站点开发的,一个高性能的HTTP和反向代理服务器。2012年,Nginx荣获年度云计算开发奖,并成长为世界第二大Web服务器。全世界流量最高的前1000名网站中,超过25%都使用Nginx来处理海量的互联网请求。

Nginx很牛掰,业界公认的首选,选择它作为api网关,可以说不用开发介入,只需要运维的同学好好规划配置即可。
技术分享图片

网关配置规划如下:
/api/ServiceA —> ServiceA
/api/ServiceB —> ServiceB
/api/ServiceC —> ServiceC
外部统一的访问入口是Nginx,然后根据服务名称路由到不同的微服务站点。
api网关的路由难题解决了,其他熔断,灰度发布,线上测试,日志拦截等功能nginx做起来相对比较吃力,不过对于中小型平台已经够用了。

3、自创api网关(重复轮子)

半年后,换了家公司,公司微服务平台搭建采用Thrift RPC作为各个微服务的通讯协议,由于当时的无知(Socket协议已被Nginx支持),所以决定写个Api代理类,没敢叫Api网关,是因为实现的功能仅限于路由!
仅仅是路由,当然是祭出 .net core Web应用。

3.1、构建初始化

MVC框架包就别包含了,轻量的性能才能好!
Main函数初始化线程池大小

			//初始化线程池最小大小 为 300
           ThreadPool.GetMinThreads(out var wt, out var ct);
           if (wt  300 || ct  300)
           {
               ThreadPool.SetMinThreads(Math.Max(wt, 300), Math.Max(ct, 300));
           }

**注意:**线程池不是越大越好,最佳选择约等于 cpu内核的2倍,有个公式参考:
技术分享图片
多说一句吧,思考下:

为什么Nginx只用4个线程发挥出的性能就大大超越了100个进程的Apache HTTPD?回想一下计算机科学的基础知识,答案其实是很明显的。

我要支持跨域访问:

public void ConfigureServices(IServiceCollection services)
{
      services.AddCors(options =>
      {
          options.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials());
      });       
  }

配置下使用中间件

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
       {           
           app.UseCors("AllowAll");
           app.UseMiddlewareProxyMiddleware>();
           // app.UseMvc();
       }

3.2、构建中间件

路由转发,一个中间件即可搞定。

    public class ProxyMiddleware
   {
       private readonly RequestDelegate m_Next;

       public ProxyMiddleware(RequestDelegate next)
       {
           this.m_Next = next;
       }

       public Task Invoke(HttpContext context)
       {
           if (context.Request.Method?.ToUpper() == "GET")
           {
               var path = context.Request.Path.HasValue ? context.Request.Path.ToString().ToLower() : string.Empty;
               if (string.IsNullOrEmpty(path) || path.Equals("/") || path.Equals("/api") || path.Equals("/api/"))
               {
                   return this.SendConstRespond(context);
               }
               //增加一个代理网关接口,返回微服务列表
               else if (path.EndsWith("/getallservices"))
               {
                   return this.SendServiceListRespond(context);
               }

               return this.SendStringRespond(context);
           }

           if (context.Request.ContentType == null || context.Request.ContentType.IndexOf("application/json")  0)
           {
               context.Response.StatusCode = 403;
               context.Response.ContentType += "application/json;charset=utf-8;";
               return context.Response.WriteAsync("Please set ContentType=application/json");
           }

           return this.SendStringRespond(context);
       }

       private Task SendStringRespond(HttpContext context)
       {
           context.Response.StatusCode = 200;
           context.Response.ContentType += "application/json;charset=utf-8;";

           Task task = new Task(() =>
           {
               if (ServerSetting.Config.QuantumConfig.RpcService.ServerType==ServerType.HttpWebApi)
               {
                  string constResp = QuantumHttpProxy.SendHttp(context);
                   using (var strStream = new StreamWriter(context.Response.Body))
                   {
                       strStream.Write(constResp);
                       strStream.Flush();
                   }
               }
               else
               {
                   QuantumHttpProxy.Send(context);
               }
           });
           task.Start();

           return task;
       }

       private Task SendConstRespond(HttpContext context)
       {
           context.Response.StatusCode = 200;
           context.Response.ContentType += "application/json;charset=utf-8;";

           Task task = new Task(() =>
           {
               var constResp = new
               {
                   rid = string.Empty,
                   c = 200,
                   msg = "Access api gateway success!"
               };
               using (var strStream = new StreamWriter(context.Response.Body))
               {
                   strStream.Write(JsonConvert.SerializeObject(constResp));
                   strStream.Flush();
               }
           });
           task.Start();
           return task;
       }
       private Task SendServiceListRespond(HttpContext context)
       {
           context.Response.StatusCode = 200;
           context.Response.ContentType += "application/json;charset=utf-8;";

           Task task = new Task(() =>
           {
               var svrs = ServerSetting.Config?.HttpProxy?.Items?.Select(x => x.Name)?.ToList() ?? new Liststring>();
               var constResp = new
               {
                   rid = Guid.NewGuid().ToString("N"),
                   c = 200,
                   v = svrs,
               };
               using (var strStream = new StreamWriter(context.Response.Body))
               {
                   strStream.Write(JsonConvert.SerializeObject(constResp));
                   strStream.Flush();
               }
           });
           task.Start();
           return task;
       }
   }

上面的代码里 有 QuantumHttpProxy,是我们封装的rpc调用,因此各位同学要用此类,请自行修改之。

4、结语

没有重试,熔断也没有降级,是否很low?
这就是实际实践的力量,不必耗费太多精力在一些非关键点上!
好吧,我承认我研究了 **polly**类库,重试,熔断,降级都准备好了,你就起航吧~~~
另外:造轮子只适合特定场景,小投入就可以完成的某些特定功能,比如上面的路由功能!如果你是云服务,可以重点考虑下 阿里API网关,亚马逊api网关!

引用链接

  1. 口袋代码仓库
  2. 在线计算器
  3. 本节源码:github


评论


亲,登录后才可以留言!