前言
沒看dotnet微服務之API閘道器Ocelot的請先看,這篇文章接上面文章
安裝consul
#自定義網路,自定義網路可以指定容器IP,這樣伺服器重啟consul叢集也可以正常執行。
docker network create --driver bridge --subnet=172.21.0.0/16 --gateway=172.21.0.16 adnc_consul
docker run -d -p 8500:8500 -p 8600:8600 -p 8301:8301 --restart=always --network=adnc_consul --ip 172.21.0.1 --privileged=true --name=consul_server_1 --name consul consul:1.15.4 agent -server -bootstrap -ui -node=consul_server_1 -client='0.0.0.0'
在GoodApi專案中修改Program.cs
先要新增Consul包
再新增Consul註冊於登出等相關程式碼
using Consul;
using System.Linq;
using System.Net;
using System.Net.Sockets;
// 建立Consul客戶端
var consulAddress = "http://10.75.174.43:8500";// Environment.GetEnvironmentVariable("CONSUL_ADDRESS"); //10.75.174.43
var consulUri = new Uri(consulAddress);
var client = new ConsulClient(config =>
{
config.Address = consulUri;
});
// 配置服務的健康檢查
var check = new AgentServiceCheck()
{
HTTP = $"http://{GetLocalIpAddress("InterNetwork").FirstOrDefault()}:8080/health", // 健康檢查地址
Interval = TimeSpan.FromSeconds(10) // 檢查間隔
};
var serviceId = "goodapi-1"; // 要登出的服務的ID
// 註冊一個服務
var registration = new AgentServiceRegistration()
{
ID = serviceId,
Name = "goodapi",
Address = GetLocalIpAddress("InterNetwork").FirstOrDefault(),
Port = 8080,
Check = check
};
await client.Agent.ServiceDeregister(serviceId);
client.Agent.ServiceRegister(registration);
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.MapControllers();
app.MapGet("/health", async context =>
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("health");
});
app.Run();
// 登出服務
await client.Agent.ServiceDeregister(serviceId);
List<string> GetLocalIpAddress(string netType)
{
string hostName = Dns.GetHostName();
IPAddress[] addresses = Dns.GetHostAddresses(hostName);
var IPList = new List<string>();
if (netType == string.Empty)
{
for (int i = 0; i < addresses.Length; i++)
{
IPList.Add(addresses[i].ToString());
}
}
else
{
//AddressFamily.InterNetwork = IPv4,
//AddressFamily.InterNetworkV6= IPv6
for (int i = 0; i < addresses.Length; i++)
{
if (addresses[i].AddressFamily.ToString() == netType)
{
IPList.Add(addresses[i].ToString());
}
}
}
return IPList;
}
加Dockerfile檔案
#使用asp.net 6作為基礎映象,起一個別名為base
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
#設定容器的工作目錄為/app
WORKDIR /app
#COPY 檔案
COPY . /app
ENV ASPNETCORE_ENVIRONMENT Production
#設定時區為東八區
ENV TZ Asia/Shanghai
#啟動服務
ENTRYPOINT ["dotnet", "GoodApi.dll"]
# 執行goodapi專案
docker stop goodapi
docker rm -f goodapi
docker build -t goodapi .
docker run --name=goodapi -d -p 8080:8080 --network=adnc_consul goodapi
在OcelotGA中加consul配置與程式碼
加包Consul,Ocelot.Provider.Consul
改ocelot.json配置
{
"Routes": [
{
"UseServiceDiscovery": true,
"UpstreamPathTemplate": "/good/{everything}",
"UpstreamHttpMethod": [ "Get" ],
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"ServiceName": "goodapi",
"LoadBalancerOptions": {
"Type": "LeastConnection"
}
},
{
"UseServiceDiscovery": true,
"UpstreamPathTemplate": "/order/{everything}",
"UpstreamHttpMethod": [ "Get" ],
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"ServiceName": "orderapi",
"LoadBalancerOptions": {
"Type": "LeastConnection"
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://gw.wxy.ink",
"ServiceDiscoveryProvider": {
"Scheme": "http",
"Host": "10.75.174.43", // 這裡是您Consul的地址
"Port": 8500, // Consul的埠
"Type": "Consul"
}
}
}
修改Program.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Consul;
using System;
using System.Collections.Generic;
using System.Net;
using Consul;
using OcelotGA;
// 建立Consul客戶端
var consulAddress = "http://10.75.174.43:8500";// Environment.GetEnvironmentVariable("CONSUL_ADDRESS"); //10.75.174.43
var consulUri = new Uri(consulAddress);
var client = new ConsulClient(config =>
{
config.Address = consulUri;
});
// 配置服務的健康檢查
var check = new AgentServiceCheck()
{
HTTP = $"http://{GetLocalIpAddress("InterNetwork").FirstOrDefault()}:8080/health", // 健康檢查地址
Interval = TimeSpan.FromSeconds(10) // 檢查間隔
};
var serviceId = "gw-1"; // 要登出的服務的ID
// 註冊一個服務
var registration = new AgentServiceRegistration()
{
ID = serviceId,
Name = "gw",
Address = GetLocalIpAddress("InterNetwork").FirstOrDefault(),
Port = 8080,
//Check = check
};
await client.Agent.ServiceDeregister(serviceId);
client.Agent.ServiceRegister(registration);
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
builder.Services.AddOcelot().AddConsul<MyConsulServiceBuilder>(); //這裡要注意MyConsulServiceBuilder
var app = builder.Build();
app.UseOcelot().Wait();
app.UseRouting();
app.MapGet("/Health", async context =>
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("Health");
});
app.Run();
// 登出服務
await client.Agent.ServiceDeregister(serviceId);
List<string> GetLocalIpAddress(string netType)
{
string hostName = Dns.GetHostName();
IPAddress[] addresses = Dns.GetHostAddresses(hostName);
var IPList = new List<string>();
if (netType == string.Empty)
{
for (int i = 0; i < addresses.Length; i++)
{
IPList.Add(addresses[i].ToString());
}
}
else
{
//AddressFamily.InterNetwork = IPv4,
//AddressFamily.InterNetworkV6= IPv6
for (int i = 0; i < addresses.Length; i++)
{
if (addresses[i].AddressFamily.ToString() == netType)
{
IPList.Add(addresses[i].ToString());
}
}
}
return IPList;
}
新增MyConsulServiceBuilder.cs
using Consul;
using Ocelot.Logging;
using Ocelot.Provider.Consul;
using Ocelot.Provider.Consul.Interfaces;
namespace OcelotGA
{
public class MyConsulServiceBuilder : DefaultConsulServiceBuilder
{
public MyConsulServiceBuilder(IHttpContextAccessor contextAccessor, IConsulClientFactory clientFactory, IOcelotLoggerFactory loggerFactory)
: base(contextAccessor, clientFactory, loggerFactory) { }
// I want to use the agent service IP address as the downstream hostname
protected override string GetDownstreamHost(ServiceEntry entry, Node node)
=> entry.Service.Address;
}
}
執行閘道器專案
我們訪問gw.wxy.ink/good/health
爬坑記錄
Program.cs中的
builder.Services.AddOcelot().AddConsul<MyConsulServiceBuilder>(); //這裡要注意MyConsulServiceBuilder
若為
builder.Services.AddOcelot().AddConsul();
則會出現下面問題
服務解析出來是node的名稱,而非服務的IP
解決方法:Service Discovery — Ocelot Gateway 23.4 documentation
就是說預設的DefaultConsulServiceBuilder會這樣處理
protected virtual string GetDownstreamHost(ServiceEntry entry, Node node)
=> node != null ? node.Name : entry.Service.Address;
而我們需要的是
protected override string GetDownstreamHost(ServiceEntry entry, Node node)
=> entry.Service.Address;
作者
吳曉陽(手機:13736969112微信同號)