SOFA BOLT源码解析之设计要点-通信模型

windows上的5种网络通信模型示例代码 一些好设计的经验: linux网络: 高性能网络编程IO复用和Epoll高效率之处-遍历的集合更小空间换时间/水平触发和边缘触发主动返回。 反应堆的设计模式-避免C风格的一个应用逻辑都需要处理多个对象而是用OO设计模式方式分离。 windows网络: select模型,WSAAsyncSelect模型,WSAEventSelect模型,重叠Overlapped IO模 阅读详情

1 设计要点解析

1.1  通信模型

        SOFA Bolt当前支持四种通信模型:

        ●  oneway:单向调用。单向调用不会阻塞请求线程,不关心响应。但是,使用时需要注意控制调用频率和节奏,防止压垮接收方;

        ●  sync:同步调用。同步调用会阻塞请求线程,待响应返回后才能进行下一个请求。这是最常用的一种通信模型;

        ●  future:future调用。Future调用在调用过程中不会阻塞线程,但在获取结果的过程中会阻塞线程;

        ●  callback:回调。回调是真正的异步调用,永远不会阻塞线程,在异步线程里处理响应结果。

        在SOFA Bolt中,实现上述四种通信模型的相关类的类图如下:

        BaseRemoting基于Netty实现了上述四种通信模型,其源码如下:

1. public abstract class BaseRemoting {
2.         ……略
3.  
4.     /**
5.      * Synchronous invocation
6.      ……略
7.      */
8.     protected RemotingCommand invokeSync(final Connection conn, finalRemotingCommand request, final int timeoutMillis) throwsRemotingException,                                                               InterruptedException {
9.         ……略
10.    }
11. 
12.    /**
13.     * Invocation with callback.
14.     ……略
15.     */
16.    protected void invokeWithCallback(final Connection conn, finalRemotingCommand request, final InvokeCallback invokeCallback, final inttimeoutMillis) {
17.     ……略
18.    }
19. 
20.    /**
21.     * Invocation with futurereturned.
22.     ……略
23.     */
24.    protected InvokeFuture invokeWithFuture(final Connection conn, finalRemotingCommand request,  final inttimeoutMillis) {
25.    ……略
26. 
27.    };
28. 
29.    /**
30.     * Oneway invocation.
31.     ……略
32.     */
33.    protected void oneway(final Connection conn, finalRemotingCommand request) {
34.                ……略
35.    };
36.……略
37.}

        通过源码可以看出,有两个类在实现上述四种通信模型中发挥了非常重要的作用:

        1. InvokeFuture接口

        一提到Future,大家一定会想到java.util.concurrent.Future。

        Future为我们提供了一个异步编程的方式。Future表现为一个异步计算的结果,可以通过Future查看计算是否完成、等待计算完成、获取计算结果。

        InvokeFuture虽然未实现Future接口,但遵循了Future的设计原理,基于网络通信的特征,为网络通信框架定义了一套同Future接口一样的功能。

1.  public interface InvokeFuture {
2.      /**
3.       * Wait response with timeout.
4.       ……略
5.       */
6.      public RemotingCommand waitResponse(final long timeoutMillis) throws InterruptedException;
7.   
8.      /**
9.       * Wait response with unlimit timeout
10.      ……略
11.      */
12.     public RemotingCommand waitResponse() throws InterruptedException;
13.  
14.     ……略
15.  
16.     /**
17.      * Put the response to the future.
18.      ……略
19.      */
20.     public void putResponse(final RemotingCommand response);
21.  
22.      ……略
23.  
24.     /**
25.      * Execute the callback.
26.      */
27.     public void executeInvokeCallback();
28.  
29.     /**
30.      * Asynchronous execute the callbackabnormally.
31.      */
32.     public void tryAsyncExecuteInvokeCallbackAbnormally();
33.  
34.      ……略
35.  
36.     /**
37.      * Get the application callback of thefuture.
38.      *
39.      * @return
40.      */
41.     public InvokeCallback getInvokeCallback();
42.  
43.     /**
44.      * Add timeout for the future.
45.      *
46.      * @param timeout
47.      */
48.     public void addTimeout(Timeout timeout);
49.  
50.     /**
51.      * Cancel the timeout.
52.      */
53.     public void cancelTimeout();
54.  
55.     /**
56.      * Whether the future is done.
57.      *
58.      * @return
59.      */
60.     public boolean isDone();
61.  
62.      ……略
63.  
64.     /**
65.      * set invoke context
66.      * @param invokeContext
67.      */
68.     public void setInvokeContext(InvokeContext invokeContext);
69.  
70.     /**
71.      * get invoke context
72.      * @return
73.      */
74.     public InvokeContext getInvokeContext();
75. }

        在InvokeFuture中,主要的方法如下:

        ●  waitResponse():等待服务端响应,直到接收到响应,或执行线程被中断;

        ●  waitResponse(final longtimeoutMillis):等待服务端响应。如果在超时时间内没有返回响应,则抛出异常;

        ●  putResponse(final RemotingCommandresponse):设置服务端响应结果;

        ●  isDone():检查此处调用是否完成;

        ●  executeInvokeCallback():执行回调;

        ●  tryAsyncExecuteInvokeCallbackAbnormally():异步执行非正常响应的回调,如调用失败、调用超时等;

        ●  addTimeout(Timeout timeout):设置超时时间;

        ●  cancelTimeout():取消超时设置;

        DefaultInvokeFuture实现了InvokeFuture接口,其主要源码如下:

