feature(container&remoting):[RIP-31][PART-A]Support RocketMQ BrokerContainer (#3976)

This commit is contained in:
rongtong
2022-03-14 14:05:53 +08:00
committed by RongtongJin
parent 467b382777
commit 27ab1dfdcd
33 changed files with 3996 additions and 187 deletions
+36
View File
@@ -0,0 +1,36 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-all</artifactId>
<version>5.0.0-BETA-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>rocketmq-container</artifactId>
<name>rocketmq-container ${project.version}</name>
<dependencies>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-broker</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import java.util.Properties;
import org.apache.rocketmq.broker.BrokerController;
public interface BrokerBootHook {
/**
* Name of the hook.
*
* @return name of the hook
*/
String hookName();
/**
* Code to execute before broker start.
*
* @param brokerController broker to start
* @param properties broker properties
* @throws Exception when execute hook
*/
void executeBeforeStart(BrokerController brokerController, Properties properties) throws Exception;
/**
* Code to execute after broker start.
*
* @param brokerController broker to start
* @param properties broker properties
* @throws Exception when execute hook
*/
void executeAfterStart(BrokerController brokerController, Properties properties) throws Exception;
}
@@ -0,0 +1,435 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.BrokerPathConfigHelper;
import org.apache.rocketmq.container.logback.BrokerLogbackConfigurator;
import org.apache.rocketmq.broker.out.BrokerOuterAPI;
import org.apache.rocketmq.common.AbstractBrokerRunnable;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.BrokerIdentity;
import org.apache.rocketmq.common.Configuration;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.ThreadFactoryImpl;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.utils.ThreadUtils;
import org.apache.rocketmq.logging.InternalLogger;
import org.apache.rocketmq.logging.InternalLoggerFactory;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.remoting.RemotingServer;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.NettyRemotingServer;
import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.store.config.BrokerRole;
import org.apache.rocketmq.store.config.MessageStoreConfig;
public class BrokerContainer implements IBrokerContainer {
private static final InternalLogger LOG = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
private final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1,
new BasicThreadFactory.Builder()
.namingPattern("BrokerContainerScheduledThread")
.daemon(true)
.build());
private final NettyServerConfig nettyServerConfig;
private final NettyClientConfig nettyClientConfig;
private final BrokerOuterAPI brokerOuterAPI;
private final ContainerClientHouseKeepingService containerClientHouseKeepingService;
private final ConcurrentMap<BrokerIdentity, InnerSalveBrokerController> slaveBrokerControllers = new ConcurrentHashMap<>();
private final ConcurrentMap<BrokerIdentity, InnerBrokerController> masterBrokerControllers = new ConcurrentHashMap<>();
private final List<BrokerBootHook> brokerBootHookList = new ArrayList<>();
private final BrokerContainerProcessor brokerContainerProcessor;
private final Configuration configuration;
private final BrokerContainerConfig brokerContainerConfig;
private RemotingServer remotingServer;
private RemotingServer fastRemotingServer;
private ExecutorService brokerContainerExecutor;
public BrokerContainer(
final BrokerContainerConfig brokerContainerConfig,
final NettyServerConfig nettyServerConfig,
final NettyClientConfig nettyClientConfig
) {
this.brokerContainerConfig = brokerContainerConfig;
this.nettyServerConfig = nettyServerConfig;
this.nettyClientConfig = nettyClientConfig;
this.brokerOuterAPI = new BrokerOuterAPI(nettyClientConfig);
this.brokerContainerProcessor = new BrokerContainerProcessor(this);
this.brokerContainerProcessor.registerBrokerBootHook(this.brokerBootHookList);
this.containerClientHouseKeepingService = new ContainerClientHouseKeepingService(this);
this.configuration = new Configuration(
LOG,
BrokerPathConfigHelper.getBrokerConfigPath(),
this.brokerContainerConfig, this.nettyServerConfig, this.nettyClientConfig);
}
@Override
public String getBrokerContainerAddr() {
return this.brokerContainerConfig.getBrokerContainerIP() + ":" + this.nettyServerConfig.getListenPort();
}
@Override
public BrokerContainerConfig getBrokerContainerConfig() {
return brokerContainerConfig;
}
@Override
public NettyServerConfig getNettyServerConfig() {
return nettyServerConfig;
}
public NettyClientConfig getNettyClientConfig() {
return nettyClientConfig;
}
@Override
public BrokerOuterAPI getBrokerOuterAPI() {
return brokerOuterAPI;
}
@Override
public RemotingServer getRemotingServer() {
return remotingServer;
}
public Configuration getConfiguration() {
return this.configuration;
}
public boolean initialize() {
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.containerClientHouseKeepingService);
this.fastRemotingServer = this.remotingServer.newRemotingServer(this.nettyServerConfig.getListenPort() - 2);
this.brokerContainerExecutor = new ThreadPoolExecutor(
1,
1,
1000 * 60,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(10000),
new ThreadFactoryImpl("SharedBrokerThread_"));
this.registerProcessor();
if (this.brokerContainerConfig.getNamesrvAddr() != null) {
this.brokerOuterAPI.updateNameServerAddressList(this.brokerContainerConfig.getNamesrvAddr());
LOG.info("Set user specified name server address: {}", this.brokerContainerConfig.getNamesrvAddr());
// also auto update namesrv if specify
this.scheduledExecutorService.scheduleAtFixedRate(new AbstractBrokerRunnable(BrokerIdentity.BROKER_CONTAINER_IDENTITY) {
@Override
public void run2() {
try {
BrokerContainer.this.brokerOuterAPI.updateNameServerAddressList(BrokerContainer.this.brokerContainerConfig.getNamesrvAddr());
} catch (Throwable e) {
LOG.error("ScheduledTask fetchNameServerAddr exception", e);
}
}
}, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
} else if (this.brokerContainerConfig.isFetchNamesrvAddrByAddressServer()) {
this.scheduledExecutorService.scheduleAtFixedRate(new AbstractBrokerRunnable(BrokerIdentity.BROKER_CONTAINER_IDENTITY) {
@Override
public void run2() {
try {
BrokerContainer.this.brokerOuterAPI.fetchNameServerAddr();
} catch (Throwable e) {
LOG.error("ScheduledTask fetchNameServerAddr exception", e);
}
}
}, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
}
this.scheduledExecutorService.scheduleAtFixedRate(new AbstractBrokerRunnable(BrokerIdentity.BROKER_CONTAINER_IDENTITY) {
@Override
public void run2() {
try {
BrokerContainer.this.brokerOuterAPI.refreshMetadata();
} catch (Exception e) {
LOG.error("ScheduledTask refresh metadata exception", e);
}
}
}, 1, 5, TimeUnit.SECONDS);
return true;
}
private void registerProcessor() {
remotingServer.registerDefaultProcessor(brokerContainerProcessor, this.brokerContainerExecutor);
fastRemotingServer.registerDefaultProcessor(brokerContainerProcessor, this.brokerContainerExecutor);
}
@Override
public void start() throws Exception {
if (this.remotingServer != null) {
this.remotingServer.start();
}
if (this.fastRemotingServer != null) {
this.fastRemotingServer.start();
}
if (this.brokerOuterAPI != null) {
this.brokerOuterAPI.start();
}
}
@Override
public void shutdown() {
// Shutdown slave brokers
for (InnerSalveBrokerController slaveBrokerController : slaveBrokerControllers.values()) {
slaveBrokerController.shutdown();
}
slaveBrokerControllers.clear();
// Shutdown master brokers
for (BrokerController masterBrokerController : masterBrokerControllers.values()) {
masterBrokerController.shutdown();
}
masterBrokerControllers.clear();
// Shutdown the remoting server with a high priority to avoid further traffic
if (this.remotingServer != null) {
this.remotingServer.shutdown();
}
if (this.fastRemotingServer != null) {
this.fastRemotingServer.shutdown();
}
// Shutdown the request executors
ThreadUtils.shutdown(this.brokerContainerExecutor);
if (this.brokerOuterAPI != null) {
this.brokerOuterAPI.shutdown();
}
}
public void registerClientRPCHook(RPCHook rpcHook) {
this.getBrokerOuterAPI().registerRPCHook(rpcHook);
}
public void clearClientRPCHook() {
this.getBrokerOuterAPI().clearRPCHook();
}
public List<BrokerBootHook> getBrokerBootHookList() {
return brokerBootHookList;
}
public void registerBrokerBootHook(BrokerBootHook brokerBootHook) {
this.brokerBootHookList.add(brokerBootHook);
LOG.info("register BrokerBootHook, {}", brokerBootHook.hookName());
}
@Override
public InnerBrokerController addBroker(final BrokerConfig brokerConfig,
final MessageStoreConfig storeConfig) throws Exception {
if (brokerConfig.getBrokerId() == MixAll.MASTER_ID && storeConfig.getBrokerRole() != BrokerRole.SLAVE) {
return this.addMasterBroker(brokerConfig, storeConfig);
}
if (brokerConfig.getBrokerId() != MixAll.MASTER_ID && storeConfig.getBrokerRole() == BrokerRole.SLAVE) {
return this.addSlaveBroker(brokerConfig, storeConfig);
}
return null;
}
public InnerBrokerController addMasterBroker(final BrokerConfig masterBrokerConfig,
final MessageStoreConfig storeConfig) throws Exception {
masterBrokerConfig.setInBrokerContainer(true);
if (storeConfig.isDuplicationEnable()) {
LOG.error("Can not add broker to container when duplicationEnable is true currently");
throw new Exception("Can not add broker to container when duplicationEnable is true currently");
}
InnerBrokerController masterBroker = new InnerBrokerController(this, masterBrokerConfig, storeConfig);
BrokerIdentity brokerIdentity = new BrokerIdentity(masterBrokerConfig.getBrokerClusterName(),
masterBrokerConfig.getBrokerName(), masterBrokerConfig.getBrokerId());
final BrokerController previousBroker = masterBrokerControllers.putIfAbsent(brokerIdentity, masterBroker);
if (previousBroker == null) {
// New master broker added, start it
try {
BrokerLogbackConfigurator.doConfigure(masterBrokerConfig);
final boolean initResult = masterBroker.initialize();
if (!initResult) {
masterBroker.shutdown();
masterBrokerControllers.remove(brokerIdentity);
throw new Exception("Failed to init master broker " + masterBrokerConfig.getCanonicalName());
}
for (InnerSalveBrokerController slaveBroker : this.getSlaveBrokers()) {
if (slaveBroker.getMessageStore().getMasterStoreInProcess() == null) {
slaveBroker.getMessageStore().setMasterStoreInProcess(masterBroker.getMessageStore());
}
}
} catch (Exception e) {
// Remove the failed master broker and throw the exception
masterBroker.shutdown();
masterBrokerControllers.remove(brokerIdentity);
throw new Exception("Failed to initialize master broker " + masterBrokerConfig.getCanonicalName(), e);
}
return masterBroker;
}
throw new Exception(masterBrokerConfig.getCanonicalName() + " has already been added to current broker");
}
/**
* This function will create a slave broker along with the main broker, and start it with a different port.
*
* @param slaveBrokerConfig the specific slave broker config
* @throws Exception is thrown if an error occurs
*/
public InnerSalveBrokerController addSlaveBroker(final BrokerConfig slaveBrokerConfig,
final MessageStoreConfig storeConfig) throws Exception {
slaveBrokerConfig.setInBrokerContainer(true);
if (storeConfig.isDuplicationEnable()) {
LOG.error("Can not add broker to container when duplicationEnable is true currently");
throw new Exception("Can not add broker to container when duplicationEnable is true currently");
}
int ratio = storeConfig.getAccessMessageInMemoryMaxRatio() - 10;
storeConfig.setAccessMessageInMemoryMaxRatio(Math.max(ratio, 0));
InnerSalveBrokerController slaveBroker = new InnerSalveBrokerController(this, slaveBrokerConfig, storeConfig);
BrokerIdentity brokerIdentity = new BrokerIdentity(slaveBrokerConfig.getBrokerClusterName(),
slaveBrokerConfig.getBrokerName(), slaveBrokerConfig.getBrokerId());
final InnerSalveBrokerController previousBroker = slaveBrokerControllers.putIfAbsent(brokerIdentity, slaveBroker);
if (previousBroker == null) {
// New slave broker added, start it
try {
BrokerLogbackConfigurator.doConfigure(slaveBrokerConfig);
final boolean initResult = slaveBroker.initialize();
if (!initResult) {
slaveBroker.shutdown();
slaveBrokerControllers.remove(brokerIdentity);
throw new Exception("Failed to init slave broker " + slaveBrokerConfig.getCanonicalName());
}
BrokerController masterBroker = this.peekMasterBroker();
if (slaveBroker.getMessageStore().getMasterStoreInProcess() == null && masterBroker != null) {
slaveBroker.getMessageStore().setMasterStoreInProcess(masterBroker.getMessageStore());
}
} catch (Exception e) {
// Remove the failed slave broker and throw the exception
slaveBroker.shutdown();
slaveBrokerControllers.remove(brokerIdentity);
throw new Exception("Failed to initialize slave broker " + slaveBrokerConfig.getCanonicalName(), e);
}
return slaveBroker;
}
throw new Exception(slaveBrokerConfig.getCanonicalName() + " has already been added to current broker");
}
@Override
public BrokerController removeBroker(final BrokerIdentity brokerIdentity) throws Exception {
InnerSalveBrokerController slaveBroker = slaveBrokerControllers.remove(brokerIdentity);
if (slaveBroker != null) {
slaveBroker.shutdown();
return slaveBroker;
}
BrokerController masterBroker = masterBrokerControllers.remove(brokerIdentity);
BrokerController nextMasterBroker = this.peekMasterBroker();
for (InnerSalveBrokerController slave : this.getSlaveBrokers()) {
if (nextMasterBroker == null) {
slave.getMessageStore().setMasterStoreInProcess(null);
} else {
slave.getMessageStore().setMasterStoreInProcess(nextMasterBroker.getMessageStore());
}
}
if (masterBroker != null) {
masterBroker.shutdown();
return masterBroker;
}
return null;
}
@Override
public BrokerController getBroker(final BrokerIdentity brokerIdentity) {
InnerSalveBrokerController slaveBroker = slaveBrokerControllers.get(brokerIdentity);
if (slaveBroker != null) {
return slaveBroker;
}
return masterBrokerControllers.get(brokerIdentity);
}
@Override
public Collection<InnerBrokerController> getMasterBrokers() {
return masterBrokerControllers.values();
}
@Override
public Collection<InnerSalveBrokerController> getSlaveBrokers() {
return slaveBrokerControllers.values();
}
@Override
public List<BrokerController> getBrokerControllers() {
List<BrokerController> brokerControllers = new ArrayList<>();
brokerControllers.addAll(this.getMasterBrokers());
brokerControllers.addAll(this.getSlaveBrokers());
return brokerControllers;
}
@Override
public BrokerController peekMasterBroker() {
if (!masterBrokerControllers.isEmpty()) {
return masterBrokerControllers.values().iterator().next();
}
return null;
}
public BrokerController findBrokerControllerByBrokerName(String brokerName) {
for (BrokerController brokerController : masterBrokerControllers.values()) {
if (brokerController.getBrokerConfig().getBrokerName().equals(brokerName)) {
return brokerController;
}
}
for (BrokerController brokerController : slaveBrokerControllers.values()) {
if (brokerController.getBrokerConfig().getBrokerName().equals(brokerName)) {
return brokerController;
}
}
return null;
}
}
@@ -0,0 +1,84 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.annotation.ImportantField;
import org.apache.rocketmq.remoting.common.RemotingUtil;
public class BrokerContainerConfig {
private String rocketmqHome = System.getProperty(MixAll.ROCKETMQ_HOME_PROPERTY, System.getenv(MixAll.ROCKETMQ_HOME_ENV));
@ImportantField
private String namesrvAddr = System.getProperty(MixAll.NAMESRV_ADDR_PROPERTY, System.getenv(MixAll.NAMESRV_ADDR_ENV));
@ImportantField
private boolean fetchNamesrvAddrByAddressServer = false;
@ImportantField
private String brokerContainerIP = RemotingUtil.getLocalAddress();
private String brokerConfigPaths = null;
private boolean compatibleWithOldNameSrv = true;
public String getRocketmqHome() {
return rocketmqHome;
}
public void setRocketmqHome(String rocketmqHome) {
this.rocketmqHome = rocketmqHome;
}
public String getNamesrvAddr() {
return namesrvAddr;
}
public void setNamesrvAddr(String namesrvAddr) {
this.namesrvAddr = namesrvAddr;
}
public boolean isFetchNamesrvAddrByAddressServer() {
return fetchNamesrvAddrByAddressServer;
}
public void setFetchNamesrvAddrByAddressServer(boolean fetchNamesrvAddrByAddressServer) {
this.fetchNamesrvAddrByAddressServer = fetchNamesrvAddrByAddressServer;
}
public String getBrokerContainerIP() {
return brokerContainerIP;
}
public String getBrokerConfigPaths() {
return brokerConfigPaths;
}
public void setBrokerConfigPaths(String brokerConfigPaths) {
this.brokerConfigPaths = brokerConfigPaths;
}
public boolean isCompatibleWithOldNameSrv() {
return compatibleWithOldNameSrv;
}
public void setCompatibleWithOldNameSrv(boolean compatibleWithOldNameSrv) {
this.compatibleWithOldNameSrv = compatibleWithOldNameSrv;
}
}
@@ -0,0 +1,273 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import io.netty.channel.ChannelHandlerContext;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Properties;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.BrokerStartup;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.BrokerIdentity;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.protocol.RequestCode;
import org.apache.rocketmq.common.protocol.ResponseCode;
import org.apache.rocketmq.common.protocol.header.AddBrokerRequestHeader;
import org.apache.rocketmq.common.protocol.header.GetBrokerConfigResponseHeader;
import org.apache.rocketmq.common.protocol.header.RemoveBrokerRequestHeader;
import org.apache.rocketmq.logging.InternalLogger;
import org.apache.rocketmq.logging.InternalLoggerFactory;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.store.config.MessageStoreConfig;
public class BrokerContainerProcessor implements NettyRequestProcessor {
private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
private final BrokerContainer brokerContainer;
private List<BrokerBootHook> brokerBootHookList;
public BrokerContainerProcessor(BrokerContainer brokerContainer) {
this.brokerContainer = brokerContainer;
}
@Override
public RemotingCommand processRequest(ChannelHandlerContext ctx, RemotingCommand request) throws Exception {
switch (request.getCode()) {
case RequestCode.ADD_BROKER:
return this.addBroker(ctx, request);
case RequestCode.REMOVE_BROKER:
return this.removeBroker(ctx, request);
case RequestCode.GET_BROKER_CONFIG:
return this.getBrokerConfig(ctx, request);
case RequestCode.UPDATE_BROKER_CONFIG:
return this.updateBrokerConfig(ctx, request);
default:
break;
}
return null;
}
@Override public boolean rejectRequest() {
return false;
}
private synchronized RemotingCommand addBroker(ChannelHandlerContext ctx,
RemotingCommand request) throws Exception {
final RemotingCommand response = RemotingCommand.createResponseCommand(null);
final AddBrokerRequestHeader requestHeader = request.decodeCommandCustomHeader(AddBrokerRequestHeader.class);
LOGGER.info("addBroker called by {}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
Properties brokerProperties = null;
String configPath = requestHeader.getConfigPath();
if (configPath != null && !configPath.isEmpty()) {
BrokerStartup.SystemConfigFileHelper configFileHelper = new BrokerStartup.SystemConfigFileHelper();
configFileHelper.setFile(configPath);
try {
brokerProperties = configFileHelper.loadConfig();
} catch (Exception e) {
LOGGER.error("addBroker load config from {} failed, {}", configPath, e);
}
} else {
byte[] body = request.getBody();
if (body != null) {
String bodyStr = new String(body, MixAll.DEFAULT_CHARSET);
brokerProperties = MixAll.string2Properties(bodyStr);
}
}
if (brokerProperties == null) {
LOGGER.error("addBroker properties empty");
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("addBroker properties empty");
return response;
}
BrokerConfig brokerConfig = new BrokerConfig();
MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
MixAll.properties2Object(brokerProperties, brokerConfig);
MixAll.properties2Object(brokerProperties, messageStoreConfig);
messageStoreConfig.setHaListenPort(brokerConfig.getListenPort() + 1);
if (configPath != null && !configPath.isEmpty()) {
brokerConfig.setBrokerConfigPath(configPath);
}
switch (messageStoreConfig.getBrokerRole()) {
case ASYNC_MASTER:
case SYNC_MASTER:
brokerConfig.setBrokerId(MixAll.MASTER_ID);
break;
case SLAVE:
if (brokerConfig.getBrokerId() <= 0) {
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("slave broker id must be > 0");
return response;
}
break;
default:
break;
}
if (messageStoreConfig.getTotalReplicas() < messageStoreConfig.getInSyncReplicas()
|| messageStoreConfig.getTotalReplicas() < messageStoreConfig.getMinInSyncReplicas()
|| messageStoreConfig.getInSyncReplicas() < messageStoreConfig.getMinInSyncReplicas()) {
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("invalid replicas number");
return response;
}
BrokerController brokerController;
try {
brokerController = this.brokerContainer.addBroker(brokerConfig, messageStoreConfig);
} catch (Exception e) {
LOGGER.error("addBroker exception {}", e);
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark(e.getMessage());
return response;
}
if (brokerController != null) {
brokerController.getConfiguration().registerConfig(brokerProperties);
try {
for (BrokerBootHook brokerBootHook : brokerBootHookList) {
brokerBootHook.executeBeforeStart(brokerController, brokerProperties);
}
brokerController.start();
for (BrokerBootHook brokerBootHook : brokerBootHookList) {
brokerBootHook.executeAfterStart(brokerController, brokerProperties);
}
} catch (Exception e) {
LOGGER.error("start broker exception {}", e);
BrokerIdentity brokerIdentity = new BrokerIdentity(brokerConfig.getBrokerClusterName(),
brokerConfig.getBrokerName(),
brokerConfig.getBrokerId());
this.brokerContainer.removeBroker(brokerIdentity);
brokerController.shutdown();
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("start broker failed, " + e);
return response;
}
response.setCode(ResponseCode.SUCCESS);
response.setRemark(null);
} else {
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("add broker return null");
}
return response;
}
private synchronized RemotingCommand removeBroker(ChannelHandlerContext ctx,
RemotingCommand request) throws RemotingCommandException {
final RemotingCommand response = RemotingCommand.createResponseCommand(null);
final RemoveBrokerRequestHeader requestHeader = request.decodeCommandCustomHeader(RemoveBrokerRequestHeader.class);
LOGGER.info("removeBroker called by {}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
BrokerIdentity brokerIdentity = new BrokerIdentity(requestHeader.getBrokerClusterName(), requestHeader.getBrokerName(), requestHeader.getBrokerId());
BrokerController brokerController;
try {
brokerController = this.brokerContainer.removeBroker(brokerIdentity);
} catch (Exception e) {
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark(e.getMessage());
return response;
}
if (brokerController != null) {
response.setCode(ResponseCode.SUCCESS);
response.setRemark(null);
} else {
response.setCode(ResponseCode.BROKER_NOT_EXIST);
response.setRemark("Broker not exist");
}
return response;
}
public void registerBrokerBootHook(List<BrokerBootHook> brokerBootHookList) {
this.brokerBootHookList = brokerBootHookList;
}
private RemotingCommand updateBrokerConfig(ChannelHandlerContext ctx, RemotingCommand request) {
final RemotingCommand response = RemotingCommand.createResponseCommand(null);
LOGGER.info("updateSharedBrokerConfig called by {}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
byte[] body = request.getBody();
if (body != null) {
try {
String bodyStr = new String(body, MixAll.DEFAULT_CHARSET);
Properties properties = MixAll.string2Properties(bodyStr);
if (properties != null) {
LOGGER.info("updateSharedBrokerConfig, new config: [{}] client: {} ", properties, ctx.channel().remoteAddress());
this.brokerContainer.getConfiguration().update(properties);
} else {
LOGGER.error("string2Properties error");
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("string2Properties error");
return response;
}
} catch (UnsupportedEncodingException e) {
LOGGER.error("", e);
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("UnsupportedEncodingException " + e);
return response;
}
}
response.setCode(ResponseCode.SUCCESS);
response.setRemark(null);
return response;
}
private RemotingCommand getBrokerConfig(ChannelHandlerContext ctx, RemotingCommand request) {
final RemotingCommand response = RemotingCommand.createResponseCommand(GetBrokerConfigResponseHeader.class);
final GetBrokerConfigResponseHeader responseHeader = (GetBrokerConfigResponseHeader) response.readCustomHeader();
String content = this.brokerContainer.getConfiguration().getAllConfigsFormatString();
if (content != null && content.length() > 0) {
try {
response.setBody(content.getBytes(MixAll.DEFAULT_CHARSET));
} catch (UnsupportedEncodingException e) {
LOGGER.error("", e);
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("UnsupportedEncodingException " + e);
return response;
}
}
responseHeader.setVersion(this.brokerContainer.getConfiguration().getDataVersionJson());
response.setCode(ResponseCode.SUCCESS);
response.setRemark(null);
return response;
}
}
@@ -0,0 +1,482 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.BrokerPathConfigHelper;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.MQVersion;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.logging.InternalLogger;
import org.apache.rocketmq.logging.InternalLoggerFactory;
import org.apache.rocketmq.remoting.common.RemotingUtil;
import org.apache.rocketmq.remoting.common.TlsMode;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.remoting.netty.NettySystemConfig;
import org.apache.rocketmq.remoting.netty.TlsSystemConfig;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.store.config.MessageStoreConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BrokerContainerStartup {
private static final char BROKER_CONTAINER_CONFIG_OPTION = 'c';
private static final char BROKER_CONFIG_OPTION = 'b';
private static final char PRINT_PROPERTIES_OPTION = 'p';
private static final String PRINT_IMPORTANT_PROPERTIES_OPTION = "pm";
public static Properties properties = null;
public static CommandLine commandLine = null;
public static String configFile = null;
public static InternalLogger log;
public static SystemConfigFileHelper configFileHelper = new SystemConfigFileHelper();
public static String rocketmqHome = null;
public static JoranConfigurator configurator = new JoranConfigurator();
public static void main(String[] args) {
final BrokerContainer brokerContainer = startBrokerContainer(createBrokerContainer(args));
createAndStartBrokers(brokerContainer);
}
/**
* Start brokerController, for old version compatibility.
*
* @param controller brokerController
* @return brokerController
*/
public static BrokerController start(BrokerController controller) {
try {
controller.start();
String tip = "The broker[" + controller.getBrokerConfig().getBrokerName() + ", "
+ controller.getBrokerAddr() + "] boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer();
if (null != controller.getBrokerConfig().getNamesrvAddr()) {
tip += " and name server is " + controller.getBrokerConfig().getNamesrvAddr();
}
log.info(tip);
System.out.printf("%s%n", tip);
return controller;
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
}
return null;
}
public static BrokerController createBrokerController(String[] args) {
final BrokerContainer sharedBrokerController = startBrokerContainer(createBrokerContainer(args, true));
return createAndInitializeBroker(sharedBrokerController, configFile, properties);
}
public static List<BrokerController> createAndStartBrokers(BrokerContainer brokerContainer) {
String[] configPaths = parseBrokerConfigPath();
List<BrokerController> brokerControllerList = new ArrayList<>();
if (configPaths != null && configPaths.length > 0) {
SystemConfigFileHelper configFileHelper = new SystemConfigFileHelper();
for (String configPath : configPaths) {
System.out.printf("Start broker from config file path %s%n", configPath);
configFileHelper.setFile(configPath);
Properties brokerProperties = null;
try {
brokerProperties = configFileHelper.loadConfig();
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
final BrokerController brokerController = createAndInitializeBroker(brokerContainer, configPath, brokerProperties);
if (brokerController != null) {
brokerControllerList.add(brokerController);
startBrokerController(brokerContainer, brokerController, brokerProperties);
}
}
}
return brokerControllerList;
}
public static String[] parseBrokerConfigPath() {
String brokerConfigList = null;
if (commandLine.hasOption(BROKER_CONFIG_OPTION)) {
brokerConfigList = commandLine.getOptionValue(BROKER_CONFIG_OPTION);
} else if (commandLine.hasOption(BROKER_CONTAINER_CONFIG_OPTION)) {
String brokerContainerConfigPath = commandLine.getOptionValue(BROKER_CONTAINER_CONFIG_OPTION);
if (brokerContainerConfigPath != null) {
BrokerContainerConfig brokerContainerConfig = new BrokerContainerConfig();
SystemConfigFileHelper configFileHelper = new SystemConfigFileHelper();
configFileHelper.setFile(brokerContainerConfigPath);
Properties brokerContainerProperties = null;
try {
brokerContainerProperties = configFileHelper.loadConfig();
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
if (brokerContainerProperties != null) {
MixAll.properties2Object(brokerContainerProperties, brokerContainerConfig);
}
brokerConfigList = brokerContainerConfig.getBrokerConfigPaths();
}
}
if (brokerConfigList != null) {
return brokerConfigList.split(":");
}
return null;
}
public static BrokerController createAndInitializeBroker(BrokerContainer brokerContainer,
String filePath, Properties brokerProperties) {
final BrokerConfig brokerConfig = new BrokerConfig();
final MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
if (brokerProperties != null) {
properties2SystemEnv(brokerProperties);
MixAll.properties2Object(brokerProperties, brokerConfig);
MixAll.properties2Object(brokerProperties, messageStoreConfig);
}
messageStoreConfig.setHaListenPort(brokerConfig.getListenPort() + 1);
MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), brokerConfig);
switch (messageStoreConfig.getBrokerRole()) {
case ASYNC_MASTER:
case SYNC_MASTER:
brokerConfig.setBrokerId(MixAll.MASTER_ID);
break;
case SLAVE:
if (brokerConfig.getBrokerId() <= 0) {
System.out.printf("Slave's brokerId must be > 0%n");
System.exit(-3);
}
break;
default:
break;
}
if (messageStoreConfig.getTotalReplicas() < messageStoreConfig.getInSyncReplicas()
|| messageStoreConfig.getTotalReplicas() < messageStoreConfig.getMinInSyncReplicas()
|| messageStoreConfig.getInSyncReplicas() < messageStoreConfig.getMinInSyncReplicas()) {
System.out.printf("invalid replicas number%n");
System.exit(-3);
}
brokerConfig.setBrokerConfigPath(filePath);
log = InternalLoggerFactory.getLogger(brokerConfig.getLoggerIdentifier() + LoggerName.BROKER_LOGGER_NAME);
MixAll.printObjectProperties(log, brokerConfig);
MixAll.printObjectProperties(log, messageStoreConfig);
try {
BrokerController brokerController = brokerContainer.addBroker(brokerConfig, messageStoreConfig);
if (brokerController != null) {
brokerController.getConfiguration().registerConfig(brokerProperties);
return brokerController;
} else {
System.out.printf("Add broker [%s-%s] failed.%n", brokerConfig.getBrokerName(), brokerConfig.getBrokerId());
}
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
return null;
}
public static BrokerContainer startBrokerContainer(BrokerContainer brokerContainer) {
try {
brokerContainer.start();
String tip = "The broker container boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer();
if (null != brokerContainer.getBrokerContainerConfig().getNamesrvAddr()) {
tip += " and name server is " + brokerContainer.getBrokerContainerConfig().getNamesrvAddr();
}
log.info(tip);
System.out.printf("%s%n", tip);
return brokerContainer;
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
}
return null;
}
public static void startBrokerController(BrokerContainer brokerContainer,
BrokerController brokerController, Properties brokerProperties) {
try {
for (BrokerBootHook hook : brokerContainer.getBrokerBootHookList()) {
hook.executeBeforeStart(brokerController, brokerProperties);
}
brokerController.start();
for (BrokerBootHook hook : brokerContainer.getBrokerBootHookList()) {
hook.executeAfterStart(brokerController, brokerProperties);
}
String tip = String.format("Broker [%s-%s] boot success. serializeType=%s",
brokerController.getBrokerConfig().getBrokerName(),
brokerController.getBrokerConfig().getBrokerId(),
RemotingCommand.getSerializeTypeConfigInThisServer());
log.info(tip);
System.out.printf("%s%n", tip);
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
}
}
public static void shutdown(final BrokerContainer controller) {
if (null != controller) {
controller.shutdown();
}
}
public static BrokerContainer createBrokerContainer(String[] args) {
return createBrokerContainer(args, false);
}
public static BrokerContainer createBrokerContainer(String[] args, boolean useDefaultPort) {
System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));
if (null == System.getProperty(NettySystemConfig.COM_ROCKETMQ_REMOTING_SOCKET_SNDBUF_SIZE)) {
NettySystemConfig.socketSndbufSize = 131072;
}
if (null == System.getProperty(NettySystemConfig.COM_ROCKETMQ_REMOTING_SOCKET_RCVBUF_SIZE)) {
NettySystemConfig.socketRcvbufSize = 131072;
}
try {
//PackageConflictDetect.detectFastjson();
Options options = ServerUtil.buildCommandlineOptions(new Options());
commandLine = ServerUtil.parseCmdLine("mqbroker", args, buildCommandlineOptions(options),
new DefaultParser());
if (null == commandLine) {
System.exit(-1);
}
final BrokerContainerConfig brokerConfig = new BrokerContainerConfig();
final NettyServerConfig nettyServerConfig = new NettyServerConfig();
final NettyClientConfig nettyClientConfig = new NettyClientConfig();
nettyClientConfig.setUseTLS(Boolean.parseBoolean(System.getProperty(TlsSystemConfig.TLS_ENABLE,
String.valueOf(TlsSystemConfig.tlsMode == TlsMode.ENFORCING))));
nettyServerConfig.setListenPort(10811);
if (commandLine.hasOption(BROKER_CONTAINER_CONFIG_OPTION)) {
String file = commandLine.getOptionValue(BROKER_CONTAINER_CONFIG_OPTION);
if (file != null) {
configFileHelper.setFile(file);
configFile = file;
BrokerPathConfigHelper.setBrokerConfigPath(file);
}
}
properties = configFileHelper.loadConfig();
if (properties != null) {
properties2SystemEnv(properties);
MixAll.properties2Object(properties, brokerConfig);
MixAll.properties2Object(properties, nettyServerConfig);
MixAll.properties2Object(properties, nettyClientConfig);
}
if (useDefaultPort) {
nettyServerConfig.setListenPort(10811);
}
MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), brokerConfig);
if (null == brokerConfig.getRocketmqHome()) {
System.out.printf("Please set the %s variable in your environment to match the location of the RocketMQ installation", MixAll.ROCKETMQ_HOME_ENV);
System.exit(-2);
}
rocketmqHome = brokerConfig.getRocketmqHome();
String namesrvAddr = brokerConfig.getNamesrvAddr();
if (null != namesrvAddr) {
try {
String[] addrArray = namesrvAddr.split(";");
for (String addr : addrArray) {
RemotingUtil.string2SocketAddress(addr);
}
} catch (Exception e) {
System.out.printf(
"The Name Server Address[%s] illegal, please set it as follows, \"127.0.0.1:9876;192.168.0.1:9876\"%n",
namesrvAddr);
System.exit(-3);
}
}
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
configurator.setContext(lc);
lc.reset();
//https://logback.qos.ch/manual/configuration.html
lc.setPackagingDataEnabled(false);
configurator.doConfigure(rocketmqHome + "/conf/logback_broker.xml");
if (commandLine.hasOption(PRINT_PROPERTIES_OPTION)) {
InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);
MixAll.printObjectProperties(console, brokerConfig);
MixAll.printObjectProperties(console, nettyServerConfig);
MixAll.printObjectProperties(console, nettyClientConfig);
System.exit(0);
} else if (commandLine.hasOption(PRINT_IMPORTANT_PROPERTIES_OPTION)) {
InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);
MixAll.printObjectProperties(console, brokerConfig, true);
MixAll.printObjectProperties(console, nettyServerConfig, true);
MixAll.printObjectProperties(console, nettyClientConfig, true);
System.exit(0);
}
log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
MixAll.printObjectProperties(log, brokerConfig);
MixAll.printObjectProperties(log, nettyServerConfig);
MixAll.printObjectProperties(log, nettyClientConfig);
final BrokerContainer brokerContainer = new BrokerContainer(
brokerConfig,
nettyServerConfig,
nettyClientConfig);
// remember all configs to prevent discard
brokerContainer.getConfiguration().registerConfig(properties);
boolean initResult = brokerContainer.initialize();
if (!initResult) {
brokerContainer.shutdown();
System.exit(-3);
}
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
private volatile boolean hasShutdown = false;
private AtomicInteger shutdownTimes = new AtomicInteger(0);
@Override
public void run() {
synchronized (this) {
log.info("Shutdown hook was invoked, {}", this.shutdownTimes.incrementAndGet());
if (!this.hasShutdown) {
this.hasShutdown = true;
long beginTime = System.currentTimeMillis();
brokerContainer.shutdown();
long consumingTimeTotal = System.currentTimeMillis() - beginTime;
log.info("Shutdown hook over, consuming total time(ms): {}", consumingTimeTotal);
}
}
}
}, "ShutdownHook"));
return brokerContainer;
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
}
return null;
}
private static void properties2SystemEnv(Properties properties) {
if (properties == null) {
return;
}
String rmqAddressServerDomain = properties.getProperty("rmqAddressServerDomain", MixAll.WS_DOMAIN_NAME);
String rmqAddressServerSubGroup = properties.getProperty("rmqAddressServerSubGroup", MixAll.WS_DOMAIN_SUBGROUP);
System.setProperty("rocketmq.namesrv.domain", rmqAddressServerDomain);
System.setProperty("rocketmq.namesrv.domain.subgroup", rmqAddressServerSubGroup);
}
private static Options buildCommandlineOptions(final Options options) {
Option opt = new Option("c", "configFile", true, "Config file for shared broker");
opt.setRequired(false);
options.addOption(opt);
opt = new Option("p", "printConfigItem", false, "Print all config item");
opt.setRequired(false);
options.addOption(opt);
opt = new Option("m", "printImportantConfig", false, "Print important config item");
opt.setRequired(false);
options.addOption(opt);
opt = new Option("b", "brokerConfigFiles", true, "The path of broker config files, split by ':'");
opt.setRequired(false);
options.addOption(opt);
return options;
}
public static class SystemConfigFileHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(SystemConfigFileHelper.class);
private String file;
public SystemConfigFileHelper() {
}
public Properties loadConfig() throws Exception {
InputStream in = new BufferedInputStream(new FileInputStream(file));
Properties properties = new Properties();
properties.load(in);
in.close();
return properties;
}
public void update(Properties properties) throws Exception {
LOGGER.error("[SystemConfigFileHelper] update no thing.");
}
public void setFile(String file) {
this.file = file;
}
public String getFile() {
return file;
}
}
}
@@ -0,0 +1,277 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import org.apache.rocketmq.broker.plugin.BrokerAttachedPlugin;
import org.apache.rocketmq.common.BrokerSyncInfo;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.ServiceThread;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.protocol.body.BrokerMemberGroup;
import org.apache.rocketmq.common.protocol.body.ConsumerOffsetSerializeWrapper;
import org.apache.rocketmq.logging.InternalLogger;
import org.apache.rocketmq.logging.InternalLoggerFactory;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.store.config.StorePathConfigHelper;
import org.apache.rocketmq.broker.schedule.DelayOffsetSerializeWrapper;
import org.apache.rocketmq.store.ha.HAConnectionState;
import org.apache.rocketmq.store.ha.HAConnectionStateNotificationRequest;
public class BrokerPreOnlineService extends ServiceThread {
private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
private final InnerBrokerController brokerController;
private int waitBrokerIndex = 0;
public BrokerPreOnlineService(InnerBrokerController brokerController) {
this.brokerController = brokerController;
}
@Override
public String getServiceName() {
if (this.brokerController != null && this.brokerController.getBrokerConfig().isInBrokerContainer()) {
return brokerController.getBrokerConfig().getLoggerIdentifier() + BrokerPreOnlineService.class.getSimpleName();
}
return BrokerPreOnlineService.class.getSimpleName();
}
@Override
public void run() {
LOGGER.info(this.getServiceName() + " service started");
while (!this.isStopped()) {
if (!this.brokerController.isIsolated()) {
LOGGER.info("broker {} is online", this.brokerController.getBrokerConfig().getCanonicalName());
break;
}
try {
boolean isSuccess = this.prepareForBrokerOnline();
if (!isSuccess) {
this.waitForRunning(1000);
} else {
break;
}
} catch (Exception e) {
LOGGER.error("Broker preOnline error, ", e);
}
}
LOGGER.info(this.getServiceName() + " service end");
}
CompletableFuture<Boolean> waitForHaHandshakeComplete(String brokerAddr) {
LOGGER.info("wait for handshake completion with {}", brokerAddr);
HAConnectionStateNotificationRequest request =
new HAConnectionStateNotificationRequest(HAConnectionState.TRANSFER, RemotingHelper.parseHostFromAddress(brokerAddr), true);
if (this.brokerController.getMessageStore().getHaService() != null) {
this.brokerController.getMessageStore().getHaService().putGroupConnectionStateRequest(request);
} else {
LOGGER.error("HAService is null, maybe broker config is wrong. For example, duplicationEnable is true");
request.getRequestFuture().complete(false);
}
return request.getRequestFuture();
}
private boolean futureWaitAction(boolean result, BrokerMemberGroup brokerMemberGroup) {
if (!result) {
LOGGER.error("wait for handshake completion failed, HA connection lost");
return false;
}
if (this.brokerController.getBrokerConfig().getBrokerId() != MixAll.MASTER_ID) {
LOGGER.info("slave preOnline complete, start service");
long minBrokerId = getMinBrokerId(brokerMemberGroup.getBrokerAddrs());
this.brokerController.startService(minBrokerId, brokerMemberGroup.getBrokerAddrs().get(minBrokerId));
}
return true;
}
private boolean prepareForMasterOnline(BrokerMemberGroup brokerMemberGroup) {
List<Long> brokerIdList = new ArrayList<>(brokerMemberGroup.getBrokerAddrs().keySet());
Collections.sort(brokerIdList);
while (true) {
if (waitBrokerIndex >= brokerIdList.size()) {
LOGGER.info("master preOnline complete, start service");
this.brokerController.startService(MixAll.MASTER_ID, this.brokerController.getBrokerAddr());
return true;
}
String brokerAddrToWait = brokerMemberGroup.getBrokerAddrs().get(brokerIdList.get(waitBrokerIndex));
try {
this.brokerController.getBrokerOuterAPI().
sendBrokerHaInfo(brokerAddrToWait, this.brokerController.getHAServerAddr(),
this.brokerController.getMessageStore().getBrokerInitMaxOffset(), this.brokerController.getBrokerAddr());
} catch (Exception e) {
LOGGER.error("send ha address to {} exception, {}", brokerAddrToWait, e);
return false;
}
CompletableFuture<Boolean> haHandshakeFuture = waitForHaHandshakeComplete(brokerAddrToWait)
.thenApply(result -> futureWaitAction(result, brokerMemberGroup));
try {
if (!haHandshakeFuture.get()) {
return false;
}
} catch (Exception e) {
LOGGER.error("Wait handshake completion exception, {}", e);
return false;
}
if (syncMetadataReverse(brokerAddrToWait)) {
waitBrokerIndex++;
} else {
return false;
}
}
}
private boolean syncMetadataReverse(String brokerAddr) {
try {
LOGGER.info("Get metadata reverse from {}", brokerAddr);
String delayOffset = this.brokerController.getBrokerOuterAPI().getAllDelayOffset(brokerAddr);
DelayOffsetSerializeWrapper delayOffsetSerializeWrapper =
DelayOffsetSerializeWrapper.fromJson(delayOffset, DelayOffsetSerializeWrapper.class);
ConsumerOffsetSerializeWrapper consumerOffsetSerializeWrapper = this.brokerController.getBrokerOuterAPI().getAllConsumerOffset(brokerAddr);
if (null != consumerOffsetSerializeWrapper && brokerController.getConsumerOffsetManager().getDataVersion().compare(consumerOffsetSerializeWrapper.getDataVersion()) <= 0) {
LOGGER.info("{}'s consumerOffset data version is larger than master broker, {}'s consumerOffset will be used.", brokerAddr, brokerAddr);
this.brokerController.getConsumerOffsetManager().getOffsetTable()
.putAll(consumerOffsetSerializeWrapper.getOffsetTable());
this.brokerController.getConsumerOffsetManager().getDataVersion().assignNewOne(consumerOffsetSerializeWrapper.getDataVersion());
this.brokerController.getConsumerOffsetManager().persist();
}
if (null != delayOffset && brokerController.getScheduleMessageService().getDataVersion().compare(delayOffsetSerializeWrapper.getDataVersion()) <= 0) {
LOGGER.info("{}'s scheduleMessageService data version is larger than master broker, {}'s delayOffset will be used.", brokerAddr, brokerAddr);
String fileName =
StorePathConfigHelper.getDelayOffsetStorePath(this.brokerController
.getMessageStoreConfig().getStorePathRootDir());
try {
MixAll.string2File(delayOffset, fileName);
this.brokerController.getScheduleMessageService().load();
} catch (IOException e) {
LOGGER.error("Persist file Exception, {}", fileName, e);
}
}
for (BrokerAttachedPlugin brokerAttachedPlugin : brokerController.getBrokerAttachedPlugins()) {
if (brokerAttachedPlugin != null) {
brokerAttachedPlugin.syncMetadataReverse(brokerAddr);
}
}
} catch (Exception e) {
LOGGER.error("GetMetadataReverse Failed", e);
return false;
}
return true;
}
private boolean prepareForSlaveOnline(BrokerMemberGroup brokerMemberGroup) {
BrokerSyncInfo brokerSyncInfo;
try {
brokerSyncInfo = this.brokerController.getBrokerOuterAPI()
.retrieveBrokerHaInfo(brokerMemberGroup.getBrokerAddrs().get(MixAll.MASTER_ID));
} catch (Exception e) {
LOGGER.error("retrieve master ha info exception, {}", e);
return false;
}
if (this.brokerController.getMessageStore().getMasterFlushedOffset() == 0
&& this.brokerController.getMessageStoreConfig().isSyncMasterFlushOffsetWhenStartup()) {
LOGGER.info("Set master flush offset in slave to {}", brokerSyncInfo.getMasterFlushOffset());
this.brokerController.getMessageStore().setMasterFlushedOffset(brokerSyncInfo.getMasterFlushOffset());
}
if (brokerSyncInfo.getMasterHaAddress() != null) {
this.brokerController.getMessageStore().updateHaMasterAddress(brokerSyncInfo.getMasterHaAddress());
this.brokerController.getMessageStore().updateMasterAddress(brokerSyncInfo.getMasterAddress());
} else {
LOGGER.info("fetch master ha address return null, start service directly");
long minBrokerId = getMinBrokerId(brokerMemberGroup.getBrokerAddrs());
this.brokerController.startService(minBrokerId, brokerMemberGroup.getBrokerAddrs().get(minBrokerId));
return true;
}
CompletableFuture<Boolean> haHandshakeFuture = waitForHaHandshakeComplete(brokerSyncInfo.getMasterHaAddress())
.thenApply(result -> futureWaitAction(result, brokerMemberGroup));
try {
if (!haHandshakeFuture.get()) {
return false;
}
} catch (Exception e) {
LOGGER.error("Wait handshake completion exception, {}", e);
return false;
}
return true;
}
private boolean prepareForBrokerOnline() {
BrokerMemberGroup brokerMemberGroup;
try {
brokerMemberGroup = this.brokerController.getBrokerOuterAPI().syncBrokerMemberGroup(
this.brokerController.getBrokerConfig().getBrokerClusterName(),
this.brokerController.getBrokerConfig().getBrokerName(),
this.brokerController.getBrokerContainer().getBrokerContainerConfig().isCompatibleWithOldNameSrv());
} catch (Exception e) {
LOGGER.error("syncBrokerMemberGroup from namesrv error, start service failed, will try later, ", e);
return false;
}
if (brokerMemberGroup != null && !brokerMemberGroup.getBrokerAddrs().isEmpty()) {
long minBrokerId = getMinBrokerId(brokerMemberGroup.getBrokerAddrs());
if (this.brokerController.getBrokerConfig().getBrokerId() == MixAll.MASTER_ID) {
return prepareForMasterOnline(brokerMemberGroup);
} else if (minBrokerId == MixAll.MASTER_ID) {
return prepareForSlaveOnline(brokerMemberGroup);
} else {
LOGGER.info("no master online, start service directly");
this.brokerController.startService(minBrokerId, brokerMemberGroup.getBrokerAddrs().get(minBrokerId));
}
} else {
LOGGER.info("no other broker online, will start service directly");
this.brokerController.startService(this.brokerController.getBrokerConfig().getBrokerId(), this.brokerController.getBrokerAddr());
}
return true;
}
private long getMinBrokerId(Map<Long, String> brokerAddrMap) {
Map<Long, String> brokerAddrMapCopy = new HashMap<>(brokerAddrMap);
brokerAddrMapCopy.remove(this.brokerController.getBrokerConfig().getBrokerId());
if (!brokerAddrMapCopy.isEmpty()) {
return Collections.min(brokerAddrMapCopy.keySet());
}
return this.brokerController.getBrokerConfig().getBrokerId();
}
}
@@ -0,0 +1,104 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import io.netty.channel.Channel;
import java.util.Collection;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.remoting.ChannelEventListener;
public class ContainerClientHouseKeepingService implements ChannelEventListener {
private final IBrokerContainer brokerContainer;
public ContainerClientHouseKeepingService(final IBrokerContainer brokerContainer) {
this.brokerContainer = brokerContainer;
}
@Override public void onChannelConnect(String remoteAddr, Channel channel) {
onChannelOperation(CallbackCode.CONNECT, remoteAddr, channel);
}
@Override public void onChannelClose(String remoteAddr, Channel channel) {
onChannelOperation(CallbackCode.CLOSE, remoteAddr, channel);
}
@Override public void onChannelException(String remoteAddr, Channel channel) {
onChannelOperation(CallbackCode.EXCEPTION, remoteAddr, channel);
}
@Override public void onChannelIdle(String remoteAddr, Channel channel) {
onChannelOperation(CallbackCode.IDLE, remoteAddr, channel);
}
private void onChannelOperation(CallbackCode callbackCode, String remoteAddr, Channel channel) {
Collection<InnerBrokerController> masterBrokers = this.brokerContainer.getMasterBrokers();
Collection<InnerSalveBrokerController> slaveBrokers = this.brokerContainer.getSlaveBrokers();
for (BrokerController masterBroker : masterBrokers) {
brokerOperation(masterBroker, callbackCode, remoteAddr, channel);
}
for (InnerSalveBrokerController slaveBroker : slaveBrokers) {
brokerOperation(slaveBroker, callbackCode, remoteAddr, channel);
}
}
private void brokerOperation(BrokerController brokerController, CallbackCode callbackCode, String remoteAddr,
Channel channel) {
if (callbackCode == CallbackCode.CONNECT) {
brokerController.getBrokerStatsManager().incChannelConnectNum();
return;
}
boolean removed = brokerController.getProducerManager().doChannelCloseEvent(remoteAddr, channel);
removed &= brokerController.getConsumerManager().doChannelCloseEvent(remoteAddr, channel);
if (removed) {
switch (callbackCode) {
case CLOSE:
brokerController.getBrokerStatsManager().incChannelCloseNum();
break;
case EXCEPTION:
brokerController.getBrokerStatsManager().incChannelExceptionNum();
break;
case IDLE:
brokerController.getBrokerStatsManager().incChannelIdleNum();
break;
default:
break;
}
}
}
public enum CallbackCode {
/**
* onChannelConnect
*/
CONNECT,
/**
* onChannelClose
*/
CLOSE,
/**
* onChannelException
*/
EXCEPTION,
/**
* onChannelIdle
*/
IDLE
}
}
@@ -0,0 +1,142 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import java.util.Collection;
import java.util.List;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.out.BrokerOuterAPI;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.BrokerIdentity;
import org.apache.rocketmq.remoting.RemotingServer;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.store.config.MessageStoreConfig;
/**
* An interface for broker container to hold multiple master and slave brokers.
*/
public interface IBrokerContainer {
/**
* Start broker container
*/
void start() throws Exception;
/**
* Shutdown broker container and all the brokers inside.
*/
void shutdown();
/**
* Add a broker to this container with specific broker config.
*
* @param brokerConfig the specified broker config
* @param storeConfig the specified store config
* @return the added BrokerController or null if the broker already exists
* @throws Exception when initialize broker
*/
BrokerController addBroker(BrokerConfig brokerConfig, MessageStoreConfig storeConfig) throws Exception;
/**
* Remove the broker from this container associated with the specific broker identity
*
* @param brokerIdentity the specific broker identity
* @return the removed BrokerController or null if the broker doesn't exists
*/
BrokerController removeBroker(BrokerIdentity brokerIdentity) throws Exception;
/**
* Return the broker controller associated with the specific broker identity
*
* @param brokerIdentity the specific broker identity
* @return the associated messaging broker or null
*/
BrokerController getBroker(BrokerIdentity brokerIdentity);
/**
* Return all the master brokers belong to this container
*
* @return the master broker list
*/
Collection<InnerBrokerController> getMasterBrokers();
/**
* Return all the slave brokers belong to this container
*
* @return the slave broker list
*/
Collection<InnerSalveBrokerController> getSlaveBrokers();
/**
* Return all broker controller in this container
*
* @return all broker controller
*/
List<BrokerController> getBrokerControllers();
/**
* Return the address of broker container.
*
* @return broker container address.
*/
String getBrokerContainerAddr();
/**
* Peek the first master broker in container.
*
* @return the first master broker in container
*/
BrokerController peekMasterBroker();
/**
* Return the config of the broker container
*
* @return the broker container config
*/
BrokerContainerConfig getBrokerContainerConfig();
/**
* Get netty server config.
*
* @return netty server config
*/
NettyServerConfig getNettyServerConfig();
/**
* Get netty client config.
*
* @return netty client config
*/
NettyClientConfig getNettyClientConfig();
/**
* Return the shared BrokerOuterAPI
*
* @return the shared BrokerOuterAPI
*/
BrokerOuterAPI getBrokerOuterAPI();
/**
* Return the shared RemotingServer
*
* @return the shared RemotingServer
*/
RemotingServer getRemotingServer();
}
@@ -0,0 +1,378 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.out.BrokerOuterAPI;
import org.apache.rocketmq.common.AbstractBrokerRunnable;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.ThreadFactoryImpl;
import org.apache.rocketmq.common.namesrv.RegisterBrokerResult;
import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.store.MessageStore;
import org.apache.rocketmq.store.config.MessageStoreConfig;
public class InnerBrokerController extends BrokerController {
private ScheduledExecutorService syncBrokerMemberGroupExecutorService;
private ScheduledExecutorService brokerHeartbeatExecutorService;
protected volatile long minBrokerIdInGroup = 0;
protected volatile String minBrokerAddrInGroup = null;
protected final List<ScheduledFuture<?>> scheduledFutures = new ArrayList<>();
private BrokerPreOnlineService brokerPreOnlineService;
protected BrokerContainer brokerContainer;
protected volatile boolean isIsolated = false;
public InnerBrokerController(
final BrokerContainer brokerContainer,
final BrokerConfig brokerConfig,
final MessageStoreConfig messageStoreConfig
) {
super(brokerConfig, messageStoreConfig);
this.brokerContainer = brokerContainer;
this.brokerOuterAPI = this.brokerContainer.getBrokerOuterAPI();
if (!this.brokerConfig.isSkipPreOnline()) {
this.brokerPreOnlineService = new BrokerPreOnlineService(this);
}
}
@Override
protected void initializeRemotingServer() {
this.remotingServer = this.brokerContainer.getRemotingServer().newRemotingServer(brokerConfig.getListenPort());
this.fastRemotingServer = this.brokerContainer.getRemotingServer().newRemotingServer(brokerConfig.getListenPort() - 2);
}
/**
* Initialize resources for master which will be re-used by slave.
*/
@Override
protected void initializeResources() {
super.initializeResources();
this.syncBrokerMemberGroupExecutorService = new ScheduledThreadPoolExecutor(1,
new ThreadFactoryImpl("BrokerControllerSyncBrokerScheduledThread", brokerConfig));
this.brokerHeartbeatExecutorService = new ScheduledThreadPoolExecutor(1,
new ThreadFactoryImpl("rokerControllerHeartbeatScheduledThread", brokerConfig));
}
@Override
protected void initializeScheduledTasks() {
initializeBrokerScheduledTasks();
}
@Override
public void shutdown() {
shutdownBasicService();
if (this.remotingServer != null) {
this.brokerContainer.getRemotingServer().removeRemotingServer(brokerConfig.getListenPort());
}
if (this.fastRemotingServer != null) {
this.brokerContainer.getRemotingServer().removeRemotingServer(brokerConfig.getListenPort() - 2);
}
for (ScheduledFuture<?> scheduledFuture : scheduledFutures) {
scheduledFuture.cancel(true);
}
if (this.brokerPreOnlineService != null && !this.brokerPreOnlineService.isStopped()) {
this.brokerPreOnlineService.shutdown();
}
shutdownScheduledExecutorService(this.syncBrokerMemberGroupExecutorService);
shutdownScheduledExecutorService(this.brokerHeartbeatExecutorService);
}
@Override
public String getBrokerAddr() {
return this.brokerConfig.getBrokerIP1() + ":" + this.brokerConfig.getListenPort();
}
@Override
public void start() throws Exception {
this.shouldStartTime = System.currentTimeMillis() + messageStoreConfig.getDisappearTimeAfterStart();
if (messageStoreConfig.getTotalReplicas() > 1) {
isIsolated = true;
}
startBasicService();
if (this.brokerPreOnlineService != null) {
this.brokerPreOnlineService.start();
}
if (!isIsolated) {
this.registerBrokerAll(true, false, true);
}
scheduledFutures.add(this.scheduledExecutorService.scheduleAtFixedRate(new AbstractBrokerRunnable(this.brokerConfig) {
@Override
public void run2() {
try {
if (System.currentTimeMillis() < shouldStartTime) {
BrokerController.LOG.info("Register to namesrv after {}", shouldStartTime);
return;
}
if (isIsolated) {
BrokerController.LOG.info("Skip register for broker is isolated");
return;
}
InnerBrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());
} catch (Throwable e) {
BrokerController.LOG.error("registerBrokerAll Exception", e);
}
}
}, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS));
if (this.brokerConfig.isEnableSlaveActingMaster()) {
scheduledFutures.add(this.brokerHeartbeatExecutorService.scheduleAtFixedRate(new AbstractBrokerRunnable(this.brokerConfig) {
@Override
public void run2() {
if (isIsolated) {
return;
}
try {
InnerBrokerController.this.sendHeartbeat();
} catch (Exception e) {
BrokerController.LOG.error("sendHeartbeat Exception", e);
}
}
}, 1000, brokerConfig.getBrokerHeartbeatInterval(), TimeUnit.MILLISECONDS));
scheduledFutures.add(this.syncBrokerMemberGroupExecutorService.scheduleAtFixedRate(new AbstractBrokerRunnable(this.brokerConfig) {
@Override public void run2() {
try {
InnerBrokerController.this.syncBrokerMemberGroup();
} catch (Throwable e) {
BrokerController.LOG.error("sync BrokerMemberGroup error. ", e);
}
}
}, 1000, this.brokerConfig.getSyncBrokerMemberGroupPeriod(), TimeUnit.MILLISECONDS));
}
if (!isIsolated && !messageStoreConfig.isEnableDLegerCommitLog()
&& !messageStoreConfig.isDuplicationEnable() && !this.brokerConfig.isEnableSlaveActingMaster()) {
changeSpecialServiceStatus(this.brokerConfig.getBrokerId() == MixAll.MASTER_ID);
}
if (brokerConfig.isSkipPreOnline()) {
startServiceWithoutCondition();
}
}
private void sendHeartbeat() {
if (this.brokerContainer.getBrokerContainerConfig().isCompatibleWithOldNameSrv()) {
this.brokerOuterAPI.sendHeartbeatViaDataVersion(
this.brokerConfig.getBrokerClusterName(),
this.getBrokerAddr(),
this.brokerConfig.getBrokerName(),
this.brokerConfig.getBrokerId(),
this.brokerConfig.getSendHeartbeatTimeoutMillis(),
this.getTopicConfigManager().getDataVersion(),
this.brokerConfig.isInBrokerContainer());
} else {
this.brokerOuterAPI.sendHeartbeat(
this.brokerConfig.getBrokerClusterName(),
this.getBrokerAddr(),
this.brokerConfig.getBrokerName(),
this.brokerConfig.getBrokerId(),
this.brokerConfig.getSendHeartbeatTimeoutMillis(),
this.brokerConfig.isInBrokerContainer());
}
}
public void syncBrokerMemberGroup() {
try {
brokerMemberGroup = this.brokerContainer.getBrokerOuterAPI()
.syncBrokerMemberGroup(this.brokerConfig.getBrokerClusterName(), this.brokerConfig.getBrokerName(), this.brokerContainer.getBrokerContainerConfig().isCompatibleWithOldNameSrv());
} catch (Exception e) {
BrokerController.LOG.error("syncBrokerMemberGroup from namesrv failed, ", e);
return;
}
if (brokerMemberGroup == null || brokerMemberGroup.getBrokerAddrs().size() == 0) {
BrokerController.LOG.warn("Couldn't find any broker member from namesrv in {}/{}", this.brokerConfig.getBrokerClusterName(), this.brokerConfig.getBrokerName());
return;
}
this.messageStore.setAliveReplicaNumInGroup(calcAliveBrokerNumInGroup(brokerMemberGroup.getBrokerAddrs()));
if (!this.isIsolated) {
long minBrokerId = brokerMemberGroup.minimumBrokerId();
this.updateMinBroker(minBrokerId, brokerMemberGroup.getBrokerAddrs().get(minBrokerId));
}
}
private int calcAliveBrokerNumInGroup(Map<Long, String> brokerAddrTable) {
if (brokerAddrTable.containsKey(this.brokerConfig.getBrokerId())) {
return brokerAddrTable.size();
} else {
return brokerAddrTable.size() + 1;
}
}
@Override
protected void doRegisterBrokerAll(boolean checkOrderConfig, boolean oneway,
TopicConfigSerializeWrapper topicConfigWrapper) {
if (shutdown) {
BrokerController.LOG.info("BrokerController#doResterBrokerAll: broker has shutdown, no need to register any more.");
return;
}
List<RegisterBrokerResult> registerBrokerResultList = this.brokerOuterAPI.registerBrokerAll(
this.brokerConfig.getBrokerClusterName(),
this.getBrokerAddr(),
this.brokerConfig.getBrokerName(),
this.brokerConfig.getBrokerId(),
this.getHAServerAddr(),
topicConfigWrapper,
this.filterServerManager.buildNewFilterServerList(),
oneway,
this.brokerConfig.getRegisterBrokerTimeoutMills(),
this.brokerConfig.isEnableSlaveActingMaster(),
this.brokerConfig.isCompressedRegister(),
this.brokerConfig.isEnableSlaveActingMaster() ? this.brokerConfig.getBrokerNotActiveTimeoutMillis() : null,
this.brokerConfig.isInBrokerContainer());
handleRegisterBrokerResult(registerBrokerResultList, checkOrderConfig);
}
@Override
public String getNameServerList() {
if (this.brokerContainer.getBrokerContainerConfig().getNamesrvAddr() != null) {
this.brokerContainer.getBrokerOuterAPI().updateNameServerAddressList(brokerContainer.getBrokerContainerConfig().getNamesrvAddr());
return this.brokerContainer.getBrokerContainerConfig().getNamesrvAddr();
} else if (this.brokerContainer.getBrokerContainerConfig().isFetchNamesrvAddrByAddressServer()) {
return this.brokerContainer.getBrokerOuterAPI().fetchNameServerAddr();
}
return null;
}
@Override
public String getHAServerAddr() {
return this.brokerConfig.getBrokerIP2() + ":" + this.messageStoreConfig.getHaListenPort();
}
@Override
public long getMinBrokerIdInGroup() {
return this.minBrokerIdInGroup;
}
@Override
public boolean isSpecialServiceRunning() {
if (isScheduleServiceStart() && isTransactionCheckServiceStart()) {
return true;
}
return this.ackMessageProcessor != null && this.ackMessageProcessor.isPopReviveServiceRunning();
}
@Override
public int getListenPort() {
return this.brokerConfig.getListenPort();
}
public BrokerOuterAPI getBrokerOuterAPI() {
return brokerContainer.getBrokerOuterAPI();
}
public void startService(long minBrokerId, String minBrokerAddr) {
BrokerController.LOG.info("{} start service, min broker id is {}, min broker addr: {}",
this.brokerConfig.getCanonicalName(), minBrokerId, minBrokerAddr);
this.minBrokerIdInGroup = minBrokerId;
this.minBrokerAddrInGroup = minBrokerAddr;
this.changeSpecialServiceStatus(this.brokerConfig.getBrokerId() == minBrokerId);
this.registerBrokerAll(true, false, brokerConfig.isForceRegister());
isIsolated = false;
}
public void startServiceWithoutCondition() {
BrokerController.LOG.info("{} start service", this.brokerConfig.getCanonicalName());
this.changeSpecialServiceStatus(this.brokerConfig.getBrokerId() == MixAll.MASTER_ID);
this.registerBrokerAll(true, false, brokerConfig.isForceRegister());
isIsolated = false;
}
public void stopService() {
BrokerController.LOG.info("{} stop service", this.getBrokerConfig().getCanonicalName());
isIsolated = true;
this.changeSpecialServiceStatus(false);
this.closeChannels();
}
public synchronized void closeChannels() {
this.brokerContainer.getBrokerOuterAPI().getRemotingClient().closeChannels();
}
public BrokerContainer getBrokerContainer() {
return this.brokerContainer;
}
public boolean isIsolated() {
return this.isIsolated;
}
public NettyServerConfig getNettyServerConfig() {
return brokerContainer.getNettyServerConfig();
}
public NettyClientConfig getNettyClientConfig() {
return brokerContainer.getNettyClientConfig();
}
public MessageStore getMessageStoreByBrokerName(String brokerName) {
if (this.brokerConfig.getBrokerName().equals(brokerName)) {
return this.getMessageStore();
}
BrokerController brokerController = this.brokerContainer.findBrokerControllerByBrokerName(brokerName);
if (brokerController != null) {
return brokerController.getMessageStore();
}
return null;
}
@Override
public BrokerController peekMasterBroker() {
if (brokerConfig.getBrokerId() == MixAll.MASTER_ID) {
return this;
}
return this.brokerContainer.peekMasterBroker();
}
public BrokerPreOnlineService getBrokerPreOnlineService() {
return brokerPreOnlineService;
}
}
@@ -0,0 +1,160 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import com.google.common.base.Preconditions;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.BrokerSyncInfo;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.store.config.MessageStoreConfig;
public class InnerSalveBrokerController extends InnerBrokerController {
private final Lock lock = new ReentrantLock();
public InnerSalveBrokerController(final BrokerContainer brokerContainer,
final BrokerConfig brokerConfig,
final MessageStoreConfig storeConfig) {
super(brokerContainer, brokerConfig, storeConfig);
// Check configs
checkSlaveBrokerConfig();
}
private void checkSlaveBrokerConfig() {
Preconditions.checkNotNull(brokerConfig.getBrokerClusterName());
Preconditions.checkNotNull(brokerConfig.getBrokerName());
Preconditions.checkArgument(brokerConfig.getBrokerId() != MixAll.MASTER_ID);
}
private void onMasterOffline() {
// close channels with master broker
String masterAddr = this.slaveSynchronize.getMasterAddr();
if (masterAddr != null) {
this.brokerOuterAPI.getRemotingClient().closeChannels(
Arrays.asList(masterAddr, MixAll.brokerVIPChannel(true, masterAddr)));
}
// master not available, stop sync
this.slaveSynchronize.setMasterAddr(null);
this.messageStore.updateHaMasterAddress(null);
}
private void onMasterOnline(String masterAddr, String masterHaAddr) {
boolean needSyncMasterFlushOffset = this.messageStore.getMasterFlushedOffset() == 0
&& this.messageStoreConfig.isSyncMasterFlushOffsetWhenStartup();
if (masterHaAddr == null || needSyncMasterFlushOffset) {
try {
BrokerSyncInfo brokerSyncInfo = this.brokerOuterAPI.retrieveBrokerHaInfo(masterAddr);
if (needSyncMasterFlushOffset) {
LOG.info("Set master flush offset in slave to {}", brokerSyncInfo.getMasterFlushOffset());
this.messageStore.setMasterFlushedOffset(brokerSyncInfo.getMasterFlushOffset());
}
if (masterHaAddr == null) {
this.messageStore.updateHaMasterAddress(brokerSyncInfo.getMasterHaAddress());
this.messageStore.updateMasterAddress(brokerSyncInfo.getMasterAddress());
}
} catch (Exception e) {
LOG.error("retrieve master ha info exception, {}", e);
}
}
// set master HA address.
if (masterHaAddr != null) {
this.messageStore.updateHaMasterAddress(masterHaAddr);
}
// wakeup HAClient
this.messageStore.wakeupHAClient();
}
private void onMinBrokerChange(long minBrokerId, String minBrokerAddr, String offlineBrokerAddr,
String masterHaAddr) {
LOG.info("Min broker changed, old: {}-{}, new {}-{}",
this.minBrokerIdInGroup, this.minBrokerAddrInGroup, minBrokerId, minBrokerAddr);
this.minBrokerIdInGroup = minBrokerId;
this.minBrokerAddrInGroup = minBrokerAddr;
this.changeSpecialServiceStatus(this.brokerConfig.getBrokerId() == this.minBrokerIdInGroup);
if (offlineBrokerAddr != null && offlineBrokerAddr.equals(this.slaveSynchronize.getMasterAddr())) {
// master offline
onMasterOffline();
}
if (minBrokerId == MixAll.MASTER_ID && minBrokerAddr != null) {
// master online
onMasterOnline(minBrokerAddr, masterHaAddr);
}
// notify PullRequest on hold to pull from master.
if (this.minBrokerIdInGroup == MixAll.MASTER_ID) {
this.pullRequestHoldService.notifyMasterOnline();
}
}
@Override
public void updateMinBroker(long minBrokerId, String minBrokerAddr) {
if (lock.tryLock()) {
try {
if (minBrokerId != this.minBrokerIdInGroup) {
String offlineBrokerAddr = null;
if (minBrokerId > this.minBrokerIdInGroup) {
offlineBrokerAddr = this.minBrokerAddrInGroup;
}
onMinBrokerChange(minBrokerId, minBrokerAddr, offlineBrokerAddr, null);
}
} finally {
lock.unlock();
}
}
}
@Override
public void updateMinBroker(long minBrokerId, String minBrokerAddr, String offlineBrokerAddr,
String masterHaAddr) {
try {
if (lock.tryLock(3000, TimeUnit.MILLISECONDS)) {
try {
if (minBrokerId != this.minBrokerIdInGroup) {
onMinBrokerChange(minBrokerId, minBrokerAddr, offlineBrokerAddr, masterHaAddr);
}
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
LOG.error("Update min broker error, {}", e);
}
}
@Override
public BrokerController peekMasterBroker() {
return this.brokerContainer.peekMasterBroker();
}
}
@@ -0,0 +1,187 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container.logback;
import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.RollingPolicy;
import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP;
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
import ch.qos.logback.core.rolling.TimeBasedFileNamingAndTriggeringPolicy;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import ch.qos.logback.core.util.FileSize;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.logging.InternalLogger;
import org.apache.rocketmq.logging.InternalLoggerFactory;
import org.slf4j.LoggerFactory;
public class BrokerLogbackConfigurator {
private static final InternalLogger LOG = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
private static final Set<String> CONFIGURED_BROKER_LIST = new HashSet<>();
public static final String ROCKETMQ_LOGS = "rocketmqlogs";
public static final String ROCKETMQ_PREFIX = "Rocketmq";
public static final String SUFFIX_CONSOLE = "Console";
public static final String SUFFIX_APPENDER = "Appender";
public static final String SUFFIX_INNER_APPENDER = "_inner";
public static void doConfigure(BrokerConfig brokerConfig) {
if (!CONFIGURED_BROKER_LIST.contains(brokerConfig.getCanonicalName())) {
try {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
for (ch.qos.logback.classic.Logger tempLogger : lc.getLoggerList()) {
String loggerName = tempLogger.getName();
if (loggerName.startsWith(ROCKETMQ_PREFIX)
&& !loggerName.endsWith(SUFFIX_CONSOLE)
&& !loggerName.equals(LoggerName.ACCOUNT_LOGGER_NAME)
&& !loggerName.equals(LoggerName.COMMERCIAL_LOGGER_NAME)
&& !loggerName.equals(LoggerName.CONSUMER_STATS_LOGGER_NAME)) {
ch.qos.logback.classic.Logger logger = lc.getLogger(brokerConfig.getLoggerIdentifier() + loggerName);
logger.setAdditive(tempLogger.isAdditive());
logger.setLevel(tempLogger.getLevel());
String appenderName = loggerName + SUFFIX_APPENDER;
Appender<ILoggingEvent> tempAppender = tempLogger.getAppender(appenderName);
if (tempAppender instanceof AsyncAppender) {
AsyncAppender tempAsyncAppender = (AsyncAppender) tempAppender;
AsyncAppender asyncAppender = new AsyncAppender();
asyncAppender.setName(brokerConfig.getLoggerIdentifier() + appenderName);
asyncAppender.setContext(tempAsyncAppender.getContext());
String innerAppenderName = appenderName + SUFFIX_INNER_APPENDER;
Appender<ILoggingEvent> tempInnerAppender = tempAsyncAppender.getAppender(innerAppenderName);
if (!(tempInnerAppender instanceof RollingFileAppender)) {
continue;
}
asyncAppender.addAppender(configureRollingFileAppender((RollingFileAppender<ILoggingEvent>) tempInnerAppender,
brokerConfig, innerAppenderName));
asyncAppender.start();
logger.addAppender(asyncAppender);
} else if (tempAppender instanceof RollingFileAppender) {
logger.addAppender(configureRollingFileAppender((RollingFileAppender<ILoggingEvent>) tempAppender,
brokerConfig, appenderName));
}
}
}
} catch (Exception e) {
LOG.error("Configure logback for broker {} failed, will use default broker log config instead. {}", brokerConfig.getCanonicalName(), e);
return;
}
CONFIGURED_BROKER_LIST.add(brokerConfig.getCanonicalName());
}
}
private static RollingFileAppender<ILoggingEvent> configureRollingFileAppender(
RollingFileAppender<ILoggingEvent> tempRollingFileAppender, BrokerConfig brokerConfig, String appenderName)
throws NoSuchFieldException, IllegalAccessException {
RollingFileAppender<ILoggingEvent> rollingFileAppender = new RollingFileAppender<>();
// configure appender name
rollingFileAppender.setName(brokerConfig.getLoggerIdentifier() + appenderName);
// configure file name
rollingFileAppender.setFile(tempRollingFileAppender.getFile().replaceAll(ROCKETMQ_LOGS, brokerConfig.getCanonicalName() + "_" + ROCKETMQ_LOGS));
// configure append
rollingFileAppender.setAppend(true);
// configure prudent
rollingFileAppender.setPrudent(tempRollingFileAppender.isPrudent());
// configure rollingPolicy
RollingPolicy originalRollingPolicy = tempRollingFileAppender.getRollingPolicy();
if (originalRollingPolicy instanceof TimeBasedRollingPolicy) {
TimeBasedRollingPolicy<ILoggingEvent> tempRollingPolicy = (TimeBasedRollingPolicy<ILoggingEvent>) originalRollingPolicy;
TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new TimeBasedRollingPolicy<>();
rollingPolicy.setContext(tempRollingPolicy.getContext());
rollingPolicy.setFileNamePattern(tempRollingPolicy.getFileNamePattern());
SizeAndTimeBasedFNATP<ILoggingEvent> sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP<>();
sizeAndTimeBasedFNATP.setContext(tempRollingPolicy.getContext());
TimeBasedFileNamingAndTriggeringPolicy<ILoggingEvent> timeBasedFileNamingAndTriggeringPolicy =
tempRollingPolicy.getTimeBasedFileNamingAndTriggeringPolicy();
if (timeBasedFileNamingAndTriggeringPolicy instanceof SizeAndTimeBasedFNATP) {
SizeAndTimeBasedFNATP<ILoggingEvent> originalSizeAndTimeBasedFNATP =
(SizeAndTimeBasedFNATP<ILoggingEvent>) timeBasedFileNamingAndTriggeringPolicy;
Field field = originalSizeAndTimeBasedFNATP.getClass().getDeclaredField("maxFileSize");
field.setAccessible(true);
sizeAndTimeBasedFNATP.setMaxFileSize((FileSize) field.get(originalSizeAndTimeBasedFNATP));
sizeAndTimeBasedFNATP.setTimeBasedRollingPolicy(rollingPolicy);
}
rollingPolicy.setTimeBasedFileNamingAndTriggeringPolicy(sizeAndTimeBasedFNATP);
rollingPolicy.setMaxHistory(tempRollingPolicy.getMaxHistory());
rollingPolicy.setParent(rollingFileAppender);
rollingPolicy.start();
rollingFileAppender.setRollingPolicy(rollingPolicy);
} else if (originalRollingPolicy instanceof FixedWindowRollingPolicy) {
FixedWindowRollingPolicy tempRollingPolicy = (FixedWindowRollingPolicy) originalRollingPolicy;
FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy();
rollingPolicy.setContext(tempRollingPolicy.getContext());
rollingPolicy.setFileNamePattern(tempRollingPolicy.getFileNamePattern().replaceAll(ROCKETMQ_LOGS, brokerConfig.getCanonicalName() + "_" + ROCKETMQ_LOGS));
rollingPolicy.setMaxIndex(tempRollingPolicy.getMaxIndex());
rollingPolicy.setMinIndex(tempRollingPolicy.getMinIndex());
rollingPolicy.setParent(rollingFileAppender);
rollingPolicy.start();
rollingFileAppender.setRollingPolicy(rollingPolicy);
}
// configure triggerPolicy
if (tempRollingFileAppender.getTriggeringPolicy() instanceof SizeBasedTriggeringPolicy) {
SizeBasedTriggeringPolicy<ILoggingEvent> tempTriggerPolicy = (SizeBasedTriggeringPolicy<ILoggingEvent>) tempRollingFileAppender.getTriggeringPolicy();
SizeBasedTriggeringPolicy<ILoggingEvent> triggerPolicy = new SizeBasedTriggeringPolicy<>();
triggerPolicy.setContext(tempTriggerPolicy.getContext());
Field field = triggerPolicy.getClass().getDeclaredField("maxFileSize");
field.setAccessible(true);
triggerPolicy.setMaxFileSize((FileSize) field.get(triggerPolicy));
triggerPolicy.start();
rollingFileAppender.setTriggeringPolicy(triggerPolicy);
}
// configure encoder
Encoder<ILoggingEvent> tempEncoder = tempRollingFileAppender.getEncoder();
if (tempEncoder instanceof PatternLayoutEncoder) {
PatternLayoutEncoder tempPatternLayoutEncoder = (PatternLayoutEncoder) tempEncoder;
PatternLayoutEncoder patternLayoutEncoder = new PatternLayoutEncoder();
patternLayoutEncoder.setContext(tempPatternLayoutEncoder.getContext());
patternLayoutEncoder.setPattern(tempPatternLayoutEncoder.getPattern());
patternLayoutEncoder.setCharset(tempPatternLayoutEncoder.getCharset());
patternLayoutEncoder.start();
rollingFileAppender.setEncoder(patternLayoutEncoder);
}
// configure context
rollingFileAppender.setContext(tempRollingFileAppender.getContext());
rollingFileAppender.start();
return rollingFileAppender;
}
}
@@ -0,0 +1,151 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.store.config.MessageStoreConfig;
import org.assertj.core.util.Arrays;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.assertj.core.api.Java6Assertions.assertThat;
@RunWith(MockitoJUnitRunner.class)
public class BrokerContainerStartupTest {
private static final List<File> TMP_FILE_LIST = new ArrayList<>();
private static final String BROKER_NAME_PREFIX = "TestBroker";
private static final String SHARED_BROKER_NAME_PREFIX = "TestBrokerContainer";
private static String brokerConfigPath;
private static String brokerContainerConfigPath;
@Mock
private BrokerConfig brokerConfig;
private String storePathRootDir = "store/test";
@Mock
private NettyClientConfig nettyClientConfig;
@Mock
private NettyServerConfig nettyServerConfig;
@Before
public void init() throws IOException {
String brokerName = BROKER_NAME_PREFIX + "_" + System.currentTimeMillis();
BrokerConfig brokerConfig = new BrokerConfig();
brokerConfig.setBrokerName(brokerName);
if (brokerConfig.getRocketmqHome() == null) {
brokerConfig.setRocketmqHome("../distribution");
}
MessageStoreConfig storeConfig = new MessageStoreConfig();
String baseDir = createBaseDir(brokerConfig.getBrokerName() + "_" + brokerConfig.getBrokerId()).getAbsolutePath();
storeConfig.setStorePathRootDir(baseDir);
storeConfig.setStorePathCommitLog(baseDir + File.separator + "commitlog");
brokerConfigPath = "/tmp/" + brokerName;
brokerConfig.setBrokerConfigPath(brokerConfigPath);
File file = new File(brokerConfigPath);
TMP_FILE_LIST.add(file);
Properties brokerConfigProp = MixAll.object2Properties(brokerConfig);
Properties storeConfigProp = MixAll.object2Properties(storeConfig);
for (Object key : storeConfigProp.keySet()) {
brokerConfigProp.put(key, storeConfigProp.get(key));
}
MixAll.string2File(MixAll.properties2String(brokerConfigProp), brokerConfigPath);
brokerContainerConfigPath = "/tmp/" + SHARED_BROKER_NAME_PREFIX + System.currentTimeMillis();
BrokerContainerConfig brokerContainerConfig = new BrokerContainerConfig();
brokerContainerConfig.setBrokerConfigPaths(brokerConfigPath);
if (brokerContainerConfig.getRocketmqHome() == null) {
brokerContainerConfig.setRocketmqHome("../distribution");
}
File file1 = new File(brokerContainerConfigPath);
TMP_FILE_LIST.add(file1);
Properties brokerContainerConfigProp = MixAll.object2Properties(brokerContainerConfig);
MixAll.string2File(MixAll.properties2String(brokerContainerConfigProp), brokerContainerConfigPath);
}
@After
public void destory() {
for (File file : TMP_FILE_LIST) {
UtilAll.deleteFile(file);
}
}
@Test
public void testStartBroker1() {
BrokerContainer brokerContainer = BrokerContainerStartup.startBrokerContainer(
BrokerContainerStartup.createBrokerContainer(Arrays.array("-c", brokerContainerConfigPath)));
assertThat(brokerContainer).isNotNull();
List<BrokerController> brokers = BrokerContainerStartup.createAndStartBrokers(brokerContainer);
assertThat(brokers.size()).isEqualTo(1);
brokerContainer.shutdown();
assertThat(brokerContainer.getBrokerControllers().size()).isEqualTo(0);
}
@Test
public void testStartBroker2() {
InnerBrokerController brokerController = (InnerBrokerController) BrokerContainerStartup.start(BrokerContainerStartup.createBrokerController(Arrays.array("-c", brokerConfigPath)));
assertThat(brokerController).isNotNull();
assertThat(brokerController.getBrokerContainer().getBrokerControllers().size()).isEqualTo(1);
brokerController.getBrokerContainer().shutdown();
assertThat(brokerController.getBrokerContainer().getBrokerControllers().size()).isEqualTo(0);
}
private static File createBaseDir(String prefix) {
final File file;
try {
file = Files.createTempDirectory(prefix).toFile();
TMP_FILE_LIST.add(file);
System.out.printf("create file at %s%n", file.getAbsolutePath());
return file;
} catch (IOException e) {
throw new RuntimeException("Couldn't create tmp folder", e);
}
}
@Before
public void clear() {
UtilAll.deleteFile(new File(storePathRootDir));
}
@After
public void tearDown() {
File configFile = new File(storePathRootDir);
UtilAll.deleteFile(configFile);
UtilAll.deleteEmptyDirectory(configFile);
UtilAll.deleteEmptyDirectory(configFile.getParentFile());
}
}
@@ -0,0 +1,339 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.out.BrokerOuterAPI;
import org.apache.rocketmq.common.*;
import org.apache.rocketmq.common.constant.PermName;
import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.store.config.BrokerRole;
import org.apache.rocketmq.store.config.MessageStoreConfig;
import org.junit.After;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import java.io.File;
import java.lang.reflect.Field;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Mockito.*;
public class BrokerContainerTest {
private static final List<File> TMP_FILE_LIST = new ArrayList<>();
private static final Random RANDOM = new Random();
private static final Set<Integer> PORTS_IN_USE = new HashSet<>();
/**
* Tests if the controller can be properly stopped and started.
*
* @throws Exception If fails.
*/
@Test
public void testBrokerContainerRestart() throws Exception {
BrokerContainer brokerController = new BrokerContainer(
new BrokerContainerConfig(),
new NettyServerConfig(),
new NettyClientConfig());
assertThat(brokerController.initialize()).isTrue();
brokerController.start();
brokerController.shutdown();
}
@Test
public void testRegisterIncrementBrokerData() throws Exception {
BrokerController brokerController = new BrokerController(
new BrokerConfig(),
new NettyServerConfig(),
new NettyClientConfig(),
new MessageStoreConfig());
BrokerOuterAPI brokerOuterAPI = mock(BrokerOuterAPI.class);
Field field = BrokerController.class.getDeclaredField("brokerOuterAPI");
field.setAccessible(true);
field.set(brokerController, brokerOuterAPI);
// topic-0 doesn't have queueGroupConfig.
// topic-1 has queueGroupConfig.
List<TopicConfig> topicConfigList = new ArrayList<>(2);
for (int i = 0; i < 2; i++) {
topicConfigList.add(new TopicConfig("topic-" + i));
}
DataVersion dataVersion = new DataVersion();
// Check normal condition.
testRegisterIncrementBrokerDataWithPerm(brokerController, brokerOuterAPI,
topicConfigList, dataVersion, PermName.PERM_READ | PermName.PERM_WRITE, 1);
// Check unwritable broker.
testRegisterIncrementBrokerDataWithPerm(brokerController, brokerOuterAPI,
topicConfigList, dataVersion, PermName.PERM_READ, 2);
// Check unreadable broker.
testRegisterIncrementBrokerDataWithPerm(brokerController, brokerOuterAPI,
topicConfigList, dataVersion, PermName.PERM_WRITE, 3);
}
@Test
public void testRegisterIncrementBrokerDataPerm() throws Exception {
BrokerController brokerController = new BrokerController(
new BrokerConfig(),
new NettyServerConfig(),
new NettyClientConfig(),
new MessageStoreConfig());
BrokerOuterAPI brokerOuterAPI = mock(BrokerOuterAPI.class);
Field field = BrokerController.class.getDeclaredField("brokerOuterAPI");
field.setAccessible(true);
field.set(brokerController, brokerOuterAPI);
// topic-0 doesn't have queueGroupConfig.
// topic-1 has queueGroupConfig.
List<TopicConfig> topicConfigList = new ArrayList<>(2);
for (int i = 0; i < 2; i++) {
topicConfigList.add(new TopicConfig("topic-" + i));
}
DataVersion dataVersion = new DataVersion();
brokerController.getBrokerConfig().setBrokerPermission(4);
brokerController.registerIncrementBrokerData(topicConfigList, dataVersion);
// Get topicConfigSerializeWrapper created by registerIncrementBrokerData() from brokerOuterAPI.registerBrokerAll()
ArgumentCaptor<TopicConfigSerializeWrapper> captor = ArgumentCaptor.forClass(TopicConfigSerializeWrapper.class);
verify(brokerOuterAPI).registerBrokerAll(anyString(), anyString(), anyString(), anyLong(), anyString(),
captor.capture(), ArgumentMatchers.anyList(), anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
TopicConfigSerializeWrapper wrapper = captor.getValue();
for (Map.Entry<String, TopicConfig> entry : wrapper.getTopicConfigTable().entrySet()) {
assertThat(entry.getValue().getPerm()).isEqualTo(brokerController.getBrokerConfig().getBrokerPermission());
}
}
@Test
public void testMasterScaleOut() throws Exception {
BrokerContainer brokerContainer = new BrokerContainer(
new BrokerContainerConfig(),
new NettyServerConfig(),
new NettyClientConfig());
assertThat(brokerContainer.initialize()).isTrue();
brokerContainer.getBrokerContainerConfig().setNamesrvAddr("127.0.0.1:9876");
brokerContainer.start();
BrokerConfig masterBrokerConfig = new BrokerConfig();
String baseDir = createBaseDir("unnittest-master").getAbsolutePath();
MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
messageStoreConfig.setStorePathRootDir(baseDir);
messageStoreConfig.setStorePathCommitLog(baseDir + File.separator + "commitlog");
InnerBrokerController brokerController = brokerContainer.addBroker(masterBrokerConfig, messageStoreConfig);
assertThat(brokerController.isIsolated()).isFalse();
brokerContainer.shutdown();
brokerController.getMessageStore().destroy();
}
@Test
public void testAddMasterFailed() throws Exception {
BrokerContainer brokerContainer = new BrokerContainer(
new BrokerContainerConfig(),
new NettyServerConfig(),
new NettyClientConfig());
assertThat(brokerContainer.initialize()).isTrue();
brokerContainer.start();
BrokerConfig masterBrokerConfig = new BrokerConfig();
masterBrokerConfig.setListenPort(brokerContainer.getNettyServerConfig().getListenPort());
boolean exceptionCaught = false;
try {
String baseDir = createBaseDir("unnittest-master").getAbsolutePath();
MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
messageStoreConfig.setStorePathRootDir(baseDir);
messageStoreConfig.setStorePathCommitLog(baseDir + File.separator + "commitlog");
brokerContainer.addBroker(masterBrokerConfig, messageStoreConfig);
} catch (Exception e) {
exceptionCaught = true;
} finally {
brokerContainer.shutdown();
}
assertThat(exceptionCaught).isTrue();
}
@Test
public void testAddSlaveFailed() throws Exception {
BrokerContainer sharedBrokerController = new BrokerContainer(
new BrokerContainerConfig(),
new NettyServerConfig(),
new NettyClientConfig());
assertThat(sharedBrokerController.initialize()).isTrue();
sharedBrokerController.start();
BrokerConfig slaveBrokerConfig = new BrokerConfig();
slaveBrokerConfig.setBrokerId(1);
slaveBrokerConfig.setListenPort(sharedBrokerController.getNettyServerConfig().getListenPort());
MessageStoreConfig slaveMessageStoreConfig = new MessageStoreConfig();
slaveMessageStoreConfig.setBrokerRole(BrokerRole.SLAVE);
String baseDir = createBaseDir("unnittest-slave").getAbsolutePath();
slaveMessageStoreConfig.setStorePathRootDir(baseDir);
slaveMessageStoreConfig.setStorePathCommitLog(baseDir + File.separator + "commitlog");
boolean exceptionCaught = false;
try {
sharedBrokerController.addBroker(slaveBrokerConfig, slaveMessageStoreConfig);
} catch (Exception e) {
exceptionCaught = true;
} finally {
sharedBrokerController.shutdown();
}
assertThat(exceptionCaught).isTrue();
}
@Test
public void testAddAndRemoveMaster() throws Exception {
BrokerContainer brokerContainer = new BrokerContainer(
new BrokerContainerConfig(),
new NettyServerConfig(),
new NettyClientConfig());
assertThat(brokerContainer.initialize()).isTrue();
brokerContainer.start();
BrokerConfig masterBrokerConfig = new BrokerConfig();
String baseDir = createBaseDir("unnittest-master").getAbsolutePath();
MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
messageStoreConfig.setStorePathRootDir(baseDir);
messageStoreConfig.setStorePathCommitLog(baseDir + File.separator + "commitlog");
InnerBrokerController master = brokerContainer.addBroker(masterBrokerConfig, messageStoreConfig);
assertThat(master).isNotNull();
master.start();
assertThat(master.isIsolated()).isFalse();
brokerContainer.removeBroker(new BrokerIdentity(masterBrokerConfig.getBrokerClusterName(), masterBrokerConfig.getBrokerName(), masterBrokerConfig.getBrokerId()));
assertThat(brokerContainer.getMasterBrokers().size()).isEqualTo(0);
brokerContainer.shutdown();
master.getMessageStore().destroy();
}
@Test
public void testAddAndRemoveSlaveSuccess() throws Exception {
BrokerContainer brokerContainer = new BrokerContainer(
new BrokerContainerConfig(),
new NettyServerConfig(),
new NettyClientConfig());
assertThat(brokerContainer.initialize()).isTrue();
brokerContainer.start();
BrokerConfig masterBrokerConfig = new BrokerConfig();
String baseDir = createBaseDir("unnittest-master").getAbsolutePath();
MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
messageStoreConfig.setStorePathRootDir(baseDir);
messageStoreConfig.setStorePathCommitLog(baseDir + File.separator + "commitlog");
InnerBrokerController master = brokerContainer.addBroker(masterBrokerConfig, messageStoreConfig);
assertThat(master).isNotNull();
master.start();
assertThat(master.isIsolated()).isFalse();
BrokerConfig slaveBrokerConfig = new BrokerConfig();
slaveBrokerConfig.setListenPort(generatePort(masterBrokerConfig.getListenPort(), 10000));
slaveBrokerConfig.setBrokerId(1);
MessageStoreConfig slaveMessageStoreConfig = new MessageStoreConfig();
slaveMessageStoreConfig.setBrokerRole(BrokerRole.SLAVE);
slaveMessageStoreConfig.setHaListenPort(generatePort(messageStoreConfig.getHaListenPort(), 10000));
baseDir = createBaseDir("unnittest-slave").getAbsolutePath();
slaveMessageStoreConfig.setStorePathRootDir(baseDir);
slaveMessageStoreConfig.setStorePathCommitLog(baseDir + File.separator + "commitlog");
InnerBrokerController slave = brokerContainer.addBroker(slaveBrokerConfig, slaveMessageStoreConfig);
assertThat(slave).isNotNull();
slave.start();
assertThat(slave.isIsolated()).isFalse();
brokerContainer.removeBroker(new BrokerIdentity(slaveBrokerConfig.getBrokerClusterName(), slaveBrokerConfig.getBrokerName(), slaveBrokerConfig.getBrokerId()));
assertThat(brokerContainer.getSlaveBrokers().size()).isEqualTo(0);
brokerContainer.removeBroker(new BrokerIdentity(masterBrokerConfig.getBrokerClusterName(), masterBrokerConfig.getBrokerName(), masterBrokerConfig.getBrokerId()));
assertThat(brokerContainer.getMasterBrokers().size()).isEqualTo(0);
brokerContainer.shutdown();
slave.getMessageStore().destroy();
master.getMessageStore().destroy();
}
private static File createBaseDir(String prefix) {
final File file;
try {
file = Files.createTempDirectory(prefix).toFile();
TMP_FILE_LIST.add(file);
return file;
} catch (IOException e) {
throw new RuntimeException("Couldn't create tmp folder", e);
}
}
public static int generatePort(int base, int range) {
int result = base + RANDOM.nextInt(range);
while (PORTS_IN_USE.contains(result) || PORTS_IN_USE.contains(result - 2)) {
result = base + RANDOM.nextInt(range);
}
PORTS_IN_USE.add(result);
PORTS_IN_USE.add(result - 2);
return result;
}
@After
public void destory() {
for (File file : TMP_FILE_LIST) {
UtilAll.deleteFile(file);
}
}
private void testRegisterIncrementBrokerDataWithPerm(BrokerController brokerController,
BrokerOuterAPI brokerOuterAPI,
List<TopicConfig> topicConfigList, DataVersion dataVersion, int perm, int times) {
brokerController.getBrokerConfig().setBrokerPermission(perm);
brokerController.registerIncrementBrokerData(topicConfigList, dataVersion);
// Get topicConfigSerializeWrapper created by registerIncrementBrokerData() from brokerOuterAPI.registerBrokerAll()
ArgumentCaptor<TopicConfigSerializeWrapper> captor = ArgumentCaptor.forClass(TopicConfigSerializeWrapper.class);
verify(brokerOuterAPI, times(times)).registerBrokerAll(anyString(), anyString(), anyString(), anyLong(),
anyString(), captor.capture(), ArgumentMatchers.anyList(), anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
TopicConfigSerializeWrapper wrapper = captor.getValue();
for (TopicConfig topicConfig : topicConfigList) {
topicConfig.setPerm(perm);
}
assertThat(wrapper.getDataVersion()).isEqualTo(dataVersion);
assertThat(wrapper.getTopicConfigTable()).containsExactly(
entry("topic-0", topicConfigList.get(0)),
entry("topic-1", topicConfigList.get(1)));
for (TopicConfig topicConfig : topicConfigList) {
topicConfig.setPerm(PermName.PERM_READ | PermName.PERM_WRITE);
}
}
}
@@ -0,0 +1,102 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.container;
import java.lang.reflect.Field;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.out.BrokerOuterAPI;
import org.apache.rocketmq.broker.transaction.TransactionalMessageCheckService;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.protocol.body.BrokerMemberGroup;
import org.apache.rocketmq.store.DefaultMessageStore;
import org.apache.rocketmq.store.config.MessageStoreConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.awaitility.Awaitility.await;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class BrokerPreOnlineTest {
@Mock
private BrokerContainer brokerContainer;
private InnerBrokerController innerBrokerController;
@Mock
private BrokerOuterAPI brokerOuterAPI;
public void init() throws Exception {
when(brokerContainer.getBrokerOuterAPI()).thenReturn(brokerOuterAPI);
when(brokerContainer.getBrokerContainerConfig()).thenReturn(new BrokerContainerConfig());
BrokerMemberGroup brokerMemberGroup1 = new BrokerMemberGroup();
Map<Long, String> brokerAddrMap = new HashMap<>();
brokerAddrMap.put(1L, "127.0.0.1:20911");
brokerMemberGroup1.setBrokerAddrs(brokerAddrMap);
BrokerMemberGroup brokerMemberGroup2 = new BrokerMemberGroup();
brokerMemberGroup2.setBrokerAddrs(new HashMap<>());
// when(brokerOuterAPI.syncBrokerMemberGroup(anyString(), anyString()))
// .thenReturn(brokerMemberGroup1)
// .thenReturn(brokerMemberGroup2);
// doNothing().when(brokerOuterAPI).sendBrokerHaInfo(anyString(), anyString(), anyLong(), anyString());
DefaultMessageStore defaultMessageStore = mock(DefaultMessageStore.class);
when(defaultMessageStore.getMessageStoreConfig()).thenReturn(new MessageStoreConfig());
when(defaultMessageStore.getBrokerConfig()).thenReturn(new BrokerConfig());
// HAService haService = new DefaultHAService();
// haService.init(defaultMessageStore);
// haService.start();
//
// when(defaultMessageStore.getHaService()).thenReturn(haService);
innerBrokerController = new InnerBrokerController(brokerContainer,
defaultMessageStore.getBrokerConfig(),
defaultMessageStore.getMessageStoreConfig());
innerBrokerController.setTransactionalMessageCheckService(new TransactionalMessageCheckService(innerBrokerController));
Field field = InnerBrokerController.class.getDeclaredField("isIsolated");
field.setAccessible(true);
field.set(innerBrokerController, true);
field = BrokerController.class.getDeclaredField("messageStore");
field.setAccessible(true);
field.set(innerBrokerController, defaultMessageStore);
}
@Test
public void testMasterOnlineConnTimeout() throws Exception {
init();
BrokerPreOnlineService brokerPreOnlineService = new BrokerPreOnlineService(innerBrokerController);
brokerPreOnlineService.start();
await().atMost(Duration.ofSeconds(30)).until(() -> !innerBrokerController.isIsolated());
}
}
+17 -3
View File
@@ -121,6 +121,7 @@
<module>logging</module>
<module>acl</module>
<module>example</module>
<module>container</module>
</modules>
<build>
@@ -458,6 +459,11 @@
<artifactId>rocketmq-broker</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>rocketmq-container</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>rocketmq-common</artifactId>
@@ -531,7 +537,7 @@
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.2</version>
<version>1.4</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
@@ -565,6 +571,11 @@
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@@ -610,8 +621,11 @@
<artifactId>commons-validator</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.11</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
@@ -23,5 +23,7 @@ public interface RPCHook {
void doBeforeRequest(final String remoteAddr, final RemotingCommand request);
void doAfterResponse(final String remoteAddr, final RemotingCommand request,
final RemotingCommand response);
final RemotingCommand response);
void doAfterRpcFailure(final String remoteAddr, RemotingCommand request, Boolean remoteTimeout);
}
@@ -17,12 +17,14 @@
package org.apache.rocketmq.remoting;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import org.apache.rocketmq.remoting.exception.RemotingConnectException;
import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException;
import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
import org.apache.rocketmq.remoting.netty.ResponseFuture;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public interface RemotingClient extends RemotingService {
@@ -31,6 +33,8 @@ public interface RemotingClient extends RemotingService {
List<String> getNameServerAddressList();
List<String> getAvailableNameSrvList();
RemotingCommand invokeSync(final String addr, final RemotingCommand request,
final long timeoutMillis) throws InterruptedException, RemotingConnectException,
RemotingSendRequestException, RemotingTimeoutException;
@@ -48,7 +52,12 @@ public interface RemotingClient extends RemotingService {
void setCallbackExecutor(final ExecutorService callbackExecutor);
ExecutorService getCallbackExecutor();
boolean isChannelWritable(final String addr);
void closeChannels();
void closeChannels(final List<String> addrList);
ConcurrentMap<Integer, ResponseFuture> getResponseTable();
}
@@ -36,6 +36,12 @@ public interface RemotingServer extends RemotingService {
Pair<NettyRequestProcessor, ExecutorService> getProcessorPair(final int requestCode);
Pair<NettyRequestProcessor, ExecutorService> getDefaultProcessorPair();
RemotingServer newRemotingServer(int port);
void removeRemotingServer(int port);
RemotingCommand invokeSync(final Channel channel, final RemotingCommand request,
final long timeoutMillis) throws InterruptedException, RemotingSendRequestException,
RemotingTimeoutException;
@@ -23,4 +23,9 @@ public interface RemotingService {
void shutdown();
void registerRPCHook(RPCHook rpcHook);
/**
* Remove all rpc hooks.
*/
void clearRPCHook();
}
@@ -189,6 +189,19 @@ public class RemotingHelper {
return "";
}
public static String parseHostFromAddress(String address) {
if (address == null) {
return "";
}
String[] addressSplits = address.split(":");
if (addressSplits.length < 1) {
return "";
}
return addressSplits[0];
}
public static String parseSocketAddressAddr(SocketAddress socketAddress) {
if (socketAddress != null) {
// Default toString of InetSocketAddress is "hostName/IP:port"
@@ -199,4 +212,29 @@ public class RemotingHelper {
return "";
}
public static int parseSocketAddressPort(SocketAddress socketAddress) {
if (socketAddress instanceof InetSocketAddress) {
return ((InetSocketAddress) socketAddress).getPort();
}
return -1;
}
public static int ipToInt(String ip) {
String[] ips = ip.split("\\.");
return (Integer.parseInt(ips[0]) << 24)
| (Integer.parseInt(ips[1]) << 16)
| (Integer.parseInt(ips[2]) << 8)
| Integer.parseInt(ips[3]);
}
public static boolean ipInCIDR(String ip, String cidr) {
int ipAddr = ipToInt(ip);
String[] cidrArr = cidr.split("/");
int netId = Integer.parseInt(cidrArr[1]);
int mask = 0xFFFFFFFF << (32 - netId);
int cidrIpAddr = ipToInt(cidrArr[0]);
return (ipAddr & mask) == (cidrIpAddr & mask);
}
}
@@ -218,13 +218,17 @@ public class RemotingUtil {
public static void closeChannel(Channel channel) {
final String addrRemote = RemotingHelper.parseChannelRemoteAddr(channel);
channel.close().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
log.info("closeChannel: close the connection to remote address[{}] result: {}", addrRemote,
future.isSuccess());
}
});
if (addrRemote == "") {
channel.close();
} else {
channel.close().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
log.info("closeChannel: close the connection to remote address[{}] result: {}", addrRemote,
future.isSuccess());
}
});
}
}
}
@@ -38,11 +38,17 @@ public class NettyClientConfig {
private boolean clientPooledByteBufAllocatorEnable = false;
private boolean clientCloseSocketIfTimeout = NettySystemConfig.clientCloseSocketIfTimeout;
private boolean preferredDirectByteBuffer = false;
private boolean defaultEventExecutorGroupEnable = true;
private boolean useTLS;
private int writeBufferHighWaterMark = NettySystemConfig.writeBufferHighWaterMark;
private int writeBufferLowWaterMark = NettySystemConfig.writeBufferLowWaterMark;
private boolean disableCallbackExecutor = false;
private boolean disableNettyWorkerGroup = false;
public boolean isClientCloseSocketIfTimeout() {
return clientCloseSocketIfTimeout;
}
@@ -154,4 +160,36 @@ public class NettyClientConfig {
public void setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
this.writeBufferHighWaterMark = writeBufferHighWaterMark;
}
public boolean isPreferredDirectByteBuffer() {
return preferredDirectByteBuffer;
}
public void setPreferredDirectByteBuffer(final boolean preferredDirectByteBuffer) {
this.preferredDirectByteBuffer = preferredDirectByteBuffer;
}
public boolean isDefaultEventExecutorGroupEnable() {
return defaultEventExecutorGroupEnable;
}
public void setDefaultEventExecutorGroupEnable(final boolean defaultEventExecutorGroupEnable) {
this.defaultEventExecutorGroupEnable = defaultEventExecutorGroupEnable;
}
public boolean isDisableCallbackExecutor() {
return disableCallbackExecutor;
}
public void setDisableCallbackExecutor(boolean disableCallbackExecutor) {
this.disableCallbackExecutor = disableCallbackExecutor;
}
public boolean isDisableNettyWorkerGroup() {
return disableNettyWorkerGroup;
}
public void setDisableNettyWorkerGroup(boolean disableNettyWorkerGroup) {
this.disableNettyWorkerGroup = disableNettyWorkerGroup;
}
}
@@ -88,7 +88,8 @@ public abstract class NettyRemotingAbstract {
protected final NettyEventExecutor nettyEventExecutor = new NettyEventExecutor();
/**
* The default request processor to use in case there is no exact match in {@link #processorTable} per request code.
* The default request processor to use in case there is no exact match in {@link #processorTable} per request
* code.
*/
protected Pair<NettyRequestProcessor, ExecutorService> defaultRequestProcessor;
@@ -102,7 +103,6 @@ public abstract class NettyRemotingAbstract {
*/
protected List<RPCHook> rpcHooks = new ArrayList<RPCHook>();
static {
NettyLogger.initNettyLogger();
}
@@ -168,20 +168,27 @@ public abstract class NettyRemotingAbstract {
protected void doBeforeRpcHooks(String addr, RemotingCommand request) {
if (rpcHooks.size() > 0) {
for (RPCHook rpcHook: rpcHooks) {
for (RPCHook rpcHook : rpcHooks) {
rpcHook.doBeforeRequest(addr, request);
}
}
}
protected void doAfterRpcHooks(String addr, RemotingCommand request, RemotingCommand response) {
public void doAfterRpcHooks(String addr, RemotingCommand request, RemotingCommand response) {
if (rpcHooks.size() > 0) {
for (RPCHook rpcHook: rpcHooks) {
for (RPCHook rpcHook : rpcHooks) {
rpcHook.doAfterResponse(addr, request, response);
}
}
}
public void doAfterRpcFailure(String addr, RemotingCommand request, Boolean remoteTimeout) {
if (rpcHooks.size() > 0) {
for (RPCHook rpcHook : rpcHooks) {
rpcHook.doAfterRpcFailure(addr, request, remoteTimeout);
}
}
}
/**
* Process incoming request command issued by remote peer.
@@ -198,43 +205,55 @@ public abstract class NettyRemotingAbstract {
Runnable run = new Runnable() {
@Override
public void run() {
Exception exception = null;
RemotingCommand response;
try {
String remoteAddr = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
doBeforeRpcHooks(remoteAddr, cmd);
final RemotingResponseCallback callback = new RemotingResponseCallback() {
@Override
public void callback(RemotingCommand response) {
doAfterRpcHooks(remoteAddr, cmd, response);
if (!cmd.isOnewayRPC()) {
if (response != null) {
response.setOpaque(opaque);
response.markResponseType();
try {
ctx.writeAndFlush(response);
} catch (Throwable e) {
log.error("process request over, but response failed", e);
log.error(cmd.toString());
log.error(response.toString());
}
} else {
}
}
}
};
if (pair.getObject1() instanceof AsyncNettyRequestProcessor) {
AsyncNettyRequestProcessor processor = (AsyncNettyRequestProcessor)pair.getObject1();
processor.asyncProcessRequest(ctx, cmd, callback);
try {
doBeforeRpcHooks(remoteAddr, cmd);
} catch (Exception e) {
exception = e;
}
if (exception == null) {
response = pair.getObject1().processRequest(ctx, cmd);
} else {
NettyRequestProcessor processor = pair.getObject1();
RemotingCommand response = processor.processRequest(ctx, cmd);
callback.callback(response);
response = RemotingCommand.createResponseCommand(null);
response.setCode(RemotingSysResponseCode.SYSTEM_ERROR);
}
try {
doAfterRpcHooks(remoteAddr, cmd, response);
} catch (Exception e) {
exception = e;
}
if (exception != null) {
throw exception;
}
if (!cmd.isOnewayRPC()) {
if (response != null) {
response.setOpaque(opaque);
response.markResponseType();
try {
ctx.writeAndFlush(response);
} catch (Throwable e) {
log.error("process request over, but response failed", e);
log.error(cmd.toString());
log.error(response.toString());
}
} else {
}
}
} catch (Throwable e) {
log.error("process request exception", e);
log.error(cmd.toString());
if (!cmd.isOnewayRPC()) {
final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_ERROR,
response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_ERROR,
RemotingHelper.exceptionSimpleDesc(e));
response.setOpaque(opaque);
ctx.writeAndFlush(response);
@@ -344,29 +363,24 @@ public abstract class NettyRemotingAbstract {
}
}
/**
* Custom RPC hook.
* Just be compatible with the previous version, use getRPCHooks instead.
*/
@Deprecated
protected RPCHook getRPCHook() {
if (rpcHooks.size() > 0) {
return rpcHooks.get(0);
}
return null;
}
/**
* Custom RPC hooks.
*
* @return RPC hooks if specified; null otherwise.
*/
public List<RPCHook> getRPCHooks() {
public List<RPCHook> getRPCHook() {
return rpcHooks;
}
public void registerRPCHook(RPCHook rpcHook) {
if (rpcHook != null && !rpcHooks.contains(rpcHook)) {
rpcHooks.add(rpcHook);
}
}
public void clearRPCHook() {
rpcHooks.clear();
}
/**
* This method specifies thread pool to use while invoking callback methods.
@@ -481,6 +495,10 @@ public abstract class NettyRemotingAbstract {
throw new RemotingSendRequestException(RemotingHelper.parseChannelRemoteAddr(channel), e);
}
} else {
if (this instanceof NettyRemotingClient) {
NettyRemotingClient nettyRemotingClient = (NettyRemotingClient) this;
nettyRemotingClient.doAfterRpcFailure(RemotingHelper.parseChannelRemoteAddr(channel), request, false);
}
if (timeoutMillis <= 0) {
throw new RemotingTooMuchRequestException("invokeAsyncImpl invoke too fast");
} else {
@@ -513,13 +531,14 @@ public abstract class NettyRemotingAbstract {
/**
* mark the request of the specified channel as fail and to invoke fail callback immediately
*
* @param channel the channel which is close already
*/
protected void failFast(final Channel channel) {
Iterator<Entry<Integer, ResponseFuture>> it = responseTable.entrySet().iterator();
while (it.hasNext()) {
Entry<Integer, ResponseFuture> entry = it.next();
if (entry.getValue().getProcessChannel() == channel) {
if (entry.getValue().getChannel() == channel) {
Integer opaque = entry.getKey();
if (opaque != null) {
requestFail(opaque);
@@ -17,6 +17,7 @@
package org.apache.rocketmq.remoting.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelFuture;
@@ -35,20 +36,26 @@ import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import java.io.IOException;
import java.net.SocketAddress;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -58,7 +65,6 @@ import org.apache.rocketmq.logging.InternalLogger;
import org.apache.rocketmq.logging.InternalLoggerFactory;
import org.apache.rocketmq.remoting.ChannelEventListener;
import org.apache.rocketmq.remoting.InvokeCallback;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.remoting.RemotingClient;
import org.apache.rocketmq.remoting.common.Pair;
import org.apache.rocketmq.remoting.common.RemotingHelper;
@@ -70,7 +76,7 @@ import org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public class NettyRemotingClient extends NettyRemotingAbstract implements RemotingClient {
private static final InternalLogger log = InternalLoggerFactory.getLogger(RemotingHelper.ROCKETMQ_REMOTING);
private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(RemotingHelper.ROCKETMQ_REMOTING);
private static final long LOCK_TIMEOUT_MILLIS = 3000;
@@ -83,18 +89,20 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
private final Timer timer = new Timer("ClientHouseKeepingService", true);
private final AtomicReference<List<String>> namesrvAddrList = new AtomicReference<List<String>>();
private final ConcurrentMap<String, Boolean> availableNamesrvAddrMap = new ConcurrentHashMap<String, Boolean>();
private final AtomicReference<String> namesrvAddrChoosed = new AtomicReference<String>();
private final AtomicInteger namesrvIndex = new AtomicInteger(initValueIndex());
private final Lock namesrvChannelLock = new ReentrantLock();
private final ExecutorService publicExecutor;
private final ExecutorService scanExecutor;
/**
* Invoke the callback methods in this executor when process response.
*/
private ExecutorService callbackExecutor;
private final ChannelEventListener channelEventListener;
private DefaultEventExecutorGroup defaultEventExecutorGroup;
private EventExecutorGroup defaultEventExecutorGroup;
public NettyRemotingClient(final NettyClientConfig nettyClientConfig) {
this(nettyClientConfig, null);
@@ -102,6 +110,13 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
public NettyRemotingClient(final NettyClientConfig nettyClientConfig,
final ChannelEventListener channelEventListener) {
this(nettyClientConfig, channelEventListener, null, null);
}
public NettyRemotingClient(final NettyClientConfig nettyClientConfig,
final ChannelEventListener channelEventListener,
final EventLoopGroup eventLoopGroup,
final EventExecutorGroup eventExecutorGroup) {
super(nettyClientConfig.getClientOnewaySemaphoreValue(), nettyClientConfig.getClientAsyncSemaphoreValue());
this.nettyClientConfig = nettyClientConfig;
this.channelEventListener = channelEventListener;
@@ -112,7 +127,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
}
this.publicExecutor = Executors.newFixedThreadPool(publicThreadNums, new ThreadFactory() {
private AtomicInteger threadIndex = new AtomicInteger(0);
private final AtomicInteger threadIndex = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
@@ -120,23 +135,39 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
}
});
this.eventLoopGroupWorker = new NioEventLoopGroup(1, new ThreadFactory() {
private AtomicInteger threadIndex = new AtomicInteger(0);
this.scanExecutor = new ThreadPoolExecutor(4, 10, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(32), new ThreadFactory() {
private final AtomicInteger threadIndex = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, String.format("NettyClientSelector_%d", this.threadIndex.incrementAndGet()));
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "NettyClientScan_thread_" + this.threadIndex.incrementAndGet());
}
}
});
);
if (eventLoopGroup != null) {
this.eventLoopGroupWorker = eventLoopGroup;
} else {
this.eventLoopGroupWorker = new NioEventLoopGroup(1, new ThreadFactory() {
private final AtomicInteger threadIndex = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, String.format("NettyClientSelector_%d", this.threadIndex.incrementAndGet()));
}
});
}
this.defaultEventExecutorGroup = eventExecutorGroup;
if (nettyClientConfig.isUseTLS()) {
try {
sslContext = TlsHelper.buildSslContext(true);
log.info("SSL enabled for client");
LOGGER.info("SSL enabled for client");
} catch (IOException e) {
log.error("Failed to create SSLContext", e);
LOGGER.error("Failed to create SSLContext", e);
} catch (CertificateException e) {
log.error("Failed to create SSLContext", e);
LOGGER.error("Failed to create SSLContext", e);
throw new RuntimeException("Failed to create SSLContext", e);
}
}
@@ -150,19 +181,19 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
@Override
public void start() {
this.defaultEventExecutorGroup = new DefaultEventExecutorGroup(
nettyClientConfig.getClientWorkerThreads(),
new ThreadFactory() {
private AtomicInteger threadIndex = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "NettyClientWorkerThread_" + this.threadIndex.incrementAndGet());
}
});
if (this.defaultEventExecutorGroup == null) {
this.defaultEventExecutorGroup = new DefaultEventExecutorGroup(
nettyClientConfig.getClientWorkerThreads(),
new ThreadFactory() {
private AtomicInteger threadIndex = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "NettyClientWorkerThread_" + this.threadIndex.incrementAndGet());
}
});
}
Bootstrap handler = this.bootstrap.group(this.eventLoopGroupWorker).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.SO_KEEPALIVE, false)
@@ -174,49 +205,80 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
if (nettyClientConfig.isUseTLS()) {
if (null != sslContext) {
pipeline.addFirst(defaultEventExecutorGroup, "sslHandler", sslContext.newHandler(ch.alloc()));
log.info("Prepend SSL handler");
LOGGER.info("Prepend SSL handler");
} else {
log.warn("Connections are insecure as SSLContext is null!");
LOGGER.warn("Connections are insecure as SSLContext is null!");
}
}
pipeline.addLast(
defaultEventExecutorGroup,
new NettyEncoder(),
new NettyDecoder(),
new IdleStateHandler(0, 0, nettyClientConfig.getClientChannelMaxIdleTimeSeconds()),
new NettyConnectManageHandler(),
if (nettyClientConfig.isDefaultEventExecutorGroupEnable() && !nettyClientConfig.isDisableNettyWorkerGroup()) {
ch.pipeline().addLast(defaultEventExecutorGroup);
}
ch.pipeline().addLast(//
new NettyEncoder(), //
new NettyDecoder(), //
new IdleStateHandler(0, 0, nettyClientConfig.getClientChannelMaxIdleTimeSeconds()), //
new NettyConnectManageHandler(), //
new NettyClientHandler());
}
});
if (nettyClientConfig.getClientSocketSndBufSize() > 0) {
log.info("client set SO_SNDBUF to {}", nettyClientConfig.getClientSocketSndBufSize());
LOGGER.info("client set SO_SNDBUF to {}", nettyClientConfig.getClientSocketSndBufSize());
handler.option(ChannelOption.SO_SNDBUF, nettyClientConfig.getClientSocketSndBufSize());
}
if (nettyClientConfig.getClientSocketRcvBufSize() > 0) {
log.info("client set SO_RCVBUF to {}", nettyClientConfig.getClientSocketRcvBufSize());
LOGGER.info("client set SO_RCVBUF to {}", nettyClientConfig.getClientSocketRcvBufSize());
handler.option(ChannelOption.SO_RCVBUF, nettyClientConfig.getClientSocketRcvBufSize());
}
if (nettyClientConfig.getWriteBufferLowWaterMark() > 0 && nettyClientConfig.getWriteBufferHighWaterMark() > 0) {
log.info("client set netty WRITE_BUFFER_WATER_MARK to {},{}",
LOGGER.info("client set netty WRITE_BUFFER_WATER_MARK to {},{}",
nettyClientConfig.getWriteBufferLowWaterMark(), nettyClientConfig.getWriteBufferHighWaterMark());
handler.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(
nettyClientConfig.getWriteBufferLowWaterMark(), nettyClientConfig.getWriteBufferHighWaterMark()));
}
if (nettyClientConfig.getClientSocketSndBufSize() != 0) {
handler.option(ChannelOption.SO_SNDBUF, nettyClientConfig.getClientSocketSndBufSize());
}
if (nettyClientConfig.getClientSocketRcvBufSize() != 0) {
handler.option(ChannelOption.SO_RCVBUF, nettyClientConfig.getClientSocketRcvBufSize());
}
if (nettyClientConfig.isClientPooledByteBufAllocatorEnable()) {
handler.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
}
this.timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
NettyRemotingClient.this.scanResponseTable();
} catch (Throwable e) {
log.error("scanResponseTable exception", e);
LOGGER.error("scanResponseTable exception", e);
}
}
}, 1000 * 3, 1000);
if (this.channelEventListener != null) {
this.nettyEventExecutor.start();
}
// this.timer.scheduleAtFixedRate(new TimerTask() {
// @Override
// public void run() {
// try {
// NettyRemotingClient.this.scanChannelTablesOfNameServer();
// } catch (Exception e) {
// LOGGER.error("scanChannelTablesOfNameServer exception", e);
// }
// }
// }, 1000 * 3, 10 * 1000);
this.timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
NettyRemotingClient.this.scanAvailableNameSrv();
} catch (Exception e) {
LOGGER.error("scanAvailableNameSrv exception", e);
}
}
}, 1000 * 3, this.nettyClientConfig.getConnectTimeoutMillis());
}
@Override
@@ -224,8 +286,8 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
try {
this.timer.cancel();
for (ChannelWrapper cw : this.channelTables.values()) {
this.closeChannel(null, cw.getChannel());
for (String addr : this.channelTables.keySet()) {
this.closeChannel(addr, this.channelTables.get(addr).getChannel());
}
this.channelTables.clear();
@@ -240,21 +302,30 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
this.defaultEventExecutorGroup.shutdownGracefully();
}
} catch (Exception e) {
log.error("NettyRemotingClient shutdown exception, ", e);
LOGGER.error("NettyRemotingClient shutdown exception, ", e);
}
if (this.publicExecutor != null) {
try {
this.publicExecutor.shutdown();
} catch (Exception e) {
log.error("NettyRemotingServer shutdown exception, ", e);
LOGGER.error("NettyRemotingServer shutdown exception, ", e);
}
}
if (this.scanExecutor != null) {
try {
this.scanExecutor.shutdown();
} catch (Exception e) {
LOGGER.error("NettyRemotingServer shutdown exception, ", e);
}
}
}
public void closeChannel(final String addr, final Channel channel) {
if (null == channel)
if (null == channel) {
return;
}
final String addrRemote = null == addr ? RemotingHelper.parseChannelRemoteAddr(channel) : addr;
@@ -264,46 +335,40 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
boolean removeItemFromTable = true;
final ChannelWrapper prevCW = this.channelTables.get(addrRemote);
log.info("closeChannel: begin close the channel[{}] Found: {}", addrRemote, prevCW != null);
LOGGER.info("closeChannel: begin close the channel[{}] Found: {}", addrRemote, prevCW != null);
if (null == prevCW) {
log.info("closeChannel: the channel[{}] has been removed from the channel table before", addrRemote);
LOGGER.info("closeChannel: the channel[{}] has been removed from the channel table before", addrRemote);
removeItemFromTable = false;
} else if (prevCW.getChannel() != channel) {
log.info("closeChannel: the channel[{}] has been closed before, and has been created again, nothing to do.",
LOGGER.info("closeChannel: the channel[{}] has been closed before, and has been created again, nothing to do.",
addrRemote);
removeItemFromTable = false;
}
if (removeItemFromTable) {
this.channelTables.remove(addrRemote);
log.info("closeChannel: the channel[{}] was removed from channel table", addrRemote);
LOGGER.info("closeChannel: the channel[{}] was removed from channel table", addrRemote);
}
RemotingUtil.closeChannel(channel);
} catch (Exception e) {
log.error("closeChannel: close the channel exception", e);
LOGGER.error("closeChannel: close the channel exception", e);
} finally {
this.lockChannelTables.unlock();
}
} else {
log.warn("closeChannel: try to lock channel table, but timeout, {}ms", LOCK_TIMEOUT_MILLIS);
LOGGER.warn("closeChannel: try to lock channel table, but timeout, {}ms", LOCK_TIMEOUT_MILLIS);
}
} catch (InterruptedException e) {
log.error("closeChannel exception", e);
}
}
@Override
public void registerRPCHook(RPCHook rpcHook) {
if (rpcHook != null && !rpcHooks.contains(rpcHook)) {
rpcHooks.add(rpcHook);
LOGGER.error("closeChannel exception", e);
}
}
public void closeChannel(final Channel channel) {
if (null == channel)
if (null == channel) {
return;
}
try {
if (this.lockChannelTables.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
@@ -324,25 +389,25 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
}
if (null == prevCW) {
log.info("eventCloseChannel: the channel[{}] has been removed from the channel table before", addrRemote);
LOGGER.info("eventCloseChannel: the channel[{}] has been removed from the channel table before", addrRemote);
removeItemFromTable = false;
}
if (removeItemFromTable) {
this.channelTables.remove(addrRemote);
log.info("closeChannel: the channel[{}] was removed from channel table", addrRemote);
LOGGER.info("closeChannel: the channel[{}] was removed from channel table", addrRemote);
RemotingUtil.closeChannel(channel);
}
} catch (Exception e) {
log.error("closeChannel: close the channel exception", e);
LOGGER.error("closeChannel: close the channel exception", e);
} finally {
this.lockChannelTables.unlock();
}
} else {
log.warn("closeChannel: try to lock channel table, but timeout, {}ms", LOCK_TIMEOUT_MILLIS);
LOGGER.warn("closeChannel: try to lock channel table, but timeout, {}ms", LOCK_TIMEOUT_MILLIS);
}
} catch (InterruptedException e) {
log.error("closeChannel exception", e);
LOGGER.error("closeChannel exception", e);
}
}
@@ -366,11 +431,20 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
if (update) {
Collections.shuffle(addrs);
log.info("name server address updated. NEW : {} , OLD: {}", addrs, old);
LOGGER.info("name server address updated. NEW : {} , OLD: {}", addrs, old);
this.namesrvAddrList.set(addrs);
if (!addrs.contains(this.namesrvAddrChoosed.get())) {
this.namesrvAddrChoosed.set(null);
// should close the channel if choosed addr is not exist.
if (this.namesrvAddrChoosed.get() != null && !addrs.contains(this.namesrvAddrChoosed.get())) {
String namesrvAddr = this.namesrvAddrChoosed.get();
for (String addr : this.channelTables.keySet()) {
if (addr.contains(namesrvAddr)) {
ChannelWrapper channelWrapper = this.channelTables.get(addr);
if (channelWrapper != null) {
closeChannel(channelWrapper.getChannel());
}
}
}
}
}
}
@@ -390,17 +464,20 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
}
RemotingCommand response = this.invokeSyncImpl(channel, request, timeoutMillis - costTime);
doAfterRpcHooks(RemotingHelper.parseChannelRemoteAddr(channel), request, response);
this.updateChannelLastResponseTime(addr);
return response;
} catch (RemotingSendRequestException e) {
log.warn("invokeSync: send request exception, so close the channel[{}]", addr);
LOGGER.warn("invokeSync: send request exception, so close the channel[{}]", addr);
doAfterRpcFailure(addr, request, false);
this.closeChannel(addr, channel);
throw e;
} catch (RemotingTimeoutException e) {
if (nettyClientConfig.isClientCloseSocketIfTimeout()) {
this.closeChannel(addr, channel);
log.warn("invokeSync: close socket because of timeout, {}ms, {}", timeoutMillis, addr);
LOGGER.warn("invokeSync: close socket because of timeout, {}ms, {}", timeoutMillis, addr);
}
log.warn("invokeSync: wait response timeout exception, the channel[{}]", addr);
doAfterRpcFailure(addr, request, true);
LOGGER.warn("invokeSync: wait response timeout exception, the channel[{}]", addr);
throw e;
}
} else {
@@ -409,7 +486,54 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
}
}
private Channel getAndCreateChannel(final String addr) throws RemotingConnectException, InterruptedException {
@Override
public void closeChannels() {
closeChannels(new ArrayList<String>(this.channelTables.keySet()));
}
@Override
public void closeChannels(List<String> addrList) {
for (String addr : addrList) {
ChannelWrapper cw = this.channelTables.get(addr);
if (cw == null) {
continue;
}
this.closeChannel(addr, cw.getChannel());
}
interruptPullRequests(new HashSet<String>(addrList));
}
private void interruptPullRequests(Set<String> brokerAddrSet) {
for (ResponseFuture responseFuture : responseTable.values()) {
RemotingCommand cmd = responseFuture.getRequestCommand();
if (cmd == null) {
continue;
}
String remoteAddr = RemotingHelper.parseChannelRemoteAddr(responseFuture.getChannel());
// interrupt only pull message request
if (brokerAddrSet.contains(remoteAddr) && (cmd.getCode() == 11 || cmd.getCode() == 361)) {
LOGGER.info("interrupt {}", cmd);
responseFuture.interrupt();
}
}
}
private void updateChannelLastResponseTime(final String addr) {
String address = addr;
if (address == null) {
address = this.namesrvAddrChoosed.get();
}
if (address == null) {
LOGGER.warn("[updateChannelLastResponseTime] could not find address!!");
return;
}
ChannelWrapper channelWrapper = this.channelTables.get(address);
if (channelWrapper != null && channelWrapper.isOK()) {
channelWrapper.updateLastResponseTime();
}
}
private Channel getAndCreateChannel(final String addr) throws InterruptedException {
if (null == addr) {
return getAndCreateNameserverChannel();
}
@@ -422,7 +546,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
return this.createChannel(addr);
}
private Channel getAndCreateNameserverChannel() throws RemotingConnectException, InterruptedException {
private Channel getAndCreateNameserverChannel() throws InterruptedException {
String addr = this.namesrvAddrChoosed.get();
if (addr != null) {
ChannelWrapper cw = this.channelTables.get(addr);
@@ -450,7 +574,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
String newAddr = addrList.get(index);
this.namesrvAddrChoosed.set(newAddr);
log.info("new name server is chosen. OLD: {} , NEW: {}. namesrvIndex = {}", addr, newAddr, namesrvIndex);
LOGGER.info("new name server is chosen. OLD: {} , NEW: {}. namesrvIndex = {}", addr, newAddr, namesrvIndex);
Channel channelNew = this.createChannel(newAddr);
if (channelNew != null) {
return channelNew;
@@ -458,11 +582,13 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
}
throw new RemotingConnectException(addrList.toString());
}
} catch (Exception e) {
LOGGER.error("getAndCreateNameserverChannel: create name server channel exception", e);
} finally {
this.namesrvChannelLock.unlock();
}
} else {
log.warn("getAndCreateNameserverChannel: try to lock name server, but timeout, {}ms", LOCK_TIMEOUT_MILLIS);
LOGGER.warn("getAndCreateNameserverChannel: try to lock name server, but timeout, {}ms", LOCK_TIMEOUT_MILLIS);
}
return null;
@@ -494,30 +620,30 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
if (createNewConnection) {
ChannelFuture channelFuture = this.bootstrap.connect(RemotingHelper.string2SocketAddress(addr));
log.info("createChannel: begin to connect remote host[{}] asynchronously", addr);
LOGGER.info("createChannel: begin to connect remote host[{}] asynchronously", addr);
cw = new ChannelWrapper(channelFuture);
this.channelTables.put(addr, cw);
}
} catch (Exception e) {
log.error("createChannel: create channel exception", e);
LOGGER.error("createChannel: create channel exception", e);
} finally {
this.lockChannelTables.unlock();
}
} else {
log.warn("createChannel: try to lock channel table, but timeout, {}ms", LOCK_TIMEOUT_MILLIS);
LOGGER.warn("createChannel: try to lock channel table, but timeout, {}ms", LOCK_TIMEOUT_MILLIS);
}
if (cw != null) {
ChannelFuture channelFuture = cw.getChannelFuture();
if (channelFuture.awaitUninterruptibly(this.nettyClientConfig.getConnectTimeoutMillis())) {
if (cw.isOK()) {
log.info("createChannel: connect remote host[{}] success, {}", addr, channelFuture.toString());
LOGGER.info("createChannel: connect remote host[{}] success, {}", addr, channelFuture.toString());
return cw.getChannel();
} else {
log.warn("createChannel: connect remote host[" + addr + "] failed, " + channelFuture.toString(), channelFuture.cause());
LOGGER.warn("createChannel: connect remote host[" + addr + "] failed, " + channelFuture.toString());
}
} else {
log.warn("createChannel: connect remote host[{}] timeout {}ms, {}", addr, this.nettyClientConfig.getConnectTimeoutMillis(),
LOGGER.warn("createChannel: connect remote host[{}] timeout {}ms, {}", addr, this.nettyClientConfig.getConnectTimeoutMillis(),
channelFuture.toString());
}
}
@@ -538,9 +664,9 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
if (timeoutMillis < costTime) {
throw new RemotingTooMuchRequestException("invokeAsync call the addr[" + addr + "] timeout");
}
this.invokeAsyncImpl(channel, request, timeoutMillis - costTime, invokeCallback);
this.invokeAsyncImpl(channel, request, timeoutMillis - costTime, new InvokeCallbackWrapper(invokeCallback, addr));
} catch (RemotingSendRequestException e) {
log.warn("invokeAsync: send request exception, so close the channel[{}]", addr);
LOGGER.warn("invokeAsync: send request exception, so close the channel[{}]", addr);
this.closeChannel(addr, channel);
throw e;
}
@@ -559,7 +685,8 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
doBeforeRpcHooks(addr, request);
this.invokeOnewayImpl(channel, request, timeoutMillis);
} catch (RemotingSendRequestException e) {
log.warn("invokeOneway: send request exception, so close the channel[{}]", addr);
LOGGER.warn("invokeOneway: send request exception, so close the channel[{}]", addr);
doAfterRpcFailure(addr, request, false);
this.closeChannel(addr, channel);
throw e;
}
@@ -594,6 +721,11 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
return this.namesrvAddrList.get();
}
@Override
public List<String> getAvailableNameSrvList() {
return new ArrayList<String>(this.availableNamesrvAddrMap.keySet());
}
@Override
public ChannelEventListener getChannelEventListener() {
return channelEventListener;
@@ -601,6 +733,9 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
@Override
public ExecutorService getCallbackExecutor() {
if (nettyClientConfig.isDisableCallbackExecutor()) {
return null;
}
return callbackExecutor != null ? callbackExecutor : publicExecutor;
}
@@ -609,11 +744,67 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
this.callbackExecutor = callbackExecutor;
}
@Override
public ConcurrentMap<Integer, ResponseFuture> getResponseTable() {
return this.responseTable;
}
protected void scanChannelTablesOfNameServer() {
List<String> nameServerList = this.namesrvAddrList.get();
if (nameServerList == null) {
LOGGER.warn("[SCAN] Addresses of name server is empty!");
return;
}
for (String addr : this.channelTables.keySet()) {
ChannelWrapper channelWrapper = this.channelTables.get(addr);
if (channelWrapper == null) {
continue;
}
if ((System.currentTimeMillis() - channelWrapper.getLastResponseTime()) > this.nettyClientConfig.getChannelNotActiveInterval()) {
LOGGER.warn("[SCAN] No response after {} from name server {}, so close it!", channelWrapper.getLastResponseTime(),
addr);
closeChannel(addr, channelWrapper.getChannel());
}
}
}
private void scanAvailableNameSrv() {
List<String> nameServerList = this.namesrvAddrList.get();
if (nameServerList == null) {
LOGGER.warn("scanAvailableNameSrv Addresses of name server is empty!");
return;
}
for (final String namesrvAddr : nameServerList) {
scanExecutor.execute(new Runnable() {
@Override
public void run() {
try {
Channel channel = NettyRemotingClient.this.getAndCreateChannel(namesrvAddr);
if (channel != null) {
NettyRemotingClient.this.availableNamesrvAddrMap.putIfAbsent(namesrvAddr, true);
} else {
NettyRemotingClient.this.availableNamesrvAddrMap.remove(namesrvAddr);
}
} catch (Exception e) {
LOGGER.error("scanAvailableNameSrv get channel of {} failed, ", namesrvAddr, e);
}
}
});
}
}
static class ChannelWrapper {
private final ChannelFuture channelFuture;
// only affected by sync or async request, oneway is not included.
private long lastResponseTime;
public ChannelWrapper(ChannelFuture channelFuture) {
this.channelFuture = channelFuture;
this.lastResponseTime = System.currentTimeMillis();
}
public boolean isOK() {
@@ -631,6 +822,33 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
public ChannelFuture getChannelFuture() {
return channelFuture;
}
public long getLastResponseTime() {
return this.lastResponseTime;
}
public void updateLastResponseTime() {
this.lastResponseTime = System.currentTimeMillis();
}
}
class InvokeCallbackWrapper implements InvokeCallback {
private final InvokeCallback invokeCallback;
private final String addr;
public InvokeCallbackWrapper(InvokeCallback invokeCallback, String addr) {
this.invokeCallback = invokeCallback;
this.addr = addr;
}
@Override
public void operationComplete(ResponseFuture responseFuture) {
if (responseFuture != null && responseFuture.isSendRequestOK() && responseFuture.getResponseCommand() != null) {
NettyRemotingClient.this.updateChannelLastResponseTime(addr);
}
this.invokeCallback.operationComplete(responseFuture);
}
}
class NettyClientHandler extends SimpleChannelInboundHandler<RemotingCommand> {
@@ -647,7 +865,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
ChannelPromise promise) throws Exception {
final String local = localAddress == null ? "UNKNOWN" : RemotingHelper.parseSocketAddressAddr(localAddress);
final String remote = remoteAddress == null ? "UNKNOWN" : RemotingHelper.parseSocketAddressAddr(remoteAddress);
log.info("NETTY CLIENT PIPELINE: CONNECT {} => {}", local, remote);
LOGGER.info("NETTY CLIENT PIPELINE: CONNECT {} => {}", local, remote);
super.connect(ctx, remoteAddress, localAddress, promise);
@@ -659,7 +877,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
@Override
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.info("NETTY CLIENT PIPELINE: DISCONNECT {}", remoteAddress);
LOGGER.info("NETTY CLIENT PIPELINE: DISCONNECT {}", remoteAddress);
closeChannel(ctx.channel());
super.disconnect(ctx, promise);
@@ -671,7 +889,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
@Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.info("NETTY CLIENT PIPELINE: CLOSE {}", remoteAddress);
LOGGER.info("NETTY CLIENT PIPELINE: CLOSE {}", remoteAddress);
closeChannel(ctx.channel());
super.close(ctx, promise);
NettyRemotingClient.this.failFast(ctx.channel());
@@ -686,7 +904,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state().equals(IdleState.ALL_IDLE)) {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.warn("NETTY CLIENT PIPELINE: IDLE exception [{}]", remoteAddress);
LOGGER.warn("NETTY CLIENT PIPELINE: IDLE exception [{}]", remoteAddress);
closeChannel(ctx.channel());
if (NettyRemotingClient.this.channelEventListener != null) {
NettyRemotingClient.this
@@ -701,8 +919,8 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.warn("NETTY CLIENT PIPELINE: exceptionCaught {}", remoteAddress);
log.warn("NETTY CLIENT PIPELINE: exceptionCaught exception.", cause);
LOGGER.warn("NETTY CLIENT PIPELINE: exceptionCaught {}", remoteAddress);
LOGGER.warn("NETTY CLIENT PIPELINE: exceptionCaught exception.", cause);
closeChannel(ctx.channel());
if (NettyRemotingClient.this.channelEventListener != null) {
NettyRemotingClient.this.putNettyEvent(new NettyEvent(NettyEventType.EXCEPTION, remoteAddress, ctx.channel()));
@@ -45,15 +45,17 @@ import java.security.cert.CertificateException;
import java.util.NoSuchElementException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.rocketmq.logging.InternalLogger;
import org.apache.rocketmq.logging.InternalLoggerFactory;
import org.apache.rocketmq.remoting.ChannelEventListener;
import org.apache.rocketmq.remoting.InvokeCallback;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.remoting.RemotingServer;
import org.apache.rocketmq.remoting.common.Pair;
import org.apache.rocketmq.remoting.common.RemotingHelper;
@@ -77,6 +79,11 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
private final Timer timer = new Timer("ServerHouseKeepingService", true);
private DefaultEventExecutorGroup defaultEventExecutorGroup;
/**
* NettyRemotingServer may holds multiple SubRemotingServer, each server will be stored in this container wih a
* ListenPort key.
*/
private ConcurrentMap<Integer/*Port*/, NettyRemotingAbstract> remotingServerTable = new ConcurrentHashMap<Integer, NettyRemotingAbstract>();
private int port = 0;
@@ -156,6 +163,8 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
}
loadSslContext();
this.remotingServerTable.put(this.nettyServerConfig.getListenPort(), this);
}
public void loadSslContext() {
@@ -199,7 +208,7 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
ServerBootstrap childHandler =
this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupSelector)
.channel(useEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, nettyServerConfig.getServerSocketBacklog())
.option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.SO_KEEPALIVE, false)
.childOption(ChannelOption.TCP_NODELAY, true)
@@ -293,13 +302,6 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
}
}
@Override
public void registerRPCHook(RPCHook rpcHook) {
if (rpcHook != null && !rpcHooks.contains(rpcHook)) {
rpcHooks.add(rpcHook);
}
}
@Override
public void registerProcessor(int requestCode, NettyRequestProcessor processor, ExecutorService executor) {
ExecutorService executorThis = executor;
@@ -326,6 +328,28 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
return processorTable.get(requestCode);
}
@Override
public Pair<NettyRequestProcessor, ExecutorService> getDefaultProcessorPair() {
return defaultRequestProcessor;
}
@Override
public RemotingServer newRemotingServer(final int port) {
SubRemotingServer remotingServer = new SubRemotingServer(port,
this.nettyServerConfig.getServerOnewaySemaphoreValue(),
this.nettyServerConfig.getServerAsyncSemaphoreValue());
NettyRemotingAbstract existingServer = this.remotingServerTable.putIfAbsent(port, remotingServer);
if (existingServer != null) {
throw new RuntimeException("The port " + port + " already in use by another RemotingServer");
}
return remotingServer;
}
@Override
public void removeRemotingServer(final int port) {
this.remotingServerTable.remove(port);
}
@Override
public RemotingCommand invokeSync(final Channel channel, final RemotingCommand request, final long timeoutMillis)
throws InterruptedException, RemotingSendRequestException, RemotingTimeoutException {
@@ -349,7 +373,6 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
return channelEventListener;
}
@Override
public ExecutorService getCallbackExecutor() {
return this.publicExecutor;
@@ -430,7 +453,14 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
@Override
protected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception {
processMessageReceived(ctx, msg);
int localPort = RemotingHelper.parseSocketAddressPort(ctx.channel().localAddress());
NettyRemotingAbstract remotingAbstract = NettyRemotingServer.this.remotingServerTable.get(localPort);
if (localPort != -1 && remotingAbstract != null) {
remotingAbstract.processMessageReceived(ctx, msg);
return;
}
// The related remoting server has been shutdown, so close the connected channel
RemotingUtil.closeChannel(ctx.channel());
}
}
@@ -503,4 +533,110 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
RemotingUtil.closeChannel(ctx.channel());
}
}
/**
* The NettyRemotingServer supports bind multiple ports, each port bound by a SubRemotingServer. The
* SubRemotingServer will delegate all the functions to NettyRemotingServer, so the sub server can share all the
* resources from its parent server.
*/
class SubRemotingServer extends NettyRemotingAbstract implements RemotingServer {
private final int listenPort;
private volatile Channel serverChannel;
SubRemotingServer(final int port, final int permitsOnway, final int permitsAsync) {
super(permitsOnway, permitsAsync);
listenPort = port;
}
@Override
public void registerProcessor(final int requestCode, final NettyRequestProcessor processor,
final ExecutorService executor) {
ExecutorService executorThis = executor;
if (null == executor) {
executorThis = NettyRemotingServer.this.publicExecutor;
}
Pair<NettyRequestProcessor, ExecutorService> pair = new Pair<NettyRequestProcessor, ExecutorService>(processor, executorThis);
this.processorTable.put(requestCode, pair);
}
@Override
public void registerDefaultProcessor(final NettyRequestProcessor processor, final ExecutorService executor) {
this.defaultRequestProcessor = new Pair<NettyRequestProcessor, ExecutorService>(processor, executor);
}
@Override
public int localListenPort() {
return listenPort;
}
@Override
public Pair<NettyRequestProcessor, ExecutorService> getProcessorPair(final int requestCode) {
return this.processorTable.get(requestCode);
}
@Override
public Pair<NettyRequestProcessor, ExecutorService> getDefaultProcessorPair() {
return this.defaultRequestProcessor;
}
@Override
public RemotingServer newRemotingServer(final int port) {
throw new UnsupportedOperationException("The SubRemotingServer of NettyRemotingServer " +
"doesn't support new nested RemotingServer");
}
@Override
public void removeRemotingServer(final int port) {
throw new UnsupportedOperationException("The SubRemotingServer of NettyRemotingServer " +
"doesn't support remove nested RemotingServer");
}
@Override
public RemotingCommand invokeSync(final Channel channel, final RemotingCommand request,
final long timeoutMillis) throws InterruptedException, RemotingSendRequestException, RemotingTimeoutException {
return this.invokeSyncImpl(channel, request, timeoutMillis);
}
@Override
public void invokeAsync(final Channel channel, final RemotingCommand request, final long timeoutMillis,
final InvokeCallback invokeCallback) throws InterruptedException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException {
this.invokeAsyncImpl(channel, request, timeoutMillis, invokeCallback);
}
@Override
public void invokeOneway(final Channel channel, final RemotingCommand request,
final long timeoutMillis) throws InterruptedException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException {
this.invokeOnewayImpl(channel, request, timeoutMillis);
}
@Override
public void start() {
try {
this.serverChannel = NettyRemotingServer.this.serverBootstrap.bind(listenPort).sync().channel();
} catch (InterruptedException e) {
throw new RuntimeException("this.subRemotingServer.serverBootstrap.bind().sync() InterruptedException", e);
}
}
@Override
public void shutdown() {
if (this.serverChannel != null) {
try {
this.serverChannel.close().await(5, TimeUnit.SECONDS);
} catch (InterruptedException ignored) {
}
}
}
@Override
public ChannelEventListener getChannelEventListener() {
return NettyRemotingServer.this.getChannelEventListener();
}
@Override
public ExecutorService getCallbackExecutor() {
return NettyRemotingServer.this.getCallbackExecutor();
}
}
}
@@ -27,5 +27,4 @@ public interface NettyRequestProcessor {
throws Exception;
boolean rejectRequest();
}
@@ -25,8 +25,9 @@ import org.apache.rocketmq.remoting.common.SemaphoreReleaseOnlyOnce;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public class ResponseFuture {
private final Channel channel;
private final int opaque;
private final Channel processChannel;
private final RemotingCommand request;
private final long timeoutMillis;
private final InvokeCallback invokeCallback;
private final long beginTimestamp = System.currentTimeMillis();
@@ -38,11 +39,18 @@ public class ResponseFuture {
private volatile RemotingCommand responseCommand;
private volatile boolean sendRequestOK = true;
private volatile Throwable cause;
private volatile boolean interrupted = false;
public ResponseFuture(Channel channel, int opaque, long timeoutMillis, InvokeCallback invokeCallback,
SemaphoreReleaseOnlyOnce once) {
SemaphoreReleaseOnlyOnce once) {
this(channel, opaque, null, timeoutMillis, invokeCallback, once);
}
public ResponseFuture(Channel channel, int opaque, RemotingCommand request, long timeoutMillis, InvokeCallback invokeCallback,
SemaphoreReleaseOnlyOnce once) {
this.channel = channel;
this.opaque = opaque;
this.processChannel = channel;
this.request = request;
this.timeoutMillis = timeoutMillis;
this.invokeCallback = invokeCallback;
this.once = once;
@@ -56,6 +64,11 @@ public class ResponseFuture {
}
}
public void interrupt() {
interrupted = true;
executeInvokeCallback();
}
public void release() {
if (this.once != null) {
this.once.release();
@@ -117,20 +130,23 @@ public class ResponseFuture {
return opaque;
}
public Channel getProcessChannel() {
return processChannel;
public RemotingCommand getRequestCommand() {
return request;
}
public Channel getChannel() {
return channel;
}
public boolean isInterrupted() {
return interrupted;
}
@Override
public String toString() {
return "ResponseFuture [responseCommand=" + responseCommand
+ ", sendRequestOK=" + sendRequestOK
+ ", cause=" + cause
+ ", opaque=" + opaque
+ ", processChannel=" + processChannel
+ ", timeoutMillis=" + timeoutMillis
+ ", invokeCallback=" + invokeCallback
+ ", beginTimestamp=" + beginTimestamp
return "ResponseFuture [responseCommand=" + responseCommand + ", sendRequestOK=" + sendRequestOK
+ ", cause=" + cause + ", opaque=" + opaque + ", timeoutMillis=" + timeoutMillis
+ ", invokeCallback=" + invokeCallback + ", beginTimestamp=" + beginTimestamp
+ ", countDownLatch=" + countDownLatch + "]";
}
}
@@ -123,8 +123,7 @@ public class RemotingCommand {
return createResponseCommand(RemotingSysResponseCode.SYSTEM_ERROR, "not set any response code", classHeader);
}
public static RemotingCommand buildErrorResponse(int code, String remark,
Class<? extends CommandCustomHeader> classHeader) {
public static RemotingCommand buildErrorResponse(int code, String remark, Class<? extends CommandCustomHeader> classHeader) {
final RemotingCommand response = RemotingCommand.createResponseCommand(classHeader);
response.setCode(code);
response.setRemark(remark);
@@ -256,9 +255,8 @@ public class RemotingCommand {
this.customHeader = customHeader;
}
public CommandCustomHeader decodeCommandCustomHeader(
Class<? extends CommandCustomHeader> classHeader) throws RemotingCommandException {
CommandCustomHeader objectHeader;
public <T extends CommandCustomHeader> T decodeCommandCustomHeader(Class<T> classHeader) throws RemotingCommandException {
T objectHeader;
try {
objectHeader = classHeader.newInstance();
} catch (InstantiationException e) {
@@ -43,15 +43,14 @@ public class RemotingServerTest {
public static RemotingServer createRemotingServer() throws InterruptedException {
NettyServerConfig config = new NettyServerConfig();
RemotingServer remotingServer = new NettyRemotingServer(config);
remotingServer.registerProcessor(0, new AsyncNettyRequestProcessor() {
remotingServer.registerProcessor(0, new NettyRequestProcessor() {
@Override
public RemotingCommand processRequest(ChannelHandlerContext ctx, RemotingCommand request) {
request.setRemark("Hi " + ctx.channel().remoteAddress());
return request;
}
@Override
public boolean rejectRequest() {
@Override public boolean rejectRequest() {
return false;
}
}, Executors.newCachedThreadPool());
@@ -0,0 +1,109 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.remoting;
import io.netty.channel.ChannelHandlerContext;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingConnectException;
import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.remoting.protocol.RemotingSysResponseCode;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown;
public class SubRemotingServerTest {
private static final int subServerPort = 1234;
private static RemotingServer remotingServer;
private static RemotingClient remotingClient;
private static RemotingServer subServer;
@BeforeClass
public static void setup() throws InterruptedException {
remotingServer = RemotingServerTest.createRemotingServer();
remotingClient = RemotingServerTest.createRemotingClient();
subServer = createSubRemotingServer(remotingServer);
}
@AfterClass
public static void destroy() {
remotingClient.shutdown();
remotingServer.shutdown();
}
public static RemotingServer createSubRemotingServer(RemotingServer parentServer) {
RemotingServer subServer = parentServer.newRemotingServer(subServerPort);
subServer.registerProcessor(1, new NettyRequestProcessor() {
@Override
public RemotingCommand processRequest(final ChannelHandlerContext ctx,
final RemotingCommand request) throws Exception {
request.setRemark(String.valueOf(RemotingHelper.parseSocketAddressPort(ctx.channel().localAddress())));
return request;
}
@Override
public boolean rejectRequest() {
return false;
}
}, null);
subServer.start();
return subServer;
}
@Test
public void testInvokeSubRemotingServer() throws InterruptedException, RemotingTimeoutException, RemotingConnectException, RemotingSendRequestException {
RequestHeader requestHeader = new RequestHeader();
requestHeader.setCount(1);
requestHeader.setMessageTitle("Welcome");
// Parent remoting server doesn't support RequestCode 1
RemotingCommand request = RemotingCommand.createRequestCommand(1, requestHeader);
RemotingCommand response = remotingClient.invokeSync("localhost:8888", request, 1000 * 3);
assertThat(response).isNotNull();
assertThat(response.getCode()).isEqualTo(RemotingSysResponseCode.REQUEST_CODE_NOT_SUPPORTED);
// Issue request to SubRemotingServer
response = remotingClient.invokeSync("localhost:1234", request, 1000 * 3);
assertThat(response).isNotNull();
assertThat(response.getExtFields()).hasSize(2);
assertThat(response.getRemark()).isEqualTo(String.valueOf(subServerPort));
// Issue unsupported request to SubRemotingServer
request.setCode(0);
response = remotingClient.invokeSync("localhost:1234", request, 1000 * 3);
assertThat(response).isNotNull();
assertThat(response.getCode()).isEqualTo(RemotingSysResponseCode.REQUEST_CODE_NOT_SUPPORTED);
// Issue request to a closed SubRemotingServer
request.setCode(1);
remotingServer.removeRemotingServer(subServerPort);
subServer.shutdown();
try {
remotingClient.invokeSync("localhost:1234", request, 1000 * 3);
failBecauseExceptionWasNotThrown(RemotingTimeoutException.class);
} catch (Exception e) {
assertThat(e).isInstanceOf(RemotingTimeoutException.class);
}
}
}
@@ -37,7 +37,7 @@ public class NettyRemotingAbstractTest {
@Test
public void testProcessResponseCommand() throws InterruptedException {
final Semaphore semaphore = new Semaphore(0);
ResponseFuture responseFuture = new ResponseFuture(null,1, 3000, new InvokeCallback() {
ResponseFuture responseFuture = new ResponseFuture(null, 1, 3000, new InvokeCallback() {
@Override
public void operationComplete(final ResponseFuture responseFuture) {
assertThat(semaphore.availablePermits()).isEqualTo(0);
@@ -58,7 +58,7 @@ public class NettyRemotingAbstractTest {
@Test
public void testProcessResponseCommand_NullCallBack() throws InterruptedException {
final Semaphore semaphore = new Semaphore(0);
ResponseFuture responseFuture = new ResponseFuture(null,1, 3000, null,
ResponseFuture responseFuture = new ResponseFuture(null, 1, 3000, null,
new SemaphoreReleaseOnlyOnce(semaphore));
remotingAbstract.responseTable.putIfAbsent(1, responseFuture);
@@ -73,7 +73,7 @@ public class NettyRemotingAbstractTest {
@Test
public void testProcessResponseCommand_RunCallBackInCurrentThread() throws InterruptedException {
final Semaphore semaphore = new Semaphore(0);
ResponseFuture responseFuture = new ResponseFuture(null,1, 3000, new InvokeCallback() {
ResponseFuture responseFuture = new ResponseFuture(null, 1, 3000, new InvokeCallback() {
@Override
public void operationComplete(final ResponseFuture responseFuture) {
assertThat(semaphore.availablePermits()).isEqualTo(0);
@@ -23,6 +23,7 @@ import java.util.HashSet;
import java.util.Set;
import com.alibaba.fastjson.JSON;
import java.util.HashSet;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;