1. 程式人生 > 其它 >NetCore基於Consul+Ocelot+Docker+Jenkin搭建微服務架構

NetCore基於Consul+Ocelot+Docker+Jenkin搭建微服務架構

轉載:https://www.cnblogs.com/zt102545/p/13940210.html

環境版本

釋出環境:

  • cenots7
  • Docker

開發環境:

  • vs2019
  • Net Core 3.1

版本號:

  • consul:1.6.1.1
    ocelot:16.0.1
    docker:19.03.12
    Jenkins :2.235.3

Consul

做服務發現的框架常用的有zookeeper,eureka,etcd,consul。
consul就是提供服務發現的工具,具有分散式、高可用、橫向擴充套件等特性。

Consul服務端安裝

官方文件
在Linux的docker 下安裝的和部署consul

#拉取
docker pull consul
#啟動
docker run -d --name consul -p 8500:8500 consul

Window的consul可以直接去官網下載,下載後是一個consul.exe檔案,用cmd定位到下載目錄後直接執行指令consul.exe agent --dev啟動
啟動後瀏覽器訪問http://120.79.256.192:8500/可以直接看到介面。

Consul客戶端註冊

新建NetCore 3.1的Web應用程式。
新增一個控制器做心跳和測試用的。

[Route("api/[controller]/[action]")]
[ApiController]
public class MicroservicesController : ControllerBase
{
    /// <summary>
    /// 心跳檢查
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public IActionResult Index()
    {
        return Ok();//只是個200狀態碼
    }

    /// <summary>
    /// 測試介面
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public string Get()
    {
        Console.WriteLine(Request.Host.Value);
        return Request.Host.Value;
    }
}

NuGet新增引用Consul最新版本。

新增IConfiguration的擴充套件方法,然後在Startup的Configure裡呼叫。

/// <summary>
/// 擴充套件方法
/// </summary>
/// <param name="configuration"></param>
public static void ConsulRegist(this IConfiguration configuration)
{
    ConsulClient client = new ConsulClient(c =>
    {
        c.Address = new Uri("http://120.79.256.192:8500/");//Consul服務端的地址
        c.Datacenter = "dc1";
    });

    //直接在VS裡執行會報錯,因為拿不到ip跟port,這些引數是啟動的時候傳入的,需要用指令啟動
    //dotnet WebAPI.dll --urls="http://*:5005" --ip="127.0.0.1" --port=5005
    string ip = configuration["ip"];
    int port = int.Parse(configuration["port"]);
    int weight = string.IsNullOrWhiteSpace(configuration["weight"]) ? 1 : int.Parse(configuration["weight"]);
    //客戶端向服務端註冊的方法
    client.Agent.ServiceRegister(new AgentServiceRegistration()
    {
        ID = "service " + ip + ":" + port,//ID唯一
        Name = "ZztService",//註冊名稱
        Address = ip,//註冊客戶端的IP
        Port = port,//註冊客戶端的埠號
        Tags = new string[] { weight.ToString() },//傳入引數
        Check = new AgentServiceCheck()//心跳檢測
        {
            Interval = TimeSpan.FromSeconds(12),//每12s檢查一次
            HTTP = $"http://{ip}:{port}/api/Microservices/Index",//呼叫檢測介面,該方法沒有內容,直接返回200
            Timeout = TimeSpan.FromSeconds(5),//超時時間
            DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(20)//超時後20s後服務端認為該客戶端掛了,直接斷開。
        }
    });
    //命令列引數獲取
    Console.WriteLine($"{ip}:{port}--weight:{weight}");
}
        
//Startup.Configure方法裡執行Consul註冊
#region Consul註冊 
//站點啟動完成--執行且只執行一次
this.Configuration.ConsulRegist();
#endregion

Ocelot