1.  public class DefaultInvokeFutureimplements InvokeFuture {
2.   
3.       ……略
4.   
5.      private final CountDownLatch     countDownLatch          = new CountDownLatch(1);
6.      
7.      private final AtomicBoolean      executeCallbackOnlyOnce = newAtomicBoolean(false);
8.       ……略
9.   
10.     @Override
11.     public ResponseCommand waitResponse(long timeoutMillis) throwsInterruptedException {
12.         this.countDownLatch.await(timeoutMillis,TimeUnit.MILLISECONDS);
13.         return this.responseCommand;
14.     }
15.  
16.     @Override
17.     public ResponseCommand
waitResponse() throws InterruptedException {
18.         this.countDownLatch.await();
19.         return this.responseCommand;
20.     }
21.  
22.      ……略
23.     @Override
24.     public void putResponse(RemotingCommand response) {
25.         this.responseCommand =(ResponseCommand) response;
26.         this.countDownLatch.countDown();
27.     }
28.  
29.     @Override
30.     public boolean isDone() {
31.         return this.countDownLatch.getCount()<= 0;
32.     }
33.      ……略
34.     @Override
35.     public void executeInvokeCallback() {
36.         if (callbackListener != null) {
37.             if(this.executeCallbackOnlyOnce.compareAndSet(false, true)) {
38.                 callbackListener.onResponse(this);
39.             }
40.         }
41.     }
42.      ……略
43.     @Override
44.     public void addTimeout(Timeout timeout) {
45.         this.timeout = timeout;
46.     }
47.  
48.     @Override
49.     public void cancelTimeout() {
50.         if (this.timeout != null) {
51.             this.timeout.cancel();
52.         }
53.     }
54.      ……略
55.     @Override
56.     public void tryAsyncExecuteInvokeCallbackAbnormally() {
57.         try {
58.             Protocol protocol =ProtocolManager.getProtocol(ProtocolCode.fromBytes(this.protocol));
59.             if (null != protocol) {
60.                 CommandHandler commandHandler =protocol.getCommandHandler();
61.                 if (null != commandHandler) {
62.                     ExecutorService executor =commandHandler.getDefaultExecutor();
63.                     if (null != executor)
64.                         executor.execute(newRunnable() {
65.                             @Override
66.                             public void run() {
67.                                 ClassLoaderoldClassLoader = null;
68.                                 try {
69.                                     if(DefaultInvokeFuture.this.getAppClassLoader() != null) {
70.                                         oldClassLoader= Thread.currentThread()
71.                                             .getContextClassLoader();
72.                                         Thread.currentThread().setContextClassLoader(
73.                                             DefaultInvokeFuture.this.getAppClassLoader());
74.                                     }
75.                                     DefaultInvokeFuture.this.executeInvokeCallback();
76.                                 } finally {
77.                                     if (null !=oldClassLoader) {
78.                                         Thread.currentThread()
79.                                             .setContextClassLoader(oldClassLoader);
80.                                     }
81.                                 }
82.                             }
83.                         });
84.                 } else {
85.                     logger.error("Executornull in commandHandler of protocolCode [{}].",
86.                         this.protocol);
87.                 }
88.             } else {
89.                 logger.error("protocolCode[{}] not registered!", this.protocol);
90.             }
91.         } catch (Exception e) {
92.             logger.error("Exception caughtwhen executing invoke callback abnormally.", e);
93.         }
94.     }
95.  
96. }

        在DefaultInvokeFuture中,通过CountDownLatch实现了执行线程的挂起(阻塞)和恢复(解除阻塞)。CountDownLatch是基于AQS(AbstractQueuedSynchronizer)实现的同步器,它根据同步状态的值将对调用线程进行阻塞和解除阻塞的操作,是一种无锁实现。DefaultInvokeFuture主要实现逻辑如下:

        ● 创建初始值为1的CountDownLatch对象countDownLatch,作为一个简单的开关;

        ● 创建初始值为false的AtomicBoolean对象executeCallbackOnlyOnce,作为回调只执行一次的开关;

        ● 在waitResponse()方法中,调用CountDownLatch的await方法,阻塞当前线程,等待服务端响应,直到获取到服务端响应。如果在等待过程中线程被中断,则抛出InterruptedException异常;

        ● 在waitResponse(longtimeoutMillis)方法中,调用CountDownLatch的await(long timeout, TimeUnit unit)方法,阻塞当前线程,等待服务端响应。如果在超时时间内获取到服务端响应,则返回true;否则,当等待时间超过超时时间,则返回false。如果在等待过程中线程被中断,则抛出InterruptedException异常;

       ● 在putResponse(RemotingCommand response)方法中,首先设置服务端响应结果,然后调用CountDownLatch的countDown()方法,恢复等待响应结果的线程;

        ● 在executeInvokeCallback()方法中,通过原子变量executeCallbackOnlyOnce的CAS操作,保证只有一个线程可以执行回调操作,并且只执行一次。如果compareAndSet(false, true)操作成功,则表示当前线程获取到执行权,则调用InvokeCallbackListener的onResponse方法,执行设置好的回调方法。

        ● 在tryAsyncExecuteInvokeCallbackAbnormally()方法中,如果当前RpcCommandHandler设置了执行线程池defaultExecutor,则在defaultExecutor线程池的某个线程中执行DefaultInvokeF的executeInvokeCallback()方法。executeInvokeCallback()方法的执行过程参考上一步的说明。

        ●  对于其它方法,在此不详述了。

        2.  Timeout与HashedWheelTimer

        SOFABolt采用 Netty 的超时机制:HashedWheelTimer。

        HashedWheelTimer基本原理如下所述:

        ●  首先,在发起调用前,新增一个超时任务timeoutTask到 MpscQueue(Netty 实现的一种高效的无锁队列);

        ●  然后,在循环里,不断的遍历MpscQueue队列里的超时任务(每次最多10万)。针对每个任务,根据其设置的超时时间,计算该任务所属于的 bucket 位置与剩余轮数 remainingRounds ,然后加入到对应bucket的链表结构里。随着tick++的进行,时间在不断的增长,每tick 8次,就是1个时间轮round;

        ●  最后,当对应超时任务的remainingRounds减到0时,就触发这个超时任务,执行其 run() 方法,做超时逻辑处理。

        接下来,我们看看如何基于Netty实现四种通信模型:

