SOFA BOLT源码解析之设计要点-连接管理

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.       *
<
SOFABolt 是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架。 为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO 的实现以及处理难以调试的网络问题,Netty 应运而生。 为了让中间件开发者能将更多的精力放在产品功能特性实现上,而不是重复地一遍遍制造通信框架的轮子,SOFABolt 应运而生。 Bolt 名字取自迪士尼动画-闪电狗,是一个基于 Netty 最佳实践的轻量、易用、高性能、易扩展的通信框架。 这些年我们在微服务与消息中间件在网络通信上解决过很多问题,积累了很多经验,并持续的进行着优化和完善,我们希望能把总结出的解决方案沉淀到 SOFABolt 这个基础组件里,让更多的使用网络通信的场景能够统一受益。 目前该产品已经运用在了蚂蚁中间件的微服务 (SOFARPC)、消息中心、分布式事务、分布式开关、以及配置中心等众多产品上。 SOFABolt的基础功能包括: 1、基础通信功能 ( remoting-core ) 基于 Netty 高效的网络 IO 与线程模型运用 连接管理 (无锁建连,定时断链,自动重连) 基础通信模型 ( oneway,sync,future,callback ) 超时控制 批量解包与批量提交处理器 心跳与 IDLE 事件处理 2、协议框架 ( protocol-skeleton ) 命令与命令处理器 编解码处理器 心跳触发器 3、私有协议定制实现 - RPC 通信协议 ( protocol-implementation ) RPC 通信协议的设计 灵活的反序列化时机控制 请求处理超时 FailFast 机制 用户请求处理器 ( UserProcessor ) 双工通信
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

任性之闲来无事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值