Ocelot是一個用.NET Core實現並且開源的API閘道器,它功能強大,包括了:路由、請求聚合、服務發現、認證、鑑權、限流熔斷、並內建了負載均衡器與Service Fabric、Butterfly Tracing整合。這些功能只都只需要簡單的配置即可完成。
配置的官方文件
新建NetCore 3.1的Web應用程式。
新增Json配置檔案,這裡命名為:“OcelotConfig.json”新增以下內容
注意:16版本之前根節點是 ReRoutes,現在是 Routes 。
通用配置的模板如下:

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/{url}", //服務地址
      "DownstreamPathTemplate": "/{url}", //閘道器地址
      "DownstreamScheme": "http", //請求協議
      "UpstreamHttpMethod": [ "Get", "Post" ] //請求方式
    }
  ]
}

常用的配置節點的簡單說明如下:

  • Downstream是下游服務配置
    UpStream是上游服務配置
    Aggregates 服務聚合配置
    ServiceName, LoadBalancer, UseServiceDiscovery 配置服務發現
    AuthenticationOptions 配置服務認證
    RouteClaimsRequirement 配置Claims鑑權
    RateLimitOptions為限流配置
    FileCacheOptions 快取配置
    QosOptions 服務質量與熔斷
    DownstreamHeaderTransform頭資訊轉發
    將Json檔案新增到程式裡。
public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration(config=> {
                config.AddJsonFile("OcelotConfig.json", optional: false, reloadOnChange: true);
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

將程式的整個程序的管道換成Ocelot

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)
    {
        services
            .AddOcelot()//使用Ocelot
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseOcelot();//將整個程序的管道換成Ocelot
    }
}

服務發現

可以配合Consul來實現。
NuGet新增引用Ocelot最新版本。Ocelot使用Consul還需要新增Ocelot.Provider.Consul引用

在Startup裡面註冊

 public void ConfigureServices(IServiceCollection services)
 {
     services
         .AddOcelot()//使用Ocelot
         .AddConsul();//使用Consul
 }

在Ocelot配置檔案的Routes根節點中加入節點RateLimitOptions,新增GlobalConfiguration根節點,加入節點GlobalConfiguration

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/{url}", //服務地址
      "DownstreamPathTemplate": "/{url}", //閘道器地址
      "DownstreamScheme": "http", //請求協議
      "UpstreamHttpMethod": [ "Get", "Post" ], //請求方式
      "UseServiceDiscovery": true, //是否使用服務發現
      "ServiceName": "ZztService", //Consul服務名稱
      "LoadBalancerOptions": { //負載均衡策略方式
        "Type": "RoundRobin" //輪詢
      }
    }
  ],
  "GlobalConfiguration": { //閘道器全域性配置
    //"BaseUrl": "http://127.0.0.1:6299", //閘道器對外地址
    "ServiceDiscoveryProvider": {//服務發現的全域性配置
      "Host": "120.79.256.192", //Consul服務端地址
      "Port": 8500, //Consul服務端埠號
      "Type": "Consul" //指明由Consul提供的服務發現,也可以換zookeeper,etcd等元件
    }
  }
}

服務治理

Cache

Ocelot閘道器一層加一個快取,可以對上層請求服務進行快取。
Ocelot要使用Cache需要引用Ocelot.Cache.CacheManager

建立一個自定義的快取類,實現IOcelotCache介面
這裡快取的處理使用的是MemoryCache,也可以根據專案需求換成Redis

/// <summary>
/// 自定義Ocelot的快取類,實現IOcelotCache介面
/// </summary>
public class OcelotCache : IOcelotCache<CachedResponse>
{
    /// <summary>
    /// 新增快取
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <param name="ttl"></param>
    /// <param name="region"></param>
    public void Add(string key, CachedResponse value, TimeSpan ttl, string region)
    {
        Console.WriteLine($"This is OcelotCache.Add");
        MemoryCache.Set(key, value, ttl.TotalSeconds);
    }

    /// <summary>
    /// 覆蓋快取
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <param name="ttl"></param>
    /// <param name="region"></param>
    public void AddAndDelete(string key, CachedResponse value, TimeSpan ttl, string region)
    {
        Console.WriteLine($"This is OcelotCache.AddAndDelete");
        MemoryCache.Remove(key);
        MemoryCache.Set(key, value, ttl.TotalSeconds);
    }