1.1.1  单向调用-oneway

        单向调用源码如下:

1.      /**
2.       * Oneway invocation.
3.       ……略
4.       */
5.      protected void oneway(final Connection conn, finalRemotingCommand request) {
6.          try {
7.              conn.getChannel().writeAndFlush(request).addListener(new ChannelFutureListener(){
8.   
9.                  @Override
10.                 public void operationComplete(ChannelFuture f) throws Exception {
11.                     if (!f.isSuccess()) {
12.                         logger.error("Invokesend failed. The address is {}",
13.                             RemotingUtil.parseRemoteAddress(conn.getChannel()),f.cause());
14.                     }
15.                 }
16.  
17.             });
18.         } catch (Exception e) {
19.             if (null == conn) {
20.                 logger.error("Conn isnull");
21.             } else {
22.                 logger.error("Exceptioncaught when sending invocation. The address is {}",
23.                     RemotingUtil.parseRemoteAddress(conn.getChannel()),e);
24.             }
25.         }
26.     };

        对于单向调用,实现非常简单,主要实现逻辑如下:

        1.  调用Netty的Channel接口的writeAndFlush方法,直接发送客户端请求对象request;

        2.  增加ChannelFutureListener接口的匿名实现类,监听客户端请求发送操作。当发送操作完成以后,判断发送操作是否成功。如果未成功,则记录错误日志。如果成功,则忽略,表示调用完成。

1.1.2  同步调用-sync

        同步调用源码如下:

