.Net Core微服务入门全纪录(四)——Ocelot-API网关(上)
系列文章目录
1、.Net Core微服务入门系列(一)——项目搭建
2、.Net Core微服务入门全纪录(二)——Consul-服务注册与发现(上)
3、.Net Core微服务入门全纪录(三)——Consul-服务注册与发现(下)
4、.Net Core微服务入门全纪录(四)——Ocelot-API网关(上)
5、.Net Core微服务入门全纪录(五)——Ocelot-API网关(下)
6、.Net Core微服务入门全纪录(六)——EventBus-事件总线
7、.Net Core微服务入门全纪录(八)——Docker Compose与容器网络
前言📃
关于 微服务 的概念解释网上有很多, 个人理解微服务是一种系统架构模式,它和语言无关,和框架无关,和工具无关,和服务器环境无关。
微服务思想 是将传统的单体系统按照业务拆分成多个职责单一、且可独立运行的接口服务。至于服务如何拆分,没有明确的定义。几乎任何后端语言都能做微服务开发。微服务也并不是完美无缺的,微服务架构会带来更多的问题,增加系统的复杂度,引入更多的技术栈。
上一篇 Net Core微服务入门全纪录(三)——Consul-服务注册与发现(下)已经使用 Consul
完成了服务的注册与发现,实际中光有服务注册与发现往往是不够的,我们需要一个统一的入口来连接客户端与服务。
一、Ocelot
官网:https://ocelot.readthedocs.io/
Ocelot
正是为 .Net 微服务体系提供一个统一的入口点,称为:Gateway
(网关)。
1.1 上手 Ocelot
首先创建一个空的 asp.net core web
项目。
🌈注意 ocelot.json
是我们添加的 Ocelot
的配置文件,记得设置生成时复制到输出目录。ocelot.json
的文件名不是固定的,可以自己定义。
NuGet 安装一下 Ocelot
:
只需简单的修改几处默认代码:
Program.cs
:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("ocelot.json");
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs
:
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
//添加ocelot服务
services.AddOcelot();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//设置Ocelot中间件
app.UseOcelot().Wait();
}
}
ocelot.json
:
{
"Routes": [
{
"DownstreamPathTemplate": "/products",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 9050
},
{
"Host": "localhost",
"Port": 9051
},
{
"Host": "localhost",
"Port": 9052
}
],
"UpstreamPathTemplate": "/products",
"UpstreamHttpMethod": [
"Get"
],
"LoadBalancerOptions": {
"Type": "RoundRobin" //负载均衡,轮询机制 LeastConnection/RoundRobin/NoLoadBalancer/CookieStickySessions
}
},
{
"DownstreamPathTemplate": "/orders",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 9060
},
{
"Host": "localhost",
"Port": 9061
},
{
"Host": "localhost",
"Port": 9062
}
],
"UpstreamPathTemplate": "/orders",
"UpstreamHttpMethod": [
"Get"
],
"LoadBalancerOptions": {
"Type": "RoundRobin" //负载均衡,轮询机制 LeastConnection/RoundRobin/NoLoadBalancer/CookieStickySessions
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:9070"
}
}
我们先暂时忽略 Consul
,将服务实例的地址都写在配置文件中。要知道 Consul、Ocelot
等组件都是可以独立存在的。
配置文件中的 Routes
节点用来配置路由,Downstream
代表下游,也就是 服务实例,Upstream
代表上游,也就是 客户端。
我们的路径比较简单,只有 /products、/orders
,路径中如果有不固定参数则使用 {}
匹配。我们这个配置的意思呢就是客户端访问网关的 /orders、/products
,网关会转发给服务实例的 /orders、/products
,注意这个上游的路径不一定要和下游一致,比如上游路径可以配置成/api/orders,/xxx
都可以。
LoadBalancerOptions
节点用来配置负载均衡,Ocelot
内置了 LeastConnection、RoundRobin、NoLoadBalancer、CookieStickySessions 4种负载均衡策略。
BaseUrl
节点就是配置我们 ocelot
网关将要运行的地址。
1.2 运行 gateway:
目前不考虑网关集群,就不放在 docker
里了。直接控制台执行:
dotnet Ocelot.APIGateway.dll --urls="http://*:9070"
用浏览器测试一下:
测试正常,我们通过网关可以正常的访问到服务实例。
接下来继续改造客户端代码:
因为改动太多就直接新建一个GatewayServiceHelper` 来做。
GatewayServiceHelper
:
/// <summary>
/// 通过gateway调用服务
/// </summary>
public class GatewayServiceHelper : IServiceHelper
{
public async Task<string> GetOrder()
{
var Client = new RestClient("http://localhost:9070");
var request = new RestRequest("/orders", Method.GET);
var response = await Client.ExecuteAsync(request);
return response.Content;
}
public async Task<string> GetProduct()
{
var Client = new RestClient("http://localhost:9070");
var request = new RestRequest("/products", Method.GET);
var response = await Client.ExecuteAsync(request);
return response.Content;
}
public void GetServices()
{
throw new NotImplementedException();
}
}
然后在 Startup
中修改一下注入的类型,别的就不用改了,这就是依赖注入的好处之一。。。
Startup.ConfigureServices()
:
//注入IServiceHelper
//services.AddSingleton<IServiceHelper, ServiceHelper>();
//注入IServiceHelper
services.AddSingleton<IServiceHelper, GatewayServiceHelper>();
Startup.Configure()
:
//程序启动时 获取服务列表
//serviceHelper.GetServices();
运行客户端测试:
好了,现在客户端对服务的调用都通过网关进行中转,客户端再也不用去关心那一堆服务实例的地址,只需要知道网关地址就可以了。另外,服务端也避免了服务地址直接暴露给客户端。这样做对客户端,服务都非常友好。
至于我们的 api
网关呢,又要说到服务发现的问题了。目前我们的服务地址是写在 ocelot.json
配置文件里的,当然这种做法在服务实例不经常变化的情况下是没有问题的,一旦服务变化,需要人为的修改配置文件,这又显得不太合理了。
当然,强大的 Ocelot
为我们提供了服务发现的方案。