    /// <summary>
    /// 清除快取
    /// </summary>
    /// <param name="region">key</param>
    public void ClearRegion(string region)
    {
        Console.WriteLine($"This is OcelotCache.ClearRegion");
        //簡單處理,清除所有快取,根據需要自己優化
        MemoryCache.Remove(region);
    }

    /// <summary>
    /// 獲取快取
    /// </summary>
    /// <param name="key"></param>
    /// <param name="region"></param>
    /// <returns></returns>
    public CachedResponse Get(string key, string region)
    {
        try
        {
            Console.WriteLine($"This is OcelotCache.Get");
            return (CachedResponse)MemoryCache.Get(key);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            return null;
        }
    }
}

在Startup裡面註冊

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddOcelot()//使用Ocelot
        .AddConsul()//使用Consul
        .AddCacheManager(o => o.WithDictionaryHandle());//使用Cache,預設字典儲存

    //這裡的IOcelotCache<CachedResponse>是預設的快取的約束,替換成自定義的OcelotCache
    services.AddSingleton<IOcelotCache<CachedResponse>, OcelotCache>();
}

在Ocelot配置檔案的Routes根節點中加入節點FileCacheOptions

"FileCacheOptions": {//快取配置
    "TtlSeconds": 15,//快取時間
    "Region": "UserCache" //可以呼叫Api清理
  }
}

Polly

Polly是一個被.NET基金會認可的彈性和瞬態故障處理庫,允許我們以非常順暢和執行緒安全的方式來執諸如行重試,斷路,超時,故障恢復等策略,其主要功能如下:
重試(Retry)
斷路器(Circuit-Breaker)
超時檢測(Timeout)
快取(Cache)
降級(Fallback)

Ocelot要使用Polly需要引用Ocelot.Provider.Polly

在Startup裡面註冊

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddOcelot()//使用Ocelot
        .AddConsul()//使用Consul
        .AddCacheManager(o => o.WithDictionaryHandle())//使用Cache,預設字典儲存
        .AddPolly();//使用Polly
    //這裡的IOcelotCache<CachedResponse>是預設的快取的約束,替換成自定義的OcelotCache
    services.AddSingleton<IOcelotCache<CachedResponse>, OcelotCache>();
}

熔斷

熔斷機制:
熔斷的意思是停止將請求轉發到下游服務。當下遊服務已經出現故障的時候再請求也是無功而返,並且增加下游伺服器和API閘道器的負擔。而後使用斷路器來檢測故障是否得到解決,防止請求反覆嘗試執行一個異常操作,從而減少等待糾正故障的時間。
這個功能是用的Pollly來實現的,我們只需要為路由做一些簡單配置即可。
熔斷配置
在Ocelot配置檔案的Routes根節點中加入節點QoSOptions

"QoSOptions": {//熔斷的配置
  "ExceptionsAllowedBeforeBreaking": 3, //允許多少個異常請求
  "DurationOfBreak": 10000, // 熔斷的時間,單位為ms,10S後嘗試再次請求
  "TimeoutValue": 5000 //下游處理時間超過2s則視為超時, 預設90秒
}

限流

限流機制:
對請求進行限流可以防止下游伺服器因為訪問過載而崩潰。
限制單位時間內請求次數,超過則返回指定資訊。
限流優先於快取,不管上游請求的是服務還是閘道器的快取都算請求次數的
在Ocelot配置檔案的Routes根節點中加入節點RateLimitOptions

"RateLimitOptions": { //限流的配置
  "ClientWhitelist": [ "zzt", "zzt2" ], //白名單,請求頭的新增引數ClientId來識別白名單
  "EnableRateLimiting": true, //啟動限流
  "Period": "10s", //1s, 5m, 1h, 1d,這裡表示10秒為單位統計請求次數。10秒內能請求多少次
  "PeriodTimespan": 5, //多少秒之後客戶端可以重試,單位秒
  "Limit": 5 //統計時間段內允許的最大請求數量
}