1.  /**
2.       * Synchronous invocation
3.       ……略
4.       */
5.      protected RemotingCommand invokeSync(final Connection conn, finalRemotingCommand request, final int timeoutMillis) throwsRemotingException,                                                               InterruptedException {
6.          final InvokeFuture future = createInvokeFuture(request, request.getInvokeContext());
7.          conn.addInvokeFuture(future);
8.          try {
9.              conn.getChannel().writeAndFlush(request).addListener(newChannelFutureListener() {
10.  
11.                 @Override
12.                 public void operationComplete(ChannelFuture f) throws Exception {
13.                     if (!f.isSuccess()) {
14.                         conn.removeInvokeFuture(request.getId());
15.                         future.putResponse(commandFactory.createSendFailedResponse(
16.                             conn.getRemoteAddress(),f.cause()));
17.                         logger.error("Invokesend failed, id={}", request.getId(), f.cause());
18.                     }
19.                 }
20.  
21.             });
22.         } catch (Exception e) {
23.             conn.removeInvokeFuture(request.getId());
24.             if (future != null) {
25.                 future.putResponse(commandFactory.createSendFailedResponse(conn.getRemoteAddress(),
26.                     e));
27.             }
28.             logger.error("Exception caughtwhen sending invocation, id={}", request.getId(), e);
29.         }
30.         RemotingCommand response = future.waitResponse(timeoutMillis);
31.  
32.         if (response == null) {
33.             conn.removeInvokeFuture(request.getId());
34.             response =this.commandFactory.createTimeoutResponse(conn.getRemoteAddress());
35.             logger.warn("Wait response,request id={} timeout!", request.getId());
36.         }
37.  
38.         return response;
39.     }

        对于同步调用,主要实现逻辑如下:

        1.  根据请求对象request和请求调用上下文InvokeContext,创建DefaultInvokeFuture对象future,并设置到Connection的invokeFuture属性;

        2.  调用Netty的Channel接口的writeAndFlush方法,直接发送客户端请求对象request;

        3.  增加ChannelFutureListener接口的匿名实现类,监听客户端请求发送操作。当发送操作完成以后,判断发送操作是否成功。如果未成功,则首先从当前Connection的invokeFutureMap中,根据请求ID删除对应的future,此操作是线程安全的(基于ConcurrentHashmap实现),保证同一时间只有一个线程操作成功。如果删除成功,则调用future的putResponse方法,设置调用失败响应结果,唤醒等待调用结果的线程,即第4步中阻塞的线程;

        4.  调用future的waitResponse方法,阻塞当前线程,在超时时间内等待服务端响应结果;

        5.  如果在调用过程中发生异常,则首先从当前Connection中根据请求ID删除对应的future,然后调用future的putResponse方法,设置调用失败响应结果,唤醒等待调用结果的线程,即第4步中阻塞的线程;

        6.  如果在超时时间内没有获得服务端响应结果,则调用future的putResponse方法,设置调用超时响应结果,唤醒等待调用结果的线程,即第4步中阻塞的线程;

        7.  如果在超时时间内获得服务端响应结果,则直接返回。

1.1.3  Future调用-future

        Future调用源码如下:

1.  /**
2.       * Invocation with future returned.
3.       ……略
4.       */
5.      protected InvokeFuture invokeWithFuture(final Connection conn, finalRemotingCommand request,
6.                                              final int timeoutMillis) {
7.   
8.          final InvokeFuture future = createInvokeFuture(request, request.getInvokeContext());
9.          conn.addInvokeFuture(future);
10.         try {
11.             //add timeout
12.             Timeout timeout =TimerHolder.getTimer().newTimeout(new TimerTask() {
13.                 @Override
14.                 public void run(Timeouttimeout) throws Exception {
15.                     InvokeFuture future =conn.removeInvokeFuture(request.getId());
16.                     if (future != null) {
17.                         future.putResponse(commandFactory.createTimeoutResponse(conn
18.                             .getRemoteAddress()));
19.                     }
20.                 }
21.  
22.             }, timeoutMillis,TimeUnit.MILLISECONDS);
23.             future.addTimeout(timeout);
24.  
25.             conn.getChannel().writeAndFlush(request).addListener(newChannelFutureListener() {
26.  
27.                 @Override
28.                 public voidoperationComplete(ChannelFuture cf) throws Exception {
29.                     if (!cf.isSuccess()) {
30.                         InvokeFuture f =conn.removeInvokeFuture(request.getId());
31.                         if (f != null) {
32.                             f.cancelTimeout();
33.                             f.putResponse(commandFactory.createSendFailedResponse(
34.                                 conn.getRemoteAddress(),cf.cause()));
35.                         }
36.                         logger.error("Invokesend failed. The address is {}",
37.                             RemotingUtil.parseRemoteAddress(conn.getChannel()),cf.cause());
38.                     }
39.                 }
40.  
41.             });
42.         } catch (Exception e) {
43.             InvokeFuture f =conn.removeInvokeFuture(request.getId());
44.             if (f != null) {
45.                 f.cancelTimeout();
46.                 f.putResponse(commandFactory.createSendFailedResponse(conn.getRemoteAddress(),e));
47.             }
48.             logger.error("Exception caughtwhen sending invocation. The address is {}",
49.                 RemotingUtil.parseRemoteAddress(conn.getChannel()),e);
50.         }
51.         return future;
52.     };

        对于Future调用,主要实现逻辑如下:

        1.  根据请求对象request和请求调用上下文InvokeContext,创建DefaultInvokeFuture对象future,并设置到Connection的invokeFuture属性;

        2.  根据超时时间,创建io.netty.util.HashedWheelTimer.HashedWheelTimer类型定时器,并设置定时任务。定时任务主要完成以下工作:首先从当前Connection中根据请求ID删除对应的future,然后调用future的putResponse方法,设置调用超时响应结果,唤醒等待调用结果的线程;

        3.  设置future的timeout;

        4.  调用Netty的Channel接口的writeAndFlush方法,直接发送客户端请求对象request;

        5.  增加ChannelFutureListener接口的匿名实现类,监听客户端请求发送操作。当发送操作完成以后,判断发送操作是否成功。如果未成功,则首先从当前Connection中根据请求ID删除对应的future,然后调用future的putResponse方法,设置调用失败响应结果,唤醒等待调用结果的线程;

        6.  如果在调用过程中发生异常,则首先从当前Connection中根据请求ID删除对应的future,然后调用future的putResponse方法,设置调用失败响应结果,唤醒等待调用结果的线程;

        7.  如果在调用过程中没有发生异常,则直接返回future。

