의 동기 전송 rocketmq, 한방향 전송, 어떻게 할 비동기 전송?

SendDefaultImpl 방법 중 :

            대 (; 항상 <timesTotal; 배 ++) { 
                문자열 lastBrokerName = NULL == MQ? 널 : mq.getBrokerName (); 
                MessageQueue가 mqSelected = this.selectOneMessageQueue (topicPublishInfo, lastBrokerName); 
                경우 (mqSelected = 널!) { 
                    MQ = mqSelected; 
                    brokersSent [시간] = mq.getBrokerName (); 
                    {시도 
                        beginTimestampPrev = System.currentTimeMillis는 ()를; 
                        긴 costTime = beginTimestampPrev - beginTimestampFirst; 
                        경우 (타임 아웃 <costTime) { 
                            callTimeout = TRUE; 
                            단절;
                        }
 
                        sendResult = this.sendKernelImpl (MSG, MQ, communicationMode, sendCallback, topicPublishInfo, 초과 - costTime); 
                        endTimestamp에 System.currentTimeMillis = (); 
                        this.updateFaultItem (mq.getBrokerName (), endTimestamp - beginTimestampPrev, 거짓); 
                        스위치 (communicationMode) { 
                            경우 ASYNC : 
                                는 null; 
                            경우 편도 : 
                                는 null; 
                            경우 SYNC : 
                                만약 (! sendResult.getSendStatus () = SendStatus.SEND_OK를) { 
                                    경우 (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK ()) { 
                                        계속; 
                                    } 
                                } 

                                sendResult를 반환; 
                            기본값 : 
                                휴식; 
                        } 
                    } 캐치 (RemotingException 전자) { 
                        endTimestamp에 System.currentTimeMillis = (); 
                        this.updateFaultItem (mq.getBrokerName (), endTimestamp - beginTimestampPrev, TRUE);
                        log.warn (및 String.format ( "sendKernelImpl 예외 한번에 재전송 InvokeID %의 S, RT %의 SMS, 브로커 %의 S"invokeID, endTimestamp - beginTimestampPrev, MQ), E); 
                        log.warn (msg.toString ()); 
                        예외 = E;
                        계속하다; 
                    } 캐치 (MQClientException 전자) { 
                        endTimestamp에 System.currentTimeMillis = (); 
                        this.updateFaultItem (mq.getBrokerName (), endTimestamp - beginTimestampPrev, TRUE); 
                        log.warn (및 String.format ( "sendKernelImpl 예외 한번에 재전송 InvokeID %의 S, RT %의 SMS, 브로커 %의 S"invokeID, endTimestamp - beginTimestampPrev, MQ), E); 
                        log.warn (msg.toString ()); 
                        예외 = E;
                        계속하다;

  

이 동기화되어 있으면이 성공적으로 전송해야하며, 또한 그렇지 않으면 계속 다시 시도, 성공 반환 괜찮 것을 알아야합니다.

당신이 한방향, 비동기, 다음 다이렉트는 null 아웃 인 경우에, 어떻게 비동기 재 시도를 하는가?

위의 코드는 sendKernelImpl를 보낼 수있는 핵심 기능입니다

 

               SendResult sendResult = NULL; 
                스위치 (communicationMode) { 
                    경우 ASYNC : 
                        메시지 = tmpMessage MSG; 
                        (msgBodyCompressed)는 {만약 
                            MSG 본체가 압축 된 경우 // msgbody이어야 prevBody를 이용하여 재설정. 
                            commpressed 메시지 본문을 사용하여 새 메시지를 복제하고 원점 마사지를 복구 //. 
                            // 버그 수정 : HTTPS : //github.com/apache/rocketmq-externals/issues/66 
                            tmpMessage = MessageAccessor.cloneMessage (MSG); 
                            msg.setBody (prevBody); 
                        }
                        긴 costTimeAsync에 System.currentTimeMillis = () - beginStartTime;  
                            communicationMode, 
                            sendCallback,
                        (제한 시간 <costTimeAsync가) {경우 
                            새로운 RemotingTooMuchRequestException을 던져 ( "sendKernelImpl 통화 시간 제한"); 
                        } 
                        sendResult this.mQClientFactory.getMQClientAPIImpl = () sendMessage 첨부 (. 
                            brokerAddr, 
                            mq.getBrokerName () 
                            tmpMessage, 
                            에 RequestHeader, 
                            초과 - costTimeAsync, 
                            this.defaultMQProducer.getRetryTimesWhenSendAsyncFailed ()
                            topicPublishInfo, 
                            this.mQClientFactory, 
                            문맥 
                            이); 
                        단절; 
                    경우 편도 : 
                    경우 SYNC : 
                        긴 costTimeSync =에 System.currentTimeMillis () - beginStartTime; 
                        (제한 시간 <costTimeSync가) {경우 
                            새로운 RemotingTooMuchRequestException을 던져 ( "sendKernelImpl 통화 시간 제한"); 
                        } 
                        sendResult this.mQClientFactory.getMQClientAPIImpl = (). sendMessage 첨부 ( 
                            brokerAddr, 
                            mq.getBrokerName (), 
                            MSG, 
                            에 RequestHeader, 
                            타임 아웃 - costTimeSync, 
                            communicationMode,
                            문맥 
                            이); 
                        단절; 
                    기본값 : 
                        거짓 주장; 
                        단절; 
                }

 

) (sendMessage 첨부 수신 방법은 this.defaultMQProducer.getRetryTimesWhenSendAsyncFailed으로, 비동기 모드에서, 동일하지 않은 비동기 및 비동기 모드 것을 볼 수

여기에 다시 비동기 시도 말을하지 않습니다. 너무 많은 너무 많은 자원을 차지 전송 방지하기 위해 차단 된 스레드 및 비동기의 수 rocketmq 때문에 동기는 달리, 같은 시간이 제한되는 단방향 메시지를 보냅니다.

특정 제약은 세마포어에 의해 달성된다 :

공공 NettyRemotingAbstract (최종 INT permitsOneway, 최종 INT permitsAsync) { 
this.semaphoreOneway = (permitsOneway 사실) 세마포어 새로운;
this.semaphoreAsync = 새로운 세마포어 (permitsAsync, TRUE);
}

 

한방향를 들어, this.semaphoreOneway.tryAcquire 성공은 자원을 얻을 나중에 writeandFlush 이동하는 스레드가 아무 것도 확인하지 않아도, 그 메시지를 전송했습니다, 이상이 없을만큼, 경쟁 것을 의미한다. 

공공 무효 invokeOnewayImpl (최종 채널 채널, 최종 RemotingCommand 요청, 최종 긴 timeoutMillis는) 
        예외 : InterruptedException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException {던졌습니다 
        request.markOnewayRPC를 (); 
        = this.semaphoreOneway.tryAcquire (timeoutMillis, TimeUnit.MILLISECONDS) 부울 인수; 
        (취득) {경우 
            최종 SemaphoreReleaseOnlyOnce 한번 새로운 SemaphoreReleaseOnlyOnce (this.semaphoreOneway를) =; 
            {시도 
                channel.writeAndFlush (요청) .addListener (새 ChannelFutureListener () { 
                    @Override가 
                    operationComplete (ChannelFuture의 f는) 예외 {무효 발생 공개
                        once.release (); 
                        (! f.isSuccess ()) {경우 
                            log.warn ( "채널에 요청 명령을 전송 <"+ channel.remoteAddress () + "> 실패했습니다."); 
                        } 
                    } 
                }); 
            } 캐치 (예외 전자) { 
                once.release (); 
                log.warn ( "채널에 요구 커맨드를 전송 해주기 <"+ channel.remoteAddress () + ">에 실패했습니다."); 
                새로운 RemotingSendRequestException (RemotingHelper.parseChannelRemoteAddr (채널), e)를 던져; 
            } 
        } {다른 
            경우 (timeoutMillis <= 0) {
                새로운 RemotingTooMuchRequestException ( "너무 빨리 호출 invokeOnewayImpl")를 던져; 
            } 다른 { 
                문자열 정보 = 및 String.format ( 
                    "invokeOnewayImpl 된 tryAcquire 세마포어 시간 초과 % DMS, 대기 스레드 nums : % d 개 semaphoreAsyncValue : % d 개", 
                    timeoutMillis, 
                    this.semaphoreOneway.getQueueLength (), 
                    this.semaphoreOneway.availablePermits () 
                ); 
                log.warn (정보); 
                새로운 RemotingTimeoutException (정보)를 던져; 
            } 
        } 
    }

  

 

대한 동기 호출의 경우 :

 

   공개 RemotingCommand invokeSyncImpl (최종 채널 채널 최종 RemotingCommand 요청 
        최종 긴 timeoutMillis)는 
        예외 : InterruptedException, RemotingSendRequestException, RemotingTimeoutException {발생 
        최종 INT 불투명 = request.getOpaque을 (); 

        시도 { 
            최종 ResponseFuture responseFuture = 새로운 ResponseFuture (채널 불투명 timeoutMillis, NULL, NULL); 
            this.responseTable.put (불투명 responseFuture); 
            최종의 SocketAddress 요지 channel.remoteAddress = (); 
            channel.writeAndFlush (요청) .addListener (새 ChannelFutureListener () { 
                @Override는 
                operationComplete (ChannelFuture의 F)는 예외 {공극 발생 공개
                    경우 (f.isSuccess ()) { 
                        responseFuture.setSendRequestOK (TRUE); 
                        반환; 
                    } 다른 { 
                        responseFuture.setSendRequestOK (거짓); 
                    } 

                    responseTable.remove (불투명); 
                    responseFuture.setCause (f.cause ()); 
                    responseFuture.putResponse (NULL); 
                    log.warn ( "채널에 요구 커맨드를 전송 <"ADDR + + ">에 실패했습니다."); 
                } 
            }); 

            RemotingCommand responseCommand = responseFuture.waitResponse (timeoutMillis); 
            경우 (NULL == responseCommand) {
                경우 (responseFuture.isSendRequestOK ()) { 
                    새로운 RemotingTimeoutException을 던져 (RemotingHelper.parseSocketAddressAddr (요지), timeoutMillis, 
                        responseFuture.getCause ()); 
                } 다른 { 
                    (,) responseFuture.getCause을 RemotingHelper.parseSocketAddressAddr (요지 ()) 새로운 RemotingSendRequestException을 던져; 
                } 
            } 

            responseCommand를 반환; 
        마지막} { 
            this.responseTable.remove (불투명); 
        } 
    }

  마찬가지로 두보와 함께, 스레드가 쉐인 대기, 미래의 사용을 위해 기다리고 차단, responseTable는 요청을 보냈 미래 회원의지도를 구축? 이 동기 요청이기 때문에 응답을 기다리고해야합니다. 의 Netty 기반 통신, 다음, 일반적으로 다른 스레드의 반응은, 그래서 차단, 여기에 있습니다 다음 ResponseFuture responseFuture를 백업하고 코드를 깨울

 

    공개 RemotingCommand waitResponse (최종 긴 timeoutMillis)는 예외 : InterruptedException {발생 
        this.countDownLatch.await (timeoutMillis, TimeUnit.MILLISECONDS)를; 
        this.responseCommand를 반환; 
    } 

    공개 무효 putResponse (최종 RemotingCommand responseCommand) { 
        this.responseCommand = responseCommand; 
        this.countDownLatch.countDown (); 
    }

 

위의 코드는 신속하게 널에 넣어, 실패했을 때이 putResponse 또는 그렇지 않으면이 응답을 수신 대기 processResponseCommand에, 전송됩니다. 응답 후 과정을 완료 할 수있는 동기화 요청을 얻는 것입니다.

 

우리는 전송 스레드 후 전송 된 비동기 요청이 직접 반환 될 수 있음을 알고있다.

비동기 메시지 처리는 여기에 있습니다 :

 

   개인 무효 sendMessageAsync ( 
        최종 문자열 요지로, 
        최종 문자열 brokerName에, 
        최종 메시지 MSG, 
        최종 긴 timeoutMillis, 
        최종 RemotingCommand 요청, 
        최종 SendCallback sendCallback, 
        최종 TopicPublishInfo topicPublishInfo, 
        최종 MQClientInstance 인스턴스, 
        최종 INT retryTimesWhenSendFailed, 
        최종 AtomicInteger 시간, 
        최종 SendMessageContext 컨텍스트, 
        최종 DefaultMQProducerImpl 프로듀서 
    )가 발생 예외 : InterruptedException, RemotingException { 
        this.remotingClient.invokeAsync (요지, 요청, timeoutMillis, 새로운 InvokeCallback () {
            @Override 
            공개 무효 operationComplete (ResponseFuture responseFuture) { 
                RemotingCommand 응답 responseFuture.getResponseCommand = (); 
                (! && 널 == sendCallback 응답 = NULL) 경우에 { 

                    {시도 
                        SendResult sendResult = MQClientAPIImpl.this.processSendResponse (brokerName은, MSG, 응답) 단계; 
                        만약 (! = NULL 컨텍스트 && sendResult = NULL) { 
                            context.setSendResult (sendResult); 
                            . context.getProducer () executeSendMessageHookAfter (컨텍스트); 
                        } 
                    } 캐치 (Throwable를 전자) { 
                    }

                    producer.updateFaultItem (brokerName에,에 System.currentTimeMillis () - responseFuture.getBeginTimestamp (), 거짓); 
                    반환; 
                } 

                경우 (응답 = 널!) { 
                    시도 { 
                        SendResult sendResult = MQClientAPIImpl.this.processSendResponse (brokerName은, MSG, 응답); 
                        ! sendResult = NULL을 주장; 
                        만약 (! 문맥 = NULL) { 
                            context.setSendResult (sendResult); 
                            . context.getProducer () executeSendMessageHookAfter (컨텍스트); 
                        } 

                        {시도
                            sendCallback.onSuccess (sendResult); 
                        } 캐치 (Throwable를 전자) { 
                        } 

                        producer.updateFaultItem (brokerName에,에 System.currentTimeMillis () - responseFuture.getBeginTimestamp (), 거짓); 
                    } 캐치 (예외 전자) { 
                        producer.updateFaultItem (brokerName에,에 System.currentTimeMillis () - responseFuture.getBeginTimestamp (), TRUE); 
                        onExceptionImpl (brokerName은, MSG, 0L, 요청 sendCallback, topicPublishInfo 예컨대, 
                            retryTimesWhenSendFailed, 시간, 즉, 문맥, 거짓, 제조자); 
                    } 
                } 다른 {
                    producer.updateFaultItem (brokerName에,에 System.currentTimeMillis () - responseFuture.getBeginTimestamp (), TRUE); 
                    (! responseFuture.isSendRequestOK ())하는 경우 { 
                        MQClientException 전 = 새로운 MQClientException는 ( "요청이 실패 보내기", responseFuture.getCause ()); 
                        onExceptionImpl (brokerName은, MSG, 0L, 요청, sendCallback, topicPublishInfo 예, 
                            retryTimesWhenSendFailed, 시간, 전, 문맥, 사실, 프로듀서); 
                    } 다른 경우 (responseFuture.isTimeout ()) { 
                        MQClientException 전 = 새로운 MQClientException ( "응답 시간 제한을 기다립니다"+ responseFuture.getTimeoutMillis () + "MS"
                            responseFuture.getCause ()); 
                        onExceptionImpl (brokerName은, MSG, 0L, 요청, sendCallback, topicPublishInfo 예, 
                            retryTimesWhenSendFailed, 시간, 전, 문맥, 사실, 프로듀서); 
                    } 다른 { 
                        MQClientException 전 = 새로운 MQClientException ( "알 수없는 reseaon", responseFuture.getCause ()); 
                        onExceptionImpl (brokerName은, MSG, 0L, 요청, sendCallback, topicPublishInfo 예, 
                            retryTimesWhenSendFailed, 시간, 전, 문맥, 사실, 프로듀서); 
                    } 
                } 
            } 
        }); 
    }

  

이 방법은 큰 덩어리가, 사실, 단 하나의 문장 this.remotingClient.invokeAsync는,이 외에도 ResponseFuture 수신이 콜백 InvokeCallback이 있는지 여부를 결정하는 응답 안에이 미래의 성공이 있는지 여부를 확인하는 콜백 함수 InvokeCallback입니다 비동기의 수가 고갈 될 때까지 계속 다시 시도 다시 sendMessageAsync에 온 로직, onExceptionImpl를 onExceptionImpl 입력합니다.

여기에 우리가 예외가 비동기 전송에서 발생하는 경우 보낸 사람이 성공 여부를 비동기 전송하지 않도록주의해야한다, 그것은 클라이언트가 예외를 인식하도록 예외 직접 던져이다.

이 문제없이 전송되는 경우, 잘못된 응답 또는 타임 아웃을 얻을, 다음 rocketmq가 자동으로 다시 시도 도움이 될 것입니다.

깊이있는 this.remotingClient.invokeAsync을 계속 :

   공공 무효 invokeAsyncImpl (최종 채널 채널 최종 RemotingCommand 요청 최종 긴 timeoutMillis, 
        최종 InvokeCallback invokeCallback)는 
        예외 : InterruptedException, RemotingTooMuchRequestException, RemotingTimeoutException, {RemotingSendRequestException을 발생 
        긴 beginStartTime에 System.currentTimeMillis = (); 
        최종 INT 불투명 request.getOpaque = (); 
        = this.semaphoreAsync.tryAcquire (timeoutMillis, TimeUnit.MILLISECONDS) 부울 인수; 
        (취득) {경우 
            최종 SemaphoreReleaseOnlyOnce 한번 새로운 SemaphoreReleaseOnlyOnce (this.semaphoreAsync를) =; 
            긴 costTime에 System.currentTimeMillis = () - beginStartTime; 
            경우 (timeoutMillis <costTime) {
                once.release (); 
                새로운 RemotingTimeoutException ( "invokeAsyncImpl 통화 시간 제한")를 던져; 
            } 

            최종 ResponseFuture responseFuture = 새로운 ResponseFuture (채널 불투명 timeoutMillis - costTime 일단 invokeCallback); 
            this.responseTable.put (불투명 responseFuture); 
            시도 { 
                channel.writeAndFlush (요청) .addListener (새 ChannelFutureListener () { 
                    @Override 
                    공공 무효 operationComplete (ChannelFuture의 f는) 예외 {던졌습니다 
                        경우 (f.isSuccess ()) { 
                            responseFuture.setSendRequestOK ()는 true 
                            반환;
                        } 
                        requestFail (불투명); 
                        log.warn ( "채널에 요구 커맨드를 전송 <{}>에 실패했습니다.」RemotingHelper.parseChannelRemoteAddr (채널)); 
                    } 
                }); 
            } 캐치 (예외 전자) { 
                responseFuture.release (); 
                log.warn ( "<+ RemotingHelper.parseChannelRemoteAddr (채널) +"> 예외 "E 채널에 대한 요청 명령을 전송"); 
                새로운 RemotingSendRequestException (RemotingHelper.parseChannelRemoteAddr (채널), e)를 던져; 
            } 
        } 
    }

  

이것은 위의 한 방법으로, 동기 호출이지도 키로 불투명으로 미래의 객체를 생성, 세마포어 얻을 수 확인, 상사 인 반면, 또한 미래에 상단 InvokeCallback, 전술 한 바와 같이, 재 시도가 InvokeCallback 비동기 호출에 구현되어, 다음 InvokeCallback 결국 미래에 의해 호출 될 수있다.

위의 우리는이지도가 샤시 허우이 소요되는 맵에 배치 InvokeCallback을 다시 시도 미래에 메시지를 포함 보내나요?

1 직접 실패 requestFail (불투명)이라고 전송할 때

    개인 무효 requestFail (최종 불투명 INT) { 
        ResponseFuture responseFuture = responseTable.remove (불투명); 
        경우 (responseFuture = 널!) { 
            responseFuture.setSendRequestOK (거짓); 
            responseFuture.putResponse (NULL); 
            시도 { 
                executeInvokeCallback (responseFuture); 
            } 캐치 (Throwable를 전자) { 
                log.warn ( "requestFail에 콜백을 실행하고 콜백 던져", 전자); 
            마지막} { 
                responseFuture.release (); 
            } 
        } 
    }

  

2 미래의 메시지 응답을받은 :

 

    공공 무효 processResponseCommand (ChannelHandlerContext CTX, RemotingCommand에 cmd) { 
        최종 INT 불투명 cmd.getOpaque = (); 
        ResponseFuture responseFuture = responseTable.get (불투명) 최종; 
        경우 (responseFuture = 널!) { 
            responseFuture.setResponseCommand (cmd를) 

            responseTable.remove (불투명); 

            (! responseFuture.getInvokeCallback () = NULL) {경우 
                executeInvokeCallback (responseFuture); 
            사용한다} else { 
                responseFuture.putResponse CMD (); 
                responseFuture.release (); 
            } 
        } 다른 {
            log.warn는 ( "응답을 수신하지만, 어떤 요청과 일치하지"+ RemotingHelper.parseChannelRemoteAddr (ctx.channel ())); 
            log.warn (cmd.toString ()); 
        } 
    }

 

동기 호출 시간 제한 장면보다 더 많은 동기 호출하지만, 하나처럼. 동기화 시간 제한 스레드 차단, 비동기 시간 제한 장면을 얻을 수 있습니다, 그것은 백그라운드 스레드 검사에 의존해야합니다 :

    공공 무효 scanResponseTable () { 
        최종 목록 <ResponseFuture> = rfList 새로운 LinkedList의 <ResponseFuture> (); 
        . 반복자 <항목 <정수, ResponseFuture가 >>는 this.responseTable.entrySet을 = () 반복기 (); 
        (it.hasNext ()) {동안 
            입력 <정수 ResponseFuture> 다음 it.next = (); 
            ResponseFuture 담당자 next.getValue = (); 

            경우 ((rep.getBeginTimestamp () + rep.getTimeoutMillis () + 1000) "에 System.currentTimeMillis = ()) { 
                rep.release (); 
                it.remove (); 
                rfList.add (REP); 
                log.warn ( "타임 아웃 요구를 제거"+ REP); 
            } 
        }

        (ResponseFuture의 RF : rfList)에 대한 { 
            시도 { 
                executeInvokeCallback (RF); 
            } 캐치 (Throwable를 E) { 
                log.warn ( "scanResponseTable, operationComplete 예외"E); 
            } 
        } 
    }

  

 

추천

출처www.cnblogs.com/notlate/p/11616260.html