Skip to content

Commit 6dfbcd9

Browse files
committed
完善HttpApiClientFactory
1 parent 01c5fc1 commit 6dfbcd9

File tree

8 files changed

+274
-55
lines changed

8 files changed

+274
-55
lines changed

Demo/Program.cs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,36 @@
22
using Demo.HttpServices;
33
using Microsoft.Extensions.Logging;
44
using System;
5+
using System.Net.Http;
56
using System.Threading.Tasks;
67
using WebApiClient;
78
using WebApiClient.Parameterables;
89

910
namespace Demo
1011
{
12+
class MyHandler : System.Net.Http.DelegatingHandler
13+
{
14+
protected override void Dispose(bool disposing)
15+
{
16+
Console.WriteLine($"{DateTime.Now } 我被调用disposing了");
17+
base.Dispose(disposing);
18+
}
19+
}
20+
1121
class Program
1222
{
1323
static void Main(string[] args)
1424
{
1525
HttpServer.Start(9999);
1626

17-
var config = new HttpApiConfig
27+
HttpApiClientFactory.Add<IUserApi>(c =>
1828
{
19-
HttpHost = new Uri("http://localhost:9999/"),
20-
LoggerFactory = new LoggerFactory().AddConsole()
21-
};
22-
23-
var userApi = HttpApiClient.Create<IUserApi>(config);
29+
c.HttpHost = new Uri("http://localhost:9999/");
30+
c.LoggerFactory = new LoggerFactory().AddConsole();
31+
});
32+
33+
var userApi = HttpApiClientFactory.Create<IUserApi>();
2434
Program.RequestAsync(userApi).Wait();
25-
userApi.Dispose();
26-
2735
Console.ReadLine();
2836
}
2937

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Net.Http;
3+
4+
namespace WebApiClient
5+
{
6+
/// <summary>
7+
/// 表示HttpApiClient创建工厂
8+
/// </summary>
9+
public partial class HttpApiClientFactory
10+
{
11+
/// <summary>
12+
/// 获取默认实例
13+
/// </summary>
14+
public static readonly HttpApiClientFactory Default = new HttpApiClientFactory();
15+
16+
/// <summary>
17+
/// 注册HttpApiClient对应的http接口到默认实例
18+
/// </summary>
19+
/// <typeparam name="TInterface"></typeparam>
20+
/// <exception cref="InvalidOperationException"></exception>
21+
public static void Add<TInterface>() where TInterface : class, IHttpApi
22+
{
23+
Add<TInterface>(config: null);
24+
}
25+
26+
/// <summary>
27+
/// 注册HttpApiClient对应的http接口到默认实例
28+
/// </summary>
29+
/// <typeparam name="TInterface"></typeparam>
30+
/// <param name="config">HttpApiConfig的配置</param>
31+
/// <exception cref="InvalidOperationException"></exception>
32+
public static void Add<TInterface>(Action<HttpApiConfig> config) where TInterface : class, IHttpApi
33+
{
34+
Add<TInterface>(config, handlerFactory: null);
35+
}
36+
37+
/// <summary>
38+
/// 注册HttpApiClient对应的http接口到默认实例
39+
/// </summary>
40+
/// <typeparam name="TInterface"></typeparam>
41+
/// <param name="config">HttpApiConfig的配置</param>
42+
/// <param name="handlerFactory">HttpMessageHandler创建委托</param>
43+
/// <exception cref="InvalidOperationException"></exception>
44+
public static void Add<TInterface>(Action<HttpApiConfig> config, Func<HttpMessageHandler> handlerFactory) where TInterface : class, IHttpApi
45+
{
46+
Default.AddTypedClient<TInterface>(config, handlerFactory);
47+
}
48+
49+
/// <summary>
50+
/// 使用默认实例创建实现了指定接口的HttpApiClient实例
51+
/// </summary>
52+
/// <typeparam name="TInterface"></typeparam>
53+
/// <returns></returns>
54+
public static TInterface Create<TInterface>() where TInterface : class, IHttpApi
55+
{
56+
return Default.CreateTypedClient<TInterface>();
57+
}
58+
}
59+
}

WebApiClient/HttpApiClientFactory.cs

Lines changed: 112 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
using System;
22
using System.Collections.Concurrent;
3-
using System.Collections.Generic;
4-
using System.Linq;
53
using System.Net.Http;
6-
using System.Text;
74
using System.Threading;
85
using System.Threading.Tasks;
96

@@ -12,32 +9,87 @@ namespace WebApiClient
129
/// <summary>
1310
/// 表示HttpApiClient创建工厂
1411
/// </summary>
15-
public class HttpApiClientFactory : IHttpApiClientFactory
12+
public partial class HttpApiClientFactory : IHttpApiClientFactory
1613
{
1714
/// <summary>
18-
/// 获取默认的实例
15+
/// handler的生命周期
1916
/// </summary>
20-
public static readonly HttpApiClientFactory Default = new HttpApiClientFactory();
17+
private TimeSpan lifeTime = TimeSpan.FromMinutes(2d);
2118

22-
private readonly TimeSpan defaultCleanupInterval = TimeSpan.FromSeconds(10d);
19+
/// <summary>
20+
/// 清理handler时间间隔
21+
/// </summary>
22+
private TimeSpan cleanupInterval = TimeSpan.FromSeconds(10d);
2323

24+
/// <summary>
25+
/// 过期的记录
26+
/// </summary>
2427
private readonly ConcurrentQueue<ExpiredHandlerEntry> expiredEntries;
2528

29+
/// <summary>
30+
/// 激活记录的创建工厂
31+
/// </summary>
2632
private readonly Func<Type, Lazy<ActiveHandlerEntry>> activeEntryFactory;
2733

28-
private readonly ConcurrentDictionary<Type, Action<HttpApiConfig>> configs;
34+
/// <summary>
35+
/// http接口客户端选项
36+
/// </summary>
37+
private readonly ConcurrentDictionary<Type, TypedClientOption> typedClientOptions;
2938

39+
/// <summary>
40+
/// 激活的记录
41+
/// </summary>
3042
private readonly ConcurrentDictionary<Type, Lazy<ActiveHandlerEntry>> activeEntries;
3143

3244

33-
public TimeSpan Lifetime { get; set; } = TimeSpan.FromMinutes(2d);
45+
/// <summary>
46+
/// 获取或设置HttpMessageHandler的生命周期
47+
/// </summary>
48+
/// <exception cref="ArgumentOutOfRangeException"></exception>
49+
public TimeSpan Lifetime
50+
{
51+
get
52+
{
53+
return this.lifeTime;
54+
}
55+
set
56+
{
57+
if (value <= TimeSpan.Zero)
58+
{
59+
throw new ArgumentOutOfRangeException();
60+
}
61+
this.lifeTime = value;
62+
}
63+
}
3464

65+
/// <summary>
66+
/// 获取或设置清理过期的HttpMessageHandler的时间间隔
67+
/// </summary>
68+
/// <exception cref="ArgumentOutOfRangeException"></exception>
69+
public TimeSpan CleanupInterval
70+
{
71+
get
72+
{
73+
return this.cleanupInterval;
74+
}
75+
set
76+
{
77+
if (value <= TimeSpan.Zero)
78+
{
79+
throw new ArgumentOutOfRangeException();
80+
}
81+
this.cleanupInterval = value;
82+
}
83+
}
3584

85+
/// <summary>
86+
/// HttpApiClient创建工厂
87+
/// </summary>
3688
public HttpApiClientFactory()
3789
{
3890
this.expiredEntries = new ConcurrentQueue<ExpiredHandlerEntry>();
39-
this.configs = new ConcurrentDictionary<Type, Action<HttpApiConfig>>();
4091
this.activeEntries = new ConcurrentDictionary<Type, Lazy<ActiveHandlerEntry>>();
92+
this.typedClientOptions = new ConcurrentDictionary<Type, TypedClientOption>();
4193
this.activeEntryFactory = apiType => new Lazy<ActiveHandlerEntry>(() => this.CreateActiveEntry(apiType), LazyThreadSafetyMode.ExecutionAndPublication);
4294

4395
this.RegisteCleanup();
@@ -47,69 +99,102 @@ public HttpApiClientFactory()
4799
/// 注册HttpApiClient对应的http接口
48100
/// </summary>
49101
/// <typeparam name="TInterface"></typeparam>
50-
/// <param name="config">配置</param>
51-
/// <returns></returns>
52-
public bool AddHttpApiClient<TInterface>(Action<HttpApiConfig> config) where TInterface : class, IHttpApi
102+
/// <param name="config">HttpApiConfig的配置</param>
103+
/// <param name="handlerFactory">HttpMessageHandler创建委托</param>
104+
/// <exception cref="InvalidOperationException"></exception>
105+
public void AddTypedClient<TInterface>(Action<HttpApiConfig> config, Func<HttpMessageHandler> handlerFactory) where TInterface : class, IHttpApi
53106
{
54-
return this.configs.TryAdd(typeof(TInterface), config);
107+
if (handlerFactory == null)
108+
{
109+
handlerFactory = () => new DefaultHttpClientHandler();
110+
}
111+
112+
var options = new TypedClientOption
113+
{
114+
ConfigAction = config,
115+
HandlerFactory = handlerFactory
116+
};
117+
118+
var state = this.typedClientOptions.TryAdd(typeof(TInterface), options);
119+
if (state == false)
120+
{
121+
throw new InvalidOperationException($"接口{typeof(TInterface)}不能重复注册");
122+
}
55123
}
56124

57125
/// <summary>
58126
/// 创建实现了指定接口的HttpApiClient实例
59127
/// </summary>
60128
/// <typeparam name="TInterface"></typeparam>
61129
/// <returns></returns>
62-
public TInterface CreateHttpApiClient<TInterface>() where TInterface : class, IHttpApi
130+
public TInterface CreateTypedClient<TInterface>() where TInterface : class, IHttpApi
63131
{
64132
var apiType = typeof(TInterface);
65133
var entry = this.activeEntries.GetOrAdd(apiType, this.activeEntryFactory).Value;
66134
return HttpApiClient.Create<TInterface>(entry.HttpApiConfig);
67135
}
68136

137+
/// <summary>
138+
/// 创建激活状态的Handler记录
139+
/// </summary>
140+
/// <param name="apiType">http接口类型</param>
141+
/// <returns></returns>
69142
private ActiveHandlerEntry CreateActiveEntry(Type apiType)
70143
{
71-
var handler = new LifeTimeTrackingHandler(new DefaultHttpClientHandler());
72-
var httpApiConfig = new HttpApiConfig(handler, false);
73-
74-
if (this.configs.TryGetValue(apiType, out Action<HttpApiConfig> config) == false)
144+
if (this.typedClientOptions.TryGetValue(apiType, out var option) == false)
75145
{
76146
throw new ArgumentException($"未注册的接口类型{apiType}");
77147
}
78-
else if (config != null)
148+
149+
var innder = option.HandlerFactory.Invoke();
150+
var handler = new LifeTimeTrackingHandler(innder);
151+
var httpApiConfig = new HttpApiConfig(handler, false);
152+
153+
if (option.ConfigAction != null)
79154
{
80-
config.Invoke(httpApiConfig);
155+
option.ConfigAction.Invoke(httpApiConfig);
81156
}
82157

83158
return new ActiveHandlerEntry(this)
84159
{
85160
ApiType = apiType,
86-
HttpApiConfig = httpApiConfig,
87-
InnerHandler = handler.InnerHandler
161+
Disposable = innder,
162+
HttpApiConfig = httpApiConfig
88163
};
89164
}
90165

166+
/// <summary>
167+
/// 当有记录失效时
168+
/// </summary>
169+
/// <param name="active">激活的记录</param>
91170
void IHttpApiClientFactory.OnEntryDeactivate(ActiveHandlerEntry active)
92171
{
93172
this.activeEntries.TryRemove(active.ApiType, out var _);
94173
var expired = new ExpiredHandlerEntry(active);
95174
this.expiredEntries.Enqueue(expired);
96175
}
97176

177+
/// <summary>
178+
/// 注册清理任务
179+
/// </summary>
98180
private void RegisteCleanup()
99181
{
100-
Task.Delay(this.defaultCleanupInterval)
182+
Task.Delay(this.cleanupInterval)
101183
.ConfigureAwait(false)
102184
.GetAwaiter()
103185
.OnCompleted(this.CleanupCallback);
104186
}
105187

188+
/// <summary>
189+
/// 清理任务回调
190+
/// </summary>
106191
private void CleanupCallback()
107192
{
108193
var count = this.expiredEntries.Count;
109194
for (var i = 0; i < count; i++)
110195
{
111196
this.expiredEntries.TryDequeue(out var entry);
112-
if (entry.CanDispose() == true)
197+
if (entry.CanDispose == true)
113198
{
114199
entry.Dispose();
115200
}
@@ -118,6 +203,8 @@ private void CleanupCallback()
118203
this.expiredEntries.Enqueue(entry);
119204
}
120205
}
206+
207+
121208
this.RegisteCleanup();
122209
}
123210
}

WebApiClient/Internal/HttpApiClientFactorys/ActiveHandlerEntry.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
11
using System;
2-
using System.Net.Http;
32
using System.Threading.Tasks;
43

54
namespace WebApiClient
65
{
6+
/// <summary>
7+
/// 表示激活状态的Handler记录
8+
/// </summary>
79
class ActiveHandlerEntry
810
{
11+
/// <summary>
12+
/// 获取或设置关联的http接口类型
13+
/// </summary>
914
public Type ApiType { get; set; }
1015

16+
/// <summary>
17+
/// 获取或设置http接口配置
18+
/// </summary>
1119
public HttpApiConfig HttpApiConfig { get; set; }
1220

13-
public HttpMessageHandler InnerHandler { get; set; }
21+
/// <summary>
22+
/// 获取或设置用于释放资源的对象
23+
/// </summary>
24+
public IDisposable Disposable { get; set; }
1425

26+
/// <summary>
27+
/// 激活状态的Handler记录
28+
/// </summary>
29+
/// <param name="factory">httpApiClient工厂</param>
1530
public ActiveHandlerEntry(IHttpApiClientFactory factory)
1631
{
1732
Task.Delay(factory.Lifetime)

0 commit comments

Comments
 (0)