1.1.4  回调-callback

        Callback调用源码如下:

1.  /**
2.       * Invocation with callback.
3.       ……略
4.       */
5.      protected void invokeWithCallback(final Connection conn, finalRemotingCommand request,
6.                                        finalInvokeCallback invokeCallback, final int timeoutMillis) {
7.          final InvokeFuture future = createInvokeFuture(conn, request, request.getInvokeContext(),
8.              invokeCallback);
9.          conn.addInvokeFuture(future);
10.  
11.         try {
12.             //add timeout
13.             Timeout timeout =TimerHolder.getTimer().newTimeout(new TimerTask() {
14.                 @Override
15.                 public void run(Timeouttimeout) throws Exception {
16.                     InvokeFuture future =conn.removeInvokeFuture(request.getId());
17.                     if (future != null) {
18.                         future.putResponse(commandFactory.createTimeoutResponse(conn
19.                             .getRemoteAddress()));
20.                         future.tryAsyncExecuteInvokeCallbackAbnormally();
21.                     }
22.                 }
23.  
24.             }, timeoutMillis,TimeUnit.MILLISECONDS);
25.             future.addTimeout(timeout);
26.             conn.getChannel().writeAndFlush(request).addListener(new ChannelFutureListener() {
27.  
28.                 @Override
29.                 public voidoperationComplete(ChannelFuture cf) throws Exception {
30.                     if (!cf.isSuccess()) {
31.                         InvokeFuture f =conn.removeInvokeFuture(request.getId());
32.                         if (f != null) {
33.                             f.cancelTimeout();
34.                             f.putResponse(commandFactory.createSendFailedResponse(
35.                                 conn.getRemoteAddress(),cf.cause()));
36.                             f.tryAsyncExecuteInvokeCallbackAbnormally();
37.                         }
38.                         logger.error("Invokesend failed. The address is {}",
39.                             RemotingUtil.parseRemoteAddress(conn.getChannel()),cf.cause());
40.                     }
41.                 }
42.  
43.             });
44.         } catch (Exception e) {
45.             InvokeFuture f =conn.removeInvokeFuture(request.getId());
46.             if (f != null) {
47.                 f.cancelTimeout();
48.                 f.putResponse(commandFactory.createSendFailedResponse(conn.getRemoteAddress(),e));
49.                 f.tryAsyncExecuteInvokeCallbackAbnormally();
50.             }
51.             logger.error("Exception caughtwhen sending invocation. The address is {}",
52.                 RemotingUtil.parseRemoteAddress(conn.getChannel()),e);
53.         }
54.     }

        对于Callback回调,主要实现逻辑如下:

        1.  根据请求对象request和请求调用上下文InvokeContext,创建DefaultInvokeFuture对象future,并设置到Connection的invokeFuture属性;

        2.  根据超时时间,创建io.netty.util.HashedWheelTimer.HashedWheelTimer类型定时器,并设置定时任务。定时任务主要完成以下工作:首先,从当前Connection中根据请求ID删除对应的future;然后,调用future的putResponse方法,设置调用超时响应结果;最后,调用设置的回调方法,由回调方法处理响应结果;

        3.  设置future的timeout;

        4.  调用Netty的Channel接口的writeAndFlush方法,直接发送客户端请求对象request;

        5.  增加ChannelFutureListener接口的匿名实现类,监听客户端请求发送操作。当发送操作完成以后,判断发送操作是否成功。如果未成功,则首先,从当前Connection中根据请求ID删除对应的future;然后,调用future的putResponse方法,设置调用失败响应结果;最后,调用设置的回调方法,由回调方法处理响应结果;

        6.  如果在调用过程中发生异常,则首先从当前Connection中根据请求ID删除对应的future;然后,调用future的putResponse方法,设置调用失败响应结果;最后,调用设置的回调方法,由回调方法处理响应结果;

        7.  如果在调用过程中没有发生异常,则直接返回。

        在sync、futrue和callback三种通信模型的调用过程中,有两个操作包含一些隐含的意思,对于多线程同步和整个调用过程的处理非常关键:

        ●  InvokeFuture future =conn.removeInvokeFuture(request.getId()),即从当前Connection的invokeFutureMap中,根据请求ID删除对应的future,此操作是线程安全的(基于ConcurrentHashmap实现),保证同一时间只有一个线程操作成功。如果删除成功,返回删除的InvokeFuture。否则,返回null。然后通过判断future是否为null,来决定当前线程是否具有设置响应结果或执行回调方法的权利;

        ●  在哪里设置正常的服务端响应结果。在SOFA Bolt中,是在com.alipay.remoting.rpc.protocol.RpcResponseProcessor的doProcess方法中设置。RpcResponseProcessor类主要用来处理服务端响应,通过Connection获取future,然后调用future的putResponse方法,设置正常服务端响应结果,唤醒等待调用结果的线程或调用设置好的回调方法,处理响应结果。

        了解完BaseRemoting抽象类以后,我们在简要看看其子类:

        ●  RpcRemoting抽象类,继承了BaseRemoting抽象类,该类根据RPC需求,增加了地址解析功能和连接管理功能。

        ●  RpcClientRemoting类,继承了RpcRemoting抽象类,为客户端提供了四种通信模型的API,供客户端实际调用。

        ●  RpcServerRemoting类,继承了RpcRemoting抽象类,为服务端提供了四种通信模型的API,供服务端实际调用。此API在全双工通信过程中才被服务端使用。


