1 设计要点解析
1.1 连接管理
为了提高通信效率,我们需要考虑复用连接,减少TCP三次握手的次数,因此需要完善的连接管理的机制。
另外,在业务通信场景中,还需要识别一些不得不走硬负载(比如 LVS VIP)的场景,此时如果只建立单链接,可能会出现负载不均衡的问题,因此需要建立多个连接,来缓解负载不均的问题。
为此,SOFA Bolt设计了一个针对某个连接地址(IP 与 Port 唯一确定的地址)建立特定数目连接的实现,同时保存在一个连接池里。该连接池设计了一个通用的 PoolKey,不限定 Key 的类型。
需要注意的是,这里的建立连接的过程,有一个并发问题要解,比如客户端在高并发的调用建连接口时,如何保证建立的连接刚好是所设定的个数呢?为了配合 Netty 的无锁理念,我们也采用一个无锁化的建连过程来实现,利用ConcurrentHashMap 的 putIfAbsent 接口。
除此,我们的连接管理,还要具备定时断连功能,自动重连功能,自定义连接选择算法功能来适用不同的连接场景。
在SOFA Bolt中,连接管理相关类的类图如下:
1.1.1 连接定义-Connection
由于SOFA Bolt基于Netty实现,所以针对io.netty.channel.Channel抽象出Connection类,除了提供Channel所具备的基本功能外,还根据SOFA Bolt私有通信框架的需求,增加了一些额外的控制功能,以满足一些特殊场景的需求。
首先,看看Connection类源码:
1. public class Connection {
2.
3. ……略
4.
5. private Channel channel;
6.
7. private final ConcurrentHashMap<Integer,InvokeFuture> invokeFutureMap = newConcurrentHashMap<Integer, InvokeFuture>(4);
8.
9. /** Attribute key for connection */
10. public static finalAttributeKey<Connection> CONNECTION =AttributeKey.valueOf("connection");
11.
12. /** Attribute key for heartbeat count */
13. public static finalAttributeKey<Integer> HEARTBEAT_COUNT =AttributeKey.valueOf("heartbeatCount");
14.
15. /** Attribute key for heartbeat switch foreach connection */
16. public static finalAttributeKey<Boolean> HEARTBEAT_SWITCH = AttributeKey.valueOf("heartbeatSwitch");
17.
18. /** Attribute key for protocol */
19. public static finalAttributeKey<ProtocolCode>
20. PROTOCOL =AttributeKey.valueOf("protocol");
21. private ProtocolCode protocolCode;
22.
23. /** Attribute key for version */
24. public static finalAttributeKey<Byte>
25. VERSION =AttributeKey.valueOf("version");
26. private byte version = RpcProtocolV2.PROTOCOL_VERSION_1;
27.
28. private Url url;
29.
30. private finalConcurrentHashMap<Integer/* id */, String/* poolKey */>
31. id2PoolKey = new ConcurrentHashMap<Integer,String>(256);
32.
33. private Set<String> poolKeys = newConcurrentHashSet<String>();
34.
35. private AtomicBoolean closed = new AtomicBoolean(false);
36.
37. private final ConcurrentHashMap<String/*attr key*/, Object /*attr value*/>
38. attributes= new ConcurrentHashMap<String, Object>();
39.
40. /** the reference count used for thisconnection. If equals 2, it means this connection has been referenced 2 times*/
41. private final AtomicIntegerreferenceCount = new AtomicInteger();
42.
43. /** no reference of the current connection*/
44. private static final int NO_REFERENCE = 0;
45.
46. /**
47. * Constructor
48. *
49. * @param channel
50. */
51. public Connection(Channel channel) {
52. this.channel = channel;
53. this.channel.attr(CONNECTION).set(this);
54. }
55.
56. /**
57. * Constructor
58. *
59. * @param channel
60. * @param url
61. */
62.
63. public Connection(Channel channel, Urlurl) {
64. this(channel);
65. this.url = url;
66. this.poolKeys.add(url.getUniqueKey());
67. }
68.
69. /**
70. * Constructor
71. *
72. * @param channel
73. * @param protocolCode
74. * @param url
75. */
76. public Connection(Channel channel,ProtocolCode protocolCode, Url url) {
77. this(channel, url);
78. this.protocolCode = protocolCode;
79. this.init();
80. }
81.
82. /**
83. * Constructor
84. *
85. * @param channel
86. * @param protocolCode
87. * @param url
88. */
89. public Connection(Channel channel,ProtocolCode protocolCode, byte version, Url url) {
90. this(channel, url);
91. this.protocolCode = protocolCode;
92. this.version = version;
93. this.init();
94. }
95.
96. /**
97. * Initialization.
98. */
99. private void init() {
100. this.channel.attr(HEARTBEAT_COUNT).set(newInteger(0));
101. this.channel.attr(PROTOCOL).set(this.protocolCode);
102. this.channel.attr(VERSION).set(this.version);
103. this.channel.attr(HEARTBEAT_SWITCH).set(true);
104. }
105.
106. /**
107. * to check whether the connection is fineto use
108. *
109. * @return
110. */
111. public boolean isFine() {
112. return this.channel != null &&this.channel.isActive();
113. }
114.
115. /**
116. * increase the reference count
117. */
118. public void increaseRef() {
119. this.referenceCount.getAndIncrement();
120. }
121.
122. /**
123. * decrease the reference count
124. */
125. public void decreaseRef() {
126. this.referenceCount.getAndDecrement();
127. }
128.
129. /**
130. * to check whether the reference count is0
131. *
132. * @return
133. */
134. public boolean noRef() {
135. return this.referenceCount.get() ==NO_REFERENCE;
136. }
137.
138. /**
139. * Get the address of the remote peer.
140. *
141. * @return
142. */
143. public InetSocketAddress getRemoteAddress(){
144. return (InetSocketAddress)this.channel.remoteAddress();
145. }
146.
147. /**
148. * Get the remote IP.
149. *
150. * @return
151. */
152. public String getRemoteIP() {
153. returnRemotingUtil.parseRemoteIP(this.channel);
154. }
155.
156. /**
157. * Get the remote port.
158. *
159. * @return
160. */
161. public int getRemotePort() {
162. returnRemotingUtil.parseRemotePort(this.channel);
163. }
164.
165. /**
166. * Get the address of the local peer.
167. *
168. * @return
169. */
170. public InetSocketAddress getLocalAddress(){
171. return (InetSocketAddress)this.channel.localAddress();
172. }
173.
174. /**
175. * Get the local IP.
176. *
177. * @return
178. */
179. public String getLocalIP() {
180. returnRemotingUtil.parseLocalIP(this.channel);
181. }
182.
183. /**
184. * Get the local port.
185. *
186. * @return
187. */
188. public int getLocalPort() {
189. returnRemotingUtil.parseLocalPort(this.channel);
190. }
191.
192. /**
193. * Get the netty channel of the connection.
194. *
195. * @return
196. */
197. public Channel getChannel() {
198. return this.channel;
199. }
200.
201. /**
202. * Get the InvokeFuture with invokeId ofid.
203. *
204. * @param id
205. * @return
206. */
207. public InvokeFuture getInvokeFuture(int id){
208. return this.invokeFutureMap.get(id);
209. }
210.
211. /**
212. * Add an InvokeFuture
213. *
214. * @param future
215. * @return
216. */
217. public InvokeFutureaddInvokeFuture(InvokeFuture future) {
218. returnthis.invokeFutureMap.putIfAbsent(future.invokeId(), future);
219. }
220.
221. /**
222. * Remove InvokeFuture who's invokeId is id
223. *
224. * @param id
225. * @return
226. */
227. public InvokeFuture removeInvokeFuture(intid) {
228. return this.invokeFutureMap.remove(id);
229. }
230.
231. /**
232. * Do something when closing.
233. */
234. public void onClose() {
235. Iterator<Entry<Integer,InvokeFuture>> iter = invokeFutureMap.entrySet().iterator();
236. while (iter.hasNext()) {
237. Entry<Integer, InvokeFuture>entry = iter.next();
238. iter.remove();
239. InvokeFuture future =entry.getValue();
240. if (future != null) {
241. future.putResponse(future.createConnectionClosedResponse(this.getRemoteAddress()));
242. future.cancelTimeout();
243. future.tryAsyncExecuteInvokeCallbackAbnormally();
244. }
245. }
246. }
247.
248. /**
249. * Close the connection.
250. */
251. public void close() {
252. if (closed.compareAndSet(false, true)){
253. try {
254. if (this.getChannel() != null){
255. this.getChannel().close().addListener(newChannelFutureListener() {
256.
257. @Override
258. public voidoperationComplete(ChannelFuture future) throws Exception {
259. if(logger.isInfoEnabled()) {
260. logger
261. .info(
262. "Closethe connection to remote address={}, result={}, cause={}",
263. RemotingUtil.parseRemoteAddress(Connection.this
264. .getChannel()),future.isSuccess(), future.cause());
265. }
266. }
267.
268. });
269. }
270. } catch (Exception e) {
271. logger.warn("Exceptioncaught when closing connection {}",
272. RemotingUtil.parseRemoteAddress(Connection.this.getChannel()),e);
273. }
274. }
275. }
276.
277. /**
278. * Whether invokeFutures is completed
279. *
280. */
281. public boolean isInvokeFutureMapFinish() {
282. return invokeFutureMap.isEmpty();
283. }
284.
285. /**
286. * add a pool key to list
287. *
288. * @param poolKey
289. */
290. public void addPoolKey(String poolKey) {
291. poolKeys.add(poolKey);
292. }
293.
294. /**
295. * get all pool keys
296. */
297. public Set<String> getPoolKeys() {
298. return newHashSet<String>(poolKeys);
299. }
300.
301. /**
302. * remove pool key
303. *
304. * @param poolKey
305. */
306. public void removePoolKey(String poolKey) {
307. poolKeys.remove(poolKey);
308. }
309.
310. /**
311. * Getter method for property<tt>url</tt>.
312. *
<