public HeartbeatV2Result sendHeartbeatV2(
        final String addr,
        final HeartbeatData heartbeatData,
        final long timeoutMillis
    ) throws RemotingException, MQBrokerException, InterruptedException {
        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.HEART_BEAT, new HeartbeatRequestHeader());
        request.setLanguage(clientConfig.getLanguage());
        request.setBody(heartbeatData.encode());
        RemotingCommand response = this.remotingClient.invokeSync(addr, request, timeoutMillis);
        assert response != null;
        switch (response.getCode()) {
            case ResponseCode.SUCCESS: {
                if (response.getExtFields() != null) {
                    return new HeartbeatV2Result(response.getVersion(), Boolean.parseBoolean(response.getExtFields().get(MixAll.IS_SUB_CHANGE)), Boolean.parseBoolean(response.getExtFields().get(MixAll.IS_SUPPORT_HEART_BEAT_V2)));
                }
                return new HeartbeatV2Result(response.getVersion(), false, false);
            }
            default:
                break;
        }

        throw new MQBrokerException(response.getCode(), response.getRemark());
    }
@Test
    public void assertSendHeartbeatV2() throws MQBrokerException, RemotingException, InterruptedException {
        mockInvokeSync();
        HeartbeatData heartbeatData = new HeartbeatData();
        HeartbeatV2Result actual = mqClientAPI.sendHeartbeatV2(defaultBrokerAddr, heartbeatData, defaultTimeout);
        assertNotNull(actual);
        assertEquals(1, actual.getVersion());
        assertFalse(actual.isSubChange());
        assertFalse(actual.isSupportV2());
    }