Merge remote-tracking branch 'apache/develop' into 5.0.0-beta-dledger-controller

# Conflicts:
#	distribution/bin/mqshutdown
#	pom.xml
This commit is contained in:
RongtongJin
2022-07-20 10:53:39 +08:00
256 changed files with 24338 additions and 498 deletions
+2
View File
@@ -34,12 +34,14 @@ header:
- 'src/test/**/*.log'
- '*/src/test/resources/META-INF/service/*'
- '*/src/main/resources/META-INF/service/*'
- '*/src/test/resources/rmq-proxy-home/conf/rmq-proxy.json'
- '**/target/**'
- '**/*.iml'
- 'docs/**'
- 'localbin/**'
- 'distribution/LICENSE-BIN'
- 'distribution/NOTICE-BIN'
- 'distribution/conf/rmq-proxy.json'
comment: on-failure
+8
View File
@@ -19,6 +19,10 @@
<name>rocketmq-acl ${project.version}</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>rocketmq-proto</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>rocketmq-remoting</artifactId>
@@ -62,6 +66,10 @@
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
</dependency>
</dependencies>
</project>
@@ -17,9 +17,10 @@
package org.apache.rocketmq.acl;
import com.google.protobuf.GeneratedMessageV3;
import java.util.List;
import java.util.Map;
import org.apache.rocketmq.acl.common.AuthenticationHeader;
import org.apache.rocketmq.common.AclConfig;
import org.apache.rocketmq.common.DataVersion;
import org.apache.rocketmq.common.PlainAccessConfig;
@@ -36,6 +37,14 @@ public interface AccessValidator {
*/
AccessResource parse(RemotingCommand request, String remoteAddr);
/**
* Parse to get the AccessResource from gRPC protocol
* @param messageV3
* @param header
* @return Plain access resource
*/
AccessResource parse(GeneratedMessageV3 messageV3, AuthenticationHeader header);
/**
* Validate the access resource.
*
@@ -0,0 +1,22 @@
/*
* 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.acl;
public interface PermissionChecker {
void check(AccessResource checkedAccess, AccessResource ownedAccess);
}
@@ -0,0 +1,236 @@
/*
* 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.acl.common;
import com.google.common.base.MoreObjects;
public class AuthenticationHeader {
private String remoteAddress;
private String tenantId;
private String namespace;
private String authorization;
private String datetime;
private String sessionToken;
private String requestId;
private String language;
private String clientVersion;
private String protocol;
private int requestCode;
AuthenticationHeader(final String remoteAddress, final String tenantId, final String namespace,
final String authorization, final String datetime, final String sessionToken, final String requestId,
final String language, final String clientVersion, final String protocol, final int requestCode) {
this.remoteAddress = remoteAddress;
this.tenantId = tenantId;
this.namespace = namespace;
this.authorization = authorization;
this.datetime = datetime;
this.sessionToken = sessionToken;
this.requestId = requestId;
this.language = language;
this.clientVersion = clientVersion;
this.protocol = protocol;
this.requestCode = requestCode;
}
public static class MetadataHeaderBuilder {
private String remoteAddress;
private String tenantId;
private String namespace;
private String authorization;
private String datetime;
private String sessionToken;
private String requestId;
private String language;
private String clientVersion;
private String protocol;
private int requestCode;
MetadataHeaderBuilder() {
}
public AuthenticationHeader.MetadataHeaderBuilder remoteAddress(final String remoteAddress) {
this.remoteAddress = remoteAddress;
return this;
}
public AuthenticationHeader.MetadataHeaderBuilder tenantId(final String tenantId) {
this.tenantId = tenantId;
return this;
}
public AuthenticationHeader.MetadataHeaderBuilder namespace(final String namespace) {
this.namespace = namespace;
return this;
}
public AuthenticationHeader.MetadataHeaderBuilder authorization(final String authorization) {
this.authorization = authorization;
return this;
}
public AuthenticationHeader.MetadataHeaderBuilder datetime(final String datetime) {
this.datetime = datetime;
return this;
}
public AuthenticationHeader.MetadataHeaderBuilder sessionToken(final String sessionToken) {
this.sessionToken = sessionToken;
return this;
}
public AuthenticationHeader.MetadataHeaderBuilder requestId(final String requestId) {
this.requestId = requestId;
return this;
}
public AuthenticationHeader.MetadataHeaderBuilder language(final String language) {
this.language = language;
return this;
}
public AuthenticationHeader.MetadataHeaderBuilder clientVersion(final String clientVersion) {
this.clientVersion = clientVersion;
return this;
}
public AuthenticationHeader.MetadataHeaderBuilder protocol(final String protocol) {
this.protocol = protocol;
return this;
}
public AuthenticationHeader.MetadataHeaderBuilder requestCode(final int requestCode) {
this.requestCode = requestCode;
return this;
}
public AuthenticationHeader build() {
return new AuthenticationHeader(this.remoteAddress, this.tenantId, this.namespace, this.authorization,
this.datetime, this.sessionToken, this.requestId, this.language, this.clientVersion, this.protocol,
this.requestCode);
}
}
public static AuthenticationHeader.MetadataHeaderBuilder builder() {
return new AuthenticationHeader.MetadataHeaderBuilder();
}
public String getRemoteAddress() {
return this.remoteAddress;
}
public String getTenantId() {
return this.tenantId;
}
public String getNamespace() {
return this.namespace;
}
public String getAuthorization() {
return this.authorization;
}
public String getDatetime() {
return this.datetime;
}
public String getSessionToken() {
return this.sessionToken;
}
public String getRequestId() {
return this.requestId;
}
public String getLanguage() {
return this.language;
}
public String getClientVersion() {
return this.clientVersion;
}
public String getProtocol() {
return this.protocol;
}
public int getRequestCode() {
return this.requestCode;
}
public void setRemoteAddress(final String remoteAddress) {
this.remoteAddress = remoteAddress;
}
public void setTenantId(final String tenantId) {
this.tenantId = tenantId;
}
public void setNamespace(final String namespace) {
this.namespace = namespace;
}
public void setAuthorization(final String authorization) {
this.authorization = authorization;
}
public void setDatetime(final String datetime) {
this.datetime = datetime;
}
public void setSessionToken(final String sessionToken) {
this.sessionToken = sessionToken;
}
public void setRequestId(final String requestId) {
this.requestId = requestId;
}
public void setLanguage(final String language) {
this.language = language;
}
public void setClientVersion(final String clientVersion) {
this.clientVersion = clientVersion;
}
public void setProtocol(final String protocol) {
this.protocol = protocol;
}
public void setRequestCode(int requestCode) {
this.requestCode = requestCode;
}
@Override public String toString() {
return MoreObjects.toStringHelper(this)
.add("remoteAddress", remoteAddress)
.add("tenantId", tenantId)
.add("namespace", namespace)
.add("authorization", authorization)
.add("datetime", datetime)
.add("sessionToken", sessionToken)
.add("requestId", requestId)
.add("language", language)
.add("clientVersion", clientVersion)
.add("protocol", protocol)
.add("requestCode", requestCode)
.toString();
}
}
@@ -0,0 +1,121 @@
/*
* 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.acl.common;
import com.google.common.base.MoreObjects;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
public class AuthorizationHeader {
private static final String HEADER_SEPARATOR = " ";
private static final String CREDENTIALS_SEPARATOR = "/";
private static final int AUTH_HEADER_KV_LENGTH = 2;
private static final String CREDENTIAL = "Credential";
private static final String SIGNED_HEADERS = "SignedHeaders";
private static final String SIGNATURE = "Signature";
private String method;
private String accessKey;
private String[] signedHeaders;
private String signature;
/**
* Parse authorization from gRPC header.
*
* @param header gRPC header string.
* @throws Exception exception.
*/
public AuthorizationHeader(String header) throws DecoderException {
String[] result = header.split(HEADER_SEPARATOR, 2);
if (result.length != 2) {
throw new DecoderException("authorization header is incorrect");
}
this.method = result[0];
String[] keyValues = result[1].split(",");
for (String keyValue : keyValues) {
String[] kv = keyValue.trim().split("=", 2);
int kvLength = kv.length;
if (kv.length != AUTH_HEADER_KV_LENGTH) {
throw new DecoderException("authorization keyValues length is incorrect, actual length=" + kvLength);
}
String authItem = kv[0];
if (CREDENTIAL.equals(authItem)) {
String[] credential = kv[1].split(CREDENTIALS_SEPARATOR);
int credentialActualLength = credential.length;
if (credentialActualLength == 0) {
throw new DecoderException("authorization credential length is incorrect, actual length=" + credentialActualLength);
}
this.accessKey = credential[0];
continue;
}
if (SIGNED_HEADERS.equals(authItem)) {
this.signedHeaders = kv[1].split(";");
continue;
}
if (SIGNATURE.equals(authItem)) {
this.signature = this.hexToBase64(kv[1]);
}
}
}
public String hexToBase64(String input) throws DecoderException {
byte[] bytes = Hex.decodeHex(input);
return Base64.encodeBase64String(bytes);
}
public String getMethod() {
return this.method;
}
public String getAccessKey() {
return this.accessKey;
}
public String[] getSignedHeaders() {
return this.signedHeaders;
}
public String getSignature() {
return this.signature;
}
public void setMethod(final String method) {
this.method = method;
}
public void setAccessKey(final String accessKey) {
this.accessKey = accessKey;
}
public void setSignedHeaders(final String[] signedHeaders) {
this.signedHeaders = signedHeaders;
}
public void setSignature(final String signature) {
this.signature = signature;
}
@Override public String toString() {
return MoreObjects.toStringHelper(this)
.add("method", method)
.add("accessKey", accessKey)
.add("signedHeaders", signedHeaders)
.add("signature", signature)
.toString();
}
}
@@ -16,11 +16,46 @@
*/
package org.apache.rocketmq.acl.plain;
import apache.rocketmq.v2.AckMessageRequest;
import apache.rocketmq.v2.EndTransactionRequest;
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueRequest;
import apache.rocketmq.v2.HeartbeatRequest;
import apache.rocketmq.v2.Message;
import apache.rocketmq.v2.ReceiveMessageRequest;
import apache.rocketmq.v2.Resource;
import apache.rocketmq.v2.SendMessageRequest;
import apache.rocketmq.v2.Subscription;
import apache.rocketmq.v2.SubscriptionEntry;
import apache.rocketmq.v2.TelemetryCommand;
import com.google.protobuf.GeneratedMessageV3;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.rocketmq.acl.AccessResource;
import org.apache.rocketmq.acl.common.AclException;
import org.apache.rocketmq.acl.common.AclUtils;
import org.apache.rocketmq.acl.common.AuthenticationHeader;
import org.apache.rocketmq.acl.common.AuthorizationHeader;
import org.apache.rocketmq.acl.common.Permission;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.PlainAccessConfig;
import org.apache.rocketmq.common.protocol.NamespaceUtil;
import org.apache.rocketmq.common.protocol.RequestCode;
import org.apache.rocketmq.common.protocol.ResponseCode;
import org.apache.rocketmq.common.protocol.header.GetConsumerListByGroupRequestHeader;
import org.apache.rocketmq.common.protocol.header.UnregisterClientRequestHeader;
import org.apache.rocketmq.common.protocol.header.UpdateConsumerOffsetRequestHeader;
import org.apache.rocketmq.common.protocol.heartbeat.ConsumerData;
import org.apache.rocketmq.common.protocol.heartbeat.HeartbeatData;
import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public class PlainAccessResource implements AccessResource {
@@ -55,6 +90,201 @@ public class PlainAccessResource implements AccessResource {
public PlainAccessResource() {
}
public static PlainAccessResource parse(RemotingCommand request, String remoteAddr) {
PlainAccessResource accessResource = new PlainAccessResource();
if (remoteAddr != null && remoteAddr.contains(":")) {
accessResource.setWhiteRemoteAddress(remoteAddr.substring(0, remoteAddr.lastIndexOf(':')));
} else {
accessResource.setWhiteRemoteAddress(remoteAddr);
}
accessResource.setRequestCode(request.getCode());
if (request.getExtFields() == null) {
// If request's extFields is null,then return accessResource directly(users can use whiteAddress pattern)
// The following logic codes depend on the request's extFields not to be null.
return accessResource;
}
accessResource.setAccessKey(request.getExtFields().get(SessionCredentials.ACCESS_KEY));
accessResource.setSignature(request.getExtFields().get(SessionCredentials.SIGNATURE));
accessResource.setSecretToken(request.getExtFields().get(SessionCredentials.SECURITY_TOKEN));
try {
switch (request.getCode()) {
case RequestCode.SEND_MESSAGE:
final String topic = request.getExtFields().get("topic");
if (PlainAccessResource.isRetryTopic(topic)) {
accessResource.addResourceAndPerm(getRetryTopic(request.getExtFields().get("group")), Permission.SUB);
} else {
accessResource.addResourceAndPerm(topic, Permission.PUB);
}
break;
case RequestCode.SEND_MESSAGE_V2:
final String topicV2 = request.getExtFields().get("b");
if (PlainAccessResource.isRetryTopic(topicV2)) {
accessResource.addResourceAndPerm(getRetryTopic(request.getExtFields().get("a")), Permission.SUB);
} else {
accessResource.addResourceAndPerm(topicV2, Permission.PUB);
}
break;
case RequestCode.CONSUMER_SEND_MSG_BACK:
accessResource.addResourceAndPerm(getRetryTopic(request.getExtFields().get("group")), Permission.SUB);
break;
case RequestCode.PULL_MESSAGE:
accessResource.addResourceAndPerm(request.getExtFields().get("topic"), Permission.SUB);
accessResource.addResourceAndPerm(getRetryTopic(request.getExtFields().get("consumerGroup")), Permission.SUB);
break;
case RequestCode.QUERY_MESSAGE:
accessResource.addResourceAndPerm(request.getExtFields().get("topic"), Permission.SUB);
break;
case RequestCode.HEART_BEAT:
HeartbeatData heartbeatData = HeartbeatData.decode(request.getBody(), HeartbeatData.class);
for (ConsumerData data : heartbeatData.getConsumerDataSet()) {
accessResource.addResourceAndPerm(getRetryTopic(data.getGroupName()), Permission.SUB);
for (SubscriptionData subscriptionData : data.getSubscriptionDataSet()) {
accessResource.addResourceAndPerm(subscriptionData.getTopic(), Permission.SUB);
}
}
break;
case RequestCode.UNREGISTER_CLIENT:
final UnregisterClientRequestHeader unregisterClientRequestHeader =
(UnregisterClientRequestHeader) request
.decodeCommandCustomHeader(UnregisterClientRequestHeader.class);
accessResource.addResourceAndPerm(getRetryTopic(unregisterClientRequestHeader.getConsumerGroup()), Permission.SUB);
break;
case RequestCode.GET_CONSUMER_LIST_BY_GROUP:
final GetConsumerListByGroupRequestHeader getConsumerListByGroupRequestHeader =
(GetConsumerListByGroupRequestHeader) request
.decodeCommandCustomHeader(GetConsumerListByGroupRequestHeader.class);
accessResource.addResourceAndPerm(getRetryTopic(getConsumerListByGroupRequestHeader.getConsumerGroup()), Permission.SUB);
break;
case RequestCode.UPDATE_CONSUMER_OFFSET:
final UpdateConsumerOffsetRequestHeader updateConsumerOffsetRequestHeader =
(UpdateConsumerOffsetRequestHeader) request
.decodeCommandCustomHeader(UpdateConsumerOffsetRequestHeader.class);
accessResource.addResourceAndPerm(getRetryTopic(updateConsumerOffsetRequestHeader.getConsumerGroup()), Permission.SUB);
accessResource.addResourceAndPerm(updateConsumerOffsetRequestHeader.getTopic(), Permission.SUB);
break;
default:
break;
}
} catch (Throwable t) {
throw new AclException(t.getMessage(), t);
}
// Content
SortedMap<String, String> map = new TreeMap<String, String>();
for (Map.Entry<String, String> entry : request.getExtFields().entrySet()) {
if (!SessionCredentials.SIGNATURE.equals(entry.getKey())
&& !MixAll.UNIQUE_MSG_QUERY_FLAG.equals(entry.getKey())) {
map.put(entry.getKey(), entry.getValue());
}
}
accessResource.setContent(AclUtils.combineRequestContent(request, map));
return accessResource;
}
public static PlainAccessResource parse(GeneratedMessageV3 messageV3, AuthenticationHeader header) {
PlainAccessResource accessResource = new PlainAccessResource();
String remoteAddress = header.getRemoteAddress();
if (remoteAddress != null && remoteAddress.contains(":")) {
accessResource.setWhiteRemoteAddress(RemotingHelper.parseHostFromAddress(remoteAddress));
} else {
accessResource.setWhiteRemoteAddress(remoteAddress);
}
try {
AuthorizationHeader authorizationHeader = new AuthorizationHeader(header.getAuthorization());
accessResource.setAccessKey(authorizationHeader.getAccessKey());
accessResource.setSignature(authorizationHeader.getSignature());
} catch (DecoderException e) {
throw new AclException(e.getMessage(), e);
}
accessResource.setSecretToken(header.getSessionToken());
accessResource.setRequestCode(header.getRequestCode());
accessResource.setContent(header.getDatetime().getBytes(StandardCharsets.UTF_8));
try {
String rpcFullName = messageV3.getDescriptorForType().getFullName();
if (HeartbeatRequest.getDescriptor().getFullName().equals(rpcFullName)) {
HeartbeatRequest request = (HeartbeatRequest) messageV3;
if (request.hasGroup()) {
accessResource.addResourceAndPerm(request.getGroup(), Permission.SUB);
}
} else if (SendMessageRequest.getDescriptor().getFullName().equals(rpcFullName)) {
SendMessageRequest request = (SendMessageRequest) messageV3;
if (request.getMessagesCount() <= 0) {
throw new AclException("SendMessageRequest, messageCount is zero", ResponseCode.MESSAGE_ILLEGAL);
}
Resource topic = request.getMessages(0).getTopic();
for (Message message : request.getMessagesList()) {
if (!message.getTopic().equals(topic)) {
throw new AclException("SendMessageRequest, messages' topic is not consistent", ResponseCode.MESSAGE_ILLEGAL);
}
}
accessResource.addResourceAndPerm(topic, Permission.PUB);
} else if (ReceiveMessageRequest.getDescriptor().getFullName().equals(rpcFullName)) {
ReceiveMessageRequest request = (ReceiveMessageRequest) messageV3;
accessResource.addResourceAndPerm(request.getGroup(), Permission.SUB);
accessResource.addResourceAndPerm(request.getMessageQueue().getTopic(), Permission.SUB);
} else if (AckMessageRequest.getDescriptor().getFullName().equals(rpcFullName)) {
AckMessageRequest request = (AckMessageRequest) messageV3;
accessResource.addResourceAndPerm(request.getGroup(), Permission.SUB);
accessResource.addResourceAndPerm(request.getTopic(), Permission.SUB);
} else if (ForwardMessageToDeadLetterQueueRequest.getDescriptor().getFullName().equals(rpcFullName)) {
ForwardMessageToDeadLetterQueueRequest request = (ForwardMessageToDeadLetterQueueRequest) messageV3;
accessResource.addResourceAndPerm(request.getGroup(), Permission.SUB);
accessResource.addResourceAndPerm(request.getTopic(), Permission.SUB);
} else if (EndTransactionRequest.getDescriptor().getFullName().equals(rpcFullName)) {
EndTransactionRequest request = (EndTransactionRequest) messageV3;
accessResource.addResourceAndPerm(request.getTopic(), Permission.PUB);
} else if (TelemetryCommand.getDescriptor().getFullName().equals(rpcFullName)) {
TelemetryCommand command = (TelemetryCommand) messageV3;
if (command.getCommandCase() == TelemetryCommand.CommandCase.SETTINGS) {
if (command.getSettings().hasPublishing()) {
List<Resource> topicList = command.getSettings().getPublishing().getTopicsList();
for (Resource topic : topicList) {
accessResource.addResourceAndPerm(topic, Permission.PUB);
}
}
if (command.getSettings().hasSubscription()) {
Subscription subscription = command.getSettings().getSubscription();
accessResource.addResourceAndPerm(subscription.getGroup(), Permission.SUB);
for (SubscriptionEntry entry : subscription.getSubscriptionsList()) {
accessResource.addResourceAndPerm(entry.getTopic(), Permission.SUB);
}
}
}
}
} catch (Throwable t) {
throw new AclException(t.getMessage(), t);
}
return accessResource;
}
private void addResourceAndPerm(Resource resource, byte permission) {
String resourceName = NamespaceUtil.wrapNamespace(resource.getResourceNamespace(), resource.getName());
addResourceAndPerm(resourceName, permission);
}
public static PlainAccessResource build(PlainAccessConfig plainAccessConfig, RemoteAddressStrategy remoteAddressStrategy) {
PlainAccessResource plainAccessResource = new PlainAccessResource();
plainAccessResource.setAccessKey(plainAccessConfig.getAccessKey());
plainAccessResource.setSecretKey(plainAccessConfig.getSecretKey());
plainAccessResource.setWhiteRemoteAddress(plainAccessConfig.getWhiteRemoteAddress());
plainAccessResource.setAdmin(plainAccessConfig.isAdmin());
plainAccessResource.setDefaultGroupPerm(Permission.parsePermFromString(plainAccessConfig.getDefaultGroupPerm()));
plainAccessResource.setDefaultTopicPerm(Permission.parsePermFromString(plainAccessConfig.getDefaultTopicPerm()));
Permission.parseResourcePerms(plainAccessResource, false, plainAccessConfig.getGroupPerms());
Permission.parseResourcePerms(plainAccessResource, true, plainAccessConfig.getTopicPerms());
plainAccessResource.setRemoteAddressStrategy(remoteAddressStrategy);
return plainAccessResource;
}
public static boolean isRetryTopic(String topic) {
return null != topic && topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX);
}
@@ -16,31 +16,16 @@
*/
package org.apache.rocketmq.acl.plain;
import org.apache.rocketmq.acl.AccessResource;
import org.apache.rocketmq.acl.AccessValidator;
import org.apache.rocketmq.acl.common.AclException;
import org.apache.rocketmq.acl.common.AclUtils;
import org.apache.rocketmq.acl.common.Permission;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.common.AclConfig;
import org.apache.rocketmq.common.DataVersion;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.PlainAccessConfig;
import org.apache.rocketmq.common.protocol.RequestCode;
import org.apache.rocketmq.common.protocol.header.GetConsumerListByGroupRequestHeader;
import org.apache.rocketmq.common.protocol.header.UnregisterClientRequestHeader;
import org.apache.rocketmq.common.protocol.header.UpdateConsumerOffsetRequestHeader;
import org.apache.rocketmq.common.protocol.heartbeat.ConsumerData;
import org.apache.rocketmq.common.protocol.heartbeat.HeartbeatData;
import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import com.google.protobuf.GeneratedMessageV3;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import static org.apache.rocketmq.acl.plain.PlainAccessResource.getRetryTopic;
import org.apache.rocketmq.acl.AccessResource;
import org.apache.rocketmq.acl.AccessValidator;
import org.apache.rocketmq.acl.common.AuthenticationHeader;
import org.apache.rocketmq.common.AclConfig;
import org.apache.rocketmq.common.DataVersion;
import org.apache.rocketmq.common.PlainAccessConfig;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public class PlainAccessValidator implements AccessValidator {
@@ -52,98 +37,12 @@ public class PlainAccessValidator implements AccessValidator {
@Override
public AccessResource parse(RemotingCommand request, String remoteAddr) {
PlainAccessResource accessResource = new PlainAccessResource();
if (remoteAddr != null && remoteAddr.contains(":")) {
accessResource.setWhiteRemoteAddress(remoteAddr.substring(0, remoteAddr.lastIndexOf(':')));
} else {
accessResource.setWhiteRemoteAddress(remoteAddr);
}
return PlainAccessResource.parse(request, remoteAddr);
}
accessResource.setRequestCode(request.getCode());
if (request.getExtFields() == null) {
// If request's extFields is null,then return accessResource directly(users can use whiteAddress pattern)
// The following logic codes depend on the request's extFields not to be null.
return accessResource;
}
accessResource.setAccessKey(request.getExtFields().get(SessionCredentials.ACCESS_KEY));
accessResource.setSignature(request.getExtFields().get(SessionCredentials.SIGNATURE));
accessResource.setSecretToken(request.getExtFields().get(SessionCredentials.SECURITY_TOKEN));
try {
switch (request.getCode()) {
case RequestCode.SEND_MESSAGE:
final String topic = request.getExtFields().get("topic");
if (PlainAccessResource.isRetryTopic(topic)) {
accessResource.addResourceAndPerm(getRetryTopic(request.getExtFields().get("group")), Permission.SUB);
} else {
accessResource.addResourceAndPerm(topic, Permission.PUB);
}
break;
case RequestCode.SEND_MESSAGE_V2:
final String topicV2 = request.getExtFields().get("b");
if (PlainAccessResource.isRetryTopic(topicV2)) {
accessResource.addResourceAndPerm(getRetryTopic(request.getExtFields().get("a")), Permission.SUB);
} else {
accessResource.addResourceAndPerm(topicV2, Permission.PUB);
}
break;
case RequestCode.CONSUMER_SEND_MSG_BACK:
accessResource.addResourceAndPerm(getRetryTopic(request.getExtFields().get("group")), Permission.SUB);
break;
case RequestCode.PULL_MESSAGE:
accessResource.addResourceAndPerm(request.getExtFields().get("topic"), Permission.SUB);
accessResource.addResourceAndPerm(getRetryTopic(request.getExtFields().get("consumerGroup")), Permission.SUB);
break;
case RequestCode.QUERY_MESSAGE:
accessResource.addResourceAndPerm(request.getExtFields().get("topic"), Permission.SUB);
break;
case RequestCode.HEART_BEAT:
HeartbeatData heartbeatData = HeartbeatData.decode(request.getBody(), HeartbeatData.class);
for (ConsumerData data : heartbeatData.getConsumerDataSet()) {
accessResource.addResourceAndPerm(getRetryTopic(data.getGroupName()), Permission.SUB);
for (SubscriptionData subscriptionData : data.getSubscriptionDataSet()) {
accessResource.addResourceAndPerm(subscriptionData.getTopic(), Permission.SUB);
}
}
break;
case RequestCode.UNREGISTER_CLIENT:
final UnregisterClientRequestHeader unregisterClientRequestHeader =
(UnregisterClientRequestHeader) request
.decodeCommandCustomHeader(UnregisterClientRequestHeader.class);
accessResource.addResourceAndPerm(getRetryTopic(unregisterClientRequestHeader.getConsumerGroup()), Permission.SUB);
break;
case RequestCode.GET_CONSUMER_LIST_BY_GROUP:
final GetConsumerListByGroupRequestHeader getConsumerListByGroupRequestHeader =
(GetConsumerListByGroupRequestHeader) request
.decodeCommandCustomHeader(GetConsumerListByGroupRequestHeader.class);
accessResource.addResourceAndPerm(getRetryTopic(getConsumerListByGroupRequestHeader.getConsumerGroup()), Permission.SUB);
break;
case RequestCode.UPDATE_CONSUMER_OFFSET:
final UpdateConsumerOffsetRequestHeader updateConsumerOffsetRequestHeader =
(UpdateConsumerOffsetRequestHeader) request
.decodeCommandCustomHeader(UpdateConsumerOffsetRequestHeader.class);
accessResource.addResourceAndPerm(getRetryTopic(updateConsumerOffsetRequestHeader.getConsumerGroup()), Permission.SUB);
accessResource.addResourceAndPerm(updateConsumerOffsetRequestHeader.getTopic(), Permission.SUB);
break;
default:
break;
}
} catch (Throwable t) {
throw new AclException(t.getMessage(), t);
}
// Content
SortedMap<String, String> map = new TreeMap<String, String>();
for (Map.Entry<String, String> entry : request.getExtFields().entrySet()) {
if (!SessionCredentials.SIGNATURE.equals(entry.getKey())
&& !MixAll.UNIQUE_MSG_QUERY_FLAG.equals(entry.getKey())) {
map.put(entry.getKey(), entry.getValue());
}
}
accessResource.setContent(AclUtils.combineRequestContent(request, map));
return accessResource;
@Override
public AccessResource parse(GeneratedMessageV3 messageV3, AuthenticationHeader header) {
return PlainAccessResource.parse(messageV3, header);
}
@Override
@@ -161,22 +60,26 @@ public class PlainAccessValidator implements AccessValidator {
return aclPlugEngine.deleteAccessConfig(accesskey);
}
@Override public String getAclConfigVersion() {
@Override
public String getAclConfigVersion() {
return aclPlugEngine.getAclConfigDataVersion();
}
@Override public boolean updateGlobalWhiteAddrsConfig(List<String> globalWhiteAddrsList) {
@Override
public boolean updateGlobalWhiteAddrsConfig(List<String> globalWhiteAddrsList) {
return aclPlugEngine.updateGlobalWhiteAddrsConfig(globalWhiteAddrsList);
}
@Override public boolean updateGlobalWhiteAddrsConfig(List<String> globalWhiteAddrsList, String aclFileFullPath) {
@Override
public boolean updateGlobalWhiteAddrsConfig(List<String> globalWhiteAddrsList, String aclFileFullPath) {
return aclPlugEngine.updateGlobalWhiteAddrsConfig(globalWhiteAddrsList, aclFileFullPath);
}
@Override public AclConfig getAllAclConfig() {
@Override
public AclConfig getAllAclConfig() {
return aclPlugEngine.getAllAclConfig();
}
@Override
public Map<String, DataVersion> getAllAclConfigVersion() {
return aclPlugEngine.getDataVersionMap();
@@ -0,0 +1,65 @@
/*
* 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.acl.plain;
import java.util.Map;
import org.apache.rocketmq.acl.AccessResource;
import org.apache.rocketmq.acl.PermissionChecker;
import org.apache.rocketmq.acl.common.AclException;
import org.apache.rocketmq.acl.common.Permission;
public class PlainPermissionChecker implements PermissionChecker {
public void check(AccessResource checkedAccess, AccessResource ownedAccess) {
PlainAccessResource checkedPlainAccess = (PlainAccessResource) checkedAccess;
PlainAccessResource ownedPlainAccess = (PlainAccessResource) ownedAccess;
if (Permission.needAdminPerm(checkedPlainAccess.getRequestCode()) && !ownedPlainAccess.isAdmin()) {
throw new AclException(String.format("Need admin permission for request code=%d, but accessKey=%s is not", checkedPlainAccess.getRequestCode(), ownedPlainAccess.getAccessKey()));
}
Map<String, Byte> needCheckedPermMap = checkedPlainAccess.getResourcePermMap();
Map<String, Byte> ownedPermMap = ownedPlainAccess.getResourcePermMap();
if (needCheckedPermMap == null) {
// If the needCheckedPermMap is null,then return
return;
}
if (ownedPermMap == null && ownedPlainAccess.isAdmin()) {
// If the ownedPermMap is null and it is an admin user, then return
return;
}
for (Map.Entry<String, Byte> needCheckedEntry : needCheckedPermMap.entrySet()) {
String resource = needCheckedEntry.getKey();
Byte neededPerm = needCheckedEntry.getValue();
boolean isGroup = PlainAccessResource.isRetryTopic(resource);
if (ownedPermMap == null || !ownedPermMap.containsKey(resource)) {
// Check the default perm
byte ownedPerm = isGroup ? ownedPlainAccess.getDefaultGroupPerm() :
ownedPlainAccess.getDefaultTopicPerm();
if (!Permission.checkPermission(neededPerm, ownedPerm)) {
throw new AclException(String.format("No default permission for %s", PlainAccessResource.printStr(resource, isGroup)));
}
continue;
}
if (!Permission.checkPermission(neededPerm, ownedPermMap.get(resource))) {
throw new AclException(String.format("No default permission for %s", PlainAccessResource.printStr(resource, isGroup)));
}
}
}
}
@@ -37,6 +37,7 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.acl.PermissionChecker;
import org.apache.rocketmq.acl.common.AclConstants;
import org.apache.rocketmq.acl.common.AclException;
import org.apache.rocketmq.acl.common.AclUtils;
@@ -81,6 +82,8 @@ public class PlainPermissionManager {
private List<String> fileList = new ArrayList<>();
private final PermissionChecker permissionChecker = new PlainPermissionChecker();
public PlainPermissionManager() {
this.defaultAclDir = MixAll.dealFilePath(fileHome + File.separator + "conf" + File.separator + "acl");
this.defaultAclFile = MixAll.dealFilePath(fileHome + File.separator + System.getProperty("rocketmq.acl.plain.file", "conf/plain_acl.yml"));
@@ -576,40 +579,7 @@ public class PlainPermissionManager {
}
void checkPerm(PlainAccessResource needCheckedAccess, PlainAccessResource ownedAccess) {
if (Permission.needAdminPerm(needCheckedAccess.getRequestCode()) && !ownedAccess.isAdmin()) {
throw new AclException(String.format("Need admin permission for request code=%d, but accessKey=%s is not", needCheckedAccess.getRequestCode(), ownedAccess.getAccessKey()));
}
Map<String, Byte> needCheckedPermMap = needCheckedAccess.getResourcePermMap();
Map<String, Byte> ownedPermMap = ownedAccess.getResourcePermMap();
if (needCheckedPermMap == null) {
// If the needCheckedPermMap is null,then return
return;
}
if (ownedPermMap == null && ownedAccess.isAdmin()) {
// If the ownedPermMap is null and it is an admin user, then return
return;
}
for (Map.Entry<String, Byte> needCheckedEntry : needCheckedPermMap.entrySet()) {
String resource = needCheckedEntry.getKey();
Byte neededPerm = needCheckedEntry.getValue();
boolean isGroup = PlainAccessResource.isRetryTopic(resource);
if (ownedPermMap == null || !ownedPermMap.containsKey(resource)) {
// Check the default perm
byte ownedPerm = isGroup ? ownedAccess.getDefaultGroupPerm() :
ownedAccess.getDefaultTopicPerm();
if (!Permission.checkPermission(neededPerm, ownedPerm)) {
throw new AclException(String.format("No default permission for %s", PlainAccessResource.printStr(resource, isGroup)));
}
continue;
}
if (!Permission.checkPermission(neededPerm, ownedPermMap.get(resource))) {
throw new AclException(String.format("No default permission for %s", PlainAccessResource.printStr(resource, isGroup)));
}
}
permissionChecker.check(needCheckedAccess, ownedAccess);
}
void clearPermissionInfo() {
@@ -631,23 +601,8 @@ public class PlainPermissionManager {
public PlainAccessResource buildPlainAccessResource(PlainAccessConfig plainAccessConfig) throws AclException {
checkPlainAccessConfig(plainAccessConfig);
PlainAccessResource plainAccessResource = new PlainAccessResource();
plainAccessResource.setAccessKey(plainAccessConfig.getAccessKey());
plainAccessResource.setSecretKey(plainAccessConfig.getSecretKey());
plainAccessResource.setWhiteRemoteAddress(plainAccessConfig.getWhiteRemoteAddress());
plainAccessResource.setAdmin(plainAccessConfig.isAdmin());
plainAccessResource.setDefaultGroupPerm(Permission.parsePermFromString(plainAccessConfig.getDefaultGroupPerm()));
plainAccessResource.setDefaultTopicPerm(Permission.parsePermFromString(plainAccessConfig.getDefaultTopicPerm()));
Permission.parseResourcePerms(plainAccessResource, false, plainAccessConfig.getGroupPerms());
Permission.parseResourcePerms(plainAccessResource, true, plainAccessConfig.getTopicPerms());
plainAccessResource.setRemoteAddressStrategy(remoteAddressStrategyFactory.
getRemoteAddressStrategy(plainAccessResource.getWhiteRemoteAddress()));
return plainAccessResource;
return PlainAccessResource.build(plainAccessConfig, remoteAddressStrategyFactory.
getRemoteAddressStrategy(plainAccessConfig.getWhiteRemoteAddress()));
}
public void validate(PlainAccessResource plainAccessResource) {
@@ -470,9 +470,12 @@ public class PlainAccessValidatorTest {
@Test
public void addAccessAclYamlConfigTest() throws InterruptedException {
String backupFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl_bak.yml".replace("/", File.separator);
String targetFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(targetFileName, Map.class);
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(backupFileName, Map.class);
AclUtils.writeDataObject(targetFileName, backUpAclConfigMap);
PlainAccessConfig plainAccessConfig = new PlainAccessConfig();
plainAccessConfig.setAccessKey("rocketmq3");
@@ -552,8 +555,12 @@ public class PlainAccessValidatorTest {
@Test
public void updateAccessAclYamlConfigTest() throws InterruptedException {
String targetFileName = System.getProperty("rocketmq.home.dir") + File.separator + "conf/plain_acl.yml";
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(targetFileName, Map.class);
String backupFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl_bak.yml".replace("/", File.separator);
String targetFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(backupFileName, Map.class);
AclUtils.writeDataObject(targetFileName, backUpAclConfigMap);
PlainAccessConfig plainAccessConfig = new PlainAccessConfig();
plainAccessConfig.setAccessKey("rocketmq3");
@@ -628,9 +635,12 @@ public class PlainAccessValidatorTest {
@Test
public void deleteAccessAclYamlConfigTest() throws InterruptedException {
String backupFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl_bak.yml".replace("/", File.separator);
String targetFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(targetFileName, Map.class);
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(backupFileName, Map.class);
AclUtils.writeDataObject(targetFileName, backUpAclConfigMap);
PlainAccessConfig plainAccessConfig = new PlainAccessConfig();
plainAccessConfig.setAccessKey("rocketmq3");
@@ -673,12 +683,15 @@ public class PlainAccessValidatorTest {
AclUtils.writeDataObject(targetFileName, backUpAclConfigMap);
}
@Test
public void updateGlobalWhiteRemoteAddressesTest() throws InterruptedException {
String backupFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl_bak.yml".replace("/", File.separator);
String targetFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(targetFileName, Map.class);
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(backupFileName, Map.class);
AclUtils.writeDataObject(targetFileName, backUpAclConfigMap);
List<String> globalWhiteAddrsList = new ArrayList<>();
globalWhiteAddrsList.add("192.168.1.*");
@@ -794,9 +807,12 @@ public class PlainAccessValidatorTest {
@Test(expected = AclException.class)
public void createAndUpdateAccessAclNullSkExceptionTest() {
String backupFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl_bak.yml".replace("/", File.separator);
String targetFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/acl/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(targetFileName, Map.class);
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(backupFileName, Map.class);
AclUtils.writeDataObject(targetFileName, backUpAclConfigMap);
PlainAccessConfig plainAccessConfig = new PlainAccessConfig();
plainAccessConfig.setAccessKey("RocketMQ33");
@@ -811,9 +827,12 @@ public class PlainAccessValidatorTest {
@Test
public void addAccessDefaultAclYamlConfigTest() throws InterruptedException {
PlainAccessValidator plainAccessValidator = new PlainAccessValidator();
String backupFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl_bak.yml".replace("/", File.separator);
String targetFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(targetFileName, Map.class);
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(backupFileName, Map.class);
AclUtils.writeDataObject(targetFileName, backUpAclConfigMap);
PlainAccessConfig plainAccessConfig = new PlainAccessConfig();
plainAccessConfig.setAccessKey("watchrocketmqh");
@@ -903,9 +922,12 @@ public class PlainAccessValidatorTest {
@Test
public void updateAccessConfigEmptyPermListTest() {
String backupFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl_bak.yml".replace("/", File.separator);
String targetFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(targetFileName, Map.class);
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(backupFileName, Map.class);
AclUtils.writeDataObject(targetFileName, backUpAclConfigMap);
PlainAccessValidator plainAccessValidator = new PlainAccessValidator();
PlainAccessConfig plainAccessConfig = new PlainAccessConfig();
@@ -932,9 +954,12 @@ public class PlainAccessValidatorTest {
@Test
public void updateAccessConfigEmptyWhiteRemoteAddressTest() {
String backupFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl_bak.yml".replace("/", File.separator);
String targetFileName = System.getProperty("rocketmq.home.dir")
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(targetFileName, Map.class);
+ File.separator + "conf/plain_acl.yml".replace("/", File.separator);
Map<String, Object> backUpAclConfigMap = AclUtils.getYamlDataObject(backupFileName, Map.class);
AclUtils.writeDataObject(targetFileName, backUpAclConfigMap);
PlainAccessValidator plainAccessValidator = new PlainAccessValidator();
PlainAccessConfig plainAccessConfig = new PlainAccessConfig();
@@ -29,6 +29,7 @@ import org.apache.rocketmq.acl.common.AclException;
import org.apache.rocketmq.acl.common.AclUtils;
import org.apache.rocketmq.acl.common.Permission;
import org.apache.rocketmq.common.PlainAccessConfig;
import org.assertj.core.util.Lists;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -300,4 +301,28 @@ public class PlainPermissionManagerTest {
transport.delete();
System.setProperty("rocketmq.home.dir", "src/test/resources");
}
@Test
public void updateAccessConfigTest() {
Assert.assertThrows(AclException.class, () -> plainPermissionManager.updateAccessConfig(null));
plainAccessConfig.setAccessKey("admin_test");
// Invalid parameter
plainAccessConfig.setSecretKey("123456");
plainAccessConfig.setAdmin(true);
Assert.assertThrows(AclException.class, () -> plainPermissionManager.updateAccessConfig(plainAccessConfig));
plainAccessConfig.setSecretKey("12345678");
// Invalid parameter
plainAccessConfig.setGroupPerms(Lists.newArrayList("groupA!SUB"));
Assert.assertThrows(AclException.class, () -> plainPermissionManager.updateAccessConfig(plainAccessConfig));
// first update
plainAccessConfig.setGroupPerms(Lists.newArrayList("groupA=SUB"));
plainPermissionManager.updateAccessConfig(plainAccessConfig);
// second update
plainAccessConfig.setTopicPerms(Lists.newArrayList("topicA=SUB"));
plainPermissionManager.updateAccessConfig(plainAccessConfig);
}
}
@@ -0,0 +1,39 @@
# 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.
## suggested format
globalWhiteRemoteAddresses:
- 10.10.103.*
- 192.168.0.*
accounts:
- accessKey: RocketMQ
secretKey: 12345678
whiteRemoteAddress: 192.168.0.*
admin: false
defaultTopicPerm: DENY
defaultGroupPerm: SUB
topicPerms:
- topicA=DENY
- topicB=PUB|SUB
- topicC=SUB
groupPerms:
- groupA=DENY
- groupB=SUB
- groupC=SUB
- accessKey: rocketmq2
secretKey: 12345678
whiteRemoteAddress: 192.168.1.*
admin: true
@@ -186,6 +186,7 @@ public class BrokerController {
protected final Broker2Client broker2Client;
protected final SubscriptionGroupManager subscriptionGroupManager;
protected final ConsumerIdsChangeListener consumerIdsChangeListener;
protected final EndTransactionProcessor endTransactionProcessor;
private final RebalanceLockManager rebalanceLockManager = new RebalanceLockManager();
protected BrokerOuterAPI brokerOuterAPI;
protected ScheduledExecutorService scheduledExecutorService;
@@ -319,6 +320,7 @@ public class BrokerController {
this.queryAssignmentProcessor = new QueryAssignmentProcessor(this);
this.clientManageProcessor = new ClientManageProcessor(this);
this.slaveSynchronize = new SlaveSynchronize(this);
this.endTransactionProcessor = new EndTransactionProcessor(this);
this.sendThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getSendThreadPoolQueueCapacity());
this.putThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getPutThreadPoolQueueCapacity());
@@ -1010,8 +1012,8 @@ public class BrokerController {
/**
* EndTransactionProcessor
*/
this.remotingServer.registerProcessor(RequestCode.END_TRANSACTION, new EndTransactionProcessor(this), this.endTransactionExecutor);
this.fastRemotingServer.registerProcessor(RequestCode.END_TRANSACTION, new EndTransactionProcessor(this), this.endTransactionExecutor);
this.remotingServer.registerProcessor(RequestCode.END_TRANSACTION, endTransactionProcessor, this.endTransactionExecutor);
this.fastRemotingServer.registerProcessor(RequestCode.END_TRANSACTION, endTransactionProcessor, this.endTransactionExecutor);
/*
* Default
@@ -1152,6 +1154,14 @@ public class BrokerController {
return popMessageProcessor;
}
public AckMessageProcessor getAckMessageProcessor() {
return ackMessageProcessor;
}
public ChangeInvisibleTimeProcessor getChangeInvisibleTimeProcessor() {
return changeInvisibleTimeProcessor;
}
protected void shutdownBasicService() {
shutdown = true;
@@ -2074,6 +2084,10 @@ public class BrokerController {
return assignmentManager;
}
public ClientManageProcessor getClientManageProcessor() {
return clientManageProcessor;
}
public SendMessageProcessor getSendMessageProcessor() {
return sendMessageProcessor;
}
@@ -2134,6 +2148,10 @@ public class BrokerController {
return brokerPreOnlineService;
}
public EndTransactionProcessor getEndTransactionProcessor() {
return endTransactionProcessor;
}
public boolean isScheduleServiceStart() {
return isScheduleServiceStart;
}
@@ -29,5 +29,13 @@ public enum ConsumerGroupEvent {
/**
* The group of consumer is registered.
*/
REGISTER
REGISTER,
/**
* The client of this consumer is new registered.
*/
CLIENT_REGISTER,
/**
* The client of this consumer is unregistered.
*/
CLIENT_UNREGISTER
}
@@ -98,23 +98,24 @@ public class ConsumerGroupInfo {
return result;
}
public void unregisterChannel(final ClientChannelInfo clientChannelInfo) {
public boolean unregisterChannel(final ClientChannelInfo clientChannelInfo) {
ClientChannelInfo old = this.channelInfoTable.remove(clientChannelInfo.getChannel());
if (old != null) {
log.info("unregister a consumer[{}] from consumerGroupInfo {}", this.groupName, old.toString());
return true;
}
return false;
}
public boolean doChannelCloseEvent(final String remoteAddr, final Channel channel) {
public ClientChannelInfo doChannelCloseEvent(final String remoteAddr, final Channel channel) {
final ClientChannelInfo info = this.channelInfoTable.remove(channel);
if (info != null) {
log.warn(
"NETTY EVENT: remove not active channel[{}] from ConsumerGroupInfo groupChannelTable, consumer group: {}",
info.toString(), groupName);
return true;
}
return false;
return info;
}
/**
@@ -18,12 +18,15 @@ package org.apache.rocketmq.broker.client;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import io.netty.channel.Channel;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType;
@@ -40,16 +43,17 @@ public class ConsumerManager {
private static final long CHANNEL_EXPIRED_TIMEOUT = 1000 * 120;
private final ConcurrentMap<String, ConsumerGroupInfo> consumerTable =
new ConcurrentHashMap<String, ConsumerGroupInfo>(1024);
private final ConsumerIdsChangeListener consumerIdsChangeListener;
private final List<ConsumerIdsChangeListener> consumerIdsChangeListenerList = new CopyOnWriteArrayList<>();
protected final BrokerStatsManager brokerStatsManager;
public ConsumerManager(final ConsumerIdsChangeListener consumerIdsChangeListener) {
this.consumerIdsChangeListener = consumerIdsChangeListener;
this.consumerIdsChangeListenerList.add(consumerIdsChangeListener);
this.brokerStatsManager = null;
}
public ConsumerManager(final ConsumerIdsChangeListener consumerIdsChangeListener, final BrokerStatsManager brokerStatsManager) {
this.consumerIdsChangeListener = consumerIdsChangeListener;
public ConsumerManager(final ConsumerIdsChangeListener consumerIdsChangeListener,
final BrokerStatsManager brokerStatsManager) {
this.consumerIdsChangeListenerList.add(consumerIdsChangeListener);
this.brokerStatsManager = brokerStatsManager;
}
@@ -93,18 +97,19 @@ public class ConsumerManager {
while (it.hasNext()) {
Entry<String, ConsumerGroupInfo> next = it.next();
ConsumerGroupInfo info = next.getValue();
removed = info.doChannelCloseEvent(remoteAddr, channel);
if (removed) {
ClientChannelInfo clientChannelInfo = info.doChannelCloseEvent(remoteAddr, channel);
if (clientChannelInfo != null) {
callConsumerIdsChangeListener(ConsumerGroupEvent.CLIENT_UNREGISTER, next.getKey(), clientChannelInfo, info.getSubscribeTopics());
if (info.getChannelInfoTable().isEmpty()) {
ConsumerGroupInfo remove = this.consumerTable.remove(next.getKey());
if (remove != null) {
LOGGER.info("unregister consumer ok, no any connection, and remove consumer group, {}",
next.getKey());
this.consumerIdsChangeListener.handle(ConsumerGroupEvent.UNREGISTER, next.getKey());
callConsumerIdsChangeListener(ConsumerGroupEvent.UNREGISTER, next.getKey());
}
}
this.consumerIdsChangeListener.handle(ConsumerGroupEvent.CHANGE, next.getKey(), info.getAllChannel());
callConsumerIdsChangeListener(ConsumerGroupEvent.CHANGE, next.getKey(), info.getAllChannel());
}
}
return removed;
@@ -113,9 +118,18 @@ public class ConsumerManager {
public boolean registerConsumer(final String group, final ClientChannelInfo clientChannelInfo,
ConsumeType consumeType, MessageModel messageModel, ConsumeFromWhere consumeFromWhere,
final Set<SubscriptionData> subList, boolean isNotifyConsumerIdsChangedEnable) {
return registerConsumer(group, clientChannelInfo, consumeType, messageModel, consumeFromWhere, subList,
isNotifyConsumerIdsChangedEnable, true);
}
public boolean registerConsumer(final String group, final ClientChannelInfo clientChannelInfo,
ConsumeType consumeType, MessageModel messageModel, ConsumeFromWhere consumeFromWhere,
final Set<SubscriptionData> subList, boolean isNotifyConsumerIdsChangedEnable, boolean updateSubscription) {
long start = System.currentTimeMillis();
ConsumerGroupInfo consumerGroupInfo = this.consumerTable.get(group);
if (null == consumerGroupInfo) {
callConsumerIdsChangeListener(ConsumerGroupEvent.CLIENT_REGISTER, group, clientChannelInfo,
subList.stream().map(SubscriptionData::getTopic).collect(Collectors.toSet()));
ConsumerGroupInfo tmp = new ConsumerGroupInfo(group, consumeType, messageModel, consumeFromWhere);
ConsumerGroupInfo prev = this.consumerTable.putIfAbsent(group, tmp);
consumerGroupInfo = prev != null ? prev : tmp;
@@ -124,18 +138,21 @@ public class ConsumerManager {
boolean r1 =
consumerGroupInfo.updateChannel(clientChannelInfo, consumeType, messageModel,
consumeFromWhere);
boolean r2 = consumerGroupInfo.updateSubscription(subList);
boolean r2 = false;
if (updateSubscription) {
r2 = consumerGroupInfo.updateSubscription(subList);
}
if (r1 || r2) {
if (isNotifyConsumerIdsChangedEnable) {
this.consumerIdsChangeListener.handle(ConsumerGroupEvent.CHANGE, group, consumerGroupInfo.getAllChannel());
callConsumerIdsChangeListener(ConsumerGroupEvent.CHANGE, group, consumerGroupInfo.getAllChannel());
}
}
if (null != this.brokerStatsManager) {
this.brokerStatsManager.incConsumerRegisterTime((int) (System.currentTimeMillis() - start));
}
this.consumerIdsChangeListener.handle(ConsumerGroupEvent.REGISTER, group, subList);
callConsumerIdsChangeListener(ConsumerGroupEvent.REGISTER, group, subList);
return r1 || r2;
}
@@ -144,17 +161,20 @@ public class ConsumerManager {
boolean isNotifyConsumerIdsChangedEnable) {
ConsumerGroupInfo consumerGroupInfo = this.consumerTable.get(group);
if (null != consumerGroupInfo) {
consumerGroupInfo.unregisterChannel(clientChannelInfo);
boolean removed = consumerGroupInfo.unregisterChannel(clientChannelInfo);
if (removed) {
callConsumerIdsChangeListener(ConsumerGroupEvent.CLIENT_UNREGISTER, group, clientChannelInfo, consumerGroupInfo.getSubscribeTopics());
}
if (consumerGroupInfo.getChannelInfoTable().isEmpty()) {
ConsumerGroupInfo remove = this.consumerTable.remove(group);
if (remove != null) {
LOGGER.info("unregister consumer ok, no any connection, and remove consumer group, {}", group);
this.consumerIdsChangeListener.handle(ConsumerGroupEvent.UNREGISTER, group);
callConsumerIdsChangeListener(ConsumerGroupEvent.UNREGISTER, group);
}
}
if (isNotifyConsumerIdsChangedEnable) {
this.consumerIdsChangeListener.handle(ConsumerGroupEvent.CHANGE, group, consumerGroupInfo.getAllChannel());
callConsumerIdsChangeListener(ConsumerGroupEvent.CHANGE, group, consumerGroupInfo.getAllChannel());
}
}
}
@@ -177,6 +197,7 @@ public class ConsumerManager {
LOGGER.warn(
"SCAN: remove expired channel from ConsumerManager consumerTable. channel={}, consumerGroup={}",
RemotingHelper.parseChannelRemoteAddr(clientChannelInfo.getChannel()), group);
callConsumerIdsChangeListener(ConsumerGroupEvent.CLIENT_UNREGISTER, group, clientChannelInfo, consumerGroupInfo.getSubscribeTopics());
RemotingUtil.closeChannel(clientChannelInfo.getChannel());
itChannel.remove();
}
@@ -204,4 +225,18 @@ public class ConsumerManager {
}
return groups;
}
public void appendConsumerIdsChangeListener(ConsumerIdsChangeListener listener) {
consumerIdsChangeListenerList.add(listener);
}
protected void callConsumerIdsChangeListener(ConsumerGroupEvent event, String group, Object... args) {
for (ConsumerIdsChangeListener listener : consumerIdsChangeListenerList) {
try {
listener.handle(event, group, args);
} catch (Throwable t) {
LOGGER.error("err when call consumerIdsChangeListener", t);
}
}
}
}
@@ -90,6 +90,9 @@ public class DefaultConsumerIdsChangeListener implements ConsumerIdsChangeListen
Collection<SubscriptionData> subscriptionDataList = (Collection<SubscriptionData>) args[0];
this.brokerController.getConsumerFilterManager().register(group, subscriptionDataList);
break;
case CLIENT_REGISTER:
case CLIENT_UNREGISTER:
break;
default:
throw new RuntimeException("Unknown event " + event);
}
@@ -0,0 +1,27 @@
/*
* 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.broker.client;
/**
* producer manager will call this listener when something happen
* <p>
* event type: {@link ProducerGroupEvent}
*/
public interface ProducerChangeListener {
void handle(ProducerGroupEvent event, String group, ClientChannelInfo clientChannelInfo);
}
@@ -0,0 +1,28 @@
/*
* 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.broker.client;
public enum ProducerGroupEvent {
/**
* The group of producer is unregistered.
*/
GROUP_UNREGISTER,
/**
* The client of this producer is unregistered.
*/
CLIENT_UNREGISTER
}
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.rocketmq.broker.util.PositiveAtomicCounter;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.protocol.body.ProducerInfo;
@@ -44,6 +45,7 @@ public class ProducerManager {
private final ConcurrentHashMap<String, Channel> clientChannelTable = new ConcurrentHashMap<>();
protected final BrokerStatsManager brokerStatsManager;
private PositiveAtomicCounter positiveAtomicCounter = new PositiveAtomicCounter();
private final List<ProducerChangeListener> producerChangeListenerList = new CopyOnWriteArrayList<>();
public ProducerManager() {
this.brokerStatsManager = null;
@@ -94,8 +96,11 @@ public class ProducerManager {
}
public void scanNotActiveChannel() {
for (final Map.Entry<String, ConcurrentHashMap<Channel, ClientChannelInfo>> entry : this.groupChannelTable
.entrySet()) {
Iterator<Map.Entry<String, ConcurrentHashMap<Channel, ClientChannelInfo>>> iterator = this.groupChannelTable.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, ConcurrentHashMap<Channel, ClientChannelInfo>> entry = iterator.next();
final String group = entry.getKey();
final ConcurrentHashMap<Channel, ClientChannelInfo> chlMap = entry.getValue();
@@ -112,9 +117,16 @@ public class ProducerManager {
log.warn(
"ProducerManager#scanNotActiveChannel: remove expired channel[{}] from ProducerManager groupChannelTable, producer group name: {}",
RemotingHelper.parseChannelRemoteAddr(info.getChannel()), group);
callProducerChangeListener(ProducerGroupEvent.CLIENT_UNREGISTER, group, info);
RemotingUtil.closeChannel(info.getChannel());
}
}
if (chlMap.isEmpty()) {
log.warn("SCAN: remove expired channel from ProducerManager groupChannelTable, all clear, group={}", group);
iterator.remove();
callProducerChangeListener(ProducerGroupEvent.GROUP_UNREGISTER, group, null);
}
}
}
@@ -134,6 +146,14 @@ public class ProducerManager {
log.info(
"NETTY EVENT: remove channel[{}][{}] from ProducerManager groupChannelTable, producer group: {}",
clientChannelInfo.toString(), remoteAddr, group);
callProducerChangeListener(ProducerGroupEvent.CLIENT_UNREGISTER, group, clientChannelInfo);
if (clientChannelInfoTable.isEmpty()) {
ConcurrentHashMap<Channel, ClientChannelInfo> oldGroupTable = this.groupChannelTable.remove(group);
if (oldGroupTable != null) {
log.info("unregister a producer group[{}] from groupChannelTable", group);
callProducerChangeListener(ProducerGroupEvent.GROUP_UNREGISTER, group, null);
}
}
}
}
@@ -172,10 +192,12 @@ public class ProducerManager {
if (old != null) {
log.info("unregister a producer[{}] from groupChannelTable {}", group,
clientChannelInfo.toString());
callProducerChangeListener(ProducerGroupEvent.CLIENT_UNREGISTER, group, clientChannelInfo);
}
if (channelTable.isEmpty()) {
this.groupChannelTable.remove(group);
callProducerChangeListener(ProducerGroupEvent.GROUP_UNREGISTER, group, null);
log.info("unregister a producer group[{}] from groupChannelTable", group);
}
}
@@ -224,4 +246,19 @@ public class ProducerManager {
public Channel findChannel(String clientId) {
return clientChannelTable.get(clientId);
}
private void callProducerChangeListener(ProducerGroupEvent event, String group,
ClientChannelInfo clientChannelInfo) {
for (ProducerChangeListener listener : producerChangeListenerList) {
try {
listener.handle(event, group, clientChannelInfo);
} catch (Throwable t) {
log.error("err when call producerChangeListener", t);
}
}
}
public void appendProducerChangeListener(ProducerChangeListener producerChangeListener) {
producerChangeListenerList.add(producerChangeListener);
}
}
@@ -18,7 +18,6 @@ package org.apache.rocketmq.broker.processor;
import io.netty.channel.ChannelHandlerContext;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
@@ -32,15 +31,10 @@ import org.apache.rocketmq.broker.mqtrace.SendMessageContext;
import org.apache.rocketmq.broker.mqtrace.SendMessageHook;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.MQVersion;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageType;
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.common.topic.TopicValidator;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.TopicFilterType;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.constant.DBMsgConstants;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.constant.PermName;
@@ -48,21 +42,24 @@ import org.apache.rocketmq.common.help.FAQUrl;
import org.apache.rocketmq.common.message.MessageAccessor;
import org.apache.rocketmq.common.message.MessageConst;
import org.apache.rocketmq.common.message.MessageDecoder;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageExtBrokerInner;
import org.apache.rocketmq.common.message.MessageType;
import org.apache.rocketmq.common.protocol.NamespaceUtil;
import org.apache.rocketmq.common.protocol.RequestCode;
import org.apache.rocketmq.common.protocol.ResponseCode;
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader;
import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeaderV2;
import org.apache.rocketmq.common.protocol.header.SendMessageResponseHeader;
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.common.sysflag.MessageSysFlag;
import org.apache.rocketmq.common.sysflag.TopicSysFlag;
import org.apache.rocketmq.common.topic.TopicValidator;
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.common.message.MessageExtBrokerInner;
import org.apache.rocketmq.store.PutMessageResult;
import org.apache.rocketmq.store.stats.BrokerStatsManager;
@@ -574,102 +571,8 @@ public abstract class AbstractSendMessageProcessor implements NettyRequestProces
}
}
protected SendMessageRequestHeader parseRequestHeader(RemotingCommand request)
throws RemotingCommandException {
SendMessageRequestHeaderV2 requestHeaderV2 = null;
SendMessageRequestHeader requestHeader = null;
switch (request.getCode()) {
case RequestCode.SEND_BATCH_MESSAGE:
case RequestCode.SEND_MESSAGE_V2:
requestHeaderV2 =
(SendMessageRequestHeaderV2) request
.decodeCommandCustomHeader(SendMessageRequestHeaderV2.class);
case RequestCode.SEND_MESSAGE:
if (null == requestHeaderV2) {
requestHeader =
(SendMessageRequestHeader) request
.decodeCommandCustomHeader(SendMessageRequestHeader.class);
} else {
requestHeader = SendMessageRequestHeaderV2.createSendMessageRequestHeaderV1(requestHeaderV2);
}
default:
break;
}
return requestHeader;
}
static SendMessageRequestHeaderV2 decodeSendMessageHeaderV2(RemotingCommand request)
throws RemotingCommandException {
SendMessageRequestHeaderV2 r = new SendMessageRequestHeaderV2();
HashMap<String, String> fields = request.getExtFields();
if (fields == null) {
throw new RemotingCommandException("the ext fields is null");
}
String s = fields.get("a");
checkNotNull(s, "the custom field <a> is null");
r.setA(s);
s = fields.get("b");
checkNotNull(s, "the custom field <b> is null");
r.setB(s);
s = fields.get("c");
checkNotNull(s, "the custom field <c> is null");
r.setC(s);
s = fields.get("d");
checkNotNull(s, "the custom field <d> is null");
r.setD(Integer.parseInt(s));
s = fields.get("e");
checkNotNull(s, "the custom field <e> is null");
r.setE(Integer.parseInt(s));
s = fields.get("f");
checkNotNull(s, "the custom field <f> is null");
r.setF(Integer.parseInt(s));
s = fields.get("g");
checkNotNull(s, "the custom field <g> is null");
r.setG(Long.parseLong(s));
s = fields.get("h");
checkNotNull(s, "the custom field <h> is null");
r.setH(Integer.parseInt(s));
s = fields.get("i");
if (s != null) {
r.setI(s);
}
s = fields.get("j");
if (s != null) {
r.setJ(Integer.parseInt(s));
}
s = fields.get("k");
if (s != null) {
r.setK(Boolean.parseBoolean(s));
}
s = fields.get("l");
if (s != null) {
r.setL(Integer.parseInt(s));
}
s = fields.get("m");
if (s != null) {
r.setM(Boolean.parseBoolean(s));
}
return r;
}
private static void checkNotNull(String s, String msg) throws RemotingCommandException {
if (s == null) {
throw new RemotingCommandException(msg);
}
protected SendMessageRequestHeader parseRequestHeader(RemotingCommand request) throws RemotingCommandException {
return SendMessageRequestHeader.parseRequestHeader(request);
}
protected int randomQueueId(int writeQueueNums) {
@@ -179,6 +179,7 @@ public class ChangeInvisibleTimeProcessor implements NettyRequestProcessor {
ck.setTopic(requestHeader.getTopic());
ck.setQueueId((byte) queueId);
ck.addDiff(0);
ck.setBrokerName(brokerName);
msgInner.setBody(JSON.toJSONString(ck).getBytes(DataConverter.charset));
msgInner.setQueueId(reviveQid);
@@ -33,7 +33,6 @@ import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.client.ConsumerGroupInfo;
import org.apache.rocketmq.broker.filter.ConsumerFilterData;
import org.apache.rocketmq.broker.filter.ConsumerFilterManager;
import org.apache.rocketmq.broker.filter.ExpressionMessageFilter;
@@ -251,7 +250,7 @@ public class PopMessageProcessor implements NettyRequestProcessor {
}
if (requestHeader.isTimeoutTooMuch()) {
response.setCode(POLLING_TIMEOUT);
response.setCode(ResponseCode.POLLING_TIMEOUT);
response.setRemark(String.format("the broker[%s] poping message is timeout too much",
this.brokerController.getBrokerConfig().getBrokerIP1()));
return response;
@@ -304,14 +303,6 @@ public class PopMessageProcessor implements NettyRequestProcessor {
requestHeader.getConsumerGroup(), FAQUrl.suggestTodo(FAQUrl.SUBSCRIPTION_GROUP_NOT_EXIST)));
return response;
}
ConsumerGroupInfo consumerGroupInfo =
this.brokerController.getConsumerManager().getConsumerGroupInfo(requestHeader.getConsumerGroup());
if (null == consumerGroupInfo) {
POP_LOGGER.warn("the consumer's group info not exist, group: {}", requestHeader.getConsumerGroup());
response.setCode(ResponseCode.SUBSCRIPTION_NOT_EXIST);
response.setRemark("the consumer's group info not exist" + FAQUrl.suggestTodo(FAQUrl.SAME_GROUP_DIFFERENT_TOPIC));
return response;
}
if (!subscriptionGroupConfig.isConsumeEnable()) {
response.setCode(ResponseCode.NO_PERMISSION);
@@ -463,6 +454,8 @@ public class PopMessageProcessor implements NettyRequestProcessor {
response = null;
}
break;
case ResponseCode.POLLING_TIMEOUT:
return response;
default:
assert false;
}
@@ -47,8 +47,8 @@ public class SlaveSynchronize {
public void setMasterAddr(String masterAddr) {
if (!StringUtils.equals(this.masterAddr, masterAddr)) {
this.masterAddr = masterAddr;
LOGGER.info("Update master address from {} to {}", this.masterAddr, masterAddr);
this.masterAddr = masterAddr;
}
}
@@ -42,8 +42,8 @@ public class TransactionalMessageCheckService extends ServiceThread {
@Override
public void run() {
log.info("Start transaction check service thread!");
long checkInterval = brokerController.getBrokerConfig().getTransactionCheckInterval();
while (!this.isStopped()) {
long checkInterval = brokerController.getBrokerConfig().getTransactionCheckInterval();
this.waitForRunning(checkInterval);
}
log.info("End transaction check service thread!");
@@ -0,0 +1,146 @@
/*
* 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.broker.client;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import org.apache.rocketmq.remoting.protocol.LanguageCode;
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.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class ConsumerManagerScannerTest {
private ConsumerManager consumerManager;
private String group = "FooBar";
private String clientId = "clientId";
private ClientChannelInfo clientInfo;
private Map<ConsumerGroupEvent, List<ConsumerIdsChangeListenerData>> groupEventListMap = new HashMap<>();
@Mock
private Channel channel;
@Before
public void init() {
clientInfo = new ClientChannelInfo(channel, clientId, LanguageCode.JAVA, 0);
consumerManager = new ConsumerManager(new ConsumerIdsChangeListener() {
@Override
public void handle(ConsumerGroupEvent event, String group, Object... args) {
groupEventListMap.compute(event, (eventKey, dataListVal) -> {
if (dataListVal == null) {
dataListVal = new ArrayList<>();
}
dataListVal.add(new ConsumerIdsChangeListenerData(event, group, args));
return dataListVal;
});
}
@Override
public void shutdown() {
}
});
}
private static class ConsumerIdsChangeListenerData {
private ConsumerGroupEvent event;
private String group;
private Object[] args;
public ConsumerIdsChangeListenerData(ConsumerGroupEvent event, String group, Object[] args) {
this.event = event;
this.group = group;
this.args = args;
}
}
@Test
public void testClientUnregisterEventInDoChannelCloseEvent() {
assertThat(consumerManager.registerConsumer(
group,
clientInfo,
ConsumeType.CONSUME_PASSIVELY,
MessageModel.CLUSTERING,
ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET,
new HashSet<>(),
false
)).isTrue();
consumerManager.doChannelCloseEvent("remoteAddr", channel);
assertThat(groupEventListMap.get(ConsumerGroupEvent.CLIENT_UNREGISTER).size()).isEqualTo(1);
assertThat(groupEventListMap.get(ConsumerGroupEvent.CLIENT_UNREGISTER).get(0).args[0]).isInstanceOf(ClientChannelInfo.class);
ClientChannelInfo clientChannelInfo = (ClientChannelInfo) groupEventListMap.get(ConsumerGroupEvent.CLIENT_UNREGISTER).get(0).args[0];
assertThat(clientChannelInfo).isSameAs(clientInfo);
}
@Test
public void testClientUnregisterEventInUnregisterConsumer() {
assertThat(consumerManager.registerConsumer(
group,
clientInfo,
ConsumeType.CONSUME_PASSIVELY,
MessageModel.CLUSTERING,
ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET,
new HashSet<>(),
false
)).isTrue();
consumerManager.unregisterConsumer(group, clientInfo, false);
assertThat(groupEventListMap.get(ConsumerGroupEvent.CLIENT_UNREGISTER).size()).isEqualTo(1);
assertThat(groupEventListMap.get(ConsumerGroupEvent.CLIENT_UNREGISTER).get(0).args[0]).isInstanceOf(ClientChannelInfo.class);
ClientChannelInfo clientChannelInfo = (ClientChannelInfo) groupEventListMap.get(ConsumerGroupEvent.CLIENT_UNREGISTER).get(0).args[0];
assertThat(clientChannelInfo).isSameAs(clientInfo);
}
@Test
public void testClientUnregisterEventInScanNotActiveChannel() {
assertThat(consumerManager.registerConsumer(
group,
clientInfo,
ConsumeType.CONSUME_PASSIVELY,
MessageModel.CLUSTERING,
ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET,
new HashSet<>(),
false
)).isTrue();
clientInfo.setLastUpdateTimestamp(0);
when(channel.close()).thenReturn(mock(ChannelFuture.class));
consumerManager.scanNotActiveChannel();
assertThat(groupEventListMap.get(ConsumerGroupEvent.CLIENT_UNREGISTER).size()).isEqualTo(1);
assertThat(groupEventListMap.get(ConsumerGroupEvent.CLIENT_UNREGISTER).get(0).args[0]).isInstanceOf(ClientChannelInfo.class);
ClientChannelInfo clientChannelInfo = (ClientChannelInfo) groupEventListMap.get(ConsumerGroupEvent.CLIENT_UNREGISTER).get(0).args[0];
assertThat(clientChannelInfo).isSameAs(clientInfo);
}
}
@@ -0,0 +1,155 @@
/*
* 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.broker.client;
import io.netty.channel.Channel;
import java.util.HashSet;
import java.util.Set;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.client.net.Broker2Client;
import org.apache.rocketmq.broker.filter.ConsumerFilterManager;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
import org.apache.rocketmq.remoting.protocol.LanguageCode;
import org.apache.rocketmq.store.stats.BrokerStatsManager;
import org.assertj.core.api.Assertions;
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.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class ConsumerManagerTest {
private ClientChannelInfo clientChannelInfo;
@Mock
private Channel channel;
private ConsumerManager consumerManager;
private DefaultConsumerIdsChangeListener defaultConsumerIdsChangeListener;
@Mock
private BrokerController brokerController;
@Mock
private ConsumerFilterManager consumerFilterManager;
private BrokerConfig brokerConfig = new BrokerConfig();
private Broker2Client broker2Client;
private BrokerStatsManager brokerStatsManager;
private static final String GROUP = "DEFAULT_GROUP";
private static final String CLIENT_ID = "1";
private static final int VERSION = 1;
private static final String TOPIC = "DEFAULT_TOPIC";
@Before
public void before() {
clientChannelInfo = new ClientChannelInfo(channel, CLIENT_ID, LanguageCode.JAVA, VERSION);
defaultConsumerIdsChangeListener = new DefaultConsumerIdsChangeListener(brokerController);
brokerStatsManager = new BrokerStatsManager(brokerConfig);
consumerManager = new ConsumerManager(defaultConsumerIdsChangeListener, brokerStatsManager);
broker2Client = new Broker2Client(brokerController);
when(brokerController.getConsumerFilterManager()).thenReturn(consumerFilterManager);
when(brokerController.getBrokerConfig()).thenReturn(brokerConfig);
when(brokerController.getBroker2Client()).thenReturn(broker2Client);
register();
}
@Test
public void registerConsumerTest() {
final Set<SubscriptionData> subList = new HashSet<>();
SubscriptionData subscriptionData = new SubscriptionData(TOPIC, "*");
subList.add(subscriptionData);
consumerManager.registerConsumer(GROUP, clientChannelInfo, ConsumeType.CONSUME_PASSIVELY,
MessageModel.BROADCASTING, ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET, subList, true);
Assertions.assertThat(consumerManager.getConsumerTable().get(GROUP)).isNotNull();
}
@Test
public void unregisterConsumerTest() {
// register
register();
// unregister
consumerManager.unregisterConsumer(GROUP, clientChannelInfo, true);
Assertions.assertThat(consumerManager.getConsumerTable().get(GROUP)).isNull();
}
@Test
public void findChannelTest() {
final ClientChannelInfo consumerManagerChannel = consumerManager.findChannel(GROUP, CLIENT_ID);
Assertions.assertThat(consumerManagerChannel).isNotNull();
}
@Test
public void findSubscriptionDataTest() {
final SubscriptionData subscriptionData = consumerManager.findSubscriptionData(GROUP, TOPIC);
Assertions.assertThat(subscriptionData).isNotNull();
}
@Test
public void findSubscriptionDataCountTest() {
final int count = consumerManager.findSubscriptionDataCount(GROUP);
assert count > 0;
}
@Test
public void scanNotActiveChannelTest() {
clientChannelInfo.setLastUpdateTimestamp(System.currentTimeMillis() - 1000 * 200);
consumerManager.scanNotActiveChannel();
assert consumerManager.getConsumerTable().size() == 0;
}
@Test
public void queryTopicConsumeByWhoTest() {
final HashSet<String> consumeGroup = consumerManager.queryTopicConsumeByWho(TOPIC);
assert consumeGroup.size() > 0;
}
@Test
public void doChannelCloseEventTest() {
consumerManager.doChannelCloseEvent("127.0.0.1", channel);
assert consumerManager.getConsumerTable().size() == 0;
}
private void register() {
// register
final Set<SubscriptionData> subList = new HashSet<>();
SubscriptionData subscriptionData = new SubscriptionData(TOPIC, "*");
subList.add(subscriptionData);
consumerManager.registerConsumer(GROUP, clientChannelInfo, ConsumeType.CONSUME_PASSIVELY,
MessageModel.BROADCASTING, ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET, subList, true);
}
}
@@ -21,6 +21,7 @@ import io.netty.channel.ChannelFuture;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.rocketmq.remoting.protocol.LanguageCode;
import org.junit.Before;
import org.junit.Test;
@@ -50,6 +51,20 @@ public class ProducerManagerTest {
@Test
public void scanNotActiveChannel() throws Exception {
producerManager.registerProducer(group, clientInfo);
AtomicReference<String> groupRef = new AtomicReference<>();
AtomicReference<ClientChannelInfo> clientChannelInfoRef = new AtomicReference<>();
producerManager.appendProducerChangeListener((event, group, clientChannelInfo) -> {
switch (event) {
case GROUP_UNREGISTER:
groupRef.set(group);
break;
case CLIENT_UNREGISTER:
clientChannelInfoRef.set(clientChannelInfo);
break;
default:
break;
}
});
assertThat(producerManager.getGroupChannelTable().get(group).get(channel)).isNotNull();
assertThat(producerManager.findChannel("clientId")).isNotNull();
Field field = ProducerManager.class.getDeclaredField("CHANNEL_EXPIRED_TIMEOUT");
@@ -58,17 +73,35 @@ public class ProducerManagerTest {
clientInfo.setLastUpdateTimestamp(System.currentTimeMillis() - CHANNEL_EXPIRED_TIMEOUT - 10);
when(channel.close()).thenReturn(mock(ChannelFuture.class));
producerManager.scanNotActiveChannel();
assertThat(producerManager.getGroupChannelTable().get(group).get(channel)).isNull();
assertThat(producerManager.getGroupChannelTable().get(group)).isNull();
assertThat(groupRef.get()).isEqualTo(group);
assertThat(clientChannelInfoRef.get()).isSameAs(clientInfo);
assertThat(producerManager.findChannel("clientId")).isNull();
}
@Test
public void doChannelCloseEvent() throws Exception {
producerManager.registerProducer(group, clientInfo);
AtomicReference<String> groupRef = new AtomicReference<>();
AtomicReference<ClientChannelInfo> clientChannelInfoRef = new AtomicReference<>();
producerManager.appendProducerChangeListener((event, group, clientChannelInfo) -> {
switch (event) {
case GROUP_UNREGISTER:
groupRef.set(group);
break;
case CLIENT_UNREGISTER:
clientChannelInfoRef.set(clientChannelInfo);
break;
default:
break;
}
});
assertThat(producerManager.getGroupChannelTable().get(group).get(channel)).isNotNull();
assertThat(producerManager.findChannel("clientId")).isNotNull();
producerManager.doChannelCloseEvent("127.0.0.1", channel);
assertThat(producerManager.getGroupChannelTable().get(group).get(channel)).isNull();
assertThat(producerManager.getGroupChannelTable().get(group)).isNull();
assertThat(groupRef.get()).isEqualTo(group);
assertThat(clientChannelInfoRef.get()).isSameAs(clientInfo);
assertThat(producerManager.findChannel("clientId")).isNull();
}
@@ -86,6 +119,20 @@ public class ProducerManagerTest {
@Test
public void unregisterProducer() throws Exception {
producerManager.registerProducer(group, clientInfo);
AtomicReference<String> groupRef = new AtomicReference<>();
AtomicReference<ClientChannelInfo> clientChannelInfoRef = new AtomicReference<>();
producerManager.appendProducerChangeListener((event, group, clientChannelInfo) -> {
switch (event) {
case GROUP_UNREGISTER:
groupRef.set(group);
break;
case CLIENT_UNREGISTER:
clientChannelInfoRef.set(clientChannelInfo);
break;
default:
break;
}
});
Map<Channel, ClientChannelInfo> channelMap = producerManager.getGroupChannelTable().get(group);
assertThat(channelMap).isNotNull();
assertThat(channelMap.get(channel)).isEqualTo(clientInfo);
@@ -95,6 +142,8 @@ public class ProducerManagerTest {
producerManager.unregisterProducer(group, clientInfo);
channelMap = producerManager.getGroupChannelTable().get(group);
channel1 = producerManager.findChannel("clientId");
assertThat(groupRef.get()).isEqualTo(group);
assertThat(clientChannelInfoRef.get()).isSameAs(clientInfo);
assertThat(channelMap).isNull();
assertThat(channel1).isNull();
@@ -110,16 +110,6 @@ public class PopMessageProcessorTest {
assertThat(response.getRemark()).contains("topic[" + topic + "] not exist");
}
@Test
public void testProcessRequest_SubNotExist() throws RemotingCommandException {
brokerController.getConsumerManager().unregisterConsumer(group, clientChannelInfo, false);
final RemotingCommand request = createPopMsgCommand();
RemotingCommand response = popMessageProcessor.processRequest(handlerContext, request);
assertThat(response).isNotNull();
assertThat(response.getCode()).isEqualTo(ResponseCode.SUBSCRIPTION_NOT_EXIST);
assertThat(response.getRemark()).contains("consumer's group info not exist");
}
@Test
public void testProcessRequest_Found() throws RemotingCommandException {
GetMessageResult getMessageResult = createGetMessageResult(1);
@@ -35,6 +35,8 @@ import org.apache.rocketmq.remoting.protocol.RequestType;
*/
public class ClientConfig {
public static final String SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY = "com.rocketmq.sendMessageWithVIPChannel";
public static final String DECODE_READ_BODY = "com.rocketmq.read.body";
public static final String DECODE_DECOMPRESS_BODY = "com.rocketmq.decompress.body";
private String namesrvAddr = NameServerAddressUtils.getNameServerAddresses();
private String clientIP = RemotingUtil.getLocalAddress();
private String instanceName = System.getProperty("rocketmq.client.name", "DEFAULT");
@@ -58,6 +60,8 @@ public class ClientConfig {
private long pullTimeDelayMillsWhenException = 1000;
private boolean unitMode = false;
private String unitName;
private boolean decodeReadBody = Boolean.parseBoolean(System.getProperty(DECODE_READ_BODY, "true"));
private boolean decodeDecompressBody = Boolean.parseBoolean(System.getProperty(DECODE_DECOMPRESS_BODY, "true"));
private boolean vipChannelEnabled = Boolean.parseBoolean(System.getProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, "false"));
private boolean useTLS = TlsSystemConfig.tlsEnable;
@@ -172,6 +176,8 @@ public class ClientConfig {
this.namespace = cc.namespace;
this.language = cc.language;
this.mqClientApiTimeout = cc.mqClientApiTimeout;
this.decodeReadBody = cc.decodeReadBody;
this.decodeDecompressBody = cc.decodeDecompressBody;
this.enableStreamRequestType = cc.enableStreamRequestType;
}
@@ -192,6 +198,8 @@ public class ClientConfig {
cc.namespace = namespace;
cc.language = language;
cc.mqClientApiTimeout = mqClientApiTimeout;
cc.decodeReadBody = decodeReadBody;
cc.decodeDecompressBody = decodeDecompressBody;
cc.enableStreamRequestType = enableStreamRequestType;
return cc;
}
@@ -293,6 +301,22 @@ public class ClientConfig {
this.language = language;
}
public boolean isDecodeReadBody() {
return decodeReadBody;
}
public void setDecodeReadBody(boolean decodeReadBody) {
this.decodeReadBody = decodeReadBody;
}
public boolean isDecodeDecompressBody() {
return decodeDecompressBody;
}
public void setDecodeDecompressBody(boolean decodeDecompressBody) {
this.decodeDecompressBody = decodeDecompressBody;
}
public String getNamespace() {
if (namespaceInitialized) {
return namespace;
@@ -347,6 +371,7 @@ public class ClientConfig {
+ ", heartbeatBrokerInterval=" + heartbeatBrokerInterval + ", persistConsumerOffsetInterval=" + persistConsumerOffsetInterval
+ ", pullTimeDelayMillsWhenException=" + pullTimeDelayMillsWhenException + ", unitMode=" + unitMode + ", unitName=" + unitName + ", vipChannelEnabled="
+ vipChannelEnabled + ", useTLS=" + useTLS + ", language=" + language.name() + ", namespace=" + namespace + ", mqClientApiTimeout=" + mqClientApiTimeout
+ ", decodeReadBody=" + decodeReadBody + ", decodeDecompressBody=" + decodeDecompressBody
+ ", enableStreamRequestType=" + enableStreamRequestType + "]";
}
}
@@ -213,6 +213,7 @@ public class RemoteBrokerOffsetStore implements OffsetStore {
requestHeader.setConsumerGroup(this.groupName);
requestHeader.setQueueId(mq.getQueueId());
requestHeader.setCommitOffset(offset);
requestHeader.setBname(mq.getBrokerName());
if (isOneway) {
this.mQClientFactory.getMQClientAPIImpl().updateConsumerOffsetOneway(
@@ -239,6 +240,7 @@ public class RemoteBrokerOffsetStore implements OffsetStore {
requestHeader.setTopic(mq.getTopic());
requestHeader.setConsumerGroup(this.groupName);
requestHeader.setQueueId(mq.getQueueId());
requestHeader.setBname(mq.getBrokerName());
return this.mQClientFactory.getMQClientAPIImpl().queryConsumerOffset(
findBrokerResult.getBrokerAddr(), requestHeader, 1000 * 5);
@@ -37,6 +37,13 @@ public class MQClientException extends Exception {
this.errorMessage = errorMessage;
}
public MQClientException(int responseCode, String errorMessage, Throwable cause) {
super(FAQUrl.attachDefaultURL("CODE: " + UtilAll.responseCode2String(responseCode) + " DESC: "
+ errorMessage), cause);
this.responseCode = responseCode;
this.errorMessage = errorMessage;
}
public int getResponseCode() {
return responseCode;
}
@@ -193,8 +193,7 @@ public class MQAdminImpl {
if (brokerAddr != null) {
try {
return this.mQClientFactory.getMQClientAPIImpl().searchOffset(brokerAddr, mq.getTopic(), mq.getQueueId(), timestamp,
timeoutMillis);
return this.mQClientFactory.getMQClientAPIImpl().searchOffset(brokerAddr, mq, timestamp, timeoutMillis);
} catch (Exception e) {
throw new MQClientException("Invoke Broker[" + brokerAddr + "] exception", e);
}
@@ -212,7 +211,7 @@ public class MQAdminImpl {
if (brokerAddr != null) {
try {
return this.mQClientFactory.getMQClientAPIImpl().getMaxOffset(brokerAddr, mq.getTopic(), mq.getQueueId(), timeoutMillis);
return this.mQClientFactory.getMQClientAPIImpl().getMaxOffset(brokerAddr, mq, timeoutMillis);
} catch (Exception e) {
throw new MQClientException("Invoke Broker[" + brokerAddr + "] exception", e);
}
@@ -230,7 +229,7 @@ public class MQAdminImpl {
if (brokerAddr != null) {
try {
return this.mQClientFactory.getMQClientAPIImpl().getMinOffset(brokerAddr, mq.getTopic(), mq.getQueueId(), timeoutMillis);
return this.mQClientFactory.getMQClientAPIImpl().getMinOffset(brokerAddr, mq, timeoutMillis);
} catch (Exception e) {
throw new MQClientException("Invoke Broker[" + brokerAddr + "] exception", e);
}
@@ -248,8 +247,7 @@ public class MQAdminImpl {
if (brokerAddr != null) {
try {
return this.mQClientFactory.getMQClientAPIImpl().getEarliestMsgStoretime(brokerAddr, mq.getTopic(), mq.getQueueId(),
timeoutMillis);
return this.mQClientFactory.getMQClientAPIImpl().getEarliestMsgStoretime(brokerAddr, mq, timeoutMillis);
} catch (Exception e) {
throw new MQClientException("Invoke Broker[" + brokerAddr + "] exception", e);
}
@@ -31,6 +31,7 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.ClientConfig;
import org.apache.rocketmq.client.common.ClientErrorCode;
import org.apache.rocketmq.client.consumer.AckCallback;
import org.apache.rocketmq.client.consumer.AckResult;
import org.apache.rocketmq.client.consumer.AckStatus;
@@ -740,7 +741,7 @@ public class MQClientAPIImpl implements NameServerUpdateCallback {
}
}
private SendResult processSendResponse(
protected SendResult processSendResponse(
final String brokerName,
final Message msg,
final RemotingCommand response,
@@ -858,9 +859,9 @@ public class MQClientAPIImpl implements NameServerUpdateCallback {
}
} else {
if (!responseFuture.isSendRequestOK()) {
popCallback.onException(new MQClientException("send request failed to " + addr + ". Request: " + request, responseFuture.getCause()));
popCallback.onException(new MQClientException(ClientErrorCode.CONNECT_BROKER_EXCEPTION, "send request failed to " + addr + ". Request: " + request, responseFuture.getCause()));
} else if (responseFuture.isTimeout()) {
popCallback.onException(new MQClientException("wait response from " + addr + " timeout :" + responseFuture.getTimeoutMillis() + "ms" + ". Request: " + request,
popCallback.onException(new MQClientException(ClientErrorCode.ACCESS_BROKER_TIMEOUT, "wait response from " + addr + " timeout :" + responseFuture.getTimeoutMillis() + "ms" + ". Request: " + request,
responseFuture.getCause()));
} else {
popCallback.onException(new MQClientException("unknown reason. addr: " + addr + ", timeoutMillis: " + timeoutMillis + ". Request: " + request, responseFuture.getCause()));
@@ -897,9 +898,9 @@ public class MQClientAPIImpl implements NameServerUpdateCallback {
}
} else {
if (!responseFuture.isSendRequestOK()) {
ackCallback.onException(new MQClientException("send request failed to " + addr + ". Request: " + request, responseFuture.getCause()));
ackCallback.onException(new MQClientException(ClientErrorCode.CONNECT_BROKER_EXCEPTION, "send request failed to " + addr + ". Request: " + request, responseFuture.getCause()));
} else if (responseFuture.isTimeout()) {
ackCallback.onException(new MQClientException("wait response from " + addr + " timeout :" + responseFuture.getTimeoutMillis() + "ms" + ". Request: " + request,
ackCallback.onException(new MQClientException(ClientErrorCode.ACCESS_BROKER_TIMEOUT, "wait response from " + addr + " timeout :" + responseFuture.getTimeoutMillis() + "ms" + ". Request: " + request,
responseFuture.getCause()));
} else {
ackCallback.onException(new MQClientException("unknown reason. addr: " + addr + ", timeoutMillis: " + timeOut + ". Request: " + request, responseFuture.getCause()));
@@ -943,9 +944,9 @@ public class MQClientAPIImpl implements NameServerUpdateCallback {
}
} else {
if (!responseFuture.isSendRequestOK()) {
ackCallback.onException(new MQClientException("send request failed to " + addr + ". Request: " + request, responseFuture.getCause()));
ackCallback.onException(new MQClientException(ClientErrorCode.CONNECT_BROKER_EXCEPTION, "send request failed to " + addr + ". Request: " + request, responseFuture.getCause()));
} else if (responseFuture.isTimeout()) {
ackCallback.onException(new MQClientException("wait response from " + addr + " timeout :" + responseFuture.getTimeoutMillis() + "ms" + ". Request: " + request,
ackCallback.onException(new MQClientException(ClientErrorCode.ACCESS_BROKER_TIMEOUT, "wait response from " + addr + " timeout :" + responseFuture.getTimeoutMillis() + "ms" + ". Request: " + request,
responseFuture.getCause()));
} else {
ackCallback.onException(new MQClientException("unknown reason. addr: " + addr + ", timeoutMillis: " + timeoutMillis + ". Request: " + request, responseFuture.getCause()));
@@ -975,9 +976,9 @@ public class MQClientAPIImpl implements NameServerUpdateCallback {
}
} else {
if (!responseFuture.isSendRequestOK()) {
pullCallback.onException(new MQClientException("send request failed to " + addr + ". Request: " + request, responseFuture.getCause()));
pullCallback.onException(new MQClientException(ClientErrorCode.CONNECT_BROKER_EXCEPTION, "send request failed to " + addr + ". Request: " + request, responseFuture.getCause()));
} else if (responseFuture.isTimeout()) {
pullCallback.onException(new MQClientException("wait response from " + addr + " timeout :" + responseFuture.getTimeoutMillis() + "ms" + ". Request: " + request,
pullCallback.onException(new MQClientException(ClientErrorCode.ACCESS_BROKER_TIMEOUT, "wait response from " + addr + " timeout :" + responseFuture.getTimeoutMillis() + "ms" + ". Request: " + request,
responseFuture.getCause()));
} else {
pullCallback.onException(new MQClientException("unknown reason. addr: " + addr + ", timeoutMillis: " + timeoutMillis + ". Request: " + request, responseFuture.getCause()));
@@ -1034,7 +1035,11 @@ public class MQClientAPIImpl implements NameServerUpdateCallback {
case ResponseCode.SUCCESS:
popStatus = PopStatus.FOUND;
ByteBuffer byteBuffer = ByteBuffer.wrap(response.getBody());
msgFoundList = MessageDecoder.decodes(byteBuffer);
msgFoundList = MessageDecoder.decodesBatch(
byteBuffer,
clientConfig.isDecodeReadBody(),
clientConfig.isDecodeDecompressBody(),
true);
break;
case ResponseCode.POLLING_FULL:
popStatus = PopStatus.POLLING_FULL;
@@ -1141,6 +1146,7 @@ public class MQClientAPIImpl implements NameServerUpdateCallback {
throw new MQBrokerException(response.getCode(), response.getRemark(), addr);
}
@Deprecated
public long searchOffset(final String addr, final String topic, final int queueId, final long timestamp,
final long timeoutMillis)
throws RemotingException, MQBrokerException, InterruptedException {
@@ -1166,11 +1172,37 @@ public class MQClientAPIImpl implements NameServerUpdateCallback {
throw new MQBrokerException(response.getCode(), response.getRemark(), addr);
}
public long getMaxOffset(final String addr, final String topic, final int queueId, final long timeoutMillis)
public long searchOffset(final String addr, final MessageQueue messageQueue, final long timestamp, final long timeoutMillis)
throws RemotingException, MQBrokerException, InterruptedException {
SearchOffsetRequestHeader requestHeader = new SearchOffsetRequestHeader();
requestHeader.setTopic(messageQueue.getTopic());
requestHeader.setQueueId(messageQueue.getQueueId());
requestHeader.setBname(messageQueue.getBrokerName());
requestHeader.setTimestamp(timestamp);
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.SEARCH_OFFSET_BY_TIMESTAMP, requestHeader);
RemotingCommand response = this.remotingClient.invokeSync(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr),
request, timeoutMillis);
assert response != null;
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
SearchOffsetResponseHeader responseHeader =
(SearchOffsetResponseHeader) response.decodeCommandCustomHeader(SearchOffsetResponseHeader.class);
return responseHeader.getOffset();
}
default:
break;
}
throw new MQBrokerException(response.getCode(), response.getRemark(), addr);
}
public long getMaxOffset(final String addr, final MessageQueue messageQueue, final long timeoutMillis)
throws RemotingException, MQBrokerException, InterruptedException {
GetMaxOffsetRequestHeader requestHeader = new GetMaxOffsetRequestHeader();
requestHeader.setTopic(topic);
requestHeader.setQueueId(queueId);
requestHeader.setTopic(messageQueue.getTopic());
requestHeader.setQueueId(messageQueue.getQueueId());
requestHeader.setBname(messageQueue.getBrokerName());
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_MAX_OFFSET, requestHeader);
RemotingCommand response = this.remotingClient.invokeSync(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr),
@@ -1217,11 +1249,12 @@ public class MQClientAPIImpl implements NameServerUpdateCallback {
throw new MQBrokerException(response.getCode(), response.getRemark(), addr);
}
public long getMinOffset(final String addr, final String topic, final int queueId, final long timeoutMillis)
public long getMinOffset(final String addr, final MessageQueue messageQueue, final long timeoutMillis)
throws RemotingException, MQBrokerException, InterruptedException {
GetMinOffsetRequestHeader requestHeader = new GetMinOffsetRequestHeader();
requestHeader.setTopic(topic);
requestHeader.setQueueId(queueId);
requestHeader.setTopic(messageQueue.getTopic());
requestHeader.setQueueId(messageQueue.getQueueId());
requestHeader.setBname(messageQueue.getBrokerName());
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_MIN_OFFSET, requestHeader);
RemotingCommand response = this.remotingClient.invokeSync(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr),
@@ -1241,12 +1274,12 @@ public class MQClientAPIImpl implements NameServerUpdateCallback {
throw new MQBrokerException(response.getCode(), response.getRemark(), addr);
}
public long getEarliestMsgStoretime(final String addr, final String topic, final int queueId,
final long timeoutMillis)
public long getEarliestMsgStoretime(final String addr, final MessageQueue mq, final long timeoutMillis)
throws RemotingException, MQBrokerException, InterruptedException {
GetEarliestMsgStoretimeRequestHeader requestHeader = new GetEarliestMsgStoretimeRequestHeader();
requestHeader.setTopic(topic);
requestHeader.setQueueId(queueId);
requestHeader.setTopic(mq.getTopic());
requestHeader.setQueueId(mq.getQueueId());
requestHeader.setBname(mq.getBrokerName());
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_EARLIEST_MSG_STORETIME, requestHeader);
RemotingCommand response = this.remotingClient.invokeSync(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr),
@@ -568,6 +568,18 @@ public class DefaultLitePullConsumerImpl implements MQConsumerInner {
assignedMessageQueue.updateConsumeOffset(consumeRequest.getMessageQueue(), offset);
//If namespace not null , reset Topic without namespace.
this.resetTopic(messages);
if (!this.consumeMessageHookList.isEmpty()) {
ConsumeMessageContext consumeMessageContext = new ConsumeMessageContext();
consumeMessageContext.setNamespace(defaultLitePullConsumer.getNamespace());
consumeMessageContext.setConsumerGroup(this.groupName());
consumeMessageContext.setMq(consumeRequest.getMessageQueue());
consumeMessageContext.setMsgList(messages);
consumeMessageContext.setSuccess(false);
this.executeHookBefore(consumeMessageContext);
consumeMessageContext.setStatus(ConsumeConcurrentlyStatus.CONSUME_SUCCESS.toString());
consumeMessageContext.setSuccess(true);
this.executeHookAfter(consumeMessageContext);
}
return messages;
}
} catch (InterruptedException ignore) {
@@ -949,18 +961,6 @@ public class DefaultLitePullConsumerImpl implements MQConsumerInner {
null
);
this.pullAPIWrapper.processPullResult(mq, pullResult, subscriptionData);
if (!this.consumeMessageHookList.isEmpty()) {
ConsumeMessageContext consumeMessageContext = new ConsumeMessageContext();
consumeMessageContext.setNamespace(defaultLitePullConsumer.getNamespace());
consumeMessageContext.setConsumerGroup(this.groupName());
consumeMessageContext.setMq(mq);
consumeMessageContext.setMsgList(pullResult.getMsgFoundList());
consumeMessageContext.setSuccess(false);
this.executeHookBefore(consumeMessageContext);
consumeMessageContext.setStatus(ConsumeConcurrentlyStatus.CONSUME_SUCCESS.toString());
consumeMessageContext.setSuccess(true);
this.executeHookAfter(consumeMessageContext);
}
return pullResult;
}
@@ -78,7 +78,12 @@ public class PullAPIWrapper {
this.updatePullFromWhichNode(mq, pullResultExt.getSuggestWhichBrokerId());
if (PullStatus.FOUND == pullResult.getPullStatus()) {
ByteBuffer byteBuffer = ByteBuffer.wrap(pullResultExt.getMessageBinary());
List<MessageExt> msgList = MessageDecoder.decodes(byteBuffer);
List<MessageExt> msgList = MessageDecoder.decodesBatch(
byteBuffer,
this.mQClientFactory.getClientConfig().isDecodeReadBody(),
this.mQClientFactory.getClientConfig().isDecodeDecompressBody(),
true
);
boolean needDecodeInnerMessage = false;
for (MessageExt messageExt: msgList) {
@@ -227,13 +232,13 @@ public class PullAPIWrapper {
requestHeader.setSubVersion(subVersion);
requestHeader.setMaxMsgBytes(maxSizeInBytes);
requestHeader.setExpressionType(expressionType);
requestHeader.setBname(mq.getBrokerName());
String brokerAddr = findBrokerResult.getBrokerAddr();
if (PullSysFlag.hasClassFilterFlag(sysFlagInner)) {
brokerAddr = computePullFromWhichFilterServer(mq.getTopic(), brokerAddr);
}
PullResult pullResult = this.mQClientFactory.getMQClientAPIImpl().pullMessage(
brokerAddr,
requestHeader,
@@ -46,12 +46,12 @@ public class SendMessageTraceHookImpl implements SendMessageHook {
if (context == null || context.getMessage().getTopic().startsWith(((AsyncTraceDispatcher) localDispatcher).getTraceTopicName())) {
return;
}
//build the context content of TuxeTraceContext
TraceContext tuxeContext = new TraceContext();
tuxeContext.setTraceBeans(new ArrayList<TraceBean>(1));
context.setMqTraceContext(tuxeContext);
tuxeContext.setTraceType(TraceType.Pub);
tuxeContext.setGroupName(NamespaceUtil.withoutNamespace(context.getProducerGroup()));
//build the context content of TraceContext
TraceContext traceContext = new TraceContext();
traceContext.setTraceBeans(new ArrayList<TraceBean>(1));
context.setMqTraceContext(traceContext);
traceContext.setTraceType(TraceType.Pub);
traceContext.setGroupName(NamespaceUtil.withoutNamespace(context.getProducerGroup()));
//build the data bean object of message trace
TraceBean traceBean = new TraceBean();
traceBean.setTopic(NamespaceUtil.withoutNamespace(context.getMessage().getTopic()));
@@ -60,7 +60,7 @@ public class SendMessageTraceHookImpl implements SendMessageHook {
traceBean.setStoreHost(context.getBrokerAddr());
traceBean.setBodyLength(context.getMessage().getBody().length);
traceBean.setMsgType(context.getMsgType());
tuxeContext.getTraceBeans().add(traceBean);
traceContext.getTraceBeans().add(traceBean);
}
@Override
@@ -80,19 +80,19 @@ public class SendMessageTraceHookImpl implements SendMessageHook {
return;
}
TraceContext tuxeContext = (TraceContext) context.getMqTraceContext();
TraceBean traceBean = tuxeContext.getTraceBeans().get(0);
int costTime = (int) ((System.currentTimeMillis() - tuxeContext.getTimeStamp()) / tuxeContext.getTraceBeans().size());
tuxeContext.setCostTime(costTime);
TraceContext traceContext = (TraceContext) context.getMqTraceContext();
TraceBean traceBean = traceContext.getTraceBeans().get(0);
int costTime = (int) ((System.currentTimeMillis() - traceContext.getTimeStamp()) / traceContext.getTraceBeans().size());
traceContext.setCostTime(costTime);
if (context.getSendResult().getSendStatus().equals(SendStatus.SEND_OK)) {
tuxeContext.setSuccess(true);
traceContext.setSuccess(true);
} else {
tuxeContext.setSuccess(false);
traceContext.setSuccess(false);
}
tuxeContext.setRegionId(context.getSendResult().getRegionId());
traceContext.setRegionId(context.getSendResult().getRegionId());
traceBean.setMsgId(context.getSendResult().getMsgId());
traceBean.setOffsetMsgId(context.getSendResult().getOffsetMsgId());
traceBean.setStoreTime(tuxeContext.getTimeStamp() + costTime / 2);
localDispatcher.append(tuxeContext);
traceBean.setStoreTime(traceContext.getTimeStamp() + costTime / 2);
localDispatcher.append(traceContext);
}
}
@@ -41,6 +41,7 @@ import org.apache.rocketmq.common.PlainAccessConfig;
import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageConst;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.protocol.RequestCode;
import org.apache.rocketmq.common.message.MessageDecoder;
import org.apache.rocketmq.common.message.MessageExt;
@@ -712,7 +713,7 @@ public class MQClientAPIImplTest {
}
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
long offset = mqClientAPI.getMaxOffset(brokerAddr, topic, 0, 10000);
long offset = mqClientAPI.getMaxOffset(brokerAddr, new MessageQueue(topic, brokerName, 0), 10000);
assertThat(offset).isEqualTo(100L);
}
@@ -733,7 +734,7 @@ public class MQClientAPIImplTest {
}
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
long offset = mqClientAPI.getMinOffset(brokerAddr, topic, 0, 10000);
long offset = mqClientAPI.getMinOffset(brokerAddr, new MessageQueue(topic, brokerName, 0), 10000);
assertThat(offset).isEqualTo(100L);
}
@@ -754,7 +755,7 @@ public class MQClientAPIImplTest {
}
}).when(remotingClient).invokeSync(anyString(), any(RemotingCommand.class), anyLong());
long t = mqClientAPI.getEarliestMsgStoretime(brokerAddr, topic, 0, 10000);
long t = mqClientAPI.getEarliestMsgStoretime(brokerAddr, new MessageQueue(topic, brokerName, 0), 10000);
assertThat(t).isEqualTo(100L);
}
+9
View File
@@ -52,5 +52,14 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
</dependencies>
</project>
@@ -45,4 +45,6 @@ public class LoggerName {
public static final String ROCKETMQ_POP_LOGGER_NAME = "RocketmqPop";
public static final String FAILOVER_LOGGER_NAME = "RocketmqFailover";
public static final String STDOUT_LOGGER_NAME = "STDOUT";
public static final String PROXY_LOGGER_NAME = "RocketmqProxy";
public static final String PROXY_WATER_MARK_LOGGER_NAME = "RocketmqProxyWatermark";
}
@@ -0,0 +1,232 @@
/*
* 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.common.consumer;
import java.util.Arrays;
import java.util.List;
import org.apache.rocketmq.common.KeyBuilder;
import org.apache.rocketmq.common.message.MessageConst;
public class ReceiptHandle {
private static final String SEPARATOR = MessageConst.KEY_SEPARATOR;
public static final String NORMAL_TOPIC = "0";
public static final String RETRY_TOPIC = "1";
private final long startOffset;
private final long retrieveTime;
private final long invisibleTime;
private final long nextVisibleTime;
private final int reviveQueueId;
private final String topicType;
private final String brokerName;
private final int queueId;
private final long offset;
private final long commitLogOffset;
private final String receiptHandle;
public String encode() {
return startOffset + SEPARATOR + retrieveTime + SEPARATOR + invisibleTime + SEPARATOR + reviveQueueId
+ SEPARATOR + topicType + SEPARATOR + brokerName + SEPARATOR + queueId + SEPARATOR + offset + SEPARATOR
+ commitLogOffset;
}
public boolean isExpired() {
return nextVisibleTime <= System.currentTimeMillis();
}
public static ReceiptHandle decode(String receiptHandle) {
List<String> dataList = Arrays.asList(receiptHandle.split(SEPARATOR));
if (dataList.size() < 8) {
throw new IllegalArgumentException("Parse failed, dataList size " + dataList.size());
}
long startOffset = Long.parseLong(dataList.get(0));
long retrieveTime = Long.parseLong(dataList.get(1));
long invisibleTime = Long.parseLong(dataList.get(2));
int reviveQueueId = Integer.parseInt(dataList.get(3));
String topicType = dataList.get(4);
String brokerName = dataList.get(5);
int queueId = Integer.parseInt(dataList.get(6));
long offset = Long.parseLong(dataList.get(7));
long commitLogOffset = -1L;
if (dataList.size() >= 9) {
commitLogOffset = Long.parseLong(dataList.get(8));
}
return new ReceiptHandleBuilder()
.startOffset(startOffset)
.retrieveTime(retrieveTime)
.invisibleTime(invisibleTime)
.reviveQueueId(reviveQueueId)
.topicType(topicType)
.brokerName(brokerName)
.queueId(queueId)
.offset(offset)
.commitLogOffset(commitLogOffset)
.receiptHandle(receiptHandle).build();
}
ReceiptHandle(final long startOffset, final long retrieveTime, final long invisibleTime, final long nextVisibleTime,
final int reviveQueueId, final String topicType, final String brokerName, final int queueId, final long offset,
final long commitLogOffset, final String receiptHandle) {
this.startOffset = startOffset;
this.retrieveTime = retrieveTime;
this.invisibleTime = invisibleTime;
this.nextVisibleTime = nextVisibleTime;
this.reviveQueueId = reviveQueueId;
this.topicType = topicType;
this.brokerName = brokerName;
this.queueId = queueId;
this.offset = offset;
this.commitLogOffset = commitLogOffset;
this.receiptHandle = receiptHandle;
}
public static class ReceiptHandleBuilder {
private long startOffset;
private long retrieveTime;
private long invisibleTime;
private int reviveQueueId;
private String topicType;
private String brokerName;
private int queueId;
private long offset;
private long commitLogOffset;
private String receiptHandle;
ReceiptHandleBuilder() {
}
public ReceiptHandle.ReceiptHandleBuilder startOffset(final long startOffset) {
this.startOffset = startOffset;
return this;
}
public ReceiptHandle.ReceiptHandleBuilder retrieveTime(final long retrieveTime) {
this.retrieveTime = retrieveTime;
return this;
}
public ReceiptHandle.ReceiptHandleBuilder invisibleTime(final long invisibleTime) {
this.invisibleTime = invisibleTime;
return this;
}
public ReceiptHandle.ReceiptHandleBuilder reviveQueueId(final int reviveQueueId) {
this.reviveQueueId = reviveQueueId;
return this;
}
public ReceiptHandle.ReceiptHandleBuilder topicType(final String topicType) {
this.topicType = topicType;
return this;
}
public ReceiptHandle.ReceiptHandleBuilder brokerName(final String brokerName) {
this.brokerName = brokerName;
return this;
}
public ReceiptHandle.ReceiptHandleBuilder queueId(final int queueId) {
this.queueId = queueId;
return this;
}
public ReceiptHandle.ReceiptHandleBuilder offset(final long offset) {
this.offset = offset;
return this;
}
public ReceiptHandle.ReceiptHandleBuilder commitLogOffset(final long commitLogOffset) {
this.commitLogOffset = commitLogOffset;
return this;
}
public ReceiptHandle.ReceiptHandleBuilder receiptHandle(final String receiptHandle) {
this.receiptHandle = receiptHandle;
return this;
}
public ReceiptHandle build() {
return new ReceiptHandle(this.startOffset, this.retrieveTime, this.invisibleTime, this.retrieveTime + this.invisibleTime,
this.reviveQueueId, this.topicType, this.brokerName, this.queueId, this.offset, this.commitLogOffset, this.receiptHandle);
}
@Override
public String toString() {
return "ReceiptHandle.ReceiptHandleBuilder(startOffset=" + this.startOffset + ", retrieveTime=" + this.retrieveTime + ", invisibleTime=" + this.invisibleTime + ", reviveQueueId=" + this.reviveQueueId + ", topic=" + this.topicType + ", brokerName=" + this.brokerName + ", queueId=" + this.queueId + ", offset=" + this.offset + ", commitLogOffset=" + this.commitLogOffset + ", receiptHandle=" + this.receiptHandle + ")";
}
}
public static ReceiptHandle.ReceiptHandleBuilder builder() {
return new ReceiptHandle.ReceiptHandleBuilder();
}
public long getStartOffset() {
return this.startOffset;
}
public long getRetrieveTime() {
return this.retrieveTime;
}
public long getInvisibleTime() {
return this.invisibleTime;
}
public long getNextVisibleTime() {
return this.nextVisibleTime;
}
public int getReviveQueueId() {
return this.reviveQueueId;
}
public String getTopicType() {
return this.topicType;
}
public String getBrokerName() {
return this.brokerName;
}
public int getQueueId() {
return this.queueId;
}
public long getOffset() {
return this.offset;
}
public long getCommitLogOffset() {
return commitLogOffset;
}
public String getReceiptHandle() {
return this.receiptHandle;
}
public boolean isRetryTopic() {
return RETRY_TOPIC.equals(topicType);
}
public String getRealTopic(String topic, String groupName) {
if (isRetryTopic()) {
return KeyBuilder.buildPopRetryTopic(topic, groupName);
}
return topic;
}
}
@@ -39,7 +39,7 @@ public class MessageBatch extends Message implements Iterable<Message> {
return messages.iterator();
}
public static MessageBatch generateFromList(Collection<Message> messages) {
public static MessageBatch generateFromList(Collection<? extends Message> messages) {
assert messages != null;
assert messages.size() > 0;
List<Message> messageList = new ArrayList<Message>(messages.size());
@@ -65,6 +65,10 @@ public class MessageConst {
public static final String PROPERTY_REDIRECT = "REDIRECT";
public static final String PROPERTY_INNER_MULTI_DISPATCH = "INNER_MULTI_DISPATCH";
public static final String PROPERTY_INNER_MULTI_QUEUE_OFFSET = "INNER_MULTI_QUEUE_OFFSET";
public static final String PROPERTY_TRACE_CONTEXT = "TRACE_CONTEXT";
public static final String PROPERTY_TIMER_DELAY_SEC = "TIMER_DELAY_SEC";
public static final String PROPERTY_TIMER_DELIVER_MS = "TIMER_DELIVER_MS";
public static final String PROPERTY_BORN_HOST = "__BORNHOST";
/**
* property which name starts with "__RMQ.TRANSIENT." is called transient one that will not stored in broker disks.
@@ -123,5 +127,6 @@ public class MessageConst {
STRING_HASH_SET.add(PROPERTY_CLUSTER);
STRING_HASH_SET.add(PROPERTY_MESSAGE_TYPE);
STRING_HASH_SET.add(PROPERTY_INNER_MULTI_QUEUE_OFFSET);
STRING_HASH_SET.add(PROPERTY_BORN_HOST);
}
}
@@ -16,6 +16,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
@@ -80,6 +81,12 @@ public class AckMessageRequestHeader implements CommandCustomHeader {
@Override
public String toString() {
return topic + "," + this.consumerGroup + "," + this.queueId + "," + this.offset + "," + this.extraInfo;
return MoreObjects.toStringHelper(this)
.add("consumerGroup", consumerGroup)
.add("topic", topic)
.add("queueId", queueId)
.add("extraInfo", extraInfo)
.add("offset", offset)
.toString();
}
}
@@ -16,6 +16,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
@@ -94,4 +95,14 @@ public class ChangeInvisibleTimeRequestHeader implements CommandCustomHeader {
this.queueId = queueId;
}
@Override public String toString() {
return MoreObjects.toStringHelper(this)
.add("consumerGroup", consumerGroup)
.add("topic", topic)
.add("queueId", queueId)
.add("extraInfo", extraInfo)
.add("offset", offset)
.add("invisibleTime", invisibleTime)
.toString();
}
}
@@ -20,6 +20,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
@@ -76,4 +77,15 @@ public class CheckTransactionStateRequestHeader implements CommandCustomHeader {
public void setOffsetMsgId(String offsetMsgId) {
this.offsetMsgId = offsetMsgId;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("tranStateTableOffset", tranStateTableOffset)
.add("commitLogOffset", commitLogOffset)
.add("msgId", msgId)
.add("transactionId", transactionId)
.add("offsetMsgId", offsetMsgId)
.toString();
}
}
@@ -20,6 +20,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
@@ -68,4 +69,14 @@ public class CloneGroupOffsetRequestHeader implements CommandCustomHeader {
public void setOffline(boolean offline) {
this.offline = offline;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("srcGroup", srcGroup)
.add("destGroup", destGroup)
.add("topic", topic)
.add("offline", offline)
.toString();
}
}
@@ -17,6 +17,7 @@
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.annotation.CFNullable;
@@ -97,4 +98,17 @@ public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomH
public void setGroupSysFlag(Integer groupSysFlag) {
this.groupSysFlag = groupSysFlag;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("consumerGroup", consumerGroup)
.add("clientId", clientId)
.add("msgId", msgId)
.add("brokerName", brokerName)
.add("topic", topic)
.add("topicSysFlag", topicSysFlag)
.add("groupSysFlag", groupSysFlag)
.toString();
}
}
@@ -17,6 +17,7 @@
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.annotation.CFNullable;
@@ -98,7 +99,14 @@ public class ConsumerSendMsgBackRequestHeader implements CommandCustomHeader {
@Override
public String toString() {
return "ConsumerSendMsgBackRequestHeader [group=" + group + ", originTopic=" + originTopic + ", originMsgId=" + originMsgId
+ ", delayLevel=" + delayLevel + ", unitMode=" + unitMode + ", maxReconsumeTimes=" + maxReconsumeTimes + "]";
return MoreObjects.toStringHelper(this)
.add("offset", offset)
.add("group", group)
.add("delayLevel", delayLevel)
.add("originMsgId", originMsgId)
.add("originTopic", originTopic)
.add("unitMode", unitMode)
.add("maxReconsumeTimes", maxReconsumeTimes)
.toString();
}
}
@@ -17,6 +17,7 @@
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
@@ -110,4 +111,18 @@ public class CreateAccessConfigRequestHeader implements CommandCustomHeader {
public void setGroupPerms(String groupPerms) {
this.groupPerms = groupPerms;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("accessKey", accessKey)
.add("secretKey", secretKey)
.add("whiteRemoteAddress", whiteRemoteAddress)
.add("admin", admin)
.add("defaultTopicPerm", defaultTopicPerm)
.add("defaultGroupPerm", defaultGroupPerm)
.add("topicPerms", topicPerms)
.add("groupPerms", groupPerms)
.toString();
}
}
@@ -20,6 +20,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.common.TopicFilterType;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
@@ -139,4 +140,20 @@ public class CreateTopicRequestHeader implements CommandCustomHeader {
public void setAttributes(String attributes) {
this.attributes = attributes;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("topic", topic)
.add("defaultTopic", defaultTopic)
.add("readQueueNums", readQueueNums)
.add("writeQueueNums", writeQueueNums)
.add("perm", perm)
.add("topicFilterType", topicFilterType)
.add("topicSysFlag", topicSysFlag)
.add("order", order)
.add("attributes", attributes)
.add("force", force)
.toString();
}
}
@@ -17,6 +17,7 @@
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.common.sysflag.MessageSysFlag;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
@@ -118,14 +119,14 @@ public class EndTransactionRequestHeader implements CommandCustomHeader {
@Override
public String toString() {
return "EndTransactionRequestHeader{" +
"producerGroup='" + producerGroup + '\'' +
", tranStateTableOffset=" + tranStateTableOffset +
", commitLogOffset=" + commitLogOffset +
", commitOrRollback=" + commitOrRollback +
", fromTransactionCheck=" + fromTransactionCheck +
", msgId='" + msgId + '\'' +
", transactionId='" + transactionId + '\'' +
'}';
return MoreObjects.toStringHelper(this)
.add("producerGroup", producerGroup)
.add("tranStateTableOffset", tranStateTableOffset)
.add("commitLogOffset", commitLogOffset)
.add("commitOrRollback", commitOrRollback)
.add("fromTransactionCheck", fromTransactionCheck)
.add("msgId", msgId)
.add("transactionId", transactionId)
.toString();
}
}
@@ -16,6 +16,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
@@ -44,4 +45,12 @@ public class GetConsumeStatsRequestHeader implements CommandCustomHeader {
public void setTopic(String topic) {
this.topic = topic;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("consumerGroup", consumerGroup)
.add("topic", topic)
.toString();
}
}
@@ -17,6 +17,7 @@
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
@@ -36,4 +37,11 @@ public class GetConsumerListByGroupRequestHeader implements CommandCustomHeader
public void setConsumerGroup(String consumerGroup) {
this.consumerGroup = consumerGroup;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("consumerGroup", consumerGroup)
.toString();
}
}
@@ -17,6 +17,7 @@
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.annotation.CFNullable;
@@ -57,4 +58,13 @@ public class GetConsumerRunningInfoRequestHeader implements CommandCustomHeader
public void setJstackEnable(boolean jstackEnable) {
this.jstackEnable = jstackEnable;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("consumerGroup", consumerGroup)
.add("clientId", clientId)
.add("jstackEnable", jstackEnable)
.toString();
}
}
@@ -17,6 +17,7 @@
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.annotation.CFNullable;
@@ -57,4 +58,13 @@ public class GetConsumerStatusRequestHeader implements CommandCustomHeader {
public void setClientAddr(String clientAddr) {
this.clientAddr = clientAddr;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("topic", topic)
.add("group", group)
.add("clientAddr", clientAddr)
.toString();
}
}
@@ -20,6 +20,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.common.rpc.TopicQueueRequestHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.annotation.CFNullable;
@@ -71,4 +72,13 @@ public class GetMaxOffsetRequestHeader extends TopicQueueRequestHeader {
public void setCommitted(final boolean committed) {
this.committed = committed;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("topic", topic)
.add("queueId", queueId)
.add("committed", committed)
.toString();
}
}
@@ -20,6 +20,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.common.rpc.TopicQueueRequestHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
@@ -53,4 +54,12 @@ public class GetMinOffsetRequestHeader extends TopicQueueRequestHeader {
public void setQueueId(Integer queueId) {
this.queueId = queueId;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("topic", topic)
.add("queueId", queueId)
.toString();
}
}
@@ -16,6 +16,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
@@ -152,4 +153,21 @@ public class PopMessageRequestHeader implements CommandCustomHeader {
public boolean isOrder() {
return this.order != null && this.order.booleanValue();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("consumerGroup", consumerGroup)
.add("topic", topic)
.add("queueId", queueId)
.add("maxMsgNums", maxMsgNums)
.add("invisibleTime", invisibleTime)
.add("pollTime", pollTime)
.add("bornTime", bornTime)
.add("initMode", initMode)
.add("expType", expType)
.add("exp", exp)
.add("order", order)
.toString();
}
}
@@ -20,6 +20,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import java.util.HashMap;
import org.apache.rocketmq.common.rpc.TopicQueueRequestHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
@@ -267,4 +268,22 @@ public class PullMessageRequestHeader extends TopicQueueRequestHeader implements
public void setMaxMsgBytes(Integer maxMsgBytes) {
this.maxMsgBytes = maxMsgBytes;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("consumerGroup", consumerGroup)
.add("topic", topic)
.add("queueId", queueId)
.add("queueOffset", queueOffset)
.add("maxMsgBytes", maxMsgBytes)
.add("maxMsgNums", maxMsgNums)
.add("sysFlag", sysFlag)
.add("commitOffset", commitOffset)
.add("suspendTimeoutMillis", suspendTimeoutMillis)
.add("subscription", subscription)
.add("subVersion", subVersion)
.add("expressionType", expressionType)
.toString();
}
}
@@ -20,6 +20,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.common.rpc.TopicQueueRequestHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
@@ -65,4 +66,12 @@ public class SearchOffsetRequestHeader extends TopicQueueRequestHeader {
this.timestamp = timestamp;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("topic", topic)
.add("queueId", queueId)
.add("timestamp", timestamp)
.toString();
}
}
@@ -20,10 +20,14 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import java.util.HashMap;
import org.apache.rocketmq.common.protocol.RequestCode;
import org.apache.rocketmq.common.rpc.TopicQueueRequestHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.annotation.CFNullable;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public class SendMessageRequestHeader extends TopicQueueRequestHeader {
@CFNotNull
@@ -163,4 +167,119 @@ public class SendMessageRequestHeader extends TopicQueueRequestHeader {
public void setBatch(boolean batch) {
this.batch = batch;
}
public static SendMessageRequestHeader parseRequestHeader(RemotingCommand request) throws RemotingCommandException {
SendMessageRequestHeaderV2 requestHeaderV2 = null;
SendMessageRequestHeader requestHeader = null;
switch (request.getCode()) {
case RequestCode.SEND_BATCH_MESSAGE:
case RequestCode.SEND_MESSAGE_V2:
requestHeaderV2 =
(SendMessageRequestHeaderV2) request
.decodeCommandCustomHeader(SendMessageRequestHeaderV2.class);
case RequestCode.SEND_MESSAGE:
if (null == requestHeaderV2) {
requestHeader =
(SendMessageRequestHeader) request
.decodeCommandCustomHeader(SendMessageRequestHeader.class);
} else {
requestHeader = SendMessageRequestHeaderV2.createSendMessageRequestHeaderV1(requestHeaderV2);
}
default:
break;
}
return requestHeader;
}
public static SendMessageRequestHeaderV2 decodeSendMessageHeaderV2(RemotingCommand request)
throws RemotingCommandException {
SendMessageRequestHeaderV2 r = new SendMessageRequestHeaderV2();
HashMap<String, String> fields = request.getExtFields();
if (fields == null) {
throw new RemotingCommandException("the ext fields is null");
}
String s = fields.get("a");
checkNotNull(s, "the custom field <a> is null");
r.setA(s);
s = fields.get("b");
checkNotNull(s, "the custom field <b> is null");
r.setB(s);
s = fields.get("c");
checkNotNull(s, "the custom field <c> is null");
r.setC(s);
s = fields.get("d");
checkNotNull(s, "the custom field <d> is null");
r.setD(Integer.parseInt(s));
s = fields.get("e");
checkNotNull(s, "the custom field <e> is null");
r.setE(Integer.parseInt(s));
s = fields.get("f");
checkNotNull(s, "the custom field <f> is null");
r.setF(Integer.parseInt(s));
s = fields.get("g");
checkNotNull(s, "the custom field <g> is null");
r.setG(Long.parseLong(s));
s = fields.get("h");
checkNotNull(s, "the custom field <h> is null");
r.setH(Integer.parseInt(s));
s = fields.get("i");
if (s != null) {
r.setI(s);
}
s = fields.get("j");
if (s != null) {
r.setJ(Integer.parseInt(s));
}
s = fields.get("k");
if (s != null) {
r.setK(Boolean.parseBoolean(s));
}
s = fields.get("l");
if (s != null) {
r.setL(Integer.parseInt(s));
}
s = fields.get("m");
if (s != null) {
r.setM(Boolean.parseBoolean(s));
}
return r;
}
private static void checkNotNull(String s, String msg) throws RemotingCommandException {
if (s == null) {
throw new RemotingCommandException(msg);
}
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("producerGroup", producerGroup)
.add("topic", topic)
.add("defaultTopic", defaultTopic)
.add("defaultTopicQueueNums", defaultTopicQueueNums)
.add("queueId", queueId)
.add("sysFlag", sysFlag)
.add("bornTimestamp", bornTimestamp)
.add("flag", flag)
.add("properties", properties)
.add("reconsumeTimes", reconsumeTimes)
.add("unitMode", unitMode)
.add("batch", batch)
.add("maxReconsumeTimes", maxReconsumeTimes)
.toString();
}
}
@@ -20,6 +20,7 @@ package org.apache.rocketmq.common.protocol.header;
import java.util.HashMap;
import org.apache.rocketmq.remoting.protocol.FastCodesHeader;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.annotation.CFNullable;
@@ -288,4 +289,23 @@ public class SendMessageRequestHeaderV2 implements CommandCustomHeader, FastCode
public void setM(boolean m) {
this.m = m;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("a", a)
.add("b", b)
.add("c", c)
.add("d", d)
.add("e", e)
.add("f", f)
.add("g", g)
.add("h", h)
.add("i", i)
.add("j", j)
.add("k", k)
.add("l", l)
.add("m", m)
.toString();
}
}
@@ -20,6 +20,7 @@
*/
package org.apache.rocketmq.common.protocol.header;
import com.google.common.base.MoreObjects;
import org.apache.rocketmq.common.rpc.TopicQueueRequestHeader;
import org.apache.rocketmq.remoting.annotation.CFNotNull;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
@@ -73,4 +74,14 @@ public class UpdateConsumerOffsetRequestHeader extends TopicQueueRequestHeader {
public void setCommitOffset(Long commitOffset) {
this.commitOffset = commitOffset;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("consumerGroup", consumerGroup)
.add("topic", topic)
.add("queueId", queueId)
.add("commitOffset", commitOffset)
.toString();
}
}
@@ -48,6 +48,13 @@ public class CustomizedRetryPolicy implements RetryPolicy {
TimeUnit.HOURS.toMillis(2)
};
public CustomizedRetryPolicy() {
}
public CustomizedRetryPolicy(long[] next) {
this.next = next;
}
public long[] getNext() {
return next;
}
@@ -20,14 +20,20 @@ package org.apache.rocketmq.common.subscription;
import com.google.common.base.MoreObjects;
import java.util.concurrent.TimeUnit;
/**
* next delay time = min(max, initial * multiplier^reconsumeTimes)
*/
public class ExponentialRetryPolicy implements RetryPolicy {
private long initial = TimeUnit.SECONDS.toMillis(5);
private long max = TimeUnit.HOURS.toMillis(2);
private long multiplier = 2;
public ExponentialRetryPolicy() {
}
public ExponentialRetryPolicy(long initial, long max, long multiplier) {
this.initial = initial;
this.max = max;
this.multiplier = multiplier;
}
public long getInitial() {
return initial;
}
@@ -0,0 +1,131 @@
/*
* 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.common.thread;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.logging.InternalLogger;
import org.apache.rocketmq.logging.InternalLoggerFactory;
public class ThreadPoolMonitor {
private static InternalLogger jstackLogger = InternalLoggerFactory.getLogger(ThreadPoolMonitor.class);
private static InternalLogger waterMarkLogger = InternalLoggerFactory.getLogger(ThreadPoolMonitor.class);
private static final List<ThreadPoolWrapper> MONITOR_EXECUTOR = new CopyOnWriteArrayList<>();
private static final ScheduledExecutorService MONITOR_SCHEDULED = Executors.newSingleThreadScheduledExecutor(
new ThreadFactoryBuilder().setNameFormat("ThreadPoolMonitor-%d").build()
);
private static volatile long threadPoolStatusPeriodTime = TimeUnit.SECONDS.toMillis(3);
private static volatile boolean enablePrintJstack = true;
private static volatile long jstackPeriodTime = 60000;
private static volatile long jstackTime = System.currentTimeMillis();
public static void config(InternalLogger jstackLoggerConfig, InternalLogger waterMarkLoggerConfig,
boolean enablePrintJstack, long jstackPeriodTimeConfig, long threadPoolStatusPeriodTimeConfig) {
jstackLogger = jstackLoggerConfig;
waterMarkLogger = waterMarkLoggerConfig;
threadPoolStatusPeriodTime = threadPoolStatusPeriodTimeConfig;
ThreadPoolMonitor.enablePrintJstack = enablePrintJstack;
jstackPeriodTime = jstackPeriodTimeConfig;
}
public static ThreadPoolExecutor createAndMonitor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
String name,
int queueCapacity) {
return createAndMonitor(corePoolSize, maximumPoolSize, keepAliveTime, unit, name, queueCapacity, Collections.emptyList());
}
public static ThreadPoolExecutor createAndMonitor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
String name,
int queueCapacity,
ThreadPoolStatusMonitor... threadPoolStatusMonitors) {
return createAndMonitor(corePoolSize, maximumPoolSize, keepAliveTime, unit, name, queueCapacity,
Lists.newArrayList(threadPoolStatusMonitors));
}
public static ThreadPoolExecutor createAndMonitor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
String name,
int queueCapacity,
List<ThreadPoolStatusMonitor> threadPoolStatusMonitors) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
new LinkedBlockingQueue<>(queueCapacity),
new ThreadFactoryBuilder().setNameFormat(name + "-%d").build(),
new ThreadPoolExecutor.DiscardOldestPolicy());
List<ThreadPoolStatusMonitor> printers = Lists.newArrayList(new ThreadPoolQueueSizeMonitor(queueCapacity));
printers.addAll(threadPoolStatusMonitors);
MONITOR_EXECUTOR.add(ThreadPoolWrapper.builder()
.name(name)
.threadPoolExecutor(executor)
.statusPrinters(printers)
.build());
return executor;
}
public static void logThreadPoolStatus() {
for (ThreadPoolWrapper threadPoolWrapper : MONITOR_EXECUTOR) {
List<ThreadPoolStatusMonitor> monitors = threadPoolWrapper.getStatusPrinters();
for (ThreadPoolStatusMonitor monitor : monitors) {
double value = monitor.value(threadPoolWrapper.getThreadPoolExecutor());
waterMarkLogger.info("\t{}\t{}\t{}", threadPoolWrapper.getName(),
monitor.describe(),
value);
if (enablePrintJstack) {
if (monitor.needPrintJstack(threadPoolWrapper.getThreadPoolExecutor(), value) &&
System.currentTimeMillis() - jstackTime > jstackPeriodTime) {
jstackTime = System.currentTimeMillis();
jstackLogger.warn("jstack start\n{}", UtilAll.jstack());
}
}
}
}
}
public static void init() {
MONITOR_SCHEDULED.scheduleAtFixedRate(ThreadPoolMonitor::logThreadPoolStatus, 20,
threadPoolStatusPeriodTime, TimeUnit.MILLISECONDS);
}
public static void shutdown() {
MONITOR_SCHEDULED.shutdown();
}
}
@@ -0,0 +1,44 @@
/*
* 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.common.thread;
import java.util.concurrent.ThreadPoolExecutor;
public class ThreadPoolQueueSizeMonitor implements ThreadPoolStatusMonitor {
private final int maxQueueCapacity;
public ThreadPoolQueueSizeMonitor(int maxQueueCapacity) {
this.maxQueueCapacity = maxQueueCapacity;
}
@Override
public String describe() {
return "queueSize";
}
@Override
public double value(ThreadPoolExecutor executor) {
return executor.getQueue().size();
}
@Override
public boolean needPrintJstack(ThreadPoolExecutor executor, double value) {
return value > maxQueueCapacity * 0.85;
}
}
@@ -0,0 +1,29 @@
/*
* 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.common.thread;
import java.util.concurrent.ThreadPoolExecutor;
public interface ThreadPoolStatusMonitor {
String describe();
double value(ThreadPoolExecutor executor);
boolean needPrintJstack(ThreadPoolExecutor executor, double value);
}
@@ -0,0 +1,120 @@
/*
* 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.common.thread;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
public class ThreadPoolWrapper {
private String name;
private ThreadPoolExecutor threadPoolExecutor;
private List<ThreadPoolStatusMonitor> statusPrinters;
ThreadPoolWrapper(final String name, final ThreadPoolExecutor threadPoolExecutor,
final List<ThreadPoolStatusMonitor> statusPrinters) {
this.name = name;
this.threadPoolExecutor = threadPoolExecutor;
this.statusPrinters = statusPrinters;
}
public static class ThreadPoolWrapperBuilder {
private String name;
private ThreadPoolExecutor threadPoolExecutor;
private List<ThreadPoolStatusMonitor> statusPrinters;
ThreadPoolWrapperBuilder() {
}
public ThreadPoolWrapper.ThreadPoolWrapperBuilder name(final String name) {
this.name = name;
return this;
}
public ThreadPoolWrapper.ThreadPoolWrapperBuilder threadPoolExecutor(
final ThreadPoolExecutor threadPoolExecutor) {
this.threadPoolExecutor = threadPoolExecutor;
return this;
}
public ThreadPoolWrapper.ThreadPoolWrapperBuilder statusPrinters(
final List<ThreadPoolStatusMonitor> statusPrinters) {
this.statusPrinters = statusPrinters;
return this;
}
public ThreadPoolWrapper build() {
return new ThreadPoolWrapper(this.name, this.threadPoolExecutor, this.statusPrinters);
}
@java.lang.Override
public java.lang.String toString() {
return "ThreadPoolWrapper.ThreadPoolWrapperBuilder(name=" + this.name + ", threadPoolExecutor=" + this.threadPoolExecutor + ", statusPrinters=" + this.statusPrinters + ")";
}
}
public static ThreadPoolWrapper.ThreadPoolWrapperBuilder builder() {
return new ThreadPoolWrapper.ThreadPoolWrapperBuilder();
}
public String getName() {
return this.name;
}
public ThreadPoolExecutor getThreadPoolExecutor() {
return this.threadPoolExecutor;
}
public List<ThreadPoolStatusMonitor> getStatusPrinters() {
return this.statusPrinters;
}
public void setName(final String name) {
this.name = name;
}
public void setThreadPoolExecutor(final ThreadPoolExecutor threadPoolExecutor) {
this.threadPoolExecutor = threadPoolExecutor;
}
public void setStatusPrinters(final List<ThreadPoolStatusMonitor> statusPrinters) {
this.statusPrinters = statusPrinters;
}
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ThreadPoolWrapper wrapper = (ThreadPoolWrapper) o;
return Objects.equal(name, wrapper.name) && Objects.equal(threadPoolExecutor, wrapper.threadPoolExecutor) && Objects.equal(statusPrinters, wrapper.statusPrinters);
}
@Override public int hashCode() {
return Objects.hashCode(name, threadPoolExecutor, statusPrinters);
}
@Override public String toString() {
return MoreObjects.toStringHelper(this)
.add("name", name)
.add("threadPoolExecutor", threadPoolExecutor)
.add("statusPrinters", statusPrinters)
.toString();
}
}
@@ -0,0 +1,46 @@
/*
* 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.common.utils;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;
public class BinaryUtil {
public static byte[] calculateMd5(byte[] binaryData) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5 algorithm not found.");
}
messageDigest.update(binaryData);
return messageDigest.digest();
}
public static String generateMd5(String bodyStr) {
byte[] bytes = calculateMd5(bodyStr.getBytes(Charset.forName("UTF-8")));
return Hex.encodeHexString(bytes, false);
}
public static String generateMd5(byte[] content) {
byte[] bytes = calculateMd5(content);
return Hex.encodeHexString(bytes, false);
}
}
+45
View File
@@ -0,0 +1,45 @@
#!/bin/sh
# 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.
if [ -z "$ROCKETMQ_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
ROCKETMQ_HOME=`dirname "$PRG"`/..
# make it fully qualified
ROCKETMQ_HOME=`cd "$ROCKETMQ_HOME" && pwd`
cd "$saveddir"
fi
export ROCKETMQ_HOME
sh ${ROCKETMQ_HOME}/bin/runserver.sh org.apache.rocketmq.proxy.ProxyStartup $@
+23
View File
@@ -0,0 +1,23 @@
@echo off
rem Licensed to the Apache Software Foundation (ASF) under one or more
rem contributor license agreements. See the NOTICE file distributed with
rem this work for additional information regarding copyright ownership.
rem The ASF licenses this file to You under the Apache License, Version 2.0
rem (the "License"); you may not use this file except in compliance with
rem the License. You may obtain a copy of the License at
rem
rem http://www.apache.org/licenses/LICENSE-2.0
rem
rem Unless required by applicable law or agreed to in writing, software
rem distributed under the License is distributed on an "AS IS" BASIS,
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
if not exist "%ROCKETMQ_HOME%\bin\runserver.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & EXIT /B 1
call "%ROCKETMQ_HOME%\bin\runserver.cmd" org.apache.rocketmq.proxy.ProxyStartup %*
IF %ERRORLEVEL% EQU 0 (
ECHO "Proxy starts OK"
)
+15 -1
View File
@@ -72,6 +72,20 @@ case $1 in
echo "Send shutdown request to mqcontroller(${pid}) OK"
;;
proxy)
pid=`ps ax | grep -i 'org.apache.rocketmq.proxy.ProxyStartup' |grep java | grep -v grep | awk '{print $1}'`
if [ -z "$pid" ] ; then
echo "No mqproxy running."
exit -1;
fi
echo "The mqproxy(${pid}) is running..."
kill ${pid}
echo "Send shutdown request to mqproxy(${pid}) OK"
;;
*)
echo "Useage: mqshutdown broker | namesrv | controller"
echo "Useage: mqshutdown broker | namesrv | controller | proxy"
esac
+420
View File
@@ -0,0 +1,420 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<configuration>
<appender name="RocketmqProxyAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/proxy.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/proxy.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>128MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqProxyAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqProxyAppender_inner"/>
</appender>
<appender name="RocketmqProxyWatermarkAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/proxy_watermark.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/proxy_watermark.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>128MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8}%m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqProxyWatermarkAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqProxyWatermarkAppender_inner"/>
</appender>
<!-- Below is the logger configuration for broker-->
<appender name="DefaultAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/broker_default.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/broker_default.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqBrokerAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/broker.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/broker.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>20</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>128MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqBrokerAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqBrokerAppender_inner"/>
</appender>
<appender name="RocketmqProtectionAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/protection.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/protection.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqProtectionAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqProtectionAppender_inner"/>
</appender>
<appender name="RocketmqWaterMarkAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/watermark.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/watermark.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqWaterMarkAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqWaterMarkAppender_inner"/>
</appender>
<appender name="RocketmqStoreAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/store.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/store.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>128MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqStoreAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqStoreAppender_inner"/>
</appender>
<appender name="RocketmqRemotingAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/remoting.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/remoting.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqRemotingAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqRemotingAppender_inner"/>
</appender>
<appender name="RocketmqStoreErrorAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/storeerror.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/storeerror.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqStoreErrorAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqStoreErrorAppender_inner"/>
</appender>
<appender name="RocketmqTransactionAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/transaction.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/transaction.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqTransactionAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqTransactionAppender_inner"/>
</appender>
<appender name="RocketmqRebalanceLockAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/lock.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/lock.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>5</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqRebalanceLockAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqRebalanceLockAppender_inner"/>
</appender>
<appender name="RocketmqFilterAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/filter.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/filter.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqFilterAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqFilterAppender_inner"/>
</appender>
<appender name="RocketmqStatsAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/stats.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/stats.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>5</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqCommercialAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/${brokerLogDir}/commercial.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/${brokerLogDir}/commercial.%i.log.gz</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>500MB</maxFileSize>
</triggeringPolicy>
</appender>
<appender name="RocketmqPopAppender_inner"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/rocketmqlogs/pop.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${user.home}/logs/rocketmqlogs/otherdays/pop.%i.log
</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>20</maxIndex>
</rollingPolicy>
<triggeringPolicy
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>128MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="RocketmqPopAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="RocketmqPopAppender_inner"/>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<append>true</append>
<encoder>
<pattern>%d{yyy-MM-dd HH\:mm\:ss,GMT+8} %p %t - %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<logger name="RocketmqBroker" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqBrokerAppender"/>
</logger>
<logger name="RocketmqProtection" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqProtectionAppender"/>
</logger>
<logger name="RocketmqWaterMark" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqWaterMarkAppender"/>
</logger>
<logger name="RocketmqCommon" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqBrokerAppender"/>
</logger>
<logger name="RocketmqStore" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqStoreAppender"/>
</logger>
<logger name="RocketmqStoreError" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqStoreErrorAppender"/>
</logger>
<logger name="RocketmqTransaction" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqTransactionAppender"/>
</logger>
<logger name="RocketmqRebalanceLock" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqRebalanceLockAppender"/>
</logger>
<logger name="RocketmqRemoting" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqRemotingAppender"/>
</logger>
<logger name="RocketmqStats" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqStatsAppender"/>
</logger>
<logger name="RocketmqCommercial" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqCommercialAppender"/>
</logger>
<logger name="RocketmqFilter" additivity="false">
<level value="INFO"/>
<appender-ref ref="RocketmqFilterAppender"/>
</logger>
<logger name="RocketmqConsole" additivity="false">
<level value="INFO"/>
<appender-ref ref="STDOUT"/>
</logger>
<logger name="RocketmqPop" additivity="false">
<level value="INFO" />
<appender-ref ref="RocketmqPopAppender" />
</logger>
<logger name="RocketmqProxy" additivity="false">
<level value="INFO" />
<appender-ref ref="RocketmqProxyAppender" />
</logger>
<logger name="RocketmqProxyWatermark" additivity="false">
<level value="INFO" />
<appender-ref ref="RocketmqProxyWatermarkAppender" />
</logger>
<root>
<level value="INFO"/>
<appender-ref ref="DefaultAppender"/>
</root>
</configuration>
+3
View File
@@ -0,0 +1,3 @@
{
}
+4
View File
@@ -38,6 +38,10 @@
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-broker</artifactId>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-proxy</artifactId>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
+3
View File
@@ -33,6 +33,9 @@
- [Cluster Deployment](dledger/deploy_guide.md)introduce how to deploy Dledger in cluster.
- [Proxy Deployment](proxy/deploy_guide.md)
Introduce how to deploy proxy (both `Local` mode and `Cluster` mode).
### 5. Operation and maintenance management
- [Operation](operation.md)introduce RocketMQ's deployment modes that including single-master mode, multi-master mode, multi-master multi-slave mode and so on, as well as the usage of operation tool mqadmin.
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+37
View File
@@ -0,0 +1,37 @@
# RocketMQ Proxy Deployment Guide
## Overview
RocketMQ Proxy supports two deployment modes, `Local` mode and `Cluster` mode.
## Configuration
The configuration applies to both the `Cluster` mode and `Local` mode, whose default path is
distribution/conf/rmq-proxy.json directory.
## `Cluster` mode
* Set configuration field `nameSrvAddr`.
* Set configuration field `proxyMode` to `cluster` (case insensitive).
Run the command below.
```shell
nohup sh mqproxy &
```
The command will only run `Proxy` itself. It requires `Namesrv` and `Broker` components running.
## `Local` mode
* Set configuration field `nameSrvAddr`.
* Set configuration field `proxyMode` to `local` (case insensitive).
Run the command below.
```shell
nohup sh mqproxy &
```
The command will not only run `Proxy`, but also run `Broker`. It requires `Namesrv` only and there's no need for
extra `Broker`.
+54 -4
View File
@@ -108,12 +108,12 @@
<javassist.version>3.20.0-GA</javassist.version>
<jna.version>4.2.2</jna.version>
<commons-lang3.version>3.4</commons-lang3.version>
<commons-io.version>2.6</commons-io.version>
<commons-io.version>2.7</commons-io.version>
<guava.version>31.0.1-jre</guava.version>
<openmessaging.version>0.3.1-alpha</openmessaging.version>
<log4j.version>1.2.17</log4j.version>
<snakeyaml.version>1.30</snakeyaml.version>
<commons-codec.version>1.9</commons-codec.version>
<commons-codec.version>1.13</commons-codec.version>
<logging-log4j.version>2.17.1</logging-log4j.version>
<commons-validator.version>1.7</commons-validator.version>
<zstd-jni.version>1.5.2-2</zstd-jni.version>
@@ -124,10 +124,13 @@
<annotations-api.version>6.0.53</annotations-api.version>
<extra-enforcer-rules.version>1.0-beta-4</extra-enforcer-rules.version>
<concurrentlinkedhashmap-lru.version>1.4.2</concurrentlinkedhashmap-lru.version>
<rocketmq-proto.version>2.0.0</rocketmq-proto.version>
<grpc.version>1.45.0</grpc.version>
<protobuf-java-util.version>3.20.1</protobuf-java-util.version>
<!-- Test dependencies -->
<junit.version>4.13.2</junit.version>
<assertj-core.version>2.6.0</assertj-core.version>
<assertj-core.version>3.22.0</assertj-core.version>
<mockito-core.version>3.10.0</mockito-core.version>
<awaitility.version>4.1.0</awaitility.version>
<truth.version>0.30</truth.version>
@@ -156,7 +159,6 @@
<!-- Exclude all generated code -->
<sonar.jacoco.itReportPath>${project.basedir}/../test/target/jacoco-it.exec</sonar.jacoco.itReportPath>
<sonar.exclusions>file:**/generated-sources/**,**/test/**</sonar.exclusions>
</properties>
<modules>
@@ -177,6 +179,7 @@
<module>example</module>
<module>container</module>
<module>controller</module>
<module>proxy</module>
</modules>
<build>
@@ -285,9 +288,11 @@
<excludes>
<exclude>.gitignore</exclude>
<exclude>.travis.yml</exclude>
<exclude>README.md</exclude>
<exclude>CONTRIBUTING.md</exclude>
<exclude>bin/README.md</exclude>
<exclude>.github/**</exclude>
<exclude>src/test/resources/**</exclude>
<exclude>src/test/resources/certs/*</exclude>
<exclude>src/test/**/*.log</exclude>
<exclude>src/test/resources/META-INF/service/*</exclude>
@@ -296,6 +301,7 @@
<exclude>*/*.iml</exclude>
<exclude>docs/**</exclude>
<exclude>localbin/**</exclude>
<exclude>conf/rmq-proxy.json</exclude>
</excludes>
</configuration>
</plugin>
@@ -482,6 +488,11 @@
<artifactId>rocketmq-controller</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>rocketmq-proto</artifactId>
<version>${rocketmq-proto.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>rocketmq-client</artifactId>
@@ -557,6 +568,11 @@
<artifactId>rocketmq-example</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>rocketmq-proxy</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.openmessaging.storage</groupId>
<artifactId>dledger</artifactId>
@@ -730,6 +746,37 @@
<artifactId>truth</artifactId>
<version>${truth.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-services</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-testing</artifactId>
<version>${grpc.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>${protobuf-java-util.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -738,16 +785,19 @@
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
+60
View File
@@ -0,0 +1,60 @@
rocketmq-proxy
--------
## Introduction
`RocketMQ Proxy` is a stateless component that makes full use of the newly introduced `pop` consumption mechanism to
achieve stateless consumption behavior. `gRPC` protocol is supported by `Proxy` now and all the message types
including `normal`, `fifo`, `transaction` and `delay` are supported via `pop` consumption mode. `Proxy` will translate
incoming traffic into customized `Remoting` protocol to access `Broker` and `Namesrv`.
`Proxy` also handles SSL, authorization/authentication and logging/tracing/metrics and is in charge of connection
management and traffic governance.
### Multi-language support.
`gRPC` combined with `Protocol Buffer` makes it easy to implement clients with both `java` and other programming
languages while the server side doesn't need extra work to support different programming languages.
See [rocketmq-clients](https://github.com/apache/rocketmq-clients) for more information.
### Multi-protocol support.
With `Proxy` served as a traffic interface, it's convenient to implement multiple protocols upon proxy. `gRPC` protocol
is implemented first and the customized `Remoting` protocol will be implemented later. HTTP/1.1 will also be taken into
consideration.
## Architecture
`RocketMQ Proxy` has two deployment modes: `Cluster` mode and `Local` mode. With both modes, `Pop` mode is natively
supported in `Proxy`.
### `Cluster` mode
While in `Cluster` mode, `Proxy` is an independent cluster that communicates with `Broker` with remote procedure call.
In this scenario, `Proxy` acts as a stateless computing component while `Broker` is a stateful component with local
storage. This form of deployment introduces the architecture of separation of computing and storage for RocketMQ.
Due to the separation of computing and storage, `RocketMQ Proxy` can be scaled out indefinitely in `Cluster` mode to
handle traffic peak while `Broker` can focus on storage engine and high availability.
![](../docs/en/images/rocketmq_proxy_cluster_mode.png)
### `Local` mode
`Proxy` in `Local` mode has more similarity with `RocketMQ` 4.x version, which is easily deployed or upgraded for
current RocketMQ users. With `Local` mode, `Proxy` deployed with `Broker` in the same process with inter-process
communication so the network overhead is reduced compared to `Cluster` mode.
![](../docs/en/images/rocketmq_proxy_local_mode.png)
## Deploy guide
See [Proxy Deployment](../docs/en/proxy/deploy_guide.md)
## Related
* [rocketmq-apis](https://github.com/apache/rocketmq-apis): Common communication protocol between server and client.
* [rocketmq-clients](https://github.com/apache/rocketmq-clients): Collection of Polyglot Clients for Apache RocketMQ.
* [RIP-37: New and Unified APIs](https://shimo.im/docs/m5kv92OeRRU8olqX): RocketMQ proposal of new and unified APIs
crossing different languages.
* [RIP-39: Support gRPC protocol](https://shimo.im/docs/gXqmeEPYgdUw5bqo): RocketMQ proposal of gRPC protocol support.
+89
View File
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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>
<artifactId>rocketmq-all</artifactId>
<groupId>org.apache.rocketmq</groupId>
<version>5.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>rocketmq-proxy</artifactId>
<name>rocketmq-proxy ${project.version}</name>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-proto</artifactId>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-broker</artifactId>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-common</artifactId>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-services</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,57 @@
/*
* 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.proxy;
public enum ProxyMode {
LOCAL("LOCAL"),
CLUSTER("CLUSTER");
private final String mode;
ProxyMode(String mode) {
this.mode = mode;
}
public static boolean isClusterMode(String mode) {
if (mode == null) {
return false;
}
return CLUSTER.mode.equals(mode.toUpperCase());
}
public static boolean isClusterMode(ProxyMode mode) {
if (mode == null) {
return false;
}
return CLUSTER.equals(mode);
}
public static boolean isLocalMode(String mode) {
if (mode == null) {
return false;
}
return LOCAL.mode.equals(mode.toUpperCase());
}
public static boolean isLocalMode(ProxyMode mode) {
if (mode == null) {
return false;
}
return LOCAL.equals(mode);
}
}
@@ -0,0 +1,173 @@
/*
* 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.proxy;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import java.util.Date;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.BrokerStartup;
import org.apache.rocketmq.client.log.ClientLogger;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.thread.ThreadPoolMonitor;
import org.apache.rocketmq.logging.InternalLogger;
import org.apache.rocketmq.logging.InternalLoggerFactory;
import org.apache.rocketmq.proxy.common.AbstractStartAndShutdown;
import org.apache.rocketmq.proxy.common.StartAndShutdown;
import org.apache.rocketmq.proxy.config.ConfigurationManager;
import org.apache.rocketmq.proxy.config.ProxyConfig;
import org.apache.rocketmq.proxy.grpc.GrpcServer;
import org.apache.rocketmq.proxy.grpc.GrpcServerBuilder;
import org.apache.rocketmq.proxy.grpc.v2.GrpcMessagingApplication;
import org.apache.rocketmq.proxy.processor.DefaultMessagingProcessor;
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
import org.slf4j.LoggerFactory;
public class ProxyStartup {
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
private static final ProxyStartAndShutdown PROXY_START_AND_SHUTDOWN = new ProxyStartAndShutdown();
private static class ProxyStartAndShutdown extends AbstractStartAndShutdown {
@Override
public void appendStartAndShutdown(StartAndShutdown startAndShutdown) {
super.appendStartAndShutdown(startAndShutdown);
}
}
public static void main(String[] args) {
try {
ConfigurationManager.initEnv();
initLogger();
ConfigurationManager.intConfig();
// init thread pool monitor for proxy.
initThreadPoolMonitor();
ThreadPoolExecutor executor = createServerExecutor();
MessagingProcessor messagingProcessor = createMessagingProcessor();
// create grpcServer
GrpcServer grpcServer = GrpcServerBuilder.newBuilder(executor, ConfigurationManager.getProxyConfig().getGrpcServerPort())
.addService(createServiceProcessor(messagingProcessor))
.configInterceptor()
.build();
PROXY_START_AND_SHUTDOWN.appendStartAndShutdown(grpcServer);
// start servers one by one.
PROXY_START_AND_SHUTDOWN.start();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("try to shutdown server");
try {
PROXY_START_AND_SHUTDOWN.shutdown();
} catch (Exception e) {
log.error("err when shutdown rocketmq-proxy", e);
}
}));
} catch (Exception e) {
System.err.println("find an unexpect err." + e);
e.printStackTrace();
log.error("find an unexpect err.", e);
System.exit(1);
}
System.out.printf("%s%n", new Date() + " rocketmq-proxy startup successfully");
log.info(new Date() + " rocketmq-proxy startup successfully");
}
private static MessagingProcessor createMessagingProcessor() {
String proxyModeStr = ConfigurationManager.getProxyConfig().getProxyMode();
MessagingProcessor messagingProcessor;
if (ProxyMode.isClusterMode(proxyModeStr)) {
messagingProcessor = DefaultMessagingProcessor.createForClusterMode();
} else if (ProxyMode.isLocalMode(proxyModeStr)) {
BrokerController brokerController = createBrokerController();
StartAndShutdown brokerControllerWrapper = new StartAndShutdown() {
@Override
public void start() throws Exception {
brokerController.start();
}
@Override
public void shutdown() throws Exception {
brokerController.shutdown();
}
};
PROXY_START_AND_SHUTDOWN.appendStartAndShutdown(brokerControllerWrapper);
messagingProcessor = DefaultMessagingProcessor.createForLocalMode(brokerController);
} else {
throw new IllegalArgumentException("try to start grpc server with wrong mode, use 'local' or 'cluster'");
}
PROXY_START_AND_SHUTDOWN.appendStartAndShutdown(messagingProcessor);
return messagingProcessor;
}
private static GrpcMessagingApplication createServiceProcessor(MessagingProcessor messagingProcessor) {
GrpcMessagingApplication application = GrpcMessagingApplication.create(messagingProcessor);
PROXY_START_AND_SHUTDOWN.appendStartAndShutdown(application);
return application;
}
private static BrokerController createBrokerController() {
String[] brokerStartupArgs = new String[] {"-c", ConfigurationManager.getProxyConfig().getBrokerConfigPath()};
return BrokerStartup.createBrokerController(brokerStartupArgs);
}
public static ThreadPoolExecutor createServerExecutor() {
ProxyConfig config = ConfigurationManager.getProxyConfig();
int threadPoolNums = config.getGrpcThreadPoolNums();
int threadPoolQueueCapacity = config.getGrpcThreadPoolQueueCapacity();
ThreadPoolExecutor executor = ThreadPoolMonitor.createAndMonitor(
threadPoolNums,
threadPoolNums,
1, TimeUnit.MINUTES,
"GrpcRequestExecutorThread",
threadPoolQueueCapacity
);
PROXY_START_AND_SHUTDOWN.appendShutdown(executor::shutdown);
return executor;
}
public static void initThreadPoolMonitor() {
ProxyConfig config = ConfigurationManager.getProxyConfig();
ThreadPoolMonitor.config(
InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME),
InternalLoggerFactory.getLogger(LoggerName.PROXY_WATER_MARK_LOGGER_NAME),
config.isEnablePrintJstack(), config.getPrintJstackInMillis(),
config.getPrintThreadPoolStatusInMillis());
ThreadPoolMonitor.init();
}
public static void initLogger() throws JoranException {
System.setProperty("brokerLogDir", "");
System.setProperty(ClientLogger.CLIENT_LOG_USESLF4J, "true");
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(lc);
lc.reset();
//https://logback.qos.ch/manual/configuration.html
lc.setPackagingDataEnabled(false);
configurator.doConfigure(ConfigurationManager.getProxyHome() + "/conf/logback_proxy.xml");
}
}
@@ -0,0 +1,54 @@
/*
* 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.proxy.common;
import com.google.common.cache.CacheLoader;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListenableFutureTask;
import java.util.concurrent.ThreadPoolExecutor;
import javax.annotation.Nonnull;
public abstract class AbstractCacheLoader<K, V> extends CacheLoader<K, V> {
private final ThreadPoolExecutor cacheRefreshExecutor;
public AbstractCacheLoader(ThreadPoolExecutor cacheRefreshExecutor) {
this.cacheRefreshExecutor = cacheRefreshExecutor;
}
@Override
public ListenableFuture<V> reload(@Nonnull K key, @Nonnull V oldValue) throws Exception {
ListenableFutureTask<V> task = ListenableFutureTask.create(() -> {
try {
return getDirectly(key);
} catch (Exception e) {
onErr(key, e);
return oldValue;
}
});
cacheRefreshExecutor.execute(task);
return task;
}
@Override
public V load(@Nonnull K key) throws Exception {
return getDirectly(key);
}
protected abstract V getDirectly(K key) throws Exception;
protected abstract void onErr(K key, Exception e);
}
@@ -0,0 +1,72 @@
/*
* 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.proxy.common;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public abstract class AbstractStartAndShutdown implements StartAndShutdown {
protected List<StartAndShutdown> startAndShutdownList = new CopyOnWriteArrayList<>();
protected void appendStartAndShutdown(StartAndShutdown startAndShutdown) {
this.startAndShutdownList.add(startAndShutdown);
}
@Override
public void start() throws Exception {
for (StartAndShutdown startAndShutdown : startAndShutdownList) {
startAndShutdown.start();
}
}
@Override
public void shutdown() throws Exception {
int index = startAndShutdownList.size() - 1;
for (; index >= 0; index--) {
startAndShutdownList.get(index).shutdown();
}
}
public void appendStart(Start start) {
this.appendStartAndShutdown(new StartAndShutdown() {
@Override
public void shutdown() throws Exception {
}
@Override
public void start() throws Exception {
start.start();
}
});
}
public void appendShutdown(Shutdown shutdown) {
this.appendStartAndShutdown(new StartAndShutdown() {
@Override
public void shutdown() throws Exception {
shutdown.shutdown();
}
@Override
public void start() throws Exception {
}
});
}
}
@@ -0,0 +1,71 @@
/*
* 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.proxy.common;
import com.google.common.net.HostAndPort;
import java.util.Objects;
public class Address {
public enum AddressScheme {
IPv4,
IPv6,
DOMAIN_NAME,
UNRECOGNIZED
}
private AddressScheme addressScheme;
private HostAndPort hostAndPort;
public Address(AddressScheme addressScheme, HostAndPort hostAndPort) {
this.addressScheme = addressScheme;
this.hostAndPort = hostAndPort;
}
public AddressScheme getAddressScheme() {
return addressScheme;
}
public void setAddressScheme(AddressScheme addressScheme) {
this.addressScheme = addressScheme;
}
public HostAndPort getHostAndPort() {
return hostAndPort;
}
public void setHostAndPort(HostAndPort hostAndPort) {
this.hostAndPort = hostAndPort;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Address address = (Address) o;
return addressScheme == address.addressScheme && Objects.equals(hostAndPort, address.hostAndPort);
}
@Override
public int hashCode() {
return Objects.hash(addressScheme, hostAndPort);
}
}
@@ -0,0 +1,28 @@
/*
* 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.proxy.common;
public class ContextVariable {
public static final String REMOTE_ADDRESS = "remote-address";
public static final String LOCAL_ADDRESS = "local-address";
public static final String CLIENT_ID = "client-id";
public static final String LANGUAGE = "language";
public static final String CLIENT_VERSION = "client-version";
public static final String REMAINING_MS = "remaining-ms";
public static final String ACTION = "action";
}
@@ -0,0 +1,131 @@
/*
* 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.proxy.common;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
public class MessageReceiptHandle {
private final String group;
private final String topic;
private final int queueId;
private final String messageId;
private final long queueOffset;
private final String originalReceiptHandle;
private final long timestamp;
private final int reconsumeTimes;
private final long expectInvisibleTime;
private String receiptHandle;
public MessageReceiptHandle(String group, String topic, int queueId, String receiptHandle, String messageId,
long queueOffset, int reconsumeTimes, long expectInvisibleTime) {
this.group = group;
this.topic = topic;
this.queueId = queueId;
this.receiptHandle = receiptHandle;
this.originalReceiptHandle = receiptHandle;
this.messageId = messageId;
this.queueOffset = queueOffset;
this.reconsumeTimes = reconsumeTimes;
this.expectInvisibleTime = expectInvisibleTime;
this.timestamp = System.currentTimeMillis();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MessageReceiptHandle handle = (MessageReceiptHandle) o;
return queueId == handle.queueId && queueOffset == handle.queueOffset && timestamp == handle.timestamp
&& reconsumeTimes == handle.reconsumeTimes && expectInvisibleTime == handle.expectInvisibleTime
&& Objects.equal(group, handle.group) && Objects.equal(topic, handle.topic)
&& Objects.equal(messageId, handle.messageId) && Objects.equal(originalReceiptHandle, handle.originalReceiptHandle)
&& Objects.equal(receiptHandle, handle.receiptHandle);
}
@Override
public int hashCode() {
return Objects.hashCode(group, topic, queueId, messageId, queueOffset, originalReceiptHandle, timestamp,
reconsumeTimes, expectInvisibleTime, receiptHandle);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("group", group)
.add("topic", topic)
.add("queueId", queueId)
.add("messageId", messageId)
.add("queueOffset", queueOffset)
.add("originalReceiptHandle", originalReceiptHandle)
.add("timestamp", timestamp)
.add("reconsumeTimes", reconsumeTimes)
.add("expectInvisibleTime", expectInvisibleTime)
.add("receiptHandle", receiptHandle)
.toString();
}
public String getGroup() {
return group;
}
public String getTopic() {
return topic;
}
public int getQueueId() {
return queueId;
}
public String getReceiptHandle() {
return receiptHandle;
}
public String getOriginalReceiptHandle() {
return originalReceiptHandle;
}
public String getMessageId() {
return messageId;
}
public long getQueueOffset() {
return queueOffset;
}
public int getReconsumeTimes() {
return reconsumeTimes;
}
public long getTimestamp() {
return timestamp;
}
public long getExpectInvisibleTime() {
return expectInvisibleTime;
}
public void update(String receiptHandle) {
this.receiptHandle = receiptHandle;
}
}
@@ -0,0 +1,115 @@
/*
* 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.proxy.common;
import java.util.HashMap;
import java.util.Map;
public class ProxyContext {
public static final String INNER_ACTION_PREFIX = "Inner";
private final Map<String, Object> value = new HashMap<>();
public static ProxyContext create() {
return new ProxyContext();
}
public static ProxyContext createForInner(String actionName) {
return create().setAction(INNER_ACTION_PREFIX + actionName);
}
public static ProxyContext createForInner(Class<?> clazz) {
return createForInner(clazz.getSimpleName());
}
public Map<String, Object> getValue() {
return this.value;
}
public ProxyContext withVal(String key, Object val) {
this.value.put(key, val);
return this;
}
public <T> T getVal(String key) {
return (T) this.value.get(key);
}
public ProxyContext setLocalAddress(String localAddress) {
this.withVal(ContextVariable.LOCAL_ADDRESS, localAddress);
return this;
}
public String getLocalAddress() {
return this.getVal(ContextVariable.LOCAL_ADDRESS);
}
public ProxyContext setRemoteAddress(String remoteAddress) {
this.withVal(ContextVariable.REMOTE_ADDRESS, remoteAddress);
return this;
}
public String getRemoteAddress() {
return this.getVal(ContextVariable.REMOTE_ADDRESS);
}
public ProxyContext setClientID(String clientID) {
this.withVal(ContextVariable.CLIENT_ID, clientID);
return this;
}
public String getClientID() {
return this.getVal(ContextVariable.CLIENT_ID);
}
public ProxyContext setLanguage(String language) {
this.withVal(ContextVariable.LANGUAGE, language);
return this;
}
public String getLanguage() {
return this.getVal(ContextVariable.LANGUAGE);
}
public ProxyContext setClientVersion(String clientVersion) {
this.withVal(ContextVariable.CLIENT_VERSION, clientVersion);
return this;
}
public String getClientVersion() {
return this.getVal(ContextVariable.CLIENT_VERSION);
}
public ProxyContext setRemainingMs(Long remainingMs) {
this.withVal(ContextVariable.REMAINING_MS, remainingMs);
return this;
}
public Long getRemainingMs() {
return this.getVal(ContextVariable.REMAINING_MS);
}
public ProxyContext setAction(String action) {
this.withVal(ContextVariable.ACTION, action);
return this;
}
public String getAction() {
return this.getVal(ContextVariable.ACTION);
}
}
@@ -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.
*/
package org.apache.rocketmq.proxy.common;
public class ProxyException extends RuntimeException {
private final ProxyExceptionCode code;
public ProxyException(ProxyExceptionCode code, String message) {
super(message);
this.code = code;
}
public ProxyException(ProxyExceptionCode code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
public ProxyExceptionCode getCode() {
return code;
}
}
@@ -0,0 +1,26 @@
/*
* 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.proxy.common;
public enum ProxyExceptionCode {
INVALID_BROKER_NAME,
TRANSACTION_DATA_NOT_FOUND,
FORBIDDEN,
MESSAGE_PROPERTY_CONFLICT_WITH_TYPE,
INVALID_RECEIPT_HANDLE,
INTERNAL_SERVER_ERROR,
}
@@ -0,0 +1,76 @@
/*
* 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.proxy.common;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
public class ReceiptHandleGroup {
private final Map<String /* msgID */, Map<String /* original handle */, MessageReceiptHandle>> receiptHandleMap = new ConcurrentHashMap<>();
public void put(String msgID, String handle, MessageReceiptHandle value) {
Map<String, MessageReceiptHandle> handleMap = receiptHandleMap.computeIfAbsent(msgID, msgIDKey -> new ConcurrentHashMap<>());
handleMap.put(handle, value);
}
public boolean isEmpty() {
return this.receiptHandleMap.isEmpty();
}
public MessageReceiptHandle remove(String msgID, String handle) {
AtomicReference<MessageReceiptHandle> resRef = new AtomicReference<>();
receiptHandleMap.computeIfPresent(msgID, (msgIDKey, handleMap) -> {
resRef.set(handleMap.remove(handle));
if (handleMap.isEmpty()) {
return null;
}
return handleMap;
});
return resRef.get();
}
public MessageReceiptHandle removeOne(String msgID) {
AtomicReference<MessageReceiptHandle> resRef = new AtomicReference<>();
receiptHandleMap.computeIfPresent(msgID, (msgIDKey, handleMap) -> {
if (handleMap.isEmpty()) {
return null;
}
Optional<String> handleKey = handleMap.keySet().stream().findAny();
resRef.set(handleMap.remove(handleKey.get()));
if (handleMap.isEmpty()) {
return null;
}
return handleMap;
});
return resRef.get();
}
public interface DataScanner {
void onData(String msgID, String handle, MessageReceiptHandle receiptHandle);
}
public void scan(DataScanner scanner) {
this.receiptHandleMap.forEach((msgID, handleMap) -> {
handleMap.forEach((handleStr, v) -> {
scanner.onData(msgID, handleStr, v);
});
});
}
}
@@ -0,0 +1,22 @@
/*
* 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.proxy.common;
public interface Shutdown {
void shutdown() throws Exception;
}
@@ -0,0 +1,22 @@
/*
* 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.proxy.common;
public interface Start {
void start() throws Exception;
}
@@ -0,0 +1,21 @@
/*
* 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.proxy.common;
public interface StartAndShutdown extends Start, Shutdown {
}
@@ -0,0 +1,45 @@
/*
* 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.proxy.common.utils;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
public class ExceptionUtils {
public static Throwable getRealException(Throwable throwable) {
if (throwable instanceof CompletionException || throwable instanceof ExecutionException) {
if (throwable.getCause() != null) {
throwable = throwable.getCause();
}
}
return throwable;
}
public static String getErrorDetailMessage(Throwable t) {
if (t == null) {
return null;
}
StringBuilder sb = new StringBuilder();
sb.append(t.getMessage()).append(". ").append(t.getClass().getSimpleName());
if (t.getStackTrace().length > 0) {
sb.append(". ").append(t.getStackTrace()[0]);
}
return sb.toString();
}
}

Some files were not shown because too many files have changed in this diff Show More