本系列文章目录:

SOFA BOLT源码解析之概述

SOFA BOLT源码解析之设计要点-网络IO

SOFA BOLT源码解析之设计要点-线程模型

SOFA BOLT源码解析之设计要点-通信模型

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

SOFA BOLT源码解析之设计要点-通信协议


SOFA-Bolt网络通信框架-其他 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 ) 双工通信 立即下载

相关推荐

基于TI C2000的CCS安装与工程创建完整示例

手把手演示基于TI C2000系列的CCS安装过程及首个工程创建步骤,涵盖环境配置与常见问题处理,助力快速上手嵌入式开发,ccs安装和工程搭建不再难。

weixin_42476987的博客 970

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

1. 设计要点解析1.1  连接管理        为了提高通信效率,我们需要考虑复用连接,减少TCP三次握手的次数,因此需要完善的连接管理的机制。        另外,在业务通信场景中,还需要识别一些不得不走硬负载(比如 LVS VIP)的场景,此时如果只建立单链接,可能会出现负载不均衡的问题,因此需要建立多个连接,来缓解负载不均的问题。        为此,SOFA Bolt设计了一个针对某个...

闲来无事 1962

RK3566添加ffpeg【Buildroot系统】

有很多人都发现了RK3566系列的芯片是不可以直接使用ffmpeg官网源码的!会报错: Not a video capture device. /dev/video0: No such device 接下来我们就来解决这个问题!!

honmy18的博客 4070

蚂蚁通讯框架SOFABolt之私有通讯协议设计

前言 SOFABolt 是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架。 为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO 的实现以及处理难以调试的网络问题,Netty 应运而生。 为了让中间件开发者能将更多的精力放在产品功能特性实现上,而不是重复地一遍遍制造通信框架的轮子,SOFABolt 应运而生。 Bolt 名...

weixin_34262482的博客 639

ENVI 5.6 + Sarscape 5.2 保姆级教程:哨兵一号数据处理全流程(含DEM自动下载)

本文提供ENVI 5.6与Sarscape 5.2处理哨兵一号数据的完整实战教程。详细介绍了从数据导入、研究区裁剪、DEM自动下载与集成,到核心的配准、滤波及地理编码全流程,最终生成可用于定量分析的地理编码后向散射系数图。重点演示了DEM数据在SAR处理中的关键作用与自动获取方法,帮助用户快速掌握SAR数据基础处理技能。

coffee的博客 682

FT601Q芯片深度解析:从USB3.0引脚定义到FPGA FIFO协议实战选型