在Ocelot配置檔案的GlobalConfiguration根節點中加入節點RateLimitOptions

"RateLimitOptions": {//限流的全域性配置
  "QuotaExceededMessage": "Too many requests", // 當請求過載被截斷時返回的訊息
  "HttpStatusCode": 666 // 當請求過載被截斷時返回的http status
}

降級

降級機制:
滿足一定條件時,直接關閉服務,減低伺服器壓力。
降級不是由配置的,而是一種目的,可以用以下策略達成降級的目的。

  • 閘道器降級——超時/熔斷/限流
  • 上游降級——關閉服務
  • 下游降級——不呼叫

Jenkin

不細說了,參考之前寫的部落格:Jenkins+Docker+Git實現自動化部署
專案結構:

#!/bin/bash
docker stop gateway
docker stop demo1
docker stop demo2
docker rm gateway
docker rm demo1
docker rm demo2
echo `pwd`
echo --------------Building GateWay Image-------------
#-f 定位Dockerfile檔案的位置,因為同一目錄下可能有多個Dockerfile
docker build -t gateway:latest -f Dockerfile_GateWay .
echo --------------Building WebAPI Image--------------
docker build -t demo:latest -f Dockerfile_WebAPI .
echo --------------Launching Container----------------
# 啟動閘道器容器
docker run --name gateway -d -p 80:80 gateway:latest
# 同個映象啟動多個容器,-e表示傳遞引數,ip和port必須傳遞,程式需要註冊到consul
docker run --name demo1 -d -p 8081:80 -e ip="120.79.256.192" -e port="8081" demo:latest
docker run --name demo2 -d -p 8082:80 -e ip="120.79.256.192" -e port="8082" demo:latest
echo --------------Delete none Image------------------
# 刪除多餘的中間映象
docker rmi $(docker images -f "dangling=true" -q)

構建成功後docker映象和容器如下:

[root@iZwz925p95hhdeZ ~]# docker images
REPOSITORY                             TAG                 IMAGE ID            CREATED             SIZE
demo                                   latest              d1b1dab1eac3        4 hours ago         233MB
gateway                                latest              01a506051910        16 hours ago        210MB
consul                                 latest              45f9911e51f6        3 days ago          122MB
jenkins/jenkins                        lts                 135a0d19f757        2 weeks ago         667MB
microsoft/dotnet                       latest              156e5cc5d7a3        7 weeks ago         1.74GB
mcr.microsoft.com/dotnet/core/sdk      3.1-buster          5f369f0d8192        2 months ago        705MB
mcr.microsoft.com/dotnet/core/aspnet   3.1-buster-slim     e2cd20adb129        5 months ago        207MB
[root@iZwz925p95hhdeZ ~]# docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                                                                      NAMES
6c3373c16ac6        demo:latest           "dotnet WebAPI.dll"      4 hours ago         Up 4 hours          0.0.0.0:8082->80/tcp                                                       demo2
407a2e0be4d5        demo:latest           "dotnet WebAPI.dll"      4 hours ago         Up 4 hours          0.0.0.0:8081->80/tcp                                                       demo1
c20ef71c2f1e        gateway:latest        "dotnet GateWay.dll"     4 hours ago         Up 4 hours          0.0.0.0:80->80/tcp, 443/tcp                                                gateway
009d29c38b7a        consul                "docker-entrypoint.s…"   16 hours ago        Up 16 hours         8300-8302/tcp, 8301-8302/udp, 8600/tcp, 8600/udp, 0.0.0.0:8500->8500/tcp   consul
0c89cd504e17        jenkins/jenkins:lts   "/sbin/tini -- /usr/…"   8 days ago          Up 7 days           0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp                           jenkins

最後訪問我們部署好的閘道器地址:http://120.79.256.192/api/Microservices/Get

Skywalking

安裝啟動

Docker下安裝最新版,注意安裝V7.0版本,目前SkyAPM.Agent.AspNetCore暫不支援最新8.0版本。這是個坑,所以自行安裝7.0版本

