增加重试策略说明
# ÖØÊÔ²ßÂÔ£¨IRetryPolicy£©Ê¹ÓÃ˵Ã÷
±¾Îĵµ½éÉÜ `NewLife.Remoting` ÖÐÐÂÔöµÄ¿ÉÑ¡ÖØÊÔÄÜÁ¦£¬°üÀ¨ÆôÓ÷½Ê½¡¢½Ó¿ÚÔ¼¶¨ÓëʾÀý²ßÂÔ¡£
## ¸ÅÊö
- Ä¿±ê£ºÌáÉý¿Í»§¶ËÔÚ˲ʱ´íÎó£¨ÍøÂç¶¶¶¯¡¢¶ÌÔݲ»¿ÉÓõȣ©ÏµĽ¡×³ÐÔ¡£
- ĬÈÏÐÐΪ£º²»ÆôÓÃÖØÊÔ£¨±£³Ö¼ÈÓÐÓïÒ壩¡£
- ÉúЧÌõ¼þ£º`ApiClient.RetryPolicy != null` ÇÒ `ApiClient.MaxRetries > 0`¡£
- ÌØÀý£º401£¨Unauthorized£©ÈÔ²ÉÓüÈÓÐÐÐΪ¡ª¡ª´¥·¢Ò»´Î `OnLoginAsync(force=true)` ºóÔÚͬһÁ¬½ÓÉÏÖØ·¢£¬²»¼ÆÈëÖØÊÔ´ÎÊý¡£
## ÆôÓ÷½Ê½
```csharp
var client = new ApiClient("tcp://127.0.0.1:12345")
{
RetryPolicy = new MyRetryPolicy(),
MaxRetries = 3 // ²»º¬Ê״γ¢ÊÔ
};
var result = await client.InvokeAsync<string>("Hello/Say", new { Name = "Stone" });
```
- `RetryPolicy`£ºÊµÏÖ `IRetryPolicy`£¬ÓÃÓÚ°´Òì³£ÀàÐÍÓ뵱ǰµÚ¼¸´ÎÖØÊÔ£¬¾ö¶¨ÊÇ·ñÖØÊÔ¡¢µÈ´ý¶à¾Ã¡¢ÊÇ·ñ¸ü»»Á¬½Ó¡£
- `MaxRetries`£º×î´óÖØÊÔ´ÎÊý£¬²»º¬Ê״γ¢ÊÔ£¨0 ±íʾ½ûÓã©¡£
## ²ßÂÔ½Ó¿Ú
```csharp
public interface IRetryPolicy
{
bool ShouldRetry(Exception exception, int attempt, out TimeSpan delay, out bool refreshClient);
}
```
- `exception`£º±¾´Îʧ°ÜµÄÒì³££¨¿É¾Ý´Ëɸѡ Transient£©¡£
- `attempt`£ºµ±Ç°ÖØÊÔÐòºÅ£¨´Ó 1 ¿ªÊ¼£¬²»º¬Ê״Σ©¡£
- `delay`£º½¨ÒéµÈ´ýʱ³¤£»`TimeSpan.Zero` ±íʾ²»µÈ´ýÁ¢¼´ÖØÊÔ¡£
- `refreshClient`£ºÊÇ·ñÔÚÖØÊÔǰ¸ü»»µ×²ãÁ¬½Ó£¨`Cluster.Return` Ö®ºóÔÙ `Cluster.Get`£©¡£
## ʾÀý²ßÂÔ£ºÖ¸ÊýÍ豆 + Á¬½Ó¿ÉÑ¡Çл»
```csharp
public sealed class MyRetryPolicy : IRetryPolicy
{
public bool ShouldRetry(Exception exception, int attempt, out TimeSpan delay, out bool refreshClient)
{
// 401 ÓÉ¿ò¼Ü²ã´¦Àí£¬²»ÔÚ²ßÂÔÄÚÖØÊÔ
if (exception is ApiException aex && aex.Code == ApiCode.Unauthorized)
{
delay = TimeSpan.Zero;
refreshClient = false;
return false;
}
// ¶ÔÓÚ³¬Ê±/ÍøÂçÒì³£µÈ£º×î¶àÍ豆 2^attempt * 100ms£¬×î¶à 2 Ãë
if (exception is TimeoutException || exception is TaskCanceledException || exception is IOException || exception is SocketException)
{
var ms = Math.Min(2000, (int)Math.Pow(2, attempt) * 100);
delay = TimeSpan.FromMilliseconds(ms);
// µÚ 2 ´Î¼°ÒÔºó³¢ÊÔÇл»Á¬½Ó
refreshClient = attempt >= 2;
return true;
}
// ÆäËüÒì³££¬²»ÖØÊÔ
delay = TimeSpan.Zero;
refreshClient = false;
return false;
}
}
```
## ÐÐΪϸ½Ú
- 401 ´¦Àí£º¿ò¼Ü²¶»ñºóµ÷Óà `OnLoginAsync(force=true)`£¬ÔÙÔÚͬһÁ¬½ÓÉÏÖØ·¢Ò»´Î£¬²»¼ÆÈë `MaxRetries`¡£
- ³¬Ê±ÏûÏ¢£ºÖØÊÔ×îÖÕÈÔʧ°Ü»ò²ßÂÔδ´¥·¢Ê±£¬`TaskCanceledException` »á±»×ªÎª¶ÌÏûÏ¢£º`"[action]³¬Ê±[Timeout]È¡Ïû"`£¬ÔÚÈÕÖ¾¼¶±ð Debug ϰüº¬¸ü¶àÉÏÏÂÎÄ¡£
- Á¬½ÓÇл»£ºµ± `refreshClient=true` ʱ£¬µ±Ç°Á¬½Ó½«¹é»¹£¬ÖØÊÔÇ°ÖØÐÂ´Ó `Cluster` »ñÈ¡¡£
- È¡ÏûÖ§³Ö£º`InvokeAsync` µÄ `CancellationToken` ½«Ôڵȴý `delay` ʱ±»¹Û²ì£¬È¡Ïû»áÁ¢¼´ÖжÏÖØÊÔ²¢Å׳ö¡£
## ½¨ÒéÓë×î¼Ñʵ¼ù
- ºÏÀíµÄ `MaxRetries`£º2~3 ´Îͨ³£×ã¹»£¬±ÜÃâÖØÊԷ籩¡£
- Í˱ܲßÂÔ£ºÊ¹ÓÃÏßÐÔ/Ö¸ÊýÍ˱ܣ¬±ÜÃâÃܼ¯ÖØÊÔ¡£
- Á¬½ÓÇл»£º½öÔÚÈ·ÈÏÁ¬½Ó²ãÒì³£»òÁ¬ÐøÊ§°ÜºóÔÙÇл»¡£
- ¹Û²â£º½áºÏ `SlowTrace` ÓëÈÕÖ¾£¬ÆÀ¹ÀÖØÊԶԶ˵½¶ËºÄʱµÄÓ°Ïì¡£
## ³£¼ûÎÊÌâ
- Q£ºÆôÓÃÖØÊÔºóÊÇ·ñ»á¸Ä±äÕý³£ÒµÎñÓïÒ壿
- A£º²»»á¡£Ä¬Èϲ»ÆôÓã»ÆôÓúóÖ»¶Ô²ßÂÔÅж¨µÄÒì³£½øÐÐÓÐÏÞ´ÎÖØÊÔ¡£
- Q£ºÓëÁ¬½Ó³Ø/µ¥Á¬½ÓģʽÊÇ·ñ¼æÈÝ£¿
- A£º¼æÈÝ¡£`refreshClient` Ϊ true ʱÔÚÁ½ÖÖģʽ϶¼»á¹é»¹²¢ÖØÐ»ñÈ¡Á¬½Ó¡£
- Q£ºÊÇ·ñÌṩÄÚÖòßÂÔ£¿
- A£ººËÐĿⲻÄÚÖ㬱ÜÃâÐÐΪÕùÒ飻¿ÉÔÚÀ©Õ¹¿â»òÒµÎñ²àÌṩͨÓòßÂÔ¡£
## ²Î¿¼
- `ApiClient.InvokeAsync<TResult>`£º¿ÉÑ¡ÖØÊÔÂß¼Èë¿Ú¡£
- `ApiClient.OnLoginAsync(...)`£º401 µÇ¼ºóÖØ·¢µÄ¹³×Ó¡£
- `IRetryPolicy`£º×Ô¶¨ÒåÖØÊÔ²ßÂÔ½Ó¿Ú¡£
|