本文深入解析FT601Q芯片在USB3.0接口设计与FPGA协议栈开发中的应用,详细介绍了USB3.0引脚定义、FT601Q芯片架构、245同步FIFO模式及硬件设计要点,帮助工程师实现高速数据传输系统。

weixin_30607659的博客 447

SOFA BOLT源码解析之设计要点-线程模型

1.设计要点解析1.1  线程模型        此部分内容主要介绍蚂蚁为什么选择Netty4作为基础网络编程框架,来源于蚂蚁技术团队发布的一篇文章:        文章名称为:蚂蚁通信框架实践;        链接地址为:https://mp.weixin.qq.com/s/JRsbK1Un2av9GKmJ8DK7IQ?        此处引用该文章的内容,主要是想让大家更好的理解SOFA Bo...

闲来无事 1399

Synopsys AXI VIP 从环境搭建到首个验证场景运行

本文详细介绍了Synopsys AXI VIP从环境搭建到首个验证场景运行的全过程,包括VIP安装、示例工程生成、验证环境解析、仿真调试技巧及进阶优化。特别针对AXI协议验证中的常见问题提供了实用解决方案,帮助工程师快速掌握这一SoC验证必备工具。

weixin_29185493的博客 343

sofa-bolt源码解读之处理器

官方文档请参考:https://www.sofastack.tech/projects/sofa-bolt/sofa-bolt-handbook/ 两类处理器 sofa-bolt提供两种类型的处理器:用户请求处理器和连接事件处理器。 继承关系类图如下:

G探险者的博客 850

动网格-网格重构之铺层(三)

运动区域可以是边界区域 (Boundary Zone),也可以是单元区域 ( Cell Zone)。指定了体网格再生方法后,FLUENT自动地根据运动区域(dynamic zones)的运动来调整内部节点以再生体网格。必须保留至少一层网格。(4)如果边面的法向和运动方向偏角较大,生成的网格扭斜度会比较大铺层算法不适用于边界形状变化较大的情况。(1)体网格再生方法的选择(Smoothing,Layering,Remeshing);(2)适用网格种类:四边形、六面体、三棱柱(网格的分布要服从一定的规则)。

weixin_44386973的博客 2095

【异常】Cursor 编辑器 “Too many computers“ 报错排查与完整解决方案

该报错通常在启动Cursor、调用AI对话/代码补全核心功能时触发,会直接阻断编辑器AI能力的正常使用。

奶爸的编程之路,也就一周冷个三天 460

Timer定时器使用

import java.util.TimerTask; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hi.SpringContextHolder; import org.nstcrm.customer.model.Company; import or

交通部PC167 925

复合材料成型仿真案例大全|覆盖模压 / RTM / 固化 / SMC,实操步骤直接抄

本文整理了一套完整的PAM-COMPOSITE复合材料成型仿真案例合集,涵盖模压、RTM、固化变形、SMC四大核心工艺,包含6个实操案例和1个全流程衔接案例。每个案例都配有详细的操作步骤、核心要点和缺陷排查方法,适用于不同水平的工程师和研究人员。案例内容源自实际项目,可直接复用,帮助用户快速掌握复合材料成型仿真的关键技术,解决褶皱、纤维堆积、气泡残留等常见问题,实现从铺层设计到强度校核的全流程仿真闭环。特别针对热固性/热塑性材料差异、RTM衍生工艺、链式仿真等难点提供了专项解决方案。

源代码杀手的博客 320

SOFA BOLT源码解析之概述

1  概述        关于SOFA Bolt的文档来自于Alipay开源社区,链接地址为:        https://github.com/alipay/sofa-bolt        https://github.com/alipay/sofa-bolt/wiki/SOFA-Bolt-Handbook        此处直接引用该文档内容,主要是便于大家理解源码解析部分。1.1  介绍...

闲来无事 2449

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

SOFA BOLT源码解析之设计要点-网络IO

1  设计要点解析        SOFA Bolt作为基础通信层,其功能在设计和实现的过程中,主要从以下几个方面考虑:        1.   网络IO;        2.   线程模型;        3.   通信模型;        4.   连接管理;        5.   批量解包和批量提交;        6.   事件触发和监听机制;        7.   全双工通信;   ...

闲来无事 1046

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

蚂蚁金服通信框架SOFABolt解析 | 协议框架解析

SOFAScalableOpenFinancialArchitecture 是蚂蚁金服自主研发的金融级分布式中间件,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。 本文为《蚂蚁金服通信框架 SOFABolt 解析》系列第三篇,作者颜洄、丞一。 《蚂蚁金服通信框架 SOFABolt 解析》系列由 SOFA 团队和源码爱...