#下載oap
[root@iZwz925p95hhdeZ ~]# docker pull apache/skywalking-oap-server
Using default tag: latest
latest: Pulling from apache/skywalking-oap-server
e7c96db7181b: Pull complete 
f910a506b6cb: Pull complete 
b6abafe80f63: Pull complete 
ba0cd243507a: Pull complete 
f28c577725a3: Pull complete 
Digest: sha256:21aab32a3d6e95d031ce291477d1e9cfa84e5a0b9e938d49b1252261631b2883
Status: Downloaded newer image for apache/skywalking-oap-server:latest
docker.io/apache/skywalking-oap-server:latest
#下載ui介面
[root@iZwz925p95hhdeZ ~]# docker pull apache/skywalking-ui
Using default tag: latest
latest: Pulling from apache/skywalking-ui
e7c96db7181b: Already exists 
f910a506b6cb: Already exists 
b6abafe80f63: Already exists 
047642b58c35: Pull complete 
159530a74c1a: Pull complete 
Digest: sha256:67d50e4deff42df439831822665b5e3827d2c33658b6d6b4e3dc3258e7f98daf
Status: Downloaded newer image for apache/skywalking-ui:latest
docker.io/apache/skywalking-ui:latest
#預設使用h2記憶體資料庫,可直接啟動oap
[root@iZwz925p95hhdeZ ~]# docker run --name skywalking -d -p 1234:1234 -p 11800:11800 -p 12800:12800 --restart always apache/skywalking-oap-server
84ad1385d4b34e5b5e5e34a58278b97f834f0e5f7763b4981694af5a66dca634
#啟動ui頁面
[root@iZwz925p95hhdeZ ~]# docker run --name skywalking-ui -d -p 8090:8080 --link skywalking:skywalking -e SW_OAP_ADDRESS=skywalking:12800 --restart always apache/skywalking-ui
e6ee24a4dafec135d7dfd2836be9dbf82e3824502f86ceb4ef62dda88af008eb

對映的埠是8090,瀏覽器直接訪問

專案應用

專案中新增引用SkyAPM.Agent.AspNetCore

建立一個skyapm.json檔案,新增以下內容,並將檔案屬性修改為始終複製

{
  "SkyWalking": {
    "ServiceName": "GateWay", //服務名稱
    "Namespace": "",
    "HeaderVersions": [
      "sw6"
    ],
    "Sampling": {
      "SamplePer3Secs": -1,
      "Percentage": -1.0
    },
    "Logging": {
      "Level": "Information",
      "FilePath": "logs/skyapm-{Date}.log"
    },
    "Transport": {
      "Interval": 3000,
      "ProtocolVersion": "v6",
      "QueueSize": 30000,
      "BatchSize": 3000,
      "gRPC": {
        "Servers": "127.0.0.1:11800", //Skywalking服務地址,生產環境替需替換成生產skyapm釋出後的地址
        "Timeout": 10000,
        "ConnectTimeout": 10000,
        "ReportTimeout": 600000
      }
    }
  }
}

在Startup.ConfigureServices方法內添加註入

services.AddSkyApmExtensions();

本地執行需在launchSettings.json檔案中的environmentVariables節點下新增系統變數,內容如下:

"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "SkyAPM.Agent.AspNetCore",
"SKYWALKING__SERVICENAME": "GateWay"

生產環境需要修改Dockerfile檔案,新增以下內容:兩個ENV變數及內容,一個是skyapm包名,一個是服務名稱,跟配置檔案一致。

ENV ASPNETCORE_HOSTINGSTARTUPASSEMBLIES=SkyAPM.Agent.AspNetCore
ENV SKYWALKING__SERVICENAME=GateWay

參考的部落格:
https://ocelot.readthedocs.io/en/latest/index.html
https://www.cnblogs.com/jesse2013/p/net-core-apigateway-ocelot-docs.html
https://www.cnblogs.com/lcyhjx/tag/%E5%BE%AE%E6%9C%8D%E5%8A%A1/