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); } } }