打不死的小強 .net core 微服務 快速開發框架 Viper 限流

杜燕明發表於2020-10-20

1、Viper是什麼?

  Viper 是.NET平臺下的Anno微服務框架的一個示例專案。入門簡單安全穩定高可用全平臺可監控。底層通訊可以隨意切換thrift grpc 自帶服務發現、呼叫鏈追蹤、Cron 排程、限流、事件匯流排、CQRS 、DDD、類似MVC的開發體驗,外掛化開發

  一個不可監控的微服務平臺是可怕的,出了問題 難以準確定位問題的根源, Anno則提供了一套完整的監控體系,包括鏈路追蹤服務佔用的系統資源、系統自身 CPU、記憶體、硬碟使用率實時可監控等等。

  今天要說的是.netcore 微服務Viper的限流,防止惡意攻擊,做一個打不死的小強

  如果對Viper不瞭解的可以看上篇文章瞭解 net core 微服務 快速開發框架 Viper 初體驗

github:
https://github.com/duyanming/Viper
文件地址:
https://duyanming.github.io/

體驗地址:(體驗使用者為anno 密碼123456 同一時間一個使用者只能在一個終端登入使用者多的時候可能發生強制退出的情況,稍後登入體驗)
http://140.143.207.244/

 

2、限流Anno.RateLimit

  限流用到兩個庫 Anno.RateLimit、IPAddressRange。Anno.RateLimit為限流元件,IPAddressRange為IP匹配元件。

IPAddressRange地址:https://github.com/jsakamoto/ipaddressrange

using NetTools;
...
// rangeA.Begin is "192.168.0.0", and rangeA.End is "192.168.0.255".
var rangeA = IPAddressRange.Parse("192.168.0.0/255.255.255.0");
rangeA.Contains(IPAddress.Parse("192.168.0.34")); // is True.
rangeA.Contains(IPAddress.Parse("192.168.10.1")); // is False.
rangeA.ToCidrString(); // is 192.168.0.0/24

// rangeB.Begin is "192.168.0.10", and rangeB.End is "192.168.10.20".
var rangeB1 = IPAddressRange.Parse("192.168.0.10 - 192.168.10.20");
rangeB1.Contains(IPAddress.Parse("192.168.3.45")); // is True.
rangeB1.Contains(IPAddress.Parse("192.168.0.9")); // is False.

// Support shortcut range description. 
// ("192.168.10.10-20" means range of begin:192.168.10.10 to end:192.168.10.20.)
var rangeB2 = IPAddressRange.Parse("192.168.10.10-20");

// Support CIDR expression and IPv6.
var rangeC = IPAddressRange.Parse("fe80::/10"); 
rangeC.Contains(IPAddress.Parse("fe80::d503:4ee:3882:c586%3")); // is True.
rangeC.Contains(IPAddress.Parse("::1")); // is False.

// "Contains()" method also support IPAddressRange argument.
var rangeD1 = IPAddressRange.Parse("192.168.0.0/16");
var rangeD2 = IPAddressRange.Parse("192.168.10.0/24");
rangeD1.Contains(rangeD2); // is True.

// IEnumerable<IPAddress> support, it's lazy evaluation.
foreach (var ip in IPAddressRange.Parse("192.168.0.1/23"))
{
    Console.WriteLine(ip);
}

// You can use LINQ via "AsEnumerable()" method.
var longValues = IPAddressRange.Parse("192.168.0.1/23")
  .AsEnumerable()
  .Select(ip => BitConvert.ToInt32(ip.GetAddressBytes(), 0))
  .Select(adr => adr.ToString("X8"));