weixin_34029949的博客 311

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【剖析 | SOFARPC 框架】系列之 SOFARPC 同步异步实现剖析

SOFAScalable Open Financial Architecture是蚂蚁金服自主研发的金融级分布式中间件,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。本文为《剖析 | SOFARPC 框架》第四篇。 《剖析 | SOFARPC 框架》系列由 SOFA 团队和源码爱好者们出品 前言这一篇,我们为大家带来了开发过程中,最常接触到的同步异步调用解析。本文会...

weixin_34274029的博客 590

2. SOFABolt源码分析-协议及消息编解码篇

SOFABolt源码分析-协议及消息编解码篇1. 前言1.1 粘包拆包:1.2 请求类型1.3 版本控制2. 协议-Protocol2.1 RpcProtocolV23. 消息载体-RpcCommand4. 消息编解码器-CommandDecoder/CommandEncoder5. 总结5.1 RpcCodec5.2 ProtocolCodeBasedEncoder 1. 前言 在上篇文章中大致介绍了SOFABolt服务端怎么用自定义的UserProcessor去处理客户端的消息,能大致了解SOFABo

Yolyn‘s blog 1152

蚂蚁金服RPC框架结构分析

蚂蚁金服近期开源了研发多年的SOFA一篮子框架,其中就有一个非常核心的RPC框架,它叫SOFA-BOLT。小编今天花了近一天的时间仔细阅读研究它的源码,阅读过程中遇到了不少问题,蚂蚁金服的相关技术人员都非常耐心的及时解答了我的疑难。这里将我从中学到的知识点一并分享给大家。 SOFA-BOLT基于开源的Netty框架,同时提供了服务器和客户端的实现。它的源码非常值得一读,结构简单,考虑周全,绝不是一...

weixin_34198453的博客 749

透视RPC协议:SOFA-BOLT协议源码分析

前提最近在看Netty相关的资料,刚好SOFA-BOLT是一个比较成熟的Netty自定义协议栈实现,于是决定研读SOFA-BOLT的源码,详细分析其协议的组成,简单分析其客户端和服务端的源...

Throwable 1176

sofa-bolt(网络通信框架)

SOFABolt 用户手册 蚂蚁通信框架实践 SOFABolt 是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架。 为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO 的实现以及处理难以调试的网络问题,Netty 应运而生。 为了让中间件开发者能将更多的精力放在产品功能特性实现上,而不是重复地一遍遍制造通信框架的轮子,SOFABolt 应运而生。 SOFABolt的基础功能: 基础通信功能 ( remoting-core ) 基于 N

weixin_41942838的博客 1905

1. SOFABolt源码分析-服务启动到消息处理

SofaBolt源码分析-UserProcessor1. Demo2. UserProcessor细节3. 注册UserProcessor 本篇文章分析一下SofaBolt中的UserProcessor时怎么起作用的,接下来先看一个例子 1. Demo 这里直接把官网的源码拉下来,自带Demo示例 代码清单1:RpcServerDemoByMain.class public class RpcServerDemoByMain { static Logger logger

Yolyn‘s blog 1580

SOFA原理学习--sofa rpc入门示例

       最近在项目开发中有用到sofa-rpc框架,其是蚂蚁金服开源的,简单的看了一下源码其和阿里巴巴另外一个比较出名的RPC框架Dubbo类似,其实sofa-rpc提供的dubbo协议最终就是使用的Dubbo的功能(sofa-rpc直接将dubbo的jar引入),所以如果对Dubbo有所了解的人我感觉在sofa-rpc学习上也是很快的。 SOFARPC 简介     SOFARPC ...

井底之蛙 3万+

SOFA BOLT源码解析之设计要点-通信协议

1. 设计要点解析1.1  通信协议        SOFA Bolt定义了两个版本的私有通信协议,即V1版和V2版。        此处以V2版为例,详细描述一下私有通信协议的设计。        一、  RpcProctocolV2请求协议格式如下:        RpcProctocolV2请求协议字段:        1.   proto:协议码ProtocolCode。如果一个端口,需要...

闲来无事 3323

invoke callback during WCF service contract operation

第一篇博文,是我在wordPress上最近写的,英文描述不是很多,就没翻译了。我wordPress博客:http://paulzhou2012.wordpress.com/ I will discuss a problem that I encountered recently. It is about to invoke callback during WCF service contract...

weixin_30454481的博客 130
上一篇: SOFA BOLT源码解析之设计要点-连接管理
下一篇: SOFA BOLT源码解析之设计要点-线程模型
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

任性之闲来无事

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

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

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

打赏作者

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

抵扣说明:

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

余额充值