.net core 微服务之Api网关(Api Gateway)
原文:.net core 微服务之Api网关(Api Gateway)
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.UseMiddleware<ProxyMiddleware>();
// 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 List<string>();
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网关!
引用链接
.net core 微服务之Api网关(Api Gateway)的更多相关文章
- .NET Core微服务二:Ocelot API网关
.NET Core微服务一:Consul服务中心 .NET Core微服务二:Ocelot API网关 .NET Core微服务三:polly熔断与降级 本文的项目代码,在文章结尾处可以下载. 本文使 ...
- (8)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- Ocelot网关(Api GateWay)
说到现在现有微服务的几点不足: 1) 对于在微服务体系中.和 Consul 通讯的微服务来讲,使用服务名即可访问.但是对于手 机.web 端等外部访问者仍然需要和 N 多服务器交互,需要记忆他们的服务 ...
- 基于.NET CORE微服务框架 -谈谈surging API网关
1.前言 对于最近surging更新的API 网关大家也有所关注,也收到了不少反馈提出是否能介绍下Api网关,那么我们将在此篇文章中剥析下surging的Api 网关 开源地址:https://git ...
- .NET Core微服务一:Consul服务中心
本文的项目代码,在文章结尾处可以下载. 防爬虫,本文的网址是:https://www.cnblogs.com/shousiji/p/12253295.html 本文使用的环境:Windows10 64 ...
- .NET Core微服务之基于Ocelot实现API网关服务
Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.啥是API网关? API 网关一般放到微服务的最前端,并且要让API 网关变成由应用所发起的每个请求的入口.这样就可以明显的简化客户端 ...
- .NET Core微服务之基于Ocelot实现API网关服务(续)
Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.负载均衡与请求缓存 1.1 负载均衡 为了验证负载均衡,这里我们配置了两个Consul Client节点,其中ClientServic ...
- .NET Core微服务之基于Steeltoe集成Zuul实现统一API网关
Tip: 此篇已加入.NET Core微服务基础系列文章索引,本篇接上一篇<基于Steeltoe使用Eureka实现服务注册与发现>,所演示的示例也是基于上一篇的基础上而扩展的. => ...
- .net core 微服务架构-docker的部署-包括网关服务(Ocelot)+认证服务(IdentityServer4)+应用服务(asp.net core web api)
本文主要介绍通过Docker来部署通过.Net Core开发的微服务架构,部署的微服务主要包括统一网关(使用Ocelot开发).统一认证(IdentityServer4).应用服务(asp.net c ...
- .NET Core 微服务—API网关(Ocelot) 教程 [二]
上篇文章(.NET Core 微服务—API网关(Ocelot) 教程 [一])介绍了Ocelot 的相关介绍. 接下来就一起来看如何使用,让它运行起来. 环境准备 为了验证Ocelot 网关效果,我 ...
随机推荐
- 【Energy Forecasting】能源预測的发展和展望
说明 本文的内容来自Tao Hong博士的Energy Forecasting: Past, Present and Future一文的翻译和整理. 引入 能源预測包括了电力行业中有关预測的广泛的内容 ...
- js检查元素是否包括在数组中
说明 在系统中须要检查税率填写的正确性,一定是国家规定的某几种税率,当然能够通过if else进行校验,可是还能够使用定义一个数组然后校验是否包括在元素中进行校验. 长处:加入税率无需改动逻辑,仅仅须 ...
- 基于Eclipse的Android JNI层測试应用开发过程记录
前言 本文记录一个Java层与JNI层參数与数据交互的应用程序开发过程.为实现一个功能完整的带Java与JNI的应用程序打下基础. 本文如果读者已搭建好Android的Eclipse与NDK开发环境, ...
- get_browser()用法
get_browser()用法 get_browser()函数是用来分析USER_AGENT的,它的执行方法是自动获取客户端的USER_AGENT,然后调用browscap.ini库进行分析得到结果 ...
- PythonNET网络编程3
IO IO input output 在内存中存在数据交换的操作都可以认为是IO操作 和终端交互 : input print 和磁盘交互 : read write 和网络交互 : recv send ...
- ClickOnce
Clic WPF ClickOnce应用程序IIS部署发布攻略 WPF程序非常适合公司内网使用,唯一缺点就是客户端要安装.net框架4.0.优势也很明显,在客户端运行的是一个WinForm程序,自 ...
- Dotfuscator use for .net4.0 error solve
在混淆的时候报错了,错误描述大致如下: Could not find a compatible version of ildasm to run on assembly C:\xxx.dll This ...
- 00091_字符输入流Reader
1.字符输入流Reader (1)字符输入流Reader我们读取拥有中文的文件时,使用的字节流在读取,那么我们读取到的都是一个一个字节: (2)只要把这些字节去查阅对应的编码表,就能够得到与之对应的字 ...
- android Email总结文档
目录:src\com.android.email.activity 一. Welcome.java 根据AndroidManifest.xml可知该文件为程序入口文件: 加载该文件时,查询数据库账户列 ...
- JS学习笔记 - fgm练习 - 输入数字求和 正则replace onkeyup事件
<style> body{font-size: 12px;} .outer{ width: 500px; margin: 0 auto; } span{ color: #999; } in ...