Console.WriteLine(string.Join(",", longValues);

// Constructors from IPAddress objects.
var ipBegin = IPAddress.Parse("192.168.0.1");
var ipEnd = IPAddress.Parse("192.168.0.128");
var ipSubnet = IPAddress.Parse("255.255.255.0");

var rangeE = new IPAddressRange(); // This means "0.0.0.0/0".
var rangeF = new IPAddressRange(ipBegin, ipEnd);
var rangeG = new IPAddressRange(ipBegin, maskLength: 24);
var rangeH = new IPAddressRange(ipBegin, IPAddressRange.SubnetMaskLength(ipSubnet));

// Calculates Cidr subnets
var rangeI = IPAddressRange.Parse("192.168.0.0-192.168.0.254");
rangeI.ToCidrString();  // is 192.168.0.0/24

 Anno.RateLimit限流元件的使用

   Anno.RateLimit支援令牌桶和漏桶兩種演算法

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Anno.RateLimit;

namespace ConsoleTest
{
    /// <summary>
    /// 限流測試
    /// </summary>
    public class RateLimitTest
    {
        /// <summary>
        /// 限流測試
        /// </summary>
        public void Handle()
        {
            var service = LimitingFactory.Build(TimeSpan.FromSeconds(1),LimitingType.TokenBucket, 20, 5);
            Console.Write("請輸入執行緒數:");
            long.TryParse(Console.ReadLine(), out long th);
            for (int i = 0; i < th; i++)
            {
                var t = Task.Factory.StartNew(() =>
                {
                    while (true)
                    {
                        var result = service.Request();
                        //如果返回true,說明可以進行業務處理,否則需要繼續等待
                        if (result)
                        {
                            Console.WriteLine($"{DateTime.Now}--{Task.CurrentId}---ok");
                            //業務處理......
                        }
                        else
                            Thread.Sleep(100);
                    }
                }, TaskCreationOptions.LongRunning);
            }
        }
    }
}

3、Viper限流

  下圖是用Jmeter測試線上http://140.143.207.244/ 的截圖,下圖紅色部分為限流直接返回失敗的記錄,呼叫方為 NewApi目標服務為NewApi可以看到返回的資訊為Trigger current limiting,

通過限流保證Viper成為一個打不死的小強

配置Viper的限流

{
  "Target": {
    "AppName": "ApiGateway",
    "IpAddress": "127.0.0.1",
    "Port": 7010,
    "TraceOnOff": true
  },
  "Limit": {
    "Enable": true,
    "TagLimits": [
      {
        "channel": "*",
        "router": "*",
        "timeSpan": "1",//限流週期單位秒
        "rps": 100,//週期內通過的請求個數
        "limitSize": 100//令牌桶,漏桶池子大小
      }
    ],
    "IpLimit": {//ip限流
      "timeSpan": 1,//限流週期單位秒
      "rps": 100,//週期內通過的請求個數
      "limitSize": 100//池子大小
    },
    "White": [//白名單 用法可以參考IPAddressRange
      "0.0.0.1",
      "192.168.1.2",
      "192.168.2.18"
    ],
    "Black": [//黑名單
      "0.0.0.2",
      "192.168.3.18"
    ]
  }
}

Limit.Enable:是否啟用限流

Limit.TagLimits:根據Tag限流

channel: Anno.Plugs.Logic//請求tag 必須引數
router: Platform      //模組名稱(沒有Module) 必須引數
method: GetFunc      //模組方法 必須引數
profile: fSFhFv5d4ZlC/JTz1EvoBDNWTr+sNtAhKWTuykqfZHU2oB8/W7aUayqsXmFJXPlR
uname: yrm

 

Limit.IpLimit:根據客戶端Ip限流

Limit.White:白名單

Limit.IpLimit:黑名單

 關於更多的Viper限流細節可參考github:https://github.com/duyanming/Viper/blob/master/Viper/Extensions/Middleware/DymWebHostBuilderExtensions.cs

 以上分享了,Viper閘道器的限流。後面分享每個微服務如何通過註解方式限流、以及請求快取、ViperService的 全域性過濾器、模組過濾器、方法過濾器。

過濾器型別有,授權過濾器、異常過濾器、Action過濾器。 Anno.EngineData.Filters

兩種常用演算法

令牌桶(Token Bucket)和漏桶(leaky bucket)是 最常用的兩種限流的演算法。

令牌桶主要是控制注入的速度,漏桶則是控制出的速度。

漏桶演算法

 

 

令牌桶演算法

 

 

關於更多限流只是可參考:

張善友部落格:https://www.cnblogs.com/shanyou/p/4280546.html

凌晨三點半:https://www.cnblogs.com/vveiliang/p/9049393.html

 Viper

github:
https://github.com/duyanming/Viper
文件地址:
https://duyanming.github.io/

體驗地址:(體驗使用者為anno 密碼123456 同一時間一個使用者只能在一個終端登入使用者多的時候可能發生強制退出的情況,稍後登入體驗)
http://140.143.207.244/

 

關於Viper的更多內容,隨後更新。敬請關注。開源不易,感謝Star。

 

相關文章