mirror of
https://github.com/apache/rocketmq.git
synced 2026-09-21 13:49:50 +08:00
refactor: [ISSUE #3949] v2 support
This commit is contained in:
@@ -248,7 +248,7 @@ public class BrokerConfig extends BrokerIdentity {
|
||||
* Transaction message check interval.
|
||||
*/
|
||||
@ImportantField
|
||||
private long transactionCheckInterval = 60 * 1000;
|
||||
private long transactionCheckInterval = 3 * 1000;
|
||||
|
||||
/**
|
||||
* Acl feature switch
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -36,11 +36,9 @@ 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.GrpcMessagingProcessor;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ProxyMode;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.ClusterGrpcService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.GrpcForwardService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.LocalGrpcService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.GrpcMessagingApplication;
|
||||
import org.apache.rocketmq.proxy.service.ServiceManager;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ProxyStartup {
|
||||
@@ -65,9 +63,11 @@ public class ProxyStartup {
|
||||
|
||||
ThreadPoolExecutor executor = createServerExecutor();
|
||||
|
||||
ServiceManager serviceManager = createServiceManager(null);
|
||||
|
||||
// create grpcServer
|
||||
GrpcServer grpcServer = GrpcServerBuilder.newBuilder(executor)
|
||||
.addService(createServiceProcessor())
|
||||
.addService(createServiceProcessor(serviceManager))
|
||||
.configInterceptor()
|
||||
.build();
|
||||
PROXY_START_AND_SHUTDOWN.appendStartAndShutdown(grpcServer);
|
||||
@@ -94,11 +94,12 @@ public class ProxyStartup {
|
||||
log.info(new Date() + " rmq-proxy startup successfully");
|
||||
}
|
||||
|
||||
private static GrpcMessagingProcessor createServiceProcessor() {
|
||||
GrpcForwardService grpcService;
|
||||
private static ServiceManager createServiceManager(RPCHook rpcHook) {
|
||||
String proxyModeStr = ConfigurationManager.getProxyConfig().getProxyMode();
|
||||
ServiceManager serviceManager;
|
||||
|
||||
if (ProxyMode.isClusterMode(proxyModeStr)) {
|
||||
grpcService = new ClusterGrpcService();
|
||||
serviceManager = ServiceManager.createForClusterMode(rpcHook);
|
||||
} else if (ProxyMode.isLocalMode(proxyModeStr)) {
|
||||
BrokerController brokerController = createBrokerController();
|
||||
StartAndShutdown brokerControllerWrapper = new StartAndShutdown() {
|
||||
@@ -113,13 +114,18 @@ public class ProxyStartup {
|
||||
}
|
||||
};
|
||||
PROXY_START_AND_SHUTDOWN.appendStartAndShutdown(brokerControllerWrapper);
|
||||
grpcService = new LocalGrpcService(brokerController);
|
||||
serviceManager = ServiceManager.createForLocalMode(brokerController, rpcHook);
|
||||
} else {
|
||||
throw new IllegalArgumentException("try to start grpc server with wrong mode, use 'local' or 'cluster'");
|
||||
}
|
||||
PROXY_START_AND_SHUTDOWN.appendStartAndShutdown(serviceManager);
|
||||
return serviceManager;
|
||||
}
|
||||
|
||||
PROXY_START_AND_SHUTDOWN.appendStartAndShutdown(grpcService);
|
||||
return new GrpcMessagingProcessor(grpcService);
|
||||
private static GrpcMessagingApplication createServiceProcessor(ServiceManager serviceManager) {
|
||||
GrpcMessagingApplication application = GrpcMessagingApplication.create(serviceManager);
|
||||
PROXY_START_AND_SHUTDOWN.appendStartAndShutdown(application);
|
||||
return application;
|
||||
}
|
||||
|
||||
private static BrokerController createBrokerController() {
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* 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.channel;
|
||||
|
||||
import io.grpc.Context;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.config.ConfigurationManager;
|
||||
import org.apache.rocketmq.proxy.grpc.interceptor.InterceptorConstants;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.channel.GrpcClientChannel;
|
||||
|
||||
public class ChannelManager {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
protected final ConcurrentMap<String /* clientId */, SimpleChannel> clientIdChannelMap = new ConcurrentHashMap<>();
|
||||
protected final ConcurrentMap<String /* group */, Set<String>/* clientId */> groupClientIdMap = new ConcurrentHashMap<>();
|
||||
|
||||
public SimpleChannel createChannel(Context ctx) {
|
||||
return createChannel(ctx, anonymousChannelId(ctx));
|
||||
}
|
||||
|
||||
public SimpleChannel createChannel(Context ctx, String clientId) {
|
||||
return createChannel(ctx, clientId, ChannelManager::createSimpleChannelDirectly, SimpleChannel.class);
|
||||
}
|
||||
|
||||
public <T extends SimpleChannel> T createChannel(Context ctx, Function<Context, T> creator, Class<T> clazz) {
|
||||
return createChannel(ctx, anonymousChannelId(ctx, clazz.getName()), creator, clazz);
|
||||
}
|
||||
|
||||
public <T extends SimpleChannel> T createChannel(Context ctx, String clientId, Function<Context, T> creator, Class<T> clazz) {
|
||||
if (StringUtils.isBlank(clientId)) {
|
||||
log.warn("ClientId is unexpected null or empty");
|
||||
return creator.apply(ctx);
|
||||
}
|
||||
|
||||
clientIdChannelMap.computeIfAbsent(clientId, key -> creator.apply(ctx));
|
||||
|
||||
T channel = clazz.cast(clientIdChannelMap.get(clientId));
|
||||
channel.updateLastAccessTime();
|
||||
return channel;
|
||||
}
|
||||
|
||||
public <T extends SimpleChannel> T getChannel(String clientId, Class<T> clazz) {
|
||||
SimpleChannel channel = clientIdChannelMap.get(clientId);
|
||||
if (channel == null) {
|
||||
return null;
|
||||
}
|
||||
return clazz.cast(channel);
|
||||
}
|
||||
|
||||
public <T extends SimpleChannel> void setChannel(String clientId, T channel) {
|
||||
clientIdChannelMap.put(clientId, channel);
|
||||
}
|
||||
|
||||
public <T extends SimpleChannel> T removeChannel(String clientId, Class<T> clazz) {
|
||||
SimpleChannel channel = clientIdChannelMap.remove(clientId);
|
||||
if (channel == null) {
|
||||
return null;
|
||||
}
|
||||
return clazz.cast(channel);
|
||||
}
|
||||
|
||||
private String anonymousChannelId(Context ctx) {
|
||||
final String clientHost = InterceptorConstants.METADATA.get(ctx)
|
||||
.get(InterceptorConstants.REMOTE_ADDRESS);
|
||||
final String localAddress = InterceptorConstants.METADATA.get(ctx)
|
||||
.get(InterceptorConstants.LOCAL_ADDRESS);
|
||||
return clientHost + "@" + localAddress;
|
||||
}
|
||||
|
||||
private String anonymousChannelId(Context ctx, String className) {
|
||||
final String clientHost = InterceptorConstants.METADATA.get(ctx)
|
||||
.get(InterceptorConstants.REMOTE_ADDRESS);
|
||||
final String localAddress = InterceptorConstants.METADATA.get(ctx)
|
||||
.get(InterceptorConstants.LOCAL_ADDRESS);
|
||||
return className + "@" + clientHost + "@" + localAddress;
|
||||
}
|
||||
|
||||
public static SimpleChannel createSimpleChannelDirectly(Context ctx) {
|
||||
final String clientHost = InterceptorConstants.METADATA.get(ctx)
|
||||
.get(InterceptorConstants.REMOTE_ADDRESS);
|
||||
final String localAddress = InterceptorConstants.METADATA.get(ctx)
|
||||
.get(InterceptorConstants.LOCAL_ADDRESS);
|
||||
return new SimpleChannel(null, clientHost, localAddress, ConfigurationManager.getProxyConfig().getChannelExpiredInSeconds());
|
||||
}
|
||||
|
||||
public void addGroupClientId(String group, String clientId) {
|
||||
groupClientIdMap.computeIfAbsent(group, k -> Collections.newSetFromMap(new ConcurrentHashMap<>()))
|
||||
.add(clientId);
|
||||
}
|
||||
|
||||
public List<String> getClientIdList(String group) {
|
||||
return new ArrayList<>(groupClientIdMap.get(group));
|
||||
}
|
||||
|
||||
public void onClientOffline(String clientId) {
|
||||
SimpleChannel simpleChannel = clientIdChannelMap.remove(clientId);
|
||||
if (simpleChannel == null) {
|
||||
return;
|
||||
}
|
||||
if (simpleChannel instanceof GrpcClientChannel) {
|
||||
GrpcClientChannel grpcClientChannel = (GrpcClientChannel) simpleChannel;
|
||||
groupClientIdMap.computeIfPresent(grpcClientChannel.getGroup(), (group, clientIds) -> {
|
||||
clientIds.remove(grpcClientChannel.getClientId());
|
||||
if (clientIds.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return clientIds;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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.channel;
|
||||
|
||||
import io.grpc.Context;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import org.apache.rocketmq.proxy.common.Cleaner;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.handler.ResponseHandler;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public abstract class InvocationChannel<R, W> extends SimpleChannel implements Cleaner {
|
||||
protected final ConcurrentMap<Integer, InvocationContext<R, W>> inFlightRequestMap;
|
||||
protected final ResponseHandler<R, W> handler;
|
||||
|
||||
public InvocationChannel(Context ctx, ResponseHandler<R, W> handler) {
|
||||
super(ChannelManager.createSimpleChannelDirectly(ctx));
|
||||
this.inFlightRequestMap = new ConcurrentHashMap<>();
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object msg) {
|
||||
if (msg instanceof RemotingCommand) {
|
||||
RemotingCommand responseCommand = (RemotingCommand) msg;
|
||||
InvocationContext<R, W> context = inFlightRequestMap.remove(responseCommand.getOpaque());
|
||||
if (null != context) {
|
||||
handler.handle(responseCommand, context);
|
||||
}
|
||||
inFlightRequestMap.remove(responseCommand.getOpaque());
|
||||
}
|
||||
return super.writeAndFlush(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable() {
|
||||
return inFlightRequestMap.size() > 0;
|
||||
}
|
||||
|
||||
public void registerInvocationContext(int opaque, InvocationContext<R, W> context) {
|
||||
inFlightRequestMap.put(opaque, context);
|
||||
}
|
||||
|
||||
public void eraseInvocationContext(int opaque) {
|
||||
inFlightRequestMap.remove(opaque);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clean() {
|
||||
Iterator<Map.Entry<Integer, InvocationContext<R, W>>> iterator = inFlightRequestMap.entrySet().iterator();
|
||||
int count = 0;
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<Integer, InvocationContext<R, W>> entry = iterator.next();
|
||||
if (entry.getValue().expired(expiredTimeSec)) {
|
||||
iterator.remove();
|
||||
count++;
|
||||
log.debug("An expired request is found, created time-point: {}, Request: {}",
|
||||
entry.getValue().getTimestamp(), entry.getValue().getRequest());
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
log.warn("[BUG] {} expired in-flight requests is cleaned.", count);
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-13
@@ -14,24 +14,40 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.proxy.common;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import com.google.common.net.HostAndPort;
|
||||
|
||||
public class TelemetryCommandManager {
|
||||
protected final ConcurrentMap<String, TelemetryCommandRecord> commandTable = new ConcurrentHashMap<>();
|
||||
protected final AtomicLong commandIdGenerator = new AtomicLong(0);
|
||||
public class Address {
|
||||
|
||||
public String putCommand(int opaque) {
|
||||
String nonce = String.valueOf(commandIdGenerator.incrementAndGet());
|
||||
commandTable.put(nonce, new TelemetryCommandRecord(nonce, opaque));
|
||||
return nonce;
|
||||
public enum AddressScheme {
|
||||
IPv4,
|
||||
IPv6,
|
||||
DOMAIN_NAME,
|
||||
UNRECOGNIZED
|
||||
}
|
||||
|
||||
public TelemetryCommandRecord getCommand(String commandId) {
|
||||
return commandTable.get(commandId);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 {
|
||||
private final Map<String, Object> value = new HashMap<>();
|
||||
|
||||
public static ProxyContext create() {
|
||||
return new ProxyContext();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
+11
-16
@@ -14,28 +14,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.proxy.common;
|
||||
|
||||
public class TelemetryCommandRecord {
|
||||
private final String nonce;
|
||||
private final Integer opaque;
|
||||
public class ProxyException extends RuntimeException {
|
||||
|
||||
public TelemetryCommandRecord(String nonce, int opaque) {
|
||||
this.nonce = nonce;
|
||||
this.opaque = opaque;
|
||||
private final ProxyExceptionCode code;
|
||||
|
||||
public ProxyException(ProxyExceptionCode code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public TelemetryCommandRecord(String nonce) {
|
||||
this.nonce = nonce;
|
||||
this.opaque = null;
|
||||
public ProxyException(ProxyExceptionCode code, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getNonce() {
|
||||
return nonce;
|
||||
}
|
||||
|
||||
public Integer getOpaque() {
|
||||
return opaque;
|
||||
public ProxyExceptionCode getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -14,8 +14,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.connector.transaction;
|
||||
package org.apache.rocketmq.proxy.common;
|
||||
|
||||
public interface TransactionStateChecker {
|
||||
void checkTransactionState(TransactionStateCheckRequest checkData);
|
||||
public enum ProxyExceptionCode {
|
||||
FORBIDDEN,
|
||||
RECEIPT_HANDLE_EXPIRED,
|
||||
INVALID_BROKER_NAME
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
package org.apache.rocketmq.proxy.config;
|
||||
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ProxyMode;
|
||||
import org.apache.rocketmq.proxy.ProxyMode;
|
||||
|
||||
public class ProxyConfig {
|
||||
public final static String CONFIG_FILE_NAME = "rmq-proxy.json";
|
||||
@@ -54,6 +54,9 @@ public class ProxyConfig {
|
||||
|
||||
private int channelExpiredInSeconds = 60;
|
||||
|
||||
private int rocketmqMQClientNum = 6;
|
||||
private double rocketmqMQClientWorkerFactor = 0.2f;
|
||||
|
||||
private int forwardConsumerNum = 2;
|
||||
private double forwardConsumerWorkerFactor = 0.2f;
|
||||
private int forwardProducerNum = 2;
|
||||
@@ -229,6 +232,22 @@ public class ProxyConfig {
|
||||
this.channelExpiredInSeconds = channelExpiredInSeconds;
|
||||
}
|
||||
|
||||
public int getRocketmqMQClientNum() {
|
||||
return rocketmqMQClientNum;
|
||||
}
|
||||
|
||||
public void setRocketmqMQClientNum(int rocketmqMQClientNum) {
|
||||
this.rocketmqMQClientNum = rocketmqMQClientNum;
|
||||
}
|
||||
|
||||
public double getRocketmqMQClientWorkerFactor() {
|
||||
return rocketmqMQClientWorkerFactor;
|
||||
}
|
||||
|
||||
public void setRocketmqMQClientWorkerFactor(double rocketmqMQClientWorkerFactor) {
|
||||
this.rocketmqMQClientWorkerFactor = rocketmqMQClientWorkerFactor;
|
||||
}
|
||||
|
||||
public int getForwardConsumerNum() {
|
||||
return forwardConsumerNum;
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* 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.connector;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import org.apache.rocketmq.proxy.connector.client.MQClientAPIExt;
|
||||
import org.apache.rocketmq.proxy.common.StartAndShutdown;
|
||||
import org.apache.rocketmq.proxy.connector.factory.ForwardClientManager;
|
||||
|
||||
public abstract class AbstractForwardClient implements StartAndShutdown {
|
||||
public static final long MAX_CONSUMER_TIMEOUT_MILLIS = Duration.ofSeconds(20).toMillis();
|
||||
public static final long DEFAULT_MQ_CLIENT_TIMEOUT = Duration.ofSeconds(3).toMillis();
|
||||
|
||||
private final ForwardClientManager clientFactory;
|
||||
private MQClientAPIExt[] clients;
|
||||
private final String gidPrefix;
|
||||
|
||||
public AbstractForwardClient(ForwardClientManager clientFactory, String gidPrefix) {
|
||||
this.clientFactory = clientFactory;
|
||||
this.gidPrefix = gidPrefix;
|
||||
}
|
||||
|
||||
protected abstract int getClientNum();
|
||||
|
||||
protected abstract MQClientAPIExt createNewClient(ForwardClientManager forwardClientFactory, String name);
|
||||
|
||||
protected String getNamePrefix() {
|
||||
return this.gidPrefix;
|
||||
}
|
||||
|
||||
protected MQClientAPIExt getClient() {
|
||||
if (clients.length == 1) {
|
||||
return this.clients[0];
|
||||
}
|
||||
int index = ThreadLocalRandom.current().nextInt(this.clients.length);
|
||||
return this.clients[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
int clientCount = getClientNum();
|
||||
this.clients = new MQClientAPIExt[clientCount];
|
||||
|
||||
for (int i = 0; i < clientCount; i++) {
|
||||
String name = getNamePrefix() + "N_" + i;
|
||||
clients[i] = createNewClient(clientFactory, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* 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.connector;
|
||||
|
||||
import org.apache.rocketmq.proxy.connector.factory.ForwardClientManager;
|
||||
import org.apache.rocketmq.proxy.connector.route.TopicRouteCache;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionHeartbeatRegisterService;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionStateChecker;
|
||||
import org.apache.rocketmq.proxy.common.AbstractStartAndShutdown;
|
||||
|
||||
public class ConnectorManager extends AbstractStartAndShutdown {
|
||||
protected ForwardClientManager forwardClientManager;
|
||||
protected DefaultForwardClient defaultForwardClient;
|
||||
protected ForwardProducer forwardProducer;
|
||||
protected ForwardReadConsumer forwardReadConsumer;
|
||||
protected ForwardWriteConsumer forwardWriteConsumer;
|
||||
|
||||
protected TopicRouteCache topicRouteCache;
|
||||
protected TransactionHeartbeatRegisterService transactionHeartbeatRegisterService;
|
||||
|
||||
public ConnectorManager(TransactionStateChecker transactionStateChecker) {
|
||||
this.init(transactionStateChecker);
|
||||
}
|
||||
|
||||
protected void init(TransactionStateChecker transactionStateChecker) {
|
||||
this.forwardClientManager = new ForwardClientManager(transactionStateChecker);
|
||||
this.defaultForwardClient = new DefaultForwardClient(this.forwardClientManager);
|
||||
this.forwardProducer = new ForwardProducer(this.forwardClientManager);
|
||||
this.forwardReadConsumer = new ForwardReadConsumer(this.forwardClientManager);
|
||||
this.forwardWriteConsumer = new ForwardWriteConsumer(this.forwardClientManager);
|
||||
|
||||
this.topicRouteCache = new TopicRouteCache(this.defaultForwardClient);
|
||||
this.transactionHeartbeatRegisterService = new TransactionHeartbeatRegisterService(this.forwardProducer, this.topicRouteCache);
|
||||
|
||||
this.appendStartAndShutdown(this.forwardClientManager);
|
||||
this.appendStartAndShutdown(this.defaultForwardClient);
|
||||
this.appendStartAndShutdown(this.forwardProducer);
|
||||
this.appendStartAndShutdown(this.forwardReadConsumer);
|
||||
this.appendStartAndShutdown(this.forwardWriteConsumer);
|
||||
this.appendStartAndShutdown(this.transactionHeartbeatRegisterService);
|
||||
}
|
||||
|
||||
public ForwardClientManager getForwardClientManager() {
|
||||
return forwardClientManager;
|
||||
}
|
||||
|
||||
public DefaultForwardClient getDefaultForwardClient() {
|
||||
return defaultForwardClient;
|
||||
}
|
||||
|
||||
public ForwardProducer getForwardProducer() {
|
||||
return forwardProducer;
|
||||
}
|
||||
|
||||
public ForwardReadConsumer getForwardReadConsumer() {
|
||||
return forwardReadConsumer;
|
||||
}
|
||||
|
||||
public ForwardWriteConsumer getForwardWriteConsumer() {
|
||||
return forwardWriteConsumer;
|
||||
}
|
||||
|
||||
public TopicRouteCache getTopicRouteCache() {
|
||||
return topicRouteCache;
|
||||
}
|
||||
|
||||
public TransactionHeartbeatRegisterService getTransactionHeartbeatRegisterService() {
|
||||
return transactionHeartbeatRegisterService;
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* 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.connector;
|
||||
|
||||
import io.grpc.Context;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.proxy.connector.client.MQClientAPIExt;
|
||||
import org.apache.rocketmq.common.protocol.header.GetConsumerListByGroupRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.proxy.config.ConfigurationManager;
|
||||
import org.apache.rocketmq.proxy.connector.factory.ForwardClientManager;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
|
||||
public class DefaultForwardClient extends AbstractForwardClient {
|
||||
private static final String CID_PREFIX = "CID_RMQ_PROXY_DEFAULT_";
|
||||
|
||||
public DefaultForwardClient(ForwardClientManager clientFactory) {
|
||||
super(clientFactory, CID_PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getClientNum() {
|
||||
return ConfigurationManager.getProxyConfig().getDefaultForwardClientNum();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MQClientAPIExt createNewClient(ForwardClientManager clientFactory, String name) {
|
||||
double workerFactor = ConfigurationManager.getProxyConfig().getDefaultForwardClientWorkerFactor();
|
||||
int threadCount = (int) Math.ceil(Runtime.getRuntime().availableProcessors() * workerFactor);
|
||||
|
||||
return clientFactory.getMQClient(name, threadCount);
|
||||
}
|
||||
|
||||
public CompletableFuture<List<String>> getConsumerListByGroup(
|
||||
Context ctx,
|
||||
String brokerAddr,
|
||||
GetConsumerListByGroupRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
) {
|
||||
return this.getClient().getConsumerListByGroupAsync(brokerAddr, requestHeader, timeoutMillis);
|
||||
}
|
||||
|
||||
public TopicRouteData getTopicRouteInfoFromNameServer(String topic)
|
||||
throws RemotingException, InterruptedException, MQClientException {
|
||||
return this.getTopicRouteInfoFromNameServer(topic, DEFAULT_MQ_CLIENT_TIMEOUT);
|
||||
}
|
||||
|
||||
public TopicRouteData getTopicRouteInfoFromNameServer(String topic, long timeoutMillis)
|
||||
throws RemotingException, InterruptedException, MQClientException {
|
||||
return this.getClient().getTopicRouteInfoFromNameServer(topic, timeoutMillis);
|
||||
}
|
||||
|
||||
public CompletableFuture<Long> getMaxOffset(Context ctx, String brokerAddr, String topic, int queueId) {
|
||||
return this.getMaxOffset(ctx, brokerAddr, topic, queueId, DEFAULT_MQ_CLIENT_TIMEOUT);
|
||||
}
|
||||
|
||||
public CompletableFuture<Long> getMaxOffset(
|
||||
Context ctx,
|
||||
String brokerAddr,
|
||||
String topic,
|
||||
int queueId,
|
||||
long timeoutMillis
|
||||
) {
|
||||
return this.getClient().getMaxOffsetAsync(brokerAddr, topic, queueId, timeoutMillis);
|
||||
}
|
||||
|
||||
public CompletableFuture<Long> searchOffset(
|
||||
Context ctx,
|
||||
String brokerAddr,
|
||||
String topic,
|
||||
int queueId,
|
||||
long timestamp
|
||||
) {
|
||||
return this.searchOffset(ctx, brokerAddr, topic, queueId, timestamp, DEFAULT_MQ_CLIENT_TIMEOUT);
|
||||
}
|
||||
|
||||
public CompletableFuture<Long> searchOffset(
|
||||
Context ctx,
|
||||
String brokerAddr,
|
||||
String topic,
|
||||
int queueId,
|
||||
long timestamp,
|
||||
long timeoutMillis
|
||||
) {
|
||||
return this.getClient().searchOffsetAsync(brokerAddr, topic, queueId, timestamp, timeoutMillis);
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* 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.connector;
|
||||
|
||||
import io.grpc.Context;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.common.protocol.ResponseCode;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.proxy.connector.client.MQClientAPIExt;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.client.producer.SendStatus;
|
||||
import org.apache.rocketmq.common.message.Message;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.EndTransactionRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.HeartbeatData;
|
||||
import org.apache.rocketmq.common.sysflag.MessageSysFlag;
|
||||
import org.apache.rocketmq.proxy.config.ConfigurationManager;
|
||||
import org.apache.rocketmq.proxy.connector.factory.ForwardClientManager;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionId;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class ForwardProducer extends AbstractForwardClient {
|
||||
private static final String PID_PREFIX = "PID_RMQ_PROXY_PUBLISH_MESSAGE_";
|
||||
|
||||
public ForwardProducer(ForwardClientManager clientFactory) {
|
||||
super(clientFactory, PID_PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getClientNum() {
|
||||
return ConfigurationManager.getProxyConfig().getForwardProducerNum();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MQClientAPIExt createNewClient(ForwardClientManager clientFactory, String name) {
|
||||
double workerFactor = ConfigurationManager.getProxyConfig().getForwardProducerWorkerFactor();
|
||||
final int threadCount = (int) Math.ceil(Runtime.getRuntime().availableProcessors() * workerFactor);
|
||||
|
||||
return clientFactory.getTransactionalProducer(name, threadCount);
|
||||
}
|
||||
|
||||
public CompletableFuture<Integer> heartBeat(Context ctx, String brokerAddr, HeartbeatData heartbeatData) throws Exception {
|
||||
return this.heartBeat(ctx, brokerAddr, heartbeatData, DEFAULT_MQ_CLIENT_TIMEOUT);
|
||||
}
|
||||
public CompletableFuture<Integer> heartBeat(Context ctx, String brokerAddr, HeartbeatData heartbeatData, long timeout) throws Exception {
|
||||
return this.getClient().sendHeartbeatAsync(brokerAddr, heartbeatData, timeout);
|
||||
}
|
||||
|
||||
public void endTransaction(Context ctx, String brokerAddr, EndTransactionRequestHeader requestHeader) throws Exception {
|
||||
this.endTransaction(ctx, brokerAddr, requestHeader, DEFAULT_MQ_CLIENT_TIMEOUT);
|
||||
}
|
||||
|
||||
public void endTransaction(Context ctx, String brokerAddr, EndTransactionRequestHeader requestHeader, long timeoutMillis) throws Exception {
|
||||
this.getClient().endTransactionOneway(brokerAddr, requestHeader, "end transaction from rmq proxy", timeoutMillis);
|
||||
}
|
||||
|
||||
public CompletableFuture<SendResult> sendMessage(
|
||||
Context ctx,
|
||||
String address,
|
||||
String brokerName,
|
||||
List<Message> msg,
|
||||
SendMessageRequestHeader requestHeader
|
||||
) {
|
||||
return this.sendMessage(ctx, address, brokerName, msg, requestHeader, DEFAULT_MQ_CLIENT_TIMEOUT);
|
||||
}
|
||||
|
||||
public CompletableFuture<SendResult> sendMessage(
|
||||
Context ctx,
|
||||
String address,
|
||||
String brokerName,
|
||||
List<Message> msg,
|
||||
SendMessageRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
) {
|
||||
CompletableFuture<SendResult> future;
|
||||
if (msg.size() == 1) {
|
||||
future = this.getClient().sendMessageAsync(address, brokerName, msg.get(0), requestHeader, timeoutMillis);
|
||||
} else {
|
||||
future = this.getClient().sendMessageAsync(address, brokerName, msg, requestHeader, timeoutMillis);
|
||||
}
|
||||
return processSendMessageResponseFuture(ctx, address, requestHeader, future);
|
||||
}
|
||||
|
||||
protected CompletableFuture<SendResult> processSendMessageResponseFuture(
|
||||
Context ctx,
|
||||
String address,
|
||||
SendMessageRequestHeader requestHeader,
|
||||
CompletableFuture<SendResult> future) {
|
||||
return future.thenApply(sendResult -> {
|
||||
int tranType = MessageSysFlag.getTransactionValue(requestHeader.getSysFlag());
|
||||
if (SendStatus.SEND_OK.equals(sendResult.getSendStatus()) && tranType == MessageSysFlag.TRANSACTION_PREPARED_TYPE) {
|
||||
TransactionId transactionId = TransactionId.genByBrokerTransactionId(address, sendResult);
|
||||
sendResult.setTransactionId(transactionId.getProxyTransactionId());
|
||||
}
|
||||
return sendResult;
|
||||
});
|
||||
}
|
||||
|
||||
public CompletableFuture<RemotingCommand> sendMessageBackThenAckOrg(Context ctx, String brokerAddr, ConsumerSendMsgBackRequestHeader sendMsgBackRequestHeader,
|
||||
AckMessageRequestHeader ackMessageRequestHeader) {
|
||||
return sendMessageBackThenAckOrg(ctx, brokerAddr, sendMsgBackRequestHeader, ackMessageRequestHeader, DEFAULT_MQ_CLIENT_TIMEOUT,
|
||||
future -> {
|
||||
});
|
||||
}
|
||||
|
||||
public CompletableFuture<RemotingCommand> sendMessageBackThenAckOrg(Context ctx, String brokerAddr, ConsumerSendMsgBackRequestHeader sendMsgBackRequestHeader,
|
||||
AckMessageRequestHeader ackMessageRequestHeader, long timeoutMillis,
|
||||
Consumer<CompletableFuture<AckResult>> ackFutureSetter) {
|
||||
return this.sendMessageBack(ctx, brokerAddr, sendMsgBackRequestHeader, timeoutMillis).whenComplete((result, throwable) -> {
|
||||
if (throwable != null || ResponseCode.SUCCESS != result.getCode()) {
|
||||
return;
|
||||
}
|
||||
ackFutureSetter.accept(this.getClient().ackMessageAsync(brokerAddr, ackMessageRequestHeader, timeoutMillis));
|
||||
});
|
||||
}
|
||||
|
||||
public CompletableFuture<RemotingCommand> sendMessageBack(Context ctx, String brokerAddr, ConsumerSendMsgBackRequestHeader requestHeader) {
|
||||
return this.sendMessageBack(ctx, brokerAddr, requestHeader, DEFAULT_MQ_CLIENT_TIMEOUT);
|
||||
}
|
||||
|
||||
public CompletableFuture<RemotingCommand> sendMessageBack(Context ctx, String brokerAddr, ConsumerSendMsgBackRequestHeader requestHeader, long timeoutMillis) {
|
||||
return this.getClient().sendMessageBackAsync(brokerAddr, requestHeader, timeoutMillis);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* 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.connector;
|
||||
|
||||
import io.grpc.Context;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.client.consumer.PopResult;
|
||||
import org.apache.rocketmq.client.consumer.PullResult;
|
||||
import org.apache.rocketmq.proxy.connector.client.MQClientAPIExt;
|
||||
import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.PullMessageRequestHeader;
|
||||
import org.apache.rocketmq.proxy.config.ConfigurationManager;
|
||||
import org.apache.rocketmq.proxy.connector.factory.ForwardClientManager;
|
||||
|
||||
public class ForwardReadConsumer extends AbstractForwardClient {
|
||||
|
||||
private static final String CID_PREFIX = "CID_RMQ_PROXY_CONSUME_MESSAGE_";
|
||||
|
||||
public ForwardReadConsumer(ForwardClientManager clientFactory) {
|
||||
super(clientFactory, CID_PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getClientNum() {
|
||||
return ConfigurationManager.getProxyConfig().getForwardConsumerNum();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MQClientAPIExt createNewClient(ForwardClientManager clientFactory, String name) {
|
||||
double workerFactor = ConfigurationManager.getProxyConfig().getForwardConsumerWorkerFactor();
|
||||
final int threadCount = (int) Math.ceil(Runtime.getRuntime().availableProcessors() * workerFactor);
|
||||
|
||||
return clientFactory.getMQClient(name, threadCount);
|
||||
}
|
||||
|
||||
public CompletableFuture<PopResult> popMessage(Context ctx, String address, String brokerName,
|
||||
PopMessageRequestHeader requestHeader) {
|
||||
return this.popMessage(ctx, address, brokerName, requestHeader, DEFAULT_MQ_CLIENT_TIMEOUT);
|
||||
}
|
||||
|
||||
public CompletableFuture<PopResult> popMessage(
|
||||
Context ctx,
|
||||
String address,
|
||||
String brokerName,
|
||||
PopMessageRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
) {
|
||||
return this.getClient().popMessageAsync(address, brokerName, requestHeader, timeoutMillis);
|
||||
}
|
||||
|
||||
public CompletableFuture<PullResult> pullMessage(Context ctx, String address, PullMessageRequestHeader requestHeader) {
|
||||
return this.pullMessage(ctx, address, requestHeader, MAX_CONSUMER_TIMEOUT_MILLIS);
|
||||
}
|
||||
|
||||
public CompletableFuture<PullResult> pullMessage(Context ctx, String address, PullMessageRequestHeader requestHeader,
|
||||
long timeoutMillis) {
|
||||
return this.getClient().pullMessageAsync(address, requestHeader, timeoutMillis);
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* 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.connector;
|
||||
|
||||
import io.grpc.Context;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.proxy.connector.client.MQClientAPIExt;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.UpdateConsumerOffsetRequestHeader;
|
||||
import org.apache.rocketmq.proxy.config.ConfigurationManager;
|
||||
import org.apache.rocketmq.proxy.connector.factory.ForwardClientManager;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
|
||||
public class ForwardWriteConsumer extends AbstractForwardClient {
|
||||
|
||||
private static final String CID_PREFIX = "CID_RMQ_PROXY_DELETE_MESSAGE_";
|
||||
|
||||
public ForwardWriteConsumer(ForwardClientManager clientFactory) {
|
||||
super(clientFactory, CID_PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getClientNum() {
|
||||
return ConfigurationManager.getProxyConfig().getForwardConsumerNum();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MQClientAPIExt createNewClient(ForwardClientManager clientFactory, String name) {
|
||||
double workerFactor = ConfigurationManager.getProxyConfig().getForwardConsumerWorkerFactor();
|
||||
final int threadCount = (int) Math.ceil(Runtime.getRuntime().availableProcessors() * workerFactor);
|
||||
|
||||
return clientFactory.getMQClient(name, threadCount);
|
||||
}
|
||||
|
||||
public CompletableFuture<AckResult> ackMessage(Context ctx, String address, String messageId, AckMessageRequestHeader requestHeader) {
|
||||
return this.ackMessage(ctx, address, messageId, requestHeader, DEFAULT_MQ_CLIENT_TIMEOUT);
|
||||
}
|
||||
|
||||
public CompletableFuture<AckResult> ackMessage(
|
||||
Context ctx,
|
||||
String address,
|
||||
String messageId,
|
||||
AckMessageRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
) {
|
||||
return this.getClient().ackMessageAsync(address, requestHeader, timeoutMillis);
|
||||
}
|
||||
|
||||
public CompletableFuture<AckResult> changeInvisibleTimeAsync(
|
||||
Context ctx,
|
||||
String address,
|
||||
String brokerName,
|
||||
String messageId,
|
||||
ChangeInvisibleTimeRequestHeader requestHeader
|
||||
) {
|
||||
return this.changeInvisibleTimeAsync(ctx, address, brokerName, messageId, requestHeader, DEFAULT_MQ_CLIENT_TIMEOUT);
|
||||
}
|
||||
|
||||
public CompletableFuture<AckResult> changeInvisibleTimeAsync(
|
||||
Context ctx,
|
||||
String address,
|
||||
String brokerName,
|
||||
String messageId,
|
||||
ChangeInvisibleTimeRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
) {
|
||||
return this.getClient().changeInvisibleTimeAsync(address, brokerName, requestHeader, timeoutMillis);
|
||||
}
|
||||
|
||||
public void updateConsumerOffsetOneWay(
|
||||
Context ctx,
|
||||
String brokerAddr,
|
||||
UpdateConsumerOffsetRequestHeader header,
|
||||
long timeoutMillis
|
||||
) throws RemotingException, InterruptedException {
|
||||
this.getClient().updateConsumerOffsetOneWay(brokerAddr, header, timeoutMillis);
|
||||
}
|
||||
}
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* 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.connector.factory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
|
||||
|
||||
public abstract class AbstractClientManager<T> {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
protected final ScheduledExecutorService scheduledExecutorService;
|
||||
protected Map<String, T> cacheTable = new ConcurrentHashMap<>();
|
||||
protected RPCHook rpcHook;
|
||||
|
||||
public AbstractClientManager(ScheduledExecutorService scheduledExecutorService, RPCHook rpcHook) {
|
||||
this.scheduledExecutorService = scheduledExecutorService;
|
||||
this.rpcHook = rpcHook;
|
||||
}
|
||||
|
||||
protected abstract T newOne(String instanceName, RPCHook rpcHook, int bootstrapWorkerThreads);
|
||||
|
||||
protected abstract boolean tryStart(T t);
|
||||
|
||||
protected abstract void shutdown(T t);
|
||||
|
||||
protected static NettyClientConfig createNettyClientConfig(int bootstrapWorkerThreads) {
|
||||
NettyClientConfig nettyClientConfig = new NettyClientConfig();
|
||||
nettyClientConfig.setBootstrapWorkerThreads(bootstrapWorkerThreads);
|
||||
nettyClientConfig.setDisableNettyWorkerGroup(true);
|
||||
nettyClientConfig.setDisableCallbackExecutor(true);
|
||||
return nettyClientConfig;
|
||||
}
|
||||
|
||||
public T getOne(String instanceName, int bootstrapWorkerThreads) {
|
||||
if (cacheTable.containsKey(instanceName)) {
|
||||
return cacheTable.get(instanceName);
|
||||
}
|
||||
|
||||
T object;
|
||||
try {
|
||||
object = this.newOne(instanceName, rpcHook, bootstrapWorkerThreads);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
T old = cacheTable.putIfAbsent(instanceName, object);
|
||||
if (old == null) {
|
||||
if (!this.tryStart(object)) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
object = old;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
public void shutdownAll() {
|
||||
this.cacheTable.forEach((k, v) -> {
|
||||
try {
|
||||
this.shutdown(v);
|
||||
} catch (Exception e) {
|
||||
log.warn("try to shutdown client err.", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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.connector.factory;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.rocketmq.client.ClientConfig;
|
||||
import org.apache.rocketmq.client.impl.ClientRemotingProcessor;
|
||||
import org.apache.rocketmq.proxy.connector.client.MQClientAPIExt;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
|
||||
public abstract class AbstractMQClientFactory extends AbstractClientManager<MQClientAPIExt> {
|
||||
|
||||
public AbstractMQClientFactory(ScheduledExecutorService scheduledExecutorService, RPCHook rpcHook) {
|
||||
super(scheduledExecutorService, rpcHook);
|
||||
}
|
||||
|
||||
protected abstract ClientRemotingProcessor createClientRemotingProcessor();
|
||||
|
||||
@Override
|
||||
protected MQClientAPIExt newOne(String instanceName, RPCHook rpcHook, int bootstrapWorkerThreads) {
|
||||
ClientConfig clientConfig = new ClientConfig();
|
||||
clientConfig.setInstanceName(instanceName);
|
||||
|
||||
return new MQClientAPIExt(
|
||||
clientConfig,
|
||||
createNettyClientConfig(bootstrapWorkerThreads),
|
||||
createClientRemotingProcessor(),
|
||||
rpcHook
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean tryStart(MQClientAPIExt client) {
|
||||
if (!client.updateNameServerAddressList()) {
|
||||
this.scheduledExecutorService.scheduleAtFixedRate(
|
||||
client::fetchNameServerAddr,
|
||||
Duration.ofSeconds(10).toMillis(),
|
||||
Duration.ofMinutes(2).toMillis(),
|
||||
TimeUnit.MILLISECONDS
|
||||
);
|
||||
}
|
||||
client.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdown(MQClientAPIExt client) {
|
||||
client.shutdown();
|
||||
}
|
||||
}
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* 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.connector.factory;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.client.ClientConfig;
|
||||
import org.apache.rocketmq.proxy.connector.client.MQClientAPIExt;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionStateChecker;
|
||||
import org.apache.rocketmq.proxy.common.StartAndShutdown;
|
||||
import org.apache.rocketmq.proxy.config.ConfigurationManager;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
|
||||
public class ForwardClientManager implements StartAndShutdown {
|
||||
|
||||
protected RPCHook rpcHook;
|
||||
|
||||
protected MQClientFactory mqClientFactory;
|
||||
protected TransactionProducerFactory transactionalProducerFactory;
|
||||
|
||||
public ForwardClientManager(TransactionStateChecker transactionStateChecker) {
|
||||
this.init(transactionStateChecker);
|
||||
}
|
||||
|
||||
public ForwardClientManager(TransactionStateChecker transactionStateChecker, RPCHook rpcHook) {
|
||||
this.rpcHook = rpcHook;
|
||||
this.init(transactionStateChecker);
|
||||
}
|
||||
|
||||
protected void init(TransactionStateChecker transactionStateChecker) {
|
||||
System.setProperty(ClientConfig.SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY,
|
||||
System.getProperty(ClientConfig.SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, "false"));
|
||||
if (StringUtils.isEmpty(ConfigurationManager.getProxyConfig().getNameSrvDomain())) {
|
||||
String nameSrvAddr = ConfigurationManager.getProxyConfig().getNameSrvAddr();
|
||||
if (StringUtils.isEmpty(nameSrvAddr)) {
|
||||
throw new IllegalArgumentException("the address of namesrv cannot be empty");
|
||||
}
|
||||
System.setProperty(MixAll.NAMESRV_ADDR_PROPERTY, nameSrvAddr);
|
||||
} else {
|
||||
System.setProperty("rocketmq.namesrv.domain", ConfigurationManager.getProxyConfig().getNameSrvDomain());
|
||||
System.setProperty("rocketmq.namesrv.domain.subgroup", ConfigurationManager.getProxyConfig().getNameSrvDomainSubgroup());
|
||||
}
|
||||
|
||||
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(
|
||||
new ThreadFactoryBuilder().setNameFormat("ForwardClientFactoryScheduledThread" + "-%d").build()
|
||||
);
|
||||
this.mqClientFactory = new MQClientFactory(scheduledExecutorService, this.rpcHook);
|
||||
this.transactionalProducerFactory = new TransactionProducerFactory(scheduledExecutorService, this.rpcHook, transactionStateChecker);
|
||||
}
|
||||
|
||||
public MQClientAPIExt getMQClient(String instanceName, int bootstrapWorkerThreads) {
|
||||
return mqClientFactory.getOne(instanceName, bootstrapWorkerThreads);
|
||||
}
|
||||
|
||||
public MQClientAPIExt getTransactionalProducer(String instanceName, int bootstrapWorkerThreads) {
|
||||
return transactionalProducerFactory.getOne(instanceName, bootstrapWorkerThreads);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws Exception {
|
||||
this.mqClientFactory.shutdownAll();
|
||||
this.transactionalProducerFactory.shutdownAll();
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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.connector.factory;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import org.apache.rocketmq.client.impl.ClientRemotingProcessor;
|
||||
import org.apache.rocketmq.proxy.connector.processor.ProxyClientRemotingProcessor;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionStateChecker;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
|
||||
public class TransactionProducerFactory extends AbstractMQClientFactory {
|
||||
protected final TransactionStateChecker transactionStateChecker;
|
||||
|
||||
public TransactionProducerFactory(ScheduledExecutorService scheduledExecutorService,
|
||||
RPCHook rpcHook, TransactionStateChecker transactionStateChecker) {
|
||||
super(scheduledExecutorService, rpcHook);
|
||||
this.transactionStateChecker = transactionStateChecker;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientRemotingProcessor createClientRemotingProcessor() {
|
||||
return new ProxyClientRemotingProcessor(this.transactionStateChecker);
|
||||
}
|
||||
}
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* 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.connector.transaction;
|
||||
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
|
||||
public class TransactionStateCheckRequest {
|
||||
private String groupId;
|
||||
private Long tranStateTableOffset;
|
||||
private Long commitLogOffset;
|
||||
private String msgId;
|
||||
private TransactionId transactionId;
|
||||
private String brokerName;
|
||||
private MessageExt messageExt;
|
||||
|
||||
public TransactionStateCheckRequest(
|
||||
String groupId,
|
||||
Long tranStateTableOffset,
|
||||
Long commitLogOffset,
|
||||
String msgId,
|
||||
TransactionId transactionId,
|
||||
String brokerName,
|
||||
MessageExt messageExt
|
||||
) {
|
||||
this.groupId = groupId;
|
||||
this.tranStateTableOffset = tranStateTableOffset;
|
||||
this.commitLogOffset = commitLogOffset;
|
||||
this.msgId = msgId;
|
||||
this.transactionId = transactionId;
|
||||
this.brokerName = brokerName;
|
||||
this.messageExt = messageExt;
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public Long getTranStateTableOffset() {
|
||||
return tranStateTableOffset;
|
||||
}
|
||||
|
||||
public void setTranStateTableOffset(Long tranStateTableOffset) {
|
||||
this.tranStateTableOffset = tranStateTableOffset;
|
||||
}
|
||||
|
||||
public Long getCommitLogOffset() {
|
||||
return commitLogOffset;
|
||||
}
|
||||
|
||||
public void setCommitLogOffset(Long commitLogOffset) {
|
||||
this.commitLogOffset = commitLogOffset;
|
||||
}
|
||||
|
||||
public String getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
|
||||
public void setMsgId(String msgId) {
|
||||
this.msgId = msgId;
|
||||
}
|
||||
|
||||
public TransactionId getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public void setTransactionId(TransactionId transactionId) {
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
|
||||
public String getBrokerName() {
|
||||
return brokerName;
|
||||
}
|
||||
|
||||
public void setBrokerName(String brokerName) {
|
||||
this.brokerName = brokerName;
|
||||
}
|
||||
|
||||
public MessageExt getMessageExt() {
|
||||
return messageExt;
|
||||
}
|
||||
|
||||
public void setMessageExt(MessageExt messageExt) {
|
||||
this.messageExt = messageExt;
|
||||
}
|
||||
}
|
||||
-1
@@ -32,7 +32,6 @@ import org.apache.rocketmq.acl.AccessValidator;
|
||||
import org.apache.rocketmq.acl.common.AclException;
|
||||
import org.apache.rocketmq.acl.common.MetadataHeader;
|
||||
import org.apache.rocketmq.acl.plain.PlainAccessResource;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.RequestMapping;
|
||||
|
||||
public class AuthenticationInterceptor implements ServerInterceptor {
|
||||
private final List<AccessValidator> accessValidatorList;
|
||||
|
||||
@@ -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.grpc.interceptor;
|
||||
|
||||
import apache.rocketmq.v2.AckMessageRequest;
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationRequest;
|
||||
import apache.rocketmq.v2.EndTransactionRequest;
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueResponse;
|
||||
import apache.rocketmq.v2.HeartbeatRequest;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationRequest;
|
||||
import apache.rocketmq.v2.QueryAssignmentRequest;
|
||||
import apache.rocketmq.v2.QueryRouteRequest;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.SendMessageRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.rocketmq.common.protocol.RequestCode;
|
||||
|
||||
public class RequestMapping {
|
||||
private final static Map<String, Integer> REQUEST_MAP = new HashMap<String, Integer>() {
|
||||
{
|
||||
// v2
|
||||
put(QueryRouteRequest.getDescriptor().getFullName(), RequestCode.GET_ROUTEINFO_BY_TOPIC);
|
||||
put(HeartbeatRequest.getDescriptor().getFullName(), RequestCode.HEART_BEAT);
|
||||
put(SendMessageRequest.getDescriptor().getFullName(), RequestCode.SEND_MESSAGE_V2);
|
||||
put(QueryAssignmentRequest.getDescriptor().getFullName(), RequestCode.GET_ROUTEINFO_BY_TOPIC);
|
||||
put(ReceiveMessageRequest.getDescriptor().getFullName(), RequestCode.PULL_MESSAGE);
|
||||
put(AckMessageRequest.getDescriptor().getFullName(), RequestCode.UPDATE_CONSUMER_OFFSET);
|
||||
put(ForwardMessageToDeadLetterQueueResponse.getDescriptor().getFullName(), RequestCode.CONSUMER_SEND_MSG_BACK);
|
||||
put(EndTransactionRequest.getDescriptor().getFullName(), RequestCode.END_TRANSACTION);
|
||||
put(NotifyClientTerminationRequest.getDescriptor().getFullName(), RequestCode.UNREGISTER_CLIENT);
|
||||
put(ChangeInvisibleDurationRequest.getDescriptor().getFullName(), RequestCode.CONSUMER_SEND_MSG_BACK);
|
||||
}
|
||||
};
|
||||
|
||||
public static int map(String rpcFullName) {
|
||||
if (REQUEST_MAP.containsKey(rpcFullName)) {
|
||||
return REQUEST_MAP.get(rpcFullName);
|
||||
}
|
||||
return RequestCode.HEART_BEAT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.grpc.v2;
|
||||
|
||||
import io.grpc.Context;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.grpc.interceptor.InterceptorConstants;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcClientSettingsManager;
|
||||
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
|
||||
import org.apache.rocketmq.remoting.protocol.LanguageCode;
|
||||
|
||||
public abstract class AbstractMessingActivity {
|
||||
|
||||
protected final MessagingProcessor messagingProcessor;
|
||||
protected final GrpcClientSettingsManager grpcClientSettingsManager;
|
||||
|
||||
public AbstractMessingActivity(MessagingProcessor messagingProcessor,
|
||||
GrpcClientSettingsManager grpcClientSettingsManager) {
|
||||
this.messagingProcessor = messagingProcessor;
|
||||
this.grpcClientSettingsManager = grpcClientSettingsManager;
|
||||
}
|
||||
|
||||
protected ProxyContext createContext(Context ctx) {
|
||||
return ProxyContext.create()
|
||||
.withVal(GrpcContextConstants.CLIENT_ID, InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.CLIENT_ID))
|
||||
.withVal(GrpcContextConstants.LANGUAGE, LanguageCode.valueOf(InterceptorConstants.METADATA.get(Context.current()).get(InterceptorConstants.LANGUAGE)));
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.grpc.v2;
|
||||
|
||||
import apache.rocketmq.v2.AckMessageRequest;
|
||||
import apache.rocketmq.v2.AckMessageResponse;
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationRequest;
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationResponse;
|
||||
import apache.rocketmq.v2.EndTransactionRequest;
|
||||
import apache.rocketmq.v2.EndTransactionResponse;
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueRequest;
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueResponse;
|
||||
import apache.rocketmq.v2.HeartbeatRequest;
|
||||
import apache.rocketmq.v2.HeartbeatResponse;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationRequest;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationResponse;
|
||||
import apache.rocketmq.v2.QueryAssignmentRequest;
|
||||
import apache.rocketmq.v2.QueryAssignmentResponse;
|
||||
import apache.rocketmq.v2.QueryRouteRequest;
|
||||
import apache.rocketmq.v2.QueryRouteResponse;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.ReceiveMessageResponse;
|
||||
import apache.rocketmq.v2.SendMessageRequest;
|
||||
import apache.rocketmq.v2.SendMessageResponse;
|
||||
import apache.rocketmq.v2.TelemetryCommand;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.common.AbstractStartAndShutdown;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.client.ClientActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcClientSettingsManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.consumer.AckMessageActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.consumer.ChangeInvisibleDurationActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.consumer.ReceiveMessageActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.producer.ForwardMessageToDLQActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.producer.SendMessageActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.route.RouteActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.transaction.EndTransactionActivity;
|
||||
import org.apache.rocketmq.proxy.processor.DefaultMessagingProcessor;
|
||||
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
|
||||
import org.apache.rocketmq.proxy.service.ServiceManager;
|
||||
|
||||
public class DefaultGrpcMessingActivity extends AbstractStartAndShutdown implements GrpcMessingActivity {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
private final ServiceManager serviceManager;
|
||||
private GrpcClientSettingsManager grpcClientSettingsManager;
|
||||
|
||||
private final ReceiveMessageActivity receiveMessageActivity;
|
||||
private final AckMessageActivity ackMessageActivity;
|
||||
private final ChangeInvisibleDurationActivity changeInvisibleDurationActivity;
|
||||
private final SendMessageActivity sendMessageActivity;
|
||||
private final ForwardMessageToDLQActivity forwardMessageToDLQActivity;
|
||||
private final EndTransactionActivity endTransactionActivity;
|
||||
private final RouteActivity routeActivity;
|
||||
private final ClientActivity clientActivity;
|
||||
|
||||
private final MessagingProcessor messagingProcessor;
|
||||
|
||||
protected DefaultGrpcMessingActivity(ServiceManager serviceManager) {
|
||||
this.serviceManager = serviceManager;
|
||||
this.grpcClientSettingsManager = new GrpcClientSettingsManager();
|
||||
|
||||
this.messagingProcessor = new DefaultMessagingProcessor(serviceManager);
|
||||
|
||||
this.receiveMessageActivity = new ReceiveMessageActivity(this.messagingProcessor, this.grpcClientSettingsManager);
|
||||
this.ackMessageActivity = new AckMessageActivity(this.messagingProcessor, this.grpcClientSettingsManager);
|
||||
this.changeInvisibleDurationActivity = new ChangeInvisibleDurationActivity(this.messagingProcessor, this.grpcClientSettingsManager);
|
||||
this.sendMessageActivity = new SendMessageActivity(this.messagingProcessor, this.grpcClientSettingsManager);
|
||||
this.forwardMessageToDLQActivity = new ForwardMessageToDLQActivity(this.messagingProcessor, this.grpcClientSettingsManager);
|
||||
this.endTransactionActivity = new EndTransactionActivity(this.messagingProcessor, this.grpcClientSettingsManager);
|
||||
this.routeActivity = new RouteActivity(this.messagingProcessor, this.grpcClientSettingsManager);
|
||||
this.clientActivity = new ClientActivity(this.messagingProcessor, this.grpcClientSettingsManager);
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
this.appendStartAndShutdown(serviceManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<QueryRouteResponse> queryRoute(Context ctx, QueryRouteRequest request) {
|
||||
return this.routeActivity.queryRoute(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<HeartbeatResponse> heartbeat(Context ctx, HeartbeatRequest request) {
|
||||
return this.clientActivity.heartbeat(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SendMessageResponse> sendMessage(Context ctx, SendMessageRequest request) {
|
||||
return this.sendMessageActivity.sendMessage(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<QueryAssignmentResponse> queryAssignment(Context ctx, QueryAssignmentRequest request) {
|
||||
return this.routeActivity.queryAssignment(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveMessage(Context ctx, ReceiveMessageRequest request, StreamObserver<ReceiveMessageResponse> responseObserver) {
|
||||
this.receiveMessageActivity.receiveMessage(ctx, request, responseObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AckMessageResponse> ackMessage(Context ctx, AckMessageRequest request) {
|
||||
return this.ackMessageActivity.ackMessage(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ForwardMessageToDeadLetterQueueResponse> forwardMessageToDeadLetterQueue(Context ctx,
|
||||
ForwardMessageToDeadLetterQueueRequest request) {
|
||||
return this.forwardMessageToDLQActivity.forwardMessageToDeadLetterQueue(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<EndTransactionResponse> endTransaction(Context ctx, EndTransactionRequest request) {
|
||||
return this.endTransactionActivity.endTransaction(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<NotifyClientTerminationResponse> notifyClientTermination(Context ctx,
|
||||
NotifyClientTerminationRequest request) {
|
||||
return this.clientActivity.notifyClientTermination(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChangeInvisibleDurationResponse> changeInvisibleDuration(Context ctx,
|
||||
ChangeInvisibleDurationRequest request) {
|
||||
return this.changeInvisibleDurationActivity.changeInvisibleDuration(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamObserver<TelemetryCommand> telemetry(Context ctx, StreamObserver<TelemetryCommand> responseObserver) {
|
||||
return this.clientActivity.telemetry(ctx, responseObserver);
|
||||
}
|
||||
}
|
||||
+4
-7
@@ -14,13 +14,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.grpc.v2.service.cluster;
|
||||
package org.apache.rocketmq.proxy.grpc.v2;
|
||||
|
||||
import apache.rocketmq.v2.SendMessageRequest;
|
||||
import io.grpc.Context;
|
||||
import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue;
|
||||
public class GrpcContextConstants {
|
||||
|
||||
public interface WriteQueueSelector {
|
||||
|
||||
SelectableMessageQueue selectQueue(Context ctx, SendMessageRequest request);
|
||||
public static final String CLIENT_ID = "client_id";
|
||||
public static final String LANGUAGE = "language";
|
||||
}
|
||||
+35
-18
@@ -43,16 +43,23 @@ import apache.rocketmq.v2.TelemetryCommand;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseWriter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.GrpcForwardService;
|
||||
import org.apache.rocketmq.proxy.common.StartAndShutdown;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseWriter;
|
||||
import org.apache.rocketmq.proxy.service.ServiceManager;
|
||||
|
||||
public class GrpcMessagingProcessor extends MessagingServiceGrpc.MessagingServiceImplBase {
|
||||
public class GrpcMessagingApplication extends MessagingServiceGrpc.MessagingServiceImplBase implements StartAndShutdown {
|
||||
|
||||
private final GrpcForwardService grpcForwardService;
|
||||
private final GrpcMessingActivity grpcMessingActivity;
|
||||
|
||||
public GrpcMessagingProcessor(GrpcForwardService grpcForwardService) {
|
||||
this.grpcForwardService = grpcForwardService;
|
||||
protected GrpcMessagingApplication(GrpcMessingActivity grpcMessingActivity) {
|
||||
this.grpcMessingActivity = grpcMessingActivity;
|
||||
}
|
||||
|
||||
public static GrpcMessagingApplication create(ServiceManager serviceManager) {
|
||||
return new GrpcMessagingApplication(new DefaultGrpcMessingActivity(
|
||||
serviceManager
|
||||
));
|
||||
}
|
||||
|
||||
protected Status convertExceptionToStatus(Throwable t) {
|
||||
@@ -61,7 +68,7 @@ public class GrpcMessagingProcessor extends MessagingServiceGrpc.MessagingServic
|
||||
|
||||
@Override
|
||||
public void queryRoute(QueryRouteRequest request, StreamObserver<QueryRouteResponse> responseObserver) {
|
||||
CompletableFuture<QueryRouteResponse> future = grpcForwardService.queryRoute(Context.current(), request);
|
||||
CompletableFuture<QueryRouteResponse> future = grpcMessingActivity.queryRoute(Context.current(), request);
|
||||
future.thenAccept(response -> ResponseWriter.write(responseObserver, response))
|
||||
.exceptionally(e -> {
|
||||
ResponseWriter.write(
|
||||
@@ -74,7 +81,7 @@ public class GrpcMessagingProcessor extends MessagingServiceGrpc.MessagingServic
|
||||
|
||||
@Override
|
||||
public void heartbeat(HeartbeatRequest request, StreamObserver<HeartbeatResponse> responseObserver) {
|
||||
CompletableFuture<HeartbeatResponse> future = grpcForwardService.heartbeat(Context.current(), request);
|
||||
CompletableFuture<HeartbeatResponse> future = grpcMessingActivity.heartbeat(Context.current(), request);
|
||||
future.thenAccept(response -> ResponseWriter.write(responseObserver, response))
|
||||
.exceptionally(e -> {
|
||||
ResponseWriter.write(
|
||||
@@ -87,7 +94,7 @@ public class GrpcMessagingProcessor extends MessagingServiceGrpc.MessagingServic
|
||||
|
||||
@Override
|
||||
public void sendMessage(SendMessageRequest request, StreamObserver<SendMessageResponse> responseObserver) {
|
||||
CompletableFuture<SendMessageResponse> future = grpcForwardService.sendMessage(Context.current(), request);
|
||||
CompletableFuture<SendMessageResponse> future = grpcMessingActivity.sendMessage(Context.current(), request);
|
||||
future.thenAccept(response -> ResponseWriter.write(responseObserver, response))
|
||||
.exceptionally(e -> {
|
||||
ResponseWriter.write(
|
||||
@@ -101,7 +108,7 @@ public class GrpcMessagingProcessor extends MessagingServiceGrpc.MessagingServic
|
||||
@Override
|
||||
public void queryAssignment(QueryAssignmentRequest request,
|
||||
StreamObserver<QueryAssignmentResponse> responseObserver) {
|
||||
CompletableFuture<QueryAssignmentResponse> future = grpcForwardService.queryAssignment(Context.current(), request);
|
||||
CompletableFuture<QueryAssignmentResponse> future = grpcMessingActivity.queryAssignment(Context.current(), request);
|
||||
future.thenAccept(response -> ResponseWriter.write(responseObserver, response))
|
||||
.exceptionally(e -> {
|
||||
ResponseWriter.write(
|
||||
@@ -114,12 +121,12 @@ public class GrpcMessagingProcessor extends MessagingServiceGrpc.MessagingServic
|
||||
|
||||
@Override
|
||||
public void receiveMessage(ReceiveMessageRequest request, StreamObserver<ReceiveMessageResponse> responseObserver) {
|
||||
grpcForwardService.receiveMessage(Context.current(), request, responseObserver);
|
||||
grpcMessingActivity.receiveMessage(Context.current(), request, responseObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ackMessage(AckMessageRequest request, StreamObserver<AckMessageResponse> responseObserver) {
|
||||
CompletableFuture<AckMessageResponse> future = grpcForwardService.ackMessage(Context.current(), request);
|
||||
CompletableFuture<AckMessageResponse> future = grpcMessingActivity.ackMessage(Context.current(), request);
|
||||
future.thenAccept(response -> ResponseWriter.write(responseObserver, response))
|
||||
.exceptionally(e -> {
|
||||
ResponseWriter.write(
|
||||
@@ -133,7 +140,7 @@ public class GrpcMessagingProcessor extends MessagingServiceGrpc.MessagingServic
|
||||
@Override
|
||||
public void forwardMessageToDeadLetterQueue(ForwardMessageToDeadLetterQueueRequest request,
|
||||
StreamObserver<ForwardMessageToDeadLetterQueueResponse> responseObserver) {
|
||||
CompletableFuture<ForwardMessageToDeadLetterQueueResponse> future = grpcForwardService.forwardMessageToDeadLetterQueue(Context.current(), request);
|
||||
CompletableFuture<ForwardMessageToDeadLetterQueueResponse> future = grpcMessingActivity.forwardMessageToDeadLetterQueue(Context.current(), request);
|
||||
future.thenAccept(response -> ResponseWriter.write(responseObserver, response))
|
||||
.exceptionally(e -> {
|
||||
ResponseWriter.write(
|
||||
@@ -146,7 +153,7 @@ public class GrpcMessagingProcessor extends MessagingServiceGrpc.MessagingServic
|
||||
|
||||
@Override
|
||||
public void endTransaction(EndTransactionRequest request, StreamObserver<EndTransactionResponse> responseObserver) {
|
||||
CompletableFuture<EndTransactionResponse> future = grpcForwardService.endTransaction(Context.current(), request);
|
||||
CompletableFuture<EndTransactionResponse> future = grpcMessingActivity.endTransaction(Context.current(), request);
|
||||
future.thenAccept(response -> ResponseWriter.write(responseObserver, response))
|
||||
.exceptionally(e -> {
|
||||
ResponseWriter.write(
|
||||
@@ -160,7 +167,7 @@ public class GrpcMessagingProcessor extends MessagingServiceGrpc.MessagingServic
|
||||
@Override
|
||||
public void notifyClientTermination(NotifyClientTerminationRequest request,
|
||||
StreamObserver<NotifyClientTerminationResponse> responseObserver) {
|
||||
CompletableFuture<NotifyClientTerminationResponse> future = grpcForwardService.notifyClientTermination(Context.current(), request);
|
||||
CompletableFuture<NotifyClientTerminationResponse> future = grpcMessingActivity.notifyClientTermination(Context.current(), request);
|
||||
future.thenAccept(response -> ResponseWriter.write(responseObserver, response))
|
||||
.exceptionally(e -> {
|
||||
ResponseWriter.write(
|
||||
@@ -174,7 +181,7 @@ public class GrpcMessagingProcessor extends MessagingServiceGrpc.MessagingServic
|
||||
@Override
|
||||
public void changeInvisibleDuration(ChangeInvisibleDurationRequest request,
|
||||
StreamObserver<ChangeInvisibleDurationResponse> responseObserver) {
|
||||
CompletableFuture<ChangeInvisibleDurationResponse> future = grpcForwardService.changeInvisibleDuration(Context.current(), request);
|
||||
CompletableFuture<ChangeInvisibleDurationResponse> future = grpcMessingActivity.changeInvisibleDuration(Context.current(), request);
|
||||
future.thenAccept(response -> ResponseWriter.write(responseObserver, response))
|
||||
.exceptionally(e -> {
|
||||
ResponseWriter.write(
|
||||
@@ -187,6 +194,16 @@ public class GrpcMessagingProcessor extends MessagingServiceGrpc.MessagingServic
|
||||
|
||||
@Override
|
||||
public StreamObserver<TelemetryCommand> telemetry(StreamObserver<TelemetryCommand> responseObserver) {
|
||||
return grpcForwardService.telemetry(Context.current(), responseObserver);
|
||||
return grpcMessingActivity.telemetry(Context.current(), responseObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws Exception {
|
||||
this.grpcMessingActivity.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
this.grpcMessingActivity.start();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.proxy.grpc.v2.service;
|
||||
package org.apache.rocketmq.proxy.grpc.v2;
|
||||
|
||||
import apache.rocketmq.v2.AckMessageRequest;
|
||||
import apache.rocketmq.v2.AckMessageResponse;
|
||||
@@ -43,7 +43,7 @@ import io.grpc.stub.StreamObserver;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.proxy.common.StartAndShutdown;
|
||||
|
||||
public interface GrpcForwardService extends StartAndShutdown {
|
||||
public interface GrpcMessingActivity extends StartAndShutdown {
|
||||
|
||||
CompletableFuture<QueryRouteResponse> queryRoute(Context ctx, QueryRouteRequest request);
|
||||
|
||||
@@ -1,733 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.adapter;
|
||||
|
||||
import apache.rocketmq.v2.AckMessageRequest;
|
||||
import apache.rocketmq.v2.Broker;
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationRequest;
|
||||
import apache.rocketmq.v2.ClientType;
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.Digest;
|
||||
import apache.rocketmq.v2.DigestType;
|
||||
import apache.rocketmq.v2.Encoding;
|
||||
import apache.rocketmq.v2.EndTransactionRequest;
|
||||
import apache.rocketmq.v2.FilterExpression;
|
||||
import apache.rocketmq.v2.FilterType;
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueRequest;
|
||||
import apache.rocketmq.v2.HeartbeatRequest;
|
||||
import apache.rocketmq.v2.Message;
|
||||
import apache.rocketmq.v2.MessageQueue;
|
||||
import apache.rocketmq.v2.MessageType;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationRequest;
|
||||
import apache.rocketmq.v2.Permission;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.Resource;
|
||||
import apache.rocketmq.v2.SendMessageRequest;
|
||||
import apache.rocketmq.v2.Settings;
|
||||
import apache.rocketmq.v2.SubscriptionEntry;
|
||||
import apache.rocketmq.v2.SystemProperties;
|
||||
import apache.rocketmq.v2.TransactionResolution;
|
||||
import apache.rocketmq.v2.TransactionSource;
|
||||
import apache.rocketmq.v2.VerifyMessageResult;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Duration;
|
||||
import com.google.protobuf.Timestamp;
|
||||
import com.google.protobuf.util.Durations;
|
||||
import com.google.protobuf.util.Timestamps;
|
||||
import io.grpc.Context;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.constant.ConsumeInitMode;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.constant.PermName;
|
||||
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.filter.ExpressionType;
|
||||
import org.apache.rocketmq.common.filter.FilterAPI;
|
||||
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.protocol.NamespaceUtil;
|
||||
import org.apache.rocketmq.common.protocol.body.CMResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.EndTransactionRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.UnregisterClientRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.HeartbeatData;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
|
||||
import org.apache.rocketmq.common.protocol.route.QueueData;
|
||||
import org.apache.rocketmq.common.sysflag.MessageSysFlag;
|
||||
import org.apache.rocketmq.common.utils.BinaryUtil;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.common.utils.ProxyUtils;
|
||||
import org.apache.rocketmq.proxy.config.ConfigurationManager;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionId;
|
||||
|
||||
public class GrpcConverter {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
public static String wrapResourceWithNamespace(Resource resource) {
|
||||
return NamespaceUtil.wrapNamespace(resource.getResourceNamespace(), resource.getName());
|
||||
}
|
||||
|
||||
public static HeartbeatData buildHeartbeatData(String clientId, HeartbeatRequest request,
|
||||
Settings clientSettings) {
|
||||
HeartbeatData heartbeatData = new HeartbeatData();
|
||||
heartbeatData.setClientID(clientId);
|
||||
switch (clientSettings.getClientType()) {
|
||||
case PRODUCER: {
|
||||
Set<org.apache.rocketmq.common.protocol.heartbeat.ProducerData> producerDataSet = new HashSet<>();
|
||||
for (Resource topic : clientSettings.getPublishing().getTopicsList()) {
|
||||
String topicName = wrapResourceWithNamespace(topic);
|
||||
producerDataSet.add(buildProducerData(topicName));
|
||||
}
|
||||
heartbeatData.setProducerDataSet(producerDataSet);
|
||||
break;
|
||||
}
|
||||
case PUSH_CONSUMER:
|
||||
case SIMPLE_CONSUMER: {
|
||||
String groupName = wrapResourceWithNamespace(request.getGroup());
|
||||
Set<org.apache.rocketmq.common.protocol.heartbeat.ConsumerData> consumerDataSet = new HashSet<>();
|
||||
consumerDataSet.add(buildConsumerData(groupName, clientSettings));
|
||||
heartbeatData.setConsumerDataSet(consumerDataSet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return heartbeatData;
|
||||
}
|
||||
|
||||
public static org.apache.rocketmq.common.protocol.heartbeat.ProducerData buildProducerData(String groupName) {
|
||||
org.apache.rocketmq.common.protocol.heartbeat.ProducerData buildProducerData
|
||||
= new org.apache.rocketmq.common.protocol.heartbeat.ProducerData();
|
||||
buildProducerData.setGroupName(groupName);
|
||||
return buildProducerData;
|
||||
}
|
||||
|
||||
public static org.apache.rocketmq.common.protocol.heartbeat.ConsumerData buildConsumerData(String groupName,
|
||||
Settings clientSettings) {
|
||||
org.apache.rocketmq.common.protocol.heartbeat.ConsumerData buildConsumerData = new org.apache.rocketmq.common.protocol.heartbeat.ConsumerData();
|
||||
buildConsumerData.setGroupName(groupName);
|
||||
buildConsumerData.setConsumeType(buildConsumeType(clientSettings.getClientType()));
|
||||
|
||||
buildConsumerData.setMessageModel(MessageModel.CLUSTERING);
|
||||
buildConsumerData.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
|
||||
Set<SubscriptionData> subscriptionDataSet =
|
||||
buildSubscriptionDataSet(clientSettings.getSubscription().getSubscriptionsList());
|
||||
buildConsumerData.setSubscriptionDataSet(subscriptionDataSet);
|
||||
return buildConsumerData;
|
||||
}
|
||||
|
||||
public static ConsumeType buildConsumeType(ClientType clientType) {
|
||||
switch (clientType) {
|
||||
case SIMPLE_CONSUMER:
|
||||
return ConsumeType.CONSUME_ACTIVELY;
|
||||
case PUSH_CONSUMER:
|
||||
return ConsumeType.CONSUME_PASSIVELY;
|
||||
default:
|
||||
throw new IllegalArgumentException("Client type is not consumer, type: " + clientType);
|
||||
}
|
||||
}
|
||||
|
||||
public static SendMessageRequestHeader buildSendMessageRequestHeader(SendMessageRequest request,
|
||||
String producerGroup, int queueId) {
|
||||
SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
|
||||
|
||||
if (request.getMessagesCount() <= 0) {
|
||||
throw new ProxyException(Code.MESSAGE_CORRUPTED, "no message to send");
|
||||
}
|
||||
if (request.getMessagesCount() > 1) {
|
||||
requestHeader.setBatch(true);
|
||||
}
|
||||
Message message = request.getMessages(0);
|
||||
SystemProperties systemProperties = message.getSystemProperties();
|
||||
|
||||
Map<String, String> property = buildMessageProperty(message, producerGroup);
|
||||
requestHeader.setProducerGroup(producerGroup);
|
||||
requestHeader.setTopic(wrapResourceWithNamespace(message.getTopic()));
|
||||
requestHeader.setDefaultTopic("");
|
||||
requestHeader.setDefaultTopicQueueNums(0);
|
||||
requestHeader.setQueueId(queueId);
|
||||
// sysFlag (body encoding & message type)
|
||||
int sysFlag = 0;
|
||||
Encoding bodyEncoding = systemProperties.getBodyEncoding();
|
||||
if (bodyEncoding.equals(Encoding.GZIP)) {
|
||||
sysFlag |= MessageSysFlag.COMPRESSED_FLAG;
|
||||
}
|
||||
// transaction
|
||||
MessageType messageType = systemProperties.getMessageType();
|
||||
if (messageType.equals(MessageType.TRANSACTION)) {
|
||||
sysFlag |= MessageSysFlag.TRANSACTION_PREPARED_TYPE;
|
||||
}
|
||||
requestHeader.setSysFlag(sysFlag);
|
||||
requestHeader.setBornTimestamp(Timestamps.toMillis(systemProperties.getBornTimestamp()));
|
||||
requestHeader.setFlag(0);
|
||||
requestHeader.setProperties(MessageDecoder.messageProperties2String(property));
|
||||
requestHeader.setReconsumeTimes(systemProperties.getDeliveryAttempt());
|
||||
|
||||
return requestHeader;
|
||||
}
|
||||
|
||||
public static PopMessageRequestHeader buildPopMessageRequestHeader(ReceiveMessageRequest request, long pollTime, boolean isFifo) {
|
||||
Resource group = request.getGroup();
|
||||
String groupName = GrpcConverter.wrapResourceWithNamespace(group);
|
||||
MessageQueue messageQueue = request.getMessageQueue();
|
||||
Resource topic = messageQueue.getTopic();
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
int queueId = messageQueue.getId();
|
||||
int maxMessageNumbers = request.getBatchSize();
|
||||
if (maxMessageNumbers > ProxyUtils.MAX_MSG_NUMS_FOR_POP_REQUEST) {
|
||||
log.warn("change maxNums from {} to {} for pop request, with info: topic:{}, group:{}",
|
||||
maxMessageNumbers, ProxyUtils.MAX_MSG_NUMS_FOR_POP_REQUEST, topicName, groupName);
|
||||
maxMessageNumbers = ProxyUtils.MAX_MSG_NUMS_FOR_POP_REQUEST;
|
||||
}
|
||||
long invisibleTime = Durations.toMillis(request.getInvisibleDuration());
|
||||
long bornTime = System.currentTimeMillis();
|
||||
|
||||
FilterExpression filterExpression = request.getFilterExpression();
|
||||
String expression = filterExpression.getExpression();
|
||||
String expressionType = GrpcConverter.buildExpressionType(filterExpression.getType());
|
||||
|
||||
PopMessageRequestHeader requestHeader = new PopMessageRequestHeader();
|
||||
requestHeader.setConsumerGroup(groupName);
|
||||
requestHeader.setTopic(topicName);
|
||||
requestHeader.setQueueId(queueId);
|
||||
requestHeader.setMaxMsgNums(maxMessageNumbers);
|
||||
requestHeader.setInvisibleTime(invisibleTime);
|
||||
requestHeader.setPollTime(pollTime);
|
||||
requestHeader.setBornTime(bornTime);
|
||||
requestHeader.setInitMode(ConsumeInitMode.MAX);
|
||||
requestHeader.setExpType(expressionType);
|
||||
requestHeader.setExp(expression);
|
||||
requestHeader.setOrder(isFifo);
|
||||
|
||||
return requestHeader;
|
||||
}
|
||||
|
||||
public static AckMessageRequestHeader buildAckMessageRequestHeader(ReceiveMessageRequest request, ReceiptHandle handle) {
|
||||
return buildAckMessageRequestHeader(request.getMessageQueue().getTopic(), request.getGroup(), handle);
|
||||
}
|
||||
|
||||
public static AckMessageRequestHeader buildAckMessageRequestHeader(AckMessageRequest request, ReceiptHandle handle) {
|
||||
return buildAckMessageRequestHeader(request.getTopic(), request.getGroup(), handle);
|
||||
}
|
||||
|
||||
public static AckMessageRequestHeader buildAckMessageRequestHeader(Resource topic, Resource group, ReceiptHandle handle) {
|
||||
String groupName = GrpcConverter.wrapResourceWithNamespace(group);
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
|
||||
AckMessageRequestHeader ackMessageRequestHeader = new AckMessageRequestHeader();
|
||||
ackMessageRequestHeader.setConsumerGroup(groupName);
|
||||
ackMessageRequestHeader.setTopic(handle.getRealTopic(topicName, groupName));
|
||||
ackMessageRequestHeader.setQueueId(handle.getQueueId());
|
||||
ackMessageRequestHeader.setExtraInfo(handle.getReceiptHandle());
|
||||
ackMessageRequestHeader.setOffset(handle.getOffset());
|
||||
return ackMessageRequestHeader;
|
||||
}
|
||||
|
||||
public static ChangeInvisibleTimeRequestHeader buildChangeInvisibleTimeRequestHeader(ChangeInvisibleDurationRequest request) {
|
||||
String groupName = GrpcConverter.wrapResourceWithNamespace(request.getGroup());
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic());
|
||||
ReceiptHandle handle = ReceiptHandle.decode(request.getReceiptHandle());
|
||||
|
||||
ChangeInvisibleTimeRequestHeader changeInvisibleTimeRequestHeader = new ChangeInvisibleTimeRequestHeader();
|
||||
changeInvisibleTimeRequestHeader.setConsumerGroup(groupName);
|
||||
changeInvisibleTimeRequestHeader.setTopic(handle.getRealTopic(topicName, groupName));
|
||||
changeInvisibleTimeRequestHeader.setQueueId(handle.getQueueId());
|
||||
changeInvisibleTimeRequestHeader.setExtraInfo(handle.getReceiptHandle());
|
||||
changeInvisibleTimeRequestHeader.setOffset(handle.getOffset());
|
||||
changeInvisibleTimeRequestHeader.setInvisibleTime(Durations.toMillis(request.getInvisibleDuration()));
|
||||
return changeInvisibleTimeRequestHeader;
|
||||
}
|
||||
|
||||
public static ChangeInvisibleTimeRequestHeader buildChangeInvisibleTimeRequestHeader(ReceiveMessageRequest request, ReceiptHandle handle) {
|
||||
String groupName = GrpcConverter.wrapResourceWithNamespace(request.getGroup());
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(request.getMessageQueue().getTopic());
|
||||
|
||||
ChangeInvisibleTimeRequestHeader changeInvisibleTimeRequestHeader = new ChangeInvisibleTimeRequestHeader();
|
||||
changeInvisibleTimeRequestHeader.setConsumerGroup(groupName);
|
||||
changeInvisibleTimeRequestHeader.setTopic(handle.getRealTopic(topicName, groupName));
|
||||
changeInvisibleTimeRequestHeader.setQueueId(handle.getQueueId());
|
||||
changeInvisibleTimeRequestHeader.setExtraInfo(handle.getReceiptHandle());
|
||||
changeInvisibleTimeRequestHeader.setOffset(handle.getOffset());
|
||||
changeInvisibleTimeRequestHeader.setInvisibleTime(Durations.toMillis(request.getInvisibleDuration()));
|
||||
return changeInvisibleTimeRequestHeader;
|
||||
}
|
||||
|
||||
public static ConsumerSendMsgBackRequestHeader buildConsumerSendMsgBackRequestHeader(ReceiveMessageRequest request,
|
||||
ReceiptHandle handle, String messageId, int maxReconsumeTimes) {
|
||||
return buildConsumerSendMsgBackRequestHeader(request.getMessageQueue().getTopic(), request.getGroup(), handle, messageId, maxReconsumeTimes);
|
||||
}
|
||||
|
||||
public static ConsumerSendMsgBackRequestHeader buildConsumerSendMsgBackRequestHeader(
|
||||
ForwardMessageToDeadLetterQueueRequest request) {
|
||||
ReceiptHandle handle = ReceiptHandle.decode(request.getReceiptHandle());
|
||||
return buildConsumerSendMsgBackRequestHeader(request.getTopic(), request.getGroup(), handle,
|
||||
request.getMessageId(), request.getMaxDeliveryAttempts());
|
||||
}
|
||||
|
||||
public static ConsumerSendMsgBackRequestHeader buildConsumerSendMsgBackRequestHeader(Resource topic, Resource group, ReceiptHandle handle,
|
||||
String messageId, int maxReconsumeTimes) {
|
||||
String groupName = GrpcConverter.wrapResourceWithNamespace(group);
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
|
||||
ConsumerSendMsgBackRequestHeader consumerSendMsgBackRequestHeader = new ConsumerSendMsgBackRequestHeader();
|
||||
consumerSendMsgBackRequestHeader.setOffset(handle.getCommitLogOffset());
|
||||
consumerSendMsgBackRequestHeader.setGroup(groupName);
|
||||
consumerSendMsgBackRequestHeader.setDelayLevel(-1);
|
||||
consumerSendMsgBackRequestHeader.setOriginMsgId(messageId);
|
||||
consumerSendMsgBackRequestHeader.setOriginTopic(handle.getRealTopic(topicName, groupName));
|
||||
consumerSendMsgBackRequestHeader.setMaxReconsumeTimes(maxReconsumeTimes);
|
||||
return consumerSendMsgBackRequestHeader;
|
||||
}
|
||||
|
||||
public static EndTransactionRequestHeader buildEndTransactionRequestHeader(EndTransactionRequest request,
|
||||
String producerGroup) {
|
||||
String messageId = request.getMessageId();
|
||||
String transactionId = request.getTransactionId();
|
||||
TransactionId handle;
|
||||
try {
|
||||
handle = TransactionId.decode(transactionId);
|
||||
} catch (Exception e) {
|
||||
throw new ProxyException(Code.INVALID_TRANSACTION_ID, "Parse transaction id failed", e);
|
||||
}
|
||||
long transactionStateTableOffset = handle.getTranStateTableOffset();
|
||||
long commitLogOffset = handle.getCommitLogOffset();
|
||||
boolean fromTransactionCheck = request.getSource() == TransactionSource.SOURCE_SERVER_CHECK;
|
||||
int commitOrRollback = GrpcConverter.buildTransactionCommitOrRollback(request.getResolution());
|
||||
|
||||
EndTransactionRequestHeader endTransactionRequestHeader = new EndTransactionRequestHeader();
|
||||
endTransactionRequestHeader.setProducerGroup(producerGroup);
|
||||
endTransactionRequestHeader.setMsgId(messageId);
|
||||
endTransactionRequestHeader.setTransactionId(handle.getBrokerTransactionId());
|
||||
endTransactionRequestHeader.setTranStateTableOffset(transactionStateTableOffset);
|
||||
endTransactionRequestHeader.setCommitLogOffset(commitLogOffset);
|
||||
endTransactionRequestHeader.setCommitOrRollback(commitOrRollback);
|
||||
endTransactionRequestHeader.setFromTransactionCheck(fromTransactionCheck);
|
||||
|
||||
return endTransactionRequestHeader;
|
||||
}
|
||||
|
||||
public static UnregisterClientRequestHeader buildUnregisterClientRequestHeader(String clientId,
|
||||
ClientType clientType, NotifyClientTerminationRequest request) {
|
||||
UnregisterClientRequestHeader header = new UnregisterClientRequestHeader();
|
||||
String groupName = GrpcConverter.wrapResourceWithNamespace(request.getGroup());
|
||||
header.setClientID(clientId);
|
||||
switch (clientType) {
|
||||
case PRODUCER: {
|
||||
header.setProducerGroup(groupName);
|
||||
break;
|
||||
}
|
||||
case PUSH_CONSUMER:
|
||||
case SIMPLE_CONSUMER: {
|
||||
header.setConsumerGroup(groupName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
public static Map<String, String> buildMessageProperty(Message message, String producerGroup) {
|
||||
org.apache.rocketmq.common.message.Message messageWithHeader = new org.apache.rocketmq.common.message.Message();
|
||||
// set user properties
|
||||
Map<String, String> userProperties = message.getUserPropertiesMap();
|
||||
for (String key : userProperties.keySet()) {
|
||||
if (MessageConst.STRING_HASH_SET.contains(key)) {
|
||||
throw new ProxyException(Code.ILLEGAL_MESSAGE_PROPERTY_KEY, "property is used by system: " + key);
|
||||
}
|
||||
}
|
||||
MessageAccessor.setProperties(messageWithHeader, Maps.newHashMap(userProperties));
|
||||
|
||||
// set tag
|
||||
String tag = message.getSystemProperties().getTag();
|
||||
if (!"".equals(tag)) {
|
||||
messageWithHeader.setTags(tag);
|
||||
}
|
||||
|
||||
// set keys
|
||||
List<String> keysList = message.getSystemProperties().getKeysList();
|
||||
if (keysList.size() > 0) {
|
||||
messageWithHeader.setKeys(keysList);
|
||||
}
|
||||
|
||||
// set message id
|
||||
String messageId = message.getSystemProperties().getMessageId();
|
||||
if ("".equals(messageId)) {
|
||||
throw new ProxyException(Code.ILLEGAL_MESSAGE_ID, "message id is empty");
|
||||
}
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, messageId);
|
||||
|
||||
// set transaction property
|
||||
MessageType messageType = message.getSystemProperties().getMessageType();
|
||||
if (messageType.equals(MessageType.TRANSACTION)) {
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_TRANSACTION_PREPARED, "true");
|
||||
|
||||
Duration transactionResolveDelay = message.getSystemProperties().getOrphanedTransactionRecoveryDuration();
|
||||
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_CHECK_IMMUNITY_TIME_IN_SECONDS,
|
||||
String.valueOf(Durations.toSeconds(transactionResolveDelay)));
|
||||
}
|
||||
|
||||
// set delay level or deliver timestamp
|
||||
if (message.getSystemProperties().hasDeliveryTimestamp()) {
|
||||
Timestamp deliveryTimestamp = message.getSystemProperties().getDeliveryTimestamp();
|
||||
String timestampString = String.valueOf(Timestamps.toMillis(deliveryTimestamp));
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_TIMER_DELIVER_MS, timestampString);
|
||||
}
|
||||
|
||||
// set reconsume times
|
||||
int reconsumeTimes = message.getSystemProperties().getDeliveryAttempt();
|
||||
MessageAccessor.setReconsumeTime(messageWithHeader, String.valueOf(reconsumeTimes));
|
||||
// set producer group
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_PRODUCER_GROUP, producerGroup);
|
||||
// set message group
|
||||
String messageGroup = message.getSystemProperties().getMessageGroup();
|
||||
if (!messageGroup.isEmpty()) {
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_SHARDING_KEY, messageGroup);
|
||||
}
|
||||
// set trace context
|
||||
String traceContext = message.getSystemProperties().getTraceContext();
|
||||
if (!traceContext.isEmpty()) {
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_TRACE_CONTEXT, traceContext);
|
||||
}
|
||||
return messageWithHeader.getProperties();
|
||||
}
|
||||
|
||||
public static List<org.apache.rocketmq.common.message.Message> buildMessage(List<Message> protoMessageList,
|
||||
Resource topic) {
|
||||
String topicName = wrapResourceWithNamespace(topic);
|
||||
List<org.apache.rocketmq.common.message.Message> messages = new ArrayList<>();
|
||||
for (Message protoMessage : protoMessageList) {
|
||||
if (!protoMessage.getTopic().equals(topic)) {
|
||||
throw new ProxyException(Code.MESSAGE_CORRUPTED, "topic in message is not same");
|
||||
}
|
||||
// here use topicName as producerGroup for transactional checker.
|
||||
messages.add(buildMessage(protoMessage, topicName));
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
public static org.apache.rocketmq.common.message.Message buildMessage(Message protoMessage, String producerGroup) {
|
||||
String topicName = wrapResourceWithNamespace(protoMessage.getTopic());
|
||||
|
||||
org.apache.rocketmq.common.message.Message message =
|
||||
new org.apache.rocketmq.common.message.Message(topicName, protoMessage.getBody().toByteArray());
|
||||
Map<String, String> messageProperty = buildMessageProperty(protoMessage, producerGroup);
|
||||
|
||||
MessageAccessor.setProperties(message, messageProperty);
|
||||
return message;
|
||||
}
|
||||
|
||||
public static MessageQueue buildMessageQueue(MessageExt messageExt, String brokerName) {
|
||||
Broker broker = Broker.getDefaultInstance();
|
||||
if (!StringUtils.isEmpty(brokerName)) {
|
||||
broker = Broker.newBuilder()
|
||||
.setName(brokerName)
|
||||
.setId(0)
|
||||
.build();
|
||||
}
|
||||
return MessageQueue.newBuilder()
|
||||
.setId(messageExt.getQueueId())
|
||||
.setTopic(Resource.newBuilder()
|
||||
.setName(NamespaceUtil.withoutNamespace(messageExt.getTopic()))
|
||||
.setResourceNamespace(NamespaceUtil.getNamespaceFromResource(messageExt.getTopic()))
|
||||
.build())
|
||||
.setBroker(broker)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static String buildExpressionType(FilterType filterType) {
|
||||
switch (filterType) {
|
||||
case SQL:
|
||||
return ExpressionType.SQL92;
|
||||
case TAG:
|
||||
default:
|
||||
return ExpressionType.TAG;
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<SubscriptionData> buildSubscriptionDataSet(List<SubscriptionEntry> subscriptionEntryList) {
|
||||
Set<SubscriptionData> subscriptionDataSet = new HashSet<>();
|
||||
for (SubscriptionEntry sub : subscriptionEntryList) {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(sub.getTopic());
|
||||
FilterExpression filterExpression = sub.getExpression();
|
||||
subscriptionDataSet.add(buildSubscriptionData(topicName, filterExpression));
|
||||
}
|
||||
return subscriptionDataSet;
|
||||
}
|
||||
|
||||
public static SubscriptionData buildSubscriptionData(String topicName, FilterExpression filterExpression) {
|
||||
String expression = filterExpression.getExpression();
|
||||
String expressionType = GrpcConverter.buildExpressionType(filterExpression.getType());
|
||||
try {
|
||||
return FilterAPI.build(topicName, expression, expressionType);
|
||||
} catch (Exception e) {
|
||||
throw new ProxyException(Code.ILLEGAL_FILTER_EXPRESSION, "expression format is not correct", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Message buildMessage(MessageExt messageExt) {
|
||||
Map<String, String> userProperties = buildUserAttributes(messageExt);
|
||||
SystemProperties systemProperties = buildSystemProperties(messageExt);
|
||||
Resource topic = buildResource(messageExt.getTopic());
|
||||
|
||||
return Message.newBuilder()
|
||||
.setTopic(topic)
|
||||
.putAllUserProperties(userProperties)
|
||||
.setSystemProperties(systemProperties)
|
||||
.setBody(ByteString.copyFrom(messageExt.getBody()))
|
||||
.build();
|
||||
}
|
||||
|
||||
protected static Map<String, String> buildUserAttributes(MessageExt messageExt) {
|
||||
Map<String, String> userAttributes = new HashMap<>();
|
||||
Map<String, String> properties = messageExt.getProperties();
|
||||
|
||||
for (Map.Entry<String, String> property : properties.entrySet()) {
|
||||
if (!MessageConst.STRING_HASH_SET.contains(property.getKey())) {
|
||||
userAttributes.put(property.getKey(), property.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return userAttributes;
|
||||
}
|
||||
|
||||
protected static SystemProperties buildSystemProperties(MessageExt messageExt) {
|
||||
SystemProperties.Builder systemPropertiesBuilder = SystemProperties.newBuilder();
|
||||
|
||||
// tag
|
||||
String tag = messageExt.getUserProperty(MessageConst.PROPERTY_TAGS);
|
||||
if (tag != null) {
|
||||
systemPropertiesBuilder.setTag(tag);
|
||||
}
|
||||
|
||||
// keys
|
||||
String keys = messageExt.getKeys();
|
||||
if (keys != null) {
|
||||
String[] keysArray = keys.split(MessageConst.KEY_SEPARATOR);
|
||||
systemPropertiesBuilder.addAllKeys(Arrays.asList(keysArray));
|
||||
}
|
||||
|
||||
// message_id
|
||||
String uniqKey = messageExt.getProperty(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);
|
||||
if (uniqKey != null) {
|
||||
systemPropertiesBuilder.setMessageId(uniqKey);
|
||||
}
|
||||
|
||||
// body_digest & body_encoding
|
||||
String md5Result = BinaryUtil.generateMd5(messageExt.getBody());
|
||||
Digest digest = Digest.newBuilder()
|
||||
.setType(DigestType.MD5)
|
||||
.setChecksum(md5Result)
|
||||
.build();
|
||||
systemPropertiesBuilder.setBodyDigest(digest);
|
||||
|
||||
if ((messageExt.getSysFlag() & MessageSysFlag.COMPRESSED_FLAG) == MessageSysFlag.COMPRESSED_FLAG) {
|
||||
systemPropertiesBuilder.setBodyEncoding(Encoding.GZIP);
|
||||
} else {
|
||||
systemPropertiesBuilder.setBodyEncoding(Encoding.IDENTITY);
|
||||
}
|
||||
|
||||
// message_type
|
||||
String isTrans = messageExt.getProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED);
|
||||
String isTransValue = "true";
|
||||
if (isTransValue.equals(isTrans)) {
|
||||
systemPropertiesBuilder.setMessageType(MessageType.TRANSACTION);
|
||||
} else if (messageExt.getProperty(MessageConst.PROPERTY_DELAY_TIME_LEVEL) != null
|
||||
|| messageExt.getProperty(MessageConst.PROPERTY_TIMER_DELIVER_MS) != null
|
||||
|| messageExt.getProperty(MessageConst.PROPERTY_TIMER_DELAY_SEC) != null) {
|
||||
systemPropertiesBuilder.setMessageType(MessageType.DELAY);
|
||||
} else if (messageExt.getProperty(MessageConst.PROPERTY_SHARDING_KEY) != null) {
|
||||
systemPropertiesBuilder.setMessageType(MessageType.FIFO);
|
||||
} else {
|
||||
systemPropertiesBuilder.setMessageType(MessageType.NORMAL);
|
||||
}
|
||||
|
||||
// born_timestamp (millis)
|
||||
long bornTimestamp = messageExt.getBornTimestamp();
|
||||
systemPropertiesBuilder.setBornTimestamp(Timestamps.fromMillis(bornTimestamp));
|
||||
|
||||
// born_host
|
||||
systemPropertiesBuilder.setBornHost(messageExt.getBornHostString());
|
||||
|
||||
// store_timestamp (millis)
|
||||
long storeTimestamp = messageExt.getStoreTimestamp();
|
||||
systemPropertiesBuilder.setStoreTimestamp(Timestamps.fromMillis(storeTimestamp));
|
||||
|
||||
// store_host
|
||||
SocketAddress storeHost = messageExt.getStoreHost();
|
||||
if (storeHost != null) {
|
||||
systemPropertiesBuilder.setStoreHost(storeHost.toString());
|
||||
}
|
||||
|
||||
// delivery_timestamp
|
||||
String deliverMsString;
|
||||
long deliverMs;
|
||||
if (messageExt.getProperty(MessageConst.PROPERTY_TIMER_DELAY_SEC) != null) {
|
||||
long delayMs = TimeUnit.SECONDS.toMillis(Long.parseLong(messageExt.getProperty(MessageConst.PROPERTY_TIMER_DELAY_SEC)));
|
||||
deliverMs = System.currentTimeMillis() + delayMs;
|
||||
systemPropertiesBuilder.setDeliveryTimestamp(Timestamps.fromMillis(deliverMs));
|
||||
} else {
|
||||
deliverMsString = messageExt.getProperty(MessageConst.PROPERTY_TIMER_DELIVER_MS);
|
||||
if (deliverMsString != null) {
|
||||
deliverMs = Long.parseLong(deliverMsString);
|
||||
systemPropertiesBuilder.setDeliveryTimestamp(Timestamps.fromMillis(deliverMs));
|
||||
}
|
||||
}
|
||||
|
||||
// sharding key
|
||||
String shardingKey = messageExt.getProperty(MessageConst.PROPERTY_SHARDING_KEY);
|
||||
if (shardingKey != null) {
|
||||
systemPropertiesBuilder.setMessageGroup(shardingKey);
|
||||
}
|
||||
|
||||
// receipt_handle && invisible_period
|
||||
ReceiptHandle receiptHandle = ReceiptHandle.create(messageExt);
|
||||
if (receiptHandle != null) {
|
||||
systemPropertiesBuilder.setReceiptHandle(receiptHandle.encode());
|
||||
}
|
||||
|
||||
// partition_id
|
||||
systemPropertiesBuilder.setQueueId(messageExt.getQueueId());
|
||||
|
||||
// partition_offset
|
||||
systemPropertiesBuilder.setQueueOffset(messageExt.getQueueOffset());
|
||||
|
||||
// delivery_attempt
|
||||
systemPropertiesBuilder.setDeliveryAttempt(messageExt.getReconsumeTimes() + 1);
|
||||
|
||||
// trace context
|
||||
String traceContext = messageExt.getProperty(MessageConst.PROPERTY_TRACE_CONTEXT);
|
||||
if (traceContext != null) {
|
||||
systemPropertiesBuilder.setTraceContext(traceContext);
|
||||
}
|
||||
|
||||
return systemPropertiesBuilder.build();
|
||||
}
|
||||
|
||||
public static int buildTransactionCommitOrRollback(TransactionResolution type) {
|
||||
switch (type) {
|
||||
case COMMIT:
|
||||
return MessageSysFlag.TRANSACTION_COMMIT_TYPE;
|
||||
case ROLLBACK:
|
||||
return MessageSysFlag.TRANSACTION_ROLLBACK_TYPE;
|
||||
default:
|
||||
return MessageSysFlag.TRANSACTION_NOT_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
public static ConsumeMessageDirectlyResult buildConsumeMessageDirectlyResult(VerifyMessageResult request) {
|
||||
ConsumeMessageDirectlyResult consumeMessageDirectlyResult = new ConsumeMessageDirectlyResult();
|
||||
switch (request.getStatus().getCode().getNumber()) {
|
||||
case Code.OK_VALUE: {
|
||||
consumeMessageDirectlyResult.setConsumeResult(CMResult.CR_SUCCESS);
|
||||
break;
|
||||
}
|
||||
case Code.FAILED_TO_CONSUME_MESSAGE_VALUE: {
|
||||
consumeMessageDirectlyResult.setConsumeResult(CMResult.CR_LATER);
|
||||
break;
|
||||
}
|
||||
case Code.MESSAGE_CORRUPTED_VALUE: {
|
||||
consumeMessageDirectlyResult.setConsumeResult(CMResult.CR_RETURN_NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
consumeMessageDirectlyResult.setRemark("From gRPC client");
|
||||
return consumeMessageDirectlyResult;
|
||||
}
|
||||
|
||||
public static Resource buildResource(String resourceNameWithNamespace) {
|
||||
return Resource.newBuilder()
|
||||
.setResourceNamespace(NamespaceUtil.getNamespaceFromResource(resourceNameWithNamespace))
|
||||
.setName(NamespaceUtil.withoutNamespace(resourceNameWithNamespace))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static long buildPollTimeFromContext(Context ctx) {
|
||||
long timeRemaining = ctx.getDeadline()
|
||||
.timeRemaining(TimeUnit.MILLISECONDS);
|
||||
long pollTime = timeRemaining - ConfigurationManager.getProxyConfig().getLongPollingReserveTimeInMillis();
|
||||
if (pollTime <= 0) {
|
||||
pollTime = timeRemaining;
|
||||
}
|
||||
|
||||
return pollTime;
|
||||
}
|
||||
|
||||
public static List<MessageQueue> genMessageQueueFromQueueData(QueueData queueData, Resource topic, Broker broker) {
|
||||
List<MessageQueue> messageQueueList = new ArrayList<>();
|
||||
|
||||
int r = 0;
|
||||
int w = 0;
|
||||
int rw = 0;
|
||||
if (PermName.isWriteable(queueData.getPerm()) && PermName.isReadable(queueData.getPerm())) {
|
||||
rw = Math.min(queueData.getWriteQueueNums(), queueData.getReadQueueNums());
|
||||
r = queueData.getReadQueueNums() - rw;
|
||||
w = queueData.getWriteQueueNums() - rw;
|
||||
} else if (PermName.isWriteable(queueData.getPerm())) {
|
||||
w = queueData.getWriteQueueNums();
|
||||
} else if (PermName.isReadable(queueData.getPerm())) {
|
||||
r = queueData.getReadQueueNums();
|
||||
}
|
||||
|
||||
// r here means readOnly queue nums, w means writeOnly queue nums, while rw means both readable and writable queue nums.
|
||||
int queueIdIndex = 0;
|
||||
for (int i = 0; i < r; i++) {
|
||||
MessageQueue messageQueue = MessageQueue.newBuilder().setBroker(broker).setTopic(topic)
|
||||
.setId(queueIdIndex++)
|
||||
.setPermission(Permission.READ)
|
||||
.build();
|
||||
messageQueueList.add(messageQueue);
|
||||
}
|
||||
|
||||
for (int i = 0; i < w; i++) {
|
||||
MessageQueue messageQueue = MessageQueue.newBuilder().setBroker(broker).setTopic(topic)
|
||||
.setId(queueIdIndex++)
|
||||
.setPermission(Permission.WRITE)
|
||||
.build();
|
||||
messageQueueList.add(messageQueue);
|
||||
}
|
||||
|
||||
for (int i = 0; i < rw; i++) {
|
||||
MessageQueue messageQueue = MessageQueue.newBuilder().setBroker(broker).setTopic(topic)
|
||||
.setId(queueIdIndex++)
|
||||
.setPermission(Permission.READ_WRITE)
|
||||
.build();
|
||||
messageQueueList.add(messageQueue);
|
||||
}
|
||||
|
||||
return messageQueueList;
|
||||
}
|
||||
|
||||
}
|
||||
-169
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.adapter.channel;
|
||||
|
||||
import apache.rocketmq.v2.PrintThreadStackTraceCommand;
|
||||
import apache.rocketmq.v2.RecoverOrphanedTransactionCommand;
|
||||
import apache.rocketmq.v2.TelemetryCommand;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.apache.rocketmq.common.message.MessageDecoder;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.RequestCode;
|
||||
import org.apache.rocketmq.common.protocol.header.CheckTransactionStateRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.GetConsumerRunningInfoRequestHeader;
|
||||
import org.apache.rocketmq.proxy.channel.ChannelManager;
|
||||
import org.apache.rocketmq.proxy.channel.SimpleChannel;
|
||||
import org.apache.rocketmq.proxy.common.TelemetryCommandManager;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionId;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.remoting.common.RemotingUtil;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class GrpcClientChannel extends SimpleChannel {
|
||||
private final AtomicReference<StreamObserver<TelemetryCommand>> telemetryCommandRef = new AtomicReference<>();
|
||||
|
||||
private final String group;
|
||||
private final String clientId;
|
||||
private final TelemetryCommandManager manager;
|
||||
|
||||
private GrpcClientChannel(Context ctx, String group, String clientId, TelemetryCommandManager manager) {
|
||||
super(ChannelManager.createSimpleChannelDirectly(ctx));
|
||||
this.group = group;
|
||||
this.clientId = clientId;
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
public void setClientObserver(StreamObserver<TelemetryCommand> future) {
|
||||
this.telemetryCommandRef.set(future);
|
||||
}
|
||||
|
||||
public static GrpcClientChannel create(
|
||||
ChannelManager channelManager,
|
||||
String group,
|
||||
String clientId,
|
||||
TelemetryCommandManager manager
|
||||
) {
|
||||
return create(Context.current(), channelManager, group, clientId, manager);
|
||||
}
|
||||
|
||||
public static GrpcClientChannel create(
|
||||
Context ctx,
|
||||
ChannelManager channelManager,
|
||||
String group,
|
||||
String clientId,
|
||||
TelemetryCommandManager manager
|
||||
) {
|
||||
GrpcClientChannel channel = channelManager.createChannel(
|
||||
ctx,
|
||||
buildKey(group, clientId),
|
||||
context -> new GrpcClientChannel(context, group, clientId, manager),
|
||||
GrpcClientChannel.class
|
||||
);
|
||||
|
||||
channelManager.addGroupClientId(group, clientId);
|
||||
return channel;
|
||||
}
|
||||
|
||||
public static GrpcClientChannel getChannel(ChannelManager channelManager, String group, String clientId) {
|
||||
return channelManager.getChannel(buildKey(group, clientId), GrpcClientChannel.class);
|
||||
}
|
||||
|
||||
public static GrpcClientChannel removeChannel(ChannelManager channelManager, String group, String clientId) {
|
||||
return channelManager.removeChannel(buildKey(group, clientId), GrpcClientChannel.class);
|
||||
}
|
||||
|
||||
private static String buildKey(String group, String clientId) {
|
||||
return group + "@" + clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable() {
|
||||
return this.telemetryCommandRef.get() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write response to corresponding remote client
|
||||
*
|
||||
* @param msg Target write object, {@link RemotingCommand} or {@link TelemetryCommand}
|
||||
* @return Always success {@link ChannelFuture}
|
||||
* <p>
|
||||
* Case {@link RequestCode#CHECK_TRANSACTION_STATE}
|
||||
* @see org.apache.rocketmq.broker.client.net.Broker2Client#checkProducerTransactionState
|
||||
* </p>
|
||||
* <p>
|
||||
* Case {@link RequestCode#GET_CONSUMER_RUNNING_INFO}
|
||||
* @see org.apache.rocketmq.broker.processor.AdminBrokerProcessor
|
||||
* getConsumerRunningInfo
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object msg) {
|
||||
StreamObserver<TelemetryCommand> streamObserver = telemetryCommandRef.get();
|
||||
if (msg instanceof RemotingCommand) {
|
||||
RemotingCommand command = (RemotingCommand) msg;
|
||||
try {
|
||||
switch (command.getCode()) {
|
||||
case RequestCode.CHECK_TRANSACTION_STATE: {
|
||||
final CheckTransactionStateRequestHeader header = (CheckTransactionStateRequestHeader) command.readCustomHeader();
|
||||
MessageExt messageExt = MessageDecoder.decode(ByteBuffer.wrap(command.getBody()), true, false, false);
|
||||
TransactionId transactionId = TransactionId.genByBrokerTransactionId(RemotingUtil.string2SocketAddress(localAddress),
|
||||
header.getTransactionId(), messageExt.getCommitLogOffset(), messageExt.getQueueOffset());
|
||||
streamObserver.onNext(TelemetryCommand.newBuilder()
|
||||
.setRecoverOrphanedTransactionCommand(RecoverOrphanedTransactionCommand.newBuilder()
|
||||
.setTransactionId(transactionId.getProxyTransactionId())
|
||||
.setOrphanedTransactionalMessage(GrpcConverter.buildMessage(messageExt))
|
||||
.setMessageQueue(GrpcConverter.buildMessageQueue(messageExt, header.getBrokerName()))
|
||||
.build())
|
||||
.build());
|
||||
break;
|
||||
}
|
||||
case RequestCode.GET_CONSUMER_RUNNING_INFO: {
|
||||
final GetConsumerRunningInfoRequestHeader header = (GetConsumerRunningInfoRequestHeader) command.readCustomHeader();
|
||||
if (!header.isJstackEnable()) {
|
||||
break;
|
||||
}
|
||||
String nonce = manager.putCommand(command.getOpaque());
|
||||
streamObserver.onNext(TelemetryCommand.newBuilder()
|
||||
.setPrintThreadStackTraceCommand(PrintThreadStackTraceCommand.newBuilder()
|
||||
.setNonce(nonce)
|
||||
.build())
|
||||
.build());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
if (msg instanceof TelemetryCommand) {
|
||||
TelemetryCommand response = (TelemetryCommand) msg;
|
||||
streamObserver.onNext(response);
|
||||
}
|
||||
return super.writeAndFlush(msg);
|
||||
}
|
||||
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.proxy.grpc.v2.channel;
|
||||
|
||||
import io.grpc.Context;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.apache.rocketmq.proxy.service.out.ProxyOutService;
|
||||
|
||||
public class GrpcChannelManager {
|
||||
private final ProxyOutService proxyOutService;
|
||||
protected final ConcurrentMap<String /* group */, Map<String, GrpcClientChannel>/* clientId */> groupClientIdChannelMap = new ConcurrentHashMap<>();
|
||||
|
||||
protected final AtomicLong nonceIdGenerator = new AtomicLong(0);
|
||||
protected final ConcurrentMap<String /* nonce */, CompletableFuture<?>> resultNonceFutureMap = new ConcurrentHashMap<>();
|
||||
|
||||
public GrpcChannelManager(ProxyOutService proxyOutService) {
|
||||
this.proxyOutService = proxyOutService;
|
||||
}
|
||||
|
||||
public GrpcClientChannel createChannel(Context ctx, String group, String clientId) {
|
||||
this.groupClientIdChannelMap.compute(group, (groupKey, clientIdMap) -> {
|
||||
if (clientIdMap == null) {
|
||||
clientIdMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
clientIdMap.computeIfAbsent(clientId, clientIdKey -> new GrpcClientChannel(proxyOutService, this, ctx, group, clientId));
|
||||
return clientIdMap;
|
||||
});
|
||||
return getChannel(group, clientId);
|
||||
}
|
||||
|
||||
public GrpcClientChannel getChannel(String group, String clientId) {
|
||||
Map<String, GrpcClientChannel> clientIdChannelMap = this.groupClientIdChannelMap.get(group);
|
||||
if (clientIdChannelMap == null) {
|
||||
return null;
|
||||
}
|
||||
return clientIdChannelMap.get(clientId);
|
||||
}
|
||||
|
||||
public GrpcClientChannel removeChannel(String group, String clientId) {
|
||||
AtomicReference<GrpcClientChannel> channelRef = new AtomicReference<>();
|
||||
this.groupClientIdChannelMap.computeIfPresent(group, (groupKey, clientIdMap) -> {
|
||||
channelRef.set(clientIdMap.remove(clientId));
|
||||
if (clientIdMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return clientIdMap;
|
||||
});
|
||||
return channelRef.get();
|
||||
}
|
||||
|
||||
public String addResponseFuture(CompletableFuture<?> responseFuture) {
|
||||
String nonce = this.nextNonce();
|
||||
this.resultNonceFutureMap.put(nonce, responseFuture);
|
||||
return nonce;
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> getAndRemoveResponseFuture(String nonce) {
|
||||
return (CompletableFuture<T>) this.resultNonceFutureMap.remove(nonce);
|
||||
}
|
||||
|
||||
protected String nextNonce() {
|
||||
return String.valueOf(this.nonceIdGenerator.getAndIncrement());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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.grpc.v2.channel;
|
||||
|
||||
import apache.rocketmq.v2.PrintThreadStackTraceCommand;
|
||||
import apache.rocketmq.v2.RecoverOrphanedTransactionCommand;
|
||||
import apache.rocketmq.v2.TelemetryCommand;
|
||||
import apache.rocketmq.v2.VerifyMessageCommand;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import io.netty.channel.ChannelId;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
|
||||
import org.apache.rocketmq.common.protocol.header.CheckTransactionStateRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumeMessageDirectlyResultRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.GetConsumerRunningInfoRequestHeader;
|
||||
import org.apache.rocketmq.proxy.grpc.interceptor.InterceptorConstants;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.service.out.ProxyChannel;
|
||||
import org.apache.rocketmq.proxy.service.out.ProxyOutService;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionId;
|
||||
import org.apache.rocketmq.remoting.common.RemotingUtil;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class GrpcClientChannel extends ProxyChannel {
|
||||
|
||||
protected static final String SEPARATOR = "@";
|
||||
|
||||
private final GrpcChannelManager grpcChannelManager;
|
||||
|
||||
private final AtomicReference<StreamObserver<TelemetryCommand>> telemetryCommandRef = new AtomicReference<>();
|
||||
private final String group;
|
||||
private final String clientId;
|
||||
private final String remoteAddress;
|
||||
private final String localAddress;
|
||||
|
||||
public GrpcClientChannel(ProxyOutService proxyOutService, GrpcChannelManager grpcChannelManager, Context ctx, String group, String clientId) {
|
||||
super(proxyOutService, null, new GrpcChannelId(group, clientId));
|
||||
this.grpcChannelManager = grpcChannelManager;
|
||||
this.remoteAddress = InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.REMOTE_ADDRESS);
|
||||
this.localAddress = InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.LOCAL_ADDRESS);
|
||||
this.group = group;
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
protected static class GrpcChannelId implements ChannelId {
|
||||
|
||||
private final String group;
|
||||
private final String clientId;
|
||||
|
||||
public GrpcChannelId(String group, String clientId) {
|
||||
this.group = group;
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asShortText() {
|
||||
return this.clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asLongText() {
|
||||
return this.group + SEPARATOR + this.clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ChannelId o) {
|
||||
if (this == o) {
|
||||
return 0;
|
||||
}
|
||||
if (o instanceof GrpcChannelId) {
|
||||
GrpcChannelId other = (GrpcChannelId) o;
|
||||
return ComparisonChain.start()
|
||||
.compare(this.group, other.group)
|
||||
.compare(this.clientId, other.clientId)
|
||||
.result();
|
||||
}
|
||||
|
||||
return asLongText().compareTo(o.asLongText());
|
||||
}
|
||||
}
|
||||
|
||||
public void setClientObserver(StreamObserver<TelemetryCommand> future) {
|
||||
this.telemetryCommandRef.set(future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return this.telemetryCommandRef.get() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return this.telemetryCommandRef.get() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable() {
|
||||
return this.telemetryCommandRef.get() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SocketAddress localAddress0() {
|
||||
return RemotingUtil.string2SocketAddress(this.localAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SocketAddress remoteAddress0() {
|
||||
return RemotingUtil.string2SocketAddress(this.remoteAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> processOtherMessage(Object msg) {
|
||||
if (msg instanceof TelemetryCommand) {
|
||||
TelemetryCommand response = (TelemetryCommand) msg;
|
||||
this.getTelemetryCommandStreamObserver().onNext(response);
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> processCheckTransaction(CheckTransactionStateRequestHeader header,
|
||||
MessageExt messageExt, TransactionId transactionId) {
|
||||
this.getTelemetryCommandStreamObserver().onNext(TelemetryCommand.newBuilder()
|
||||
.setRecoverOrphanedTransactionCommand(RecoverOrphanedTransactionCommand.newBuilder()
|
||||
.setTransactionId(transactionId.getProxyTransactionId())
|
||||
.setOrphanedTransactionalMessage(GrpcConverter.buildMessage(messageExt))
|
||||
.setMessageQueue(GrpcConverter.buildMessageQueue(messageExt, header.getBrokerName()))
|
||||
.build())
|
||||
.build());
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> processGetConsumerRunningInfo(RemotingCommand command,
|
||||
GetConsumerRunningInfoRequestHeader header,
|
||||
CompletableFuture<ConsumerRunningInfo> responseFuture) {
|
||||
if (!header.isJstackEnable()) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
this.getTelemetryCommandStreamObserver().onNext(TelemetryCommand.newBuilder()
|
||||
.setPrintThreadStackTraceCommand(PrintThreadStackTraceCommand.newBuilder()
|
||||
.setNonce(this.grpcChannelManager.addResponseFuture(responseFuture))
|
||||
.build())
|
||||
.build());
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> processConsumeMessageDirectly(RemotingCommand command,
|
||||
ConsumeMessageDirectlyResultRequestHeader header,
|
||||
MessageExt messageExt, CompletableFuture<ConsumeMessageDirectlyResult> responseFuture) {
|
||||
this.getTelemetryCommandStreamObserver().onNext(TelemetryCommand.newBuilder()
|
||||
.setVerifyMessageCommand(VerifyMessageCommand.newBuilder()
|
||||
.setNonce(this.grpcChannelManager.addResponseFuture(responseFuture))
|
||||
.setMessage(GrpcConverter.buildMessage(messageExt))
|
||||
.build())
|
||||
.build());
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public String getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
public String getLocalAddress() {
|
||||
return localAddress;
|
||||
}
|
||||
|
||||
public StreamObserver<TelemetryCommand> getTelemetryCommandStreamObserver() {
|
||||
return this.telemetryCommandRef.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* 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.grpc.v2.client;
|
||||
|
||||
import apache.rocketmq.v2.ClientType;
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.FilterExpression;
|
||||
import apache.rocketmq.v2.HeartbeatRequest;
|
||||
import apache.rocketmq.v2.HeartbeatResponse;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationRequest;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationResponse;
|
||||
import apache.rocketmq.v2.Resource;
|
||||
import apache.rocketmq.v2.Settings;
|
||||
import apache.rocketmq.v2.SubscriptionEntry;
|
||||
import apache.rocketmq.v2.TelemetryCommand;
|
||||
import apache.rocketmq.v2.ThreadStackTrace;
|
||||
import apache.rocketmq.v2.VerifyMessageResult;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.broker.client.ClientChannelInfo;
|
||||
import org.apache.rocketmq.broker.client.ConsumerGroupEvent;
|
||||
import org.apache.rocketmq.broker.client.ConsumerIdsChangeListener;
|
||||
import org.apache.rocketmq.broker.client.ProducerChangeListener;
|
||||
import org.apache.rocketmq.broker.client.ProducerGroupEvent;
|
||||
import org.apache.rocketmq.common.MQVersion;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
|
||||
import org.apache.rocketmq.common.filter.FilterAPI;
|
||||
import org.apache.rocketmq.common.protocol.body.CMResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
|
||||
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.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.channel.GrpcChannelManager;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.AbstractMessingActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcClientSettingsManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.GrpcContextConstants;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.channel.GrpcClientChannel;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcProxyException;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseWriter;
|
||||
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
|
||||
import org.apache.rocketmq.remoting.protocol.LanguageCode;
|
||||
|
||||
public class ClientActivity extends AbstractMessingActivity {
|
||||
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
private final GrpcChannelManager grpcChannelManager;
|
||||
|
||||
public ClientActivity(MessagingProcessor messagingProcessor,
|
||||
GrpcClientSettingsManager grpcClientSettingsManager) {
|
||||
super(messagingProcessor, grpcClientSettingsManager);
|
||||
|
||||
this.grpcChannelManager = new GrpcChannelManager(messagingProcessor.getProxyOutService());
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
this.messagingProcessor.registerConsumerListener(new ConsumerIdsChangeListenerImpl());
|
||||
this.messagingProcessor.registerProducerListener(new ProducerChangeListenerImpl());
|
||||
}
|
||||
|
||||
public CompletableFuture<HeartbeatResponse> heartbeat(Context ctx, HeartbeatRequest request) {
|
||||
CompletableFuture<HeartbeatResponse> future = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
ProxyContext context = createContext(ctx);
|
||||
String clientId = context.getVal(GrpcContextConstants.CLIENT_ID);
|
||||
LanguageCode languageCode = context.getVal(GrpcContextConstants.LANGUAGE);
|
||||
|
||||
Settings clientSettings = grpcClientSettingsManager.getClientSettings(clientId);
|
||||
switch (clientSettings.getClientType()) {
|
||||
case PRODUCER: {
|
||||
for (Resource topic : clientSettings.getPublishing().getTopicsList()) {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
GrpcClientChannel channel = this.grpcChannelManager.createChannel(ctx, topicName, clientId);
|
||||
ClientChannelInfo clientChannelInfo = new ClientChannelInfo(channel, clientId, languageCode, MQVersion.Version.V5_0_0.ordinal());
|
||||
// use topic name as producer group
|
||||
this.messagingProcessor.registerProducer(context, topicName, clientChannelInfo);
|
||||
this.messagingProcessor.addTransactionSubscription(context, topicName, topicName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PUSH_CONSUMER:
|
||||
case SIMPLE_CONSUMER: {
|
||||
if (!request.hasGroup()) {
|
||||
throw new GrpcProxyException(Code.ILLEGAL_CONSUMER_GROUP, "group cannot be empty for consumer");
|
||||
}
|
||||
String consumerGroup = GrpcConverter.wrapResourceWithNamespace(request.getGroup());
|
||||
GrpcClientChannel channel = this.grpcChannelManager.createChannel(ctx, consumerGroup, clientId);
|
||||
ClientChannelInfo clientChannelInfo = new ClientChannelInfo(channel, clientId, languageCode, MQVersion.Version.V5_0_0.ordinal());
|
||||
|
||||
this.messagingProcessor.registerConsumer(
|
||||
context,
|
||||
consumerGroup,
|
||||
clientChannelInfo,
|
||||
this.buildConsumeType(clientSettings.getClientType()),
|
||||
MessageModel.CLUSTERING,
|
||||
ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET,
|
||||
this.buildSubscriptionDataSet(clientSettings.getSubscription().getSubscriptionsList())
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("ClientType not exist " + clientSettings.getClientType());
|
||||
}
|
||||
}
|
||||
future.complete(HeartbeatResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build());
|
||||
return future;
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
public CompletableFuture<NotifyClientTerminationResponse> notifyClientTermination(Context ctx, NotifyClientTerminationRequest request) {
|
||||
CompletableFuture<NotifyClientTerminationResponse> future = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
ProxyContext context = createContext(ctx);
|
||||
String clientId = context.getVal(GrpcContextConstants.CLIENT_ID);
|
||||
LanguageCode languageCode = context.getVal(GrpcContextConstants.LANGUAGE);
|
||||
Settings clientSettings = grpcClientSettingsManager.getClientSettings(clientId);
|
||||
|
||||
switch (clientSettings.getClientType()) {
|
||||
case PRODUCER:
|
||||
for (Resource topic : clientSettings.getPublishing().getTopicsList()) {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
// user topic name as producer group
|
||||
GrpcClientChannel channel = this.grpcChannelManager.removeChannel(topicName, clientId);
|
||||
ClientChannelInfo clientChannelInfo = new ClientChannelInfo(channel, clientId, languageCode, MQVersion.Version.V5_0_0.ordinal());
|
||||
if (channel != null) {
|
||||
this.messagingProcessor.unRegisterProducer(context, topicName, clientChannelInfo);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PUSH_CONSUMER:
|
||||
case SIMPLE_CONSUMER:
|
||||
if (!request.hasGroup()) {
|
||||
throw new GrpcProxyException(Code.ILLEGAL_CONSUMER_GROUP, "group cannot be empty for consumer");
|
||||
}
|
||||
String consumerGroup = GrpcConverter.wrapResourceWithNamespace(request.getGroup());
|
||||
GrpcClientChannel channel = this.grpcChannelManager.removeChannel(consumerGroup, clientId);
|
||||
ClientChannelInfo clientChannelInfo = new ClientChannelInfo(channel, clientId, languageCode, MQVersion.Version.V5_0_0.ordinal());
|
||||
if (channel != null) {
|
||||
this.messagingProcessor.unRegisterConsumer(context, consumerGroup, clientChannelInfo);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
future.complete(NotifyClientTerminationResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build());
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
public StreamObserver<TelemetryCommand> telemetry(Context ctx, StreamObserver<TelemetryCommand> responseObserver) {
|
||||
return new StreamObserver<TelemetryCommand>() {
|
||||
@Override
|
||||
public void onNext(TelemetryCommand request) {
|
||||
switch (request.getCommandCase()) {
|
||||
case SETTINGS: {
|
||||
responseObserver.onNext(processClientSettings(ctx, request, responseObserver));
|
||||
break;
|
||||
}
|
||||
case THREAD_STACK_TRACE: {
|
||||
reportThreadStackTrace(ctx, request.getThreadStackTrace());
|
||||
break;
|
||||
}
|
||||
case VERIFY_MESSAGE_RESULT: {
|
||||
reportVerifyMessageResult(ctx, request.getVerifyMessageResult());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ResponseWriter.writeException(responseObserver, new StatusRuntimeException(
|
||||
Status.INVALID_ARGUMENT.withDescription("")
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
log.error("telemetry on error", t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected TelemetryCommand processClientSettings(Context ctx, TelemetryCommand request, StreamObserver<TelemetryCommand> responseObserver) {
|
||||
ProxyContext context = createContext(ctx);
|
||||
String clientId = context.getVal(GrpcContextConstants.CLIENT_ID);
|
||||
grpcClientSettingsManager.updateClientSettings(clientId, request.getSettings());
|
||||
Settings settings = grpcClientSettingsManager.getClientSettings(clientId);
|
||||
if (settings.hasPublishing()) {
|
||||
for (Resource topic : settings.getPublishing().getTopicsList()) {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
GrpcClientChannel producerChannel = this.grpcChannelManager.createChannel(ctx, topicName, clientId);
|
||||
producerChannel.setClientObserver(responseObserver);
|
||||
}
|
||||
}
|
||||
if (settings.hasSubscription()) {
|
||||
String groupName = GrpcConverter.wrapResourceWithNamespace(settings.getSubscription().getGroup());
|
||||
GrpcClientChannel consumerChannel = this.grpcChannelManager.createChannel(ctx, groupName, clientId);
|
||||
consumerChannel.setClientObserver(responseObserver);
|
||||
}
|
||||
return TelemetryCommand.newBuilder()
|
||||
.setSettings(settings)
|
||||
.build();
|
||||
}
|
||||
|
||||
protected void reportThreadStackTrace(Context ctx, ThreadStackTrace request) {
|
||||
String nonce = request.getNonce();
|
||||
String threadStack = request.getThreadStackTrace();
|
||||
CompletableFuture<ConsumerRunningInfo> responseFuture = this.grpcChannelManager.getAndRemoveResponseFuture(nonce);
|
||||
if (responseFuture != null) {
|
||||
try {
|
||||
ConsumerRunningInfo runningInfo = new ConsumerRunningInfo();
|
||||
runningInfo.setJstack(threadStack);
|
||||
responseFuture.complete(runningInfo);
|
||||
} catch (Throwable t) {
|
||||
responseFuture.completeExceptionally(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void reportVerifyMessageResult(Context ctx, VerifyMessageResult request) {
|
||||
String nonce = request.getNonce();
|
||||
CompletableFuture<ConsumeMessageDirectlyResult> responseFuture = this.grpcChannelManager.getAndRemoveResponseFuture(nonce);
|
||||
if (responseFuture != null) {
|
||||
try {
|
||||
ConsumeMessageDirectlyResult result = this.buildConsumeMessageDirectlyResult(request);
|
||||
responseFuture.complete(result);
|
||||
} catch (Throwable t) {
|
||||
responseFuture.completeExceptionally(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected ConsumeMessageDirectlyResult buildConsumeMessageDirectlyResult(VerifyMessageResult request) {
|
||||
ConsumeMessageDirectlyResult consumeMessageDirectlyResult = new ConsumeMessageDirectlyResult();
|
||||
switch (request.getStatus().getCode().getNumber()) {
|
||||
case Code.OK_VALUE: {
|
||||
consumeMessageDirectlyResult.setConsumeResult(CMResult.CR_SUCCESS);
|
||||
break;
|
||||
}
|
||||
case Code.FAILED_TO_CONSUME_MESSAGE_VALUE: {
|
||||
consumeMessageDirectlyResult.setConsumeResult(CMResult.CR_LATER);
|
||||
break;
|
||||
}
|
||||
case Code.MESSAGE_CORRUPTED_VALUE: {
|
||||
consumeMessageDirectlyResult.setConsumeResult(CMResult.CR_RETURN_NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
consumeMessageDirectlyResult.setRemark("from gRPC client");
|
||||
return consumeMessageDirectlyResult;
|
||||
}
|
||||
|
||||
protected ConsumeType buildConsumeType(ClientType clientType) {
|
||||
switch (clientType) {
|
||||
case SIMPLE_CONSUMER:
|
||||
return ConsumeType.CONSUME_ACTIVELY;
|
||||
case PUSH_CONSUMER:
|
||||
return ConsumeType.CONSUME_PASSIVELY;
|
||||
default:
|
||||
throw new IllegalArgumentException("Client type is not consumer, type: " + clientType);
|
||||
}
|
||||
}
|
||||
|
||||
protected Set<SubscriptionData> buildSubscriptionDataSet(List<SubscriptionEntry> subscriptionEntryList) {
|
||||
Set<SubscriptionData> subscriptionDataSet = new HashSet<>();
|
||||
for (SubscriptionEntry sub : subscriptionEntryList) {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(sub.getTopic());
|
||||
FilterExpression filterExpression = sub.getExpression();
|
||||
subscriptionDataSet.add(buildSubscriptionData(topicName, filterExpression));
|
||||
}
|
||||
return subscriptionDataSet;
|
||||
}
|
||||
|
||||
protected SubscriptionData buildSubscriptionData(String topicName, FilterExpression filterExpression) {
|
||||
String expression = filterExpression.getExpression();
|
||||
String expressionType = GrpcConverter.buildExpressionType(filterExpression.getType());
|
||||
try {
|
||||
return FilterAPI.build(topicName, expression, expressionType);
|
||||
} catch (Exception e) {
|
||||
throw new GrpcProxyException(Code.ILLEGAL_FILTER_EXPRESSION, "expression format is not correct", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected class ConsumerIdsChangeListenerImpl implements ConsumerIdsChangeListener {
|
||||
|
||||
@Override
|
||||
public void handle(ConsumerGroupEvent event, String group, Object... args) {
|
||||
if (event == ConsumerGroupEvent.CLIENT_UNREGISTER) {
|
||||
if (args == null || args.length < 1) {
|
||||
return;
|
||||
}
|
||||
if (args[0] instanceof ClientChannelInfo) {
|
||||
ClientChannelInfo clientChannelInfo = (ClientChannelInfo) args[0];
|
||||
grpcChannelManager.removeChannel(group, clientChannelInfo.getClientId());
|
||||
grpcClientSettingsManager.removeClientSettings(clientChannelInfo.getClientId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected class ProducerChangeListenerImpl implements ProducerChangeListener {
|
||||
|
||||
@Override
|
||||
public void handle(ProducerGroupEvent event, String group, ClientChannelInfo clientChannelInfo) {
|
||||
if (event == ProducerGroupEvent.CLIENT_UNREGISTER) {
|
||||
grpcChannelManager.removeChannel(group, clientChannelInfo.getClientId());
|
||||
grpcClientSettingsManager.removeClientSettings(clientChannelInfo.getClientId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-9
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.proxy.grpc.v2.service;
|
||||
package org.apache.rocketmq.proxy.grpc.v2.common;
|
||||
|
||||
import apache.rocketmq.v2.ExponentialBackoff;
|
||||
import apache.rocketmq.v2.Publishing;
|
||||
@@ -23,13 +23,11 @@ import apache.rocketmq.v2.RetryPolicy;
|
||||
import apache.rocketmq.v2.Settings;
|
||||
import apache.rocketmq.v2.Subscription;
|
||||
import com.google.protobuf.util.Durations;
|
||||
import io.grpc.Context;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.apache.rocketmq.proxy.common.utils.ProxyUtils;
|
||||
import org.apache.rocketmq.proxy.grpc.interceptor.InterceptorConstants;
|
||||
|
||||
public class GrpcClientManager {
|
||||
public class GrpcClientSettingsManager {
|
||||
|
||||
// TODO: read config from topic or subscription configManager
|
||||
protected static final Settings DEFAULT_PRODUCER_SETTINGS = Settings.newBuilder()
|
||||
@@ -63,11 +61,6 @@ public class GrpcClientManager {
|
||||
.build();
|
||||
protected static final Map<String, Settings> CLIENT_SETTINGS_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
public Settings getClientSettings(Context ctx) {
|
||||
String clientId = InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.CLIENT_ID);
|
||||
return CLIENT_SETTINGS_MAP.get(clientId);
|
||||
}
|
||||
|
||||
public Settings getClientSettings(String clientId) {
|
||||
return CLIENT_SETTINGS_MAP.get(clientId);
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* 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.grpc.v2.common;
|
||||
|
||||
import apache.rocketmq.v2.Broker;
|
||||
import apache.rocketmq.v2.Digest;
|
||||
import apache.rocketmq.v2.DigestType;
|
||||
import apache.rocketmq.v2.Encoding;
|
||||
import apache.rocketmq.v2.FilterType;
|
||||
import apache.rocketmq.v2.Message;
|
||||
import apache.rocketmq.v2.MessageQueue;
|
||||
import apache.rocketmq.v2.MessageType;
|
||||
import apache.rocketmq.v2.Resource;
|
||||
import apache.rocketmq.v2.SystemProperties;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.util.Timestamps;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.filter.ExpressionType;
|
||||
import org.apache.rocketmq.common.message.MessageConst;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.NamespaceUtil;
|
||||
import org.apache.rocketmq.common.sysflag.MessageSysFlag;
|
||||
import org.apache.rocketmq.common.utils.BinaryUtil;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
|
||||
public class GrpcConverter {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
public static String wrapResourceWithNamespace(Resource resource) {
|
||||
return NamespaceUtil.wrapNamespace(resource.getResourceNamespace(), resource.getName());
|
||||
}
|
||||
|
||||
public static MessageQueue buildMessageQueue(MessageExt messageExt, String brokerName) {
|
||||
Broker broker = Broker.getDefaultInstance();
|
||||
if (!StringUtils.isEmpty(brokerName)) {
|
||||
broker = Broker.newBuilder()
|
||||
.setName(brokerName)
|
||||
.setId(0)
|
||||
.build();
|
||||
}
|
||||
return MessageQueue.newBuilder()
|
||||
.setId(messageExt.getQueueId())
|
||||
.setTopic(Resource.newBuilder()
|
||||
.setName(NamespaceUtil.withoutNamespace(messageExt.getTopic()))
|
||||
.setResourceNamespace(NamespaceUtil.getNamespaceFromResource(messageExt.getTopic()))
|
||||
.build())
|
||||
.setBroker(broker)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static String buildExpressionType(FilterType filterType) {
|
||||
switch (filterType) {
|
||||
case SQL:
|
||||
return ExpressionType.SQL92;
|
||||
case TAG:
|
||||
default:
|
||||
return ExpressionType.TAG;
|
||||
}
|
||||
}
|
||||
|
||||
public static Message buildMessage(MessageExt messageExt) {
|
||||
Map<String, String> userProperties = buildUserAttributes(messageExt);
|
||||
SystemProperties systemProperties = buildSystemProperties(messageExt);
|
||||
Resource topic = buildResource(messageExt.getTopic());
|
||||
|
||||
return Message.newBuilder()
|
||||
.setTopic(topic)
|
||||
.putAllUserProperties(userProperties)
|
||||
.setSystemProperties(systemProperties)
|
||||
.setBody(ByteString.copyFrom(messageExt.getBody()))
|
||||
.build();
|
||||
}
|
||||
|
||||
protected static Map<String, String> buildUserAttributes(MessageExt messageExt) {
|
||||
Map<String, String> userAttributes = new HashMap<>();
|
||||
Map<String, String> properties = messageExt.getProperties();
|
||||
|
||||
for (Map.Entry<String, String> property : properties.entrySet()) {
|
||||
if (!MessageConst.STRING_HASH_SET.contains(property.getKey())) {
|
||||
userAttributes.put(property.getKey(), property.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return userAttributes;
|
||||
}
|
||||
|
||||
protected static SystemProperties buildSystemProperties(MessageExt messageExt) {
|
||||
SystemProperties.Builder systemPropertiesBuilder = SystemProperties.newBuilder();
|
||||
|
||||
// tag
|
||||
String tag = messageExt.getUserProperty(MessageConst.PROPERTY_TAGS);
|
||||
if (tag != null) {
|
||||
systemPropertiesBuilder.setTag(tag);
|
||||
}
|
||||
|
||||
// keys
|
||||
String keys = messageExt.getKeys();
|
||||
if (keys != null) {
|
||||
String[] keysArray = keys.split(MessageConst.KEY_SEPARATOR);
|
||||
systemPropertiesBuilder.addAllKeys(Arrays.asList(keysArray));
|
||||
}
|
||||
|
||||
// message_id
|
||||
String uniqKey = messageExt.getProperty(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);
|
||||
if (uniqKey != null) {
|
||||
systemPropertiesBuilder.setMessageId(uniqKey);
|
||||
}
|
||||
|
||||
// body_digest & body_encoding
|
||||
String md5Result = BinaryUtil.generateMd5(messageExt.getBody());
|
||||
Digest digest = Digest.newBuilder()
|
||||
.setType(DigestType.MD5)
|
||||
.setChecksum(md5Result)
|
||||
.build();
|
||||
systemPropertiesBuilder.setBodyDigest(digest);
|
||||
|
||||
if ((messageExt.getSysFlag() & MessageSysFlag.COMPRESSED_FLAG) == MessageSysFlag.COMPRESSED_FLAG) {
|
||||
systemPropertiesBuilder.setBodyEncoding(Encoding.GZIP);
|
||||
} else {
|
||||
systemPropertiesBuilder.setBodyEncoding(Encoding.IDENTITY);
|
||||
}
|
||||
|
||||
// message_type
|
||||
String isTrans = messageExt.getProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED);
|
||||
String isTransValue = "true";
|
||||
if (isTransValue.equals(isTrans)) {
|
||||
systemPropertiesBuilder.setMessageType(MessageType.TRANSACTION);
|
||||
} else if (messageExt.getProperty(MessageConst.PROPERTY_DELAY_TIME_LEVEL) != null
|
||||
|| messageExt.getProperty(MessageConst.PROPERTY_TIMER_DELIVER_MS) != null
|
||||
|| messageExt.getProperty(MessageConst.PROPERTY_TIMER_DELAY_SEC) != null) {
|
||||
systemPropertiesBuilder.setMessageType(MessageType.DELAY);
|
||||
} else if (messageExt.getProperty(MessageConst.PROPERTY_SHARDING_KEY) != null) {
|
||||
systemPropertiesBuilder.setMessageType(MessageType.FIFO);
|
||||
} else {
|
||||
systemPropertiesBuilder.setMessageType(MessageType.NORMAL);
|
||||
}
|
||||
|
||||
// born_timestamp (millis)
|
||||
long bornTimestamp = messageExt.getBornTimestamp();
|
||||
systemPropertiesBuilder.setBornTimestamp(Timestamps.fromMillis(bornTimestamp));
|
||||
|
||||
// born_host
|
||||
systemPropertiesBuilder.setBornHost(messageExt.getBornHostString());
|
||||
|
||||
// store_timestamp (millis)
|
||||
long storeTimestamp = messageExt.getStoreTimestamp();
|
||||
systemPropertiesBuilder.setStoreTimestamp(Timestamps.fromMillis(storeTimestamp));
|
||||
|
||||
// store_host
|
||||
SocketAddress storeHost = messageExt.getStoreHost();
|
||||
if (storeHost != null) {
|
||||
systemPropertiesBuilder.setStoreHost(storeHost.toString());
|
||||
}
|
||||
|
||||
// delivery_timestamp
|
||||
String deliverMsString;
|
||||
long deliverMs;
|
||||
if (messageExt.getProperty(MessageConst.PROPERTY_TIMER_DELAY_SEC) != null) {
|
||||
long delayMs = TimeUnit.SECONDS.toMillis(Long.parseLong(messageExt.getProperty(MessageConst.PROPERTY_TIMER_DELAY_SEC)));
|
||||
deliverMs = System.currentTimeMillis() + delayMs;
|
||||
systemPropertiesBuilder.setDeliveryTimestamp(Timestamps.fromMillis(deliverMs));
|
||||
} else {
|
||||
deliverMsString = messageExt.getProperty(MessageConst.PROPERTY_TIMER_DELIVER_MS);
|
||||
if (deliverMsString != null) {
|
||||
deliverMs = Long.parseLong(deliverMsString);
|
||||
systemPropertiesBuilder.setDeliveryTimestamp(Timestamps.fromMillis(deliverMs));
|
||||
}
|
||||
}
|
||||
|
||||
// sharding key
|
||||
String shardingKey = messageExt.getProperty(MessageConst.PROPERTY_SHARDING_KEY);
|
||||
if (shardingKey != null) {
|
||||
systemPropertiesBuilder.setMessageGroup(shardingKey);
|
||||
}
|
||||
|
||||
// receipt_handle && invisible_period
|
||||
ReceiptHandle receiptHandle = ReceiptHandle.create(messageExt);
|
||||
if (receiptHandle != null) {
|
||||
systemPropertiesBuilder.setReceiptHandle(receiptHandle.encode());
|
||||
}
|
||||
|
||||
// partition_id
|
||||
systemPropertiesBuilder.setQueueId(messageExt.getQueueId());
|
||||
|
||||
// partition_offset
|
||||
systemPropertiesBuilder.setQueueOffset(messageExt.getQueueOffset());
|
||||
|
||||
// delivery_attempt
|
||||
systemPropertiesBuilder.setDeliveryAttempt(messageExt.getReconsumeTimes() + 1);
|
||||
|
||||
// trace context
|
||||
String traceContext = messageExt.getProperty(MessageConst.PROPERTY_TRACE_CONTEXT);
|
||||
if (traceContext != null) {
|
||||
systemPropertiesBuilder.setTraceContext(traceContext);
|
||||
}
|
||||
|
||||
return systemPropertiesBuilder.build();
|
||||
}
|
||||
|
||||
public static Resource buildResource(String resourceNameWithNamespace) {
|
||||
return Resource.newBuilder()
|
||||
.setResourceNamespace(NamespaceUtil.getNamespaceFromResource(resourceNameWithNamespace))
|
||||
.setName(NamespaceUtil.withoutNamespace(resourceNameWithNamespace))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.grpc.v2.common;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.rocketmq.proxy.common.ProxyException;
|
||||
import org.apache.rocketmq.proxy.common.ProxyExceptionCode;
|
||||
|
||||
public class GrpcProxyException extends RuntimeException {
|
||||
|
||||
private ProxyException proxyException;
|
||||
private Code code;
|
||||
|
||||
private static final Map<ProxyExceptionCode, Code> CODE_MAPPING = new HashMap<>();
|
||||
|
||||
static {
|
||||
CODE_MAPPING.put(ProxyExceptionCode.INVALID_BROKER_NAME, Code.INVALID_RECEIPT_HANDLE);
|
||||
CODE_MAPPING.put(ProxyExceptionCode.RECEIPT_HANDLE_EXPIRED, Code.RECEIPT_HANDLE_EXPIRED);
|
||||
CODE_MAPPING.put(ProxyExceptionCode.FORBIDDEN, Code.FORBIDDEN);
|
||||
}
|
||||
|
||||
public GrpcProxyException(Code code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public GrpcProxyException(Code code, String message, Throwable t) {
|
||||
super(message, t);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public GrpcProxyException(ProxyException proxyException) {
|
||||
super(proxyException);
|
||||
this.proxyException = proxyException;
|
||||
}
|
||||
|
||||
public Code getCode() {
|
||||
if (this.code != null) {
|
||||
return this.code;
|
||||
}
|
||||
if (this.proxyException != null) {
|
||||
return CODE_MAPPING.getOrDefault(this.proxyException.getCode(), Code.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
return Code.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
}
|
||||
+11
-7
@@ -15,28 +15,32 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.proxy.grpc.v2.adapter;
|
||||
package org.apache.rocketmq.proxy.grpc.v2.common;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.Status;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.protocol.ResponseCode;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.common.ProxyException;
|
||||
import org.apache.rocketmq.proxy.common.utils.ExceptionUtils;
|
||||
|
||||
public class ResponseBuilder {
|
||||
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
public static Status buildStatus(Throwable t) {
|
||||
if (t instanceof CompletionException) {
|
||||
t = t.getCause();
|
||||
}
|
||||
t = ExceptionUtils.getRealException(t);
|
||||
|
||||
if (t instanceof ProxyException) {
|
||||
ProxyException proxyException = (ProxyException) t;
|
||||
return ResponseBuilder.buildStatus(proxyException.getCode(), proxyException.getMessage());
|
||||
t = new GrpcProxyException((ProxyException) t);
|
||||
}
|
||||
if (t instanceof GrpcProxyException) {
|
||||
GrpcProxyException grpcProxyException = (GrpcProxyException) t;
|
||||
return ResponseBuilder.buildStatus(grpcProxyException.getCode(), grpcProxyException.getMessage());
|
||||
}
|
||||
|
||||
log.error("internal server error", t);
|
||||
return ResponseBuilder.buildStatus(Code.INTERNAL_SERVER_ERROR, "internal error");
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.proxy.grpc.v2.adapter;
|
||||
package org.apache.rocketmq.proxy.grpc.v2.common;
|
||||
|
||||
import io.grpc.stub.ServerCallStreamObserver;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.grpc.v2.consumer;
|
||||
|
||||
import apache.rocketmq.v2.AckMessageEntry;
|
||||
import apache.rocketmq.v2.AckMessageRequest;
|
||||
import apache.rocketmq.v2.AckMessageResponse;
|
||||
import apache.rocketmq.v2.AckMessageResultEntry;
|
||||
import apache.rocketmq.v2.Code;
|
||||
import io.grpc.Context;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.client.consumer.AckStatus;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.AbstractMessingActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcClientSettingsManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
|
||||
|
||||
public class AckMessageActivity extends AbstractMessingActivity {
|
||||
|
||||
public AckMessageActivity(MessagingProcessor messagingProcessor,
|
||||
GrpcClientSettingsManager grpcClientSettingsManager) {
|
||||
super(messagingProcessor, grpcClientSettingsManager);
|
||||
}
|
||||
|
||||
public CompletableFuture<AckMessageResponse> ackMessage(Context ctx, AckMessageRequest request) {
|
||||
ProxyContext proxyContext = createContext(ctx);
|
||||
CompletableFuture<AckMessageResponse> future = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
CompletableFuture<AckMessageResultEntry>[] futures = new CompletableFuture[request.getEntriesCount()];
|
||||
for (int i = 0; i < request.getEntriesCount(); i++) {
|
||||
futures[i] = processAckMessage(proxyContext, request, request.getEntries(i));
|
||||
}
|
||||
CompletableFuture.allOf(futures).whenComplete((val, throwable) -> {
|
||||
if (throwable != null) {
|
||||
future.completeExceptionally(throwable);
|
||||
return;
|
||||
}
|
||||
List<AckMessageResultEntry> entryList = new ArrayList<>();
|
||||
for (CompletableFuture<AckMessageResultEntry> entryFuture : futures) {
|
||||
entryFuture.thenAccept(entryList::add);
|
||||
}
|
||||
AckMessageResponse.Builder responseBuilder = AckMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.addAllEntries(entryList);
|
||||
future.complete(responseBuilder.build());
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected CompletableFuture<AckMessageResultEntry> processAckMessage(ProxyContext ctx, AckMessageRequest request,
|
||||
AckMessageEntry ackMessageEntry) {
|
||||
CompletableFuture<AckMessageResultEntry> future = new CompletableFuture<>();
|
||||
AckMessageResultEntry.Builder failResult = AckMessageResultEntry.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.INTERNAL_SERVER_ERROR, "ack message failed"))
|
||||
.setMessageId(ackMessageEntry.getMessageId())
|
||||
.setReceiptHandle(ackMessageEntry.getReceiptHandle());
|
||||
|
||||
try {
|
||||
ReceiptHandle receiptHandle = ReceiptHandle.decode(ackMessageEntry.getReceiptHandle());
|
||||
|
||||
CompletableFuture<AckResult> ackResultFuture = this.messagingProcessor.ackMessage(
|
||||
ctx,
|
||||
receiptHandle,
|
||||
ackMessageEntry.getMessageId(),
|
||||
GrpcConverter.wrapResourceWithNamespace(request.getGroup()),
|
||||
GrpcConverter.wrapResourceWithNamespace(request.getTopic()));
|
||||
ackResultFuture
|
||||
.thenAccept(result -> future.complete(convertToAckMessageResultEntry(ctx, ackMessageEntry, result)))
|
||||
.exceptionally(throwable -> {
|
||||
future.complete(failResult.setStatus(ResponseBuilder.buildStatus(throwable)).build());
|
||||
return null;
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
future.complete(failResult.setStatus(ResponseBuilder.buildStatus(t)).build());
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected AckMessageResultEntry convertToAckMessageResultEntry(ProxyContext ctx, AckMessageEntry ackMessageEntry,
|
||||
AckResult ackResult) {
|
||||
if (AckStatus.OK.equals(ackResult.getStatus())) {
|
||||
return AckMessageResultEntry.newBuilder()
|
||||
.setMessageId(ackMessageEntry.getMessageId())
|
||||
.setReceiptHandle(ackMessageEntry.getReceiptHandle())
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build();
|
||||
}
|
||||
return AckMessageResultEntry.newBuilder()
|
||||
.setMessageId(ackMessageEntry.getMessageId())
|
||||
.setReceiptHandle(ackMessageEntry.getReceiptHandle())
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.INTERNAL_SERVER_ERROR, "ack failed: status is abnormal"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+76
@@ -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.grpc.v2.consumer;
|
||||
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationRequest;
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationResponse;
|
||||
import apache.rocketmq.v2.Code;
|
||||
import com.google.protobuf.util.Durations;
|
||||
import io.grpc.Context;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.client.consumer.AckStatus;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.AbstractMessingActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcClientSettingsManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
|
||||
|
||||
public class ChangeInvisibleDurationActivity extends AbstractMessingActivity {
|
||||
|
||||
public ChangeInvisibleDurationActivity(MessagingProcessor messagingProcessor,
|
||||
GrpcClientSettingsManager grpcClientSettingsManager) {
|
||||
super(messagingProcessor, grpcClientSettingsManager);
|
||||
}
|
||||
|
||||
public CompletableFuture<ChangeInvisibleDurationResponse> changeInvisibleDuration(Context ctx,
|
||||
ChangeInvisibleDurationRequest request) {
|
||||
ProxyContext context = createContext(ctx);
|
||||
CompletableFuture<ChangeInvisibleDurationResponse> future = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
ReceiptHandle receiptHandle = ReceiptHandle.decode(request.getReceiptHandle());
|
||||
|
||||
return this.messagingProcessor.changeInvisibleTime(
|
||||
context,
|
||||
receiptHandle,
|
||||
request.getMessageId(),
|
||||
GrpcConverter.wrapResourceWithNamespace(request.getGroup()),
|
||||
GrpcConverter.wrapResourceWithNamespace(request.getTopic()),
|
||||
Durations.toMillis(request.getInvisibleDuration())
|
||||
).thenApply(ackResult -> convertToChangeInvisibleDurationResponse(context, request, ackResult));
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected ChangeInvisibleDurationResponse convertToChangeInvisibleDurationResponse(ProxyContext ctx,
|
||||
ChangeInvisibleDurationRequest request, AckResult ackResult) {
|
||||
if (AckStatus.OK.equals(ackResult.getStatus())) {
|
||||
return ChangeInvisibleDurationResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.setReceiptHandle(ackResult.getExtraInfo())
|
||||
.build();
|
||||
}
|
||||
return ChangeInvisibleDurationResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.INTERNAL_SERVER_ERROR, "changeInvisibleDuration failed: status is abnormal"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+46
@@ -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.proxy.grpc.v2.consumer;
|
||||
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.common.utils.FilterUtils;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcClientSettingsManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.GrpcContextConstants;
|
||||
import org.apache.rocketmq.proxy.processor.PopMessageResultFilter;
|
||||
|
||||
public class PopMessageResultFilterImpl implements PopMessageResultFilter {
|
||||
|
||||
private final GrpcClientSettingsManager grpcClientSettingsManager;
|
||||
|
||||
public PopMessageResultFilterImpl(GrpcClientSettingsManager manager) {
|
||||
grpcClientSettingsManager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterResult filterMessage(ProxyContext ctx, String consumerGroup, SubscriptionData subscriptionData, MessageExt messageExt) {
|
||||
int maxAttempts = grpcClientSettingsManager.getClientSettings(ctx.getVal(GrpcContextConstants.CLIENT_ID)).getBackoffPolicy().getMaxAttempts();
|
||||
if (!FilterUtils.isTagMatched(subscriptionData.getTagsSet(), messageExt.getTags())) {
|
||||
return FilterResult.NO_MATCH;
|
||||
}
|
||||
if (messageExt.getReconsumeTimes() >= maxAttempts) {
|
||||
return FilterResult.TO_DLQ;
|
||||
}
|
||||
return FilterResult.MATCH;
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.grpc.v2.consumer;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.FilterExpression;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.ReceiveMessageResponse;
|
||||
import com.google.protobuf.util.Durations;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.constant.ConsumeInitMode;
|
||||
import org.apache.rocketmq.common.filter.FilterAPI;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.config.ConfigurationManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.AbstractMessingActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcClientSettingsManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
|
||||
import org.apache.rocketmq.proxy.processor.QueueSelector;
|
||||
import org.apache.rocketmq.proxy.service.route.MessageQueueSelector;
|
||||
import org.apache.rocketmq.proxy.service.route.MessageQueueView;
|
||||
import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue;
|
||||
|
||||
public class ReceiveMessageActivity extends AbstractMessingActivity {
|
||||
|
||||
public ReceiveMessageActivity(MessagingProcessor messagingProcessor,
|
||||
GrpcClientSettingsManager grpcClientSettingsManager) {
|
||||
super(messagingProcessor, grpcClientSettingsManager);
|
||||
}
|
||||
|
||||
public void receiveMessage(Context ctx, ReceiveMessageRequest request,
|
||||
StreamObserver<ReceiveMessageResponse> responseObserver) {
|
||||
ProxyContext proxyContext = createContext(ctx);
|
||||
boolean fifo = false;
|
||||
|
||||
ReceiveMessageResponseStreamWriter writer = new ReceiveMessageResponseStreamWriter(
|
||||
this.messagingProcessor,
|
||||
responseObserver
|
||||
);
|
||||
|
||||
long timeRemaining = ctx.getDeadline().timeRemaining(TimeUnit.MILLISECONDS);
|
||||
long pollTime = timeRemaining - ConfigurationManager.getProxyConfig().getLongPollingReserveTimeInMillis();
|
||||
if (pollTime <= 0) {
|
||||
pollTime = timeRemaining;
|
||||
}
|
||||
if (pollTime <= 0) {
|
||||
writer.write(proxyContext, Code.MESSAGE_NOT_FOUND, "no new message");
|
||||
return;
|
||||
}
|
||||
String topic = GrpcConverter.wrapResourceWithNamespace(request.getMessageQueue().getTopic());
|
||||
String group = GrpcConverter.wrapResourceWithNamespace(request.getGroup());
|
||||
FilterExpression filterExpression = request.getFilterExpression();
|
||||
SubscriptionData subscriptionData;
|
||||
try {
|
||||
subscriptionData = FilterAPI.build(topic, filterExpression.getExpression(),
|
||||
GrpcConverter.buildExpressionType(filterExpression.getType()));
|
||||
} catch (Exception e) {
|
||||
writer.write(proxyContext, Code.ILLEGAL_FILTER_EXPRESSION, e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
this.messagingProcessor.popMessage(
|
||||
proxyContext,
|
||||
new ReceiveMessageQueueSelector(
|
||||
request.getMessageQueue().getBroker().getName()
|
||||
),
|
||||
group,
|
||||
topic,
|
||||
request.getBatchSize(),
|
||||
Durations.toMillis(request.getInvisibleDuration()),
|
||||
pollTime,
|
||||
ConsumeInitMode.MAX,
|
||||
subscriptionData,
|
||||
fifo,
|
||||
new PopMessageResultFilterImpl(grpcClientSettingsManager),
|
||||
timeRemaining
|
||||
).thenAccept(popResult -> writer.write(proxyContext, request, popResult))
|
||||
.exceptionally(t -> {
|
||||
writer.write(proxyContext, request, t);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
protected static class ReceiveMessageQueueSelector implements QueueSelector {
|
||||
|
||||
private final String brokerName;
|
||||
|
||||
public ReceiveMessageQueueSelector(String brokerName) {
|
||||
this.brokerName = brokerName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectableMessageQueue select(ProxyContext ctx, MessageQueueView messageQueueView) {
|
||||
try {
|
||||
SelectableMessageQueue selectableMessageQueue = null;
|
||||
MessageQueueSelector messageQueueSelector = messageQueueView.getReadSelector();
|
||||
|
||||
if (StringUtils.isNotBlank(brokerName)) {
|
||||
selectableMessageQueue = messageQueueSelector.getQueueByBrokerName(brokerName);
|
||||
}
|
||||
|
||||
if (selectableMessageQueue == null) {
|
||||
selectableMessageQueue = messageQueueSelector.selectOne(true);
|
||||
}
|
||||
return selectableMessageQueue;
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.grpc.v2.consumer;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.Message;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.ReceiveMessageResponse;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.time.Duration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.apache.rocketmq.client.consumer.PopResult;
|
||||
import org.apache.rocketmq.client.consumer.PopStatus;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseWriter;
|
||||
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
|
||||
|
||||
public class ReceiveMessageResponseStreamWriter {
|
||||
|
||||
protected static final long NACK_INVISIBLE_TIME = Duration.ofSeconds(1).toMillis();
|
||||
|
||||
protected final MessagingProcessor messagingProcessor;
|
||||
protected final StreamObserver<ReceiveMessageResponse> streamObserver;
|
||||
|
||||
public ReceiveMessageResponseStreamWriter(
|
||||
MessagingProcessor messagingProcessor,
|
||||
StreamObserver<ReceiveMessageResponse> observer) {
|
||||
this.messagingProcessor = messagingProcessor;
|
||||
this.streamObserver = observer;
|
||||
}
|
||||
|
||||
public void write(ProxyContext ctx, ReceiveMessageRequest request, PopResult popResult) {
|
||||
PopStatus status = popResult.getPopStatus();
|
||||
List<MessageExt> messageFoundList = popResult.getMsgFoundList();
|
||||
try {
|
||||
switch (status) {
|
||||
case FOUND:
|
||||
if (messageFoundList.isEmpty()) {
|
||||
streamObserver.onNext(ReceiveMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.MESSAGE_NOT_FOUND, "no new message"))
|
||||
.build());
|
||||
} else {
|
||||
streamObserver.onNext(ReceiveMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build());
|
||||
Iterator<MessageExt> messageIterator = messageFoundList.iterator();
|
||||
while (messageIterator.hasNext()) {
|
||||
MessageExt curMessageExt = messageIterator.next();
|
||||
Message curMessage = convertToMessage(curMessageExt);
|
||||
try {
|
||||
streamObserver.onNext(ReceiveMessageResponse.newBuilder()
|
||||
.setMessage(curMessage)
|
||||
.build());
|
||||
} catch (Throwable t) {
|
||||
this.processThrowableWhenWriteMessage(t, ctx, request, curMessageExt);
|
||||
messageIterator.forEachRemaining(messageExt ->
|
||||
this.processThrowableWhenWriteMessage(t, ctx, request, messageExt));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case POLLING_FULL:
|
||||
streamObserver.onNext(ReceiveMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.TOO_MANY_REQUESTS, "polling full"))
|
||||
.build());
|
||||
break;
|
||||
case NO_NEW_MSG:
|
||||
case POLLING_NOT_FOUND:
|
||||
default:
|
||||
streamObserver.onNext(ReceiveMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.MESSAGE_NOT_FOUND, "no new message"))
|
||||
.build());
|
||||
break;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
write(ctx, request, t);
|
||||
} finally {
|
||||
streamObserver.onCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
protected Message convertToMessage(MessageExt messageExt) {
|
||||
return GrpcConverter.buildMessage(messageExt);
|
||||
}
|
||||
|
||||
protected void processThrowableWhenWriteMessage(Throwable throwable,
|
||||
ProxyContext ctx, ReceiveMessageRequest request, MessageExt messageExt) {
|
||||
|
||||
ReceiptHandle handle = ReceiptHandle.create(messageExt);
|
||||
if (handle == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.messagingProcessor.changeInvisibleTime(
|
||||
ctx,
|
||||
handle,
|
||||
messageExt.getMsgId(),
|
||||
GrpcConverter.wrapResourceWithNamespace(request.getGroup()),
|
||||
GrpcConverter.wrapResourceWithNamespace(request.getMessageQueue().getTopic()),
|
||||
NACK_INVISIBLE_TIME
|
||||
);
|
||||
}
|
||||
|
||||
public void write(ProxyContext ctx, Code code, String message) {
|
||||
ResponseWriter.write(
|
||||
streamObserver,
|
||||
ReceiveMessageResponse.newBuilder().setStatus(ResponseBuilder.buildStatus(code, message)).build()
|
||||
);
|
||||
}
|
||||
|
||||
public void write(ProxyContext ctx, ReceiveMessageRequest request, Throwable throwable) {
|
||||
ResponseWriter.write(
|
||||
streamObserver,
|
||||
ReceiveMessageResponse.newBuilder().setStatus(ResponseBuilder.buildStatus(throwable)).build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+65
@@ -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.proxy.grpc.v2.producer;
|
||||
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueRequest;
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueResponse;
|
||||
import io.grpc.Context;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.AbstractMessingActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcClientSettingsManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class ForwardMessageToDLQActivity extends AbstractMessingActivity {
|
||||
|
||||
public ForwardMessageToDLQActivity(MessagingProcessor messagingProcessor,
|
||||
GrpcClientSettingsManager grpcClientSettingsManager) {
|
||||
super(messagingProcessor, grpcClientSettingsManager);
|
||||
}
|
||||
|
||||
public CompletableFuture<ForwardMessageToDeadLetterQueueResponse> forwardMessageToDeadLetterQueue(Context ctx,
|
||||
ForwardMessageToDeadLetterQueueRequest request) {
|
||||
CompletableFuture<ForwardMessageToDeadLetterQueueResponse> future = new CompletableFuture<>();
|
||||
try {
|
||||
ProxyContext context = createContext(ctx);
|
||||
ReceiptHandle receiptHandle = ReceiptHandle.decode(request.getReceiptHandle());
|
||||
|
||||
return this.messagingProcessor.forwardMessageToDeadLetterQueue(
|
||||
context,
|
||||
receiptHandle,
|
||||
request.getMessageId(),
|
||||
GrpcConverter.wrapResourceWithNamespace(request.getGroup()),
|
||||
GrpcConverter.wrapResourceWithNamespace(request.getTopic())
|
||||
).thenApply(result -> convertToForwardMessageToDeadLetterQueueResponse(ctx, result));
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected ForwardMessageToDeadLetterQueueResponse convertToForwardMessageToDeadLetterQueueResponse(Context ctx,
|
||||
RemotingCommand result) {
|
||||
return ForwardMessageToDeadLetterQueueResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(result.getCode(), result.getRemark()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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.grpc.v2.producer;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.Encoding;
|
||||
import apache.rocketmq.v2.Message;
|
||||
import apache.rocketmq.v2.MessageType;
|
||||
import apache.rocketmq.v2.Resource;
|
||||
import apache.rocketmq.v2.SendMessageRequest;
|
||||
import apache.rocketmq.v2.SendMessageResponse;
|
||||
import apache.rocketmq.v2.SendReceipt;
|
||||
import apache.rocketmq.v2.SystemProperties;
|
||||
import com.beust.jcommander.internal.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.protobuf.Duration;
|
||||
import com.google.protobuf.Timestamp;
|
||||
import com.google.protobuf.util.Durations;
|
||||
import com.google.protobuf.util.Timestamps;
|
||||
import io.grpc.Context;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.client.producer.SendStatus;
|
||||
import org.apache.rocketmq.common.message.MessageAccessor;
|
||||
import org.apache.rocketmq.common.message.MessageConst;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.sysflag.MessageSysFlag;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.AbstractMessingActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcClientSettingsManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcProxyException;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
|
||||
import org.apache.rocketmq.proxy.processor.QueueSelector;
|
||||
import org.apache.rocketmq.proxy.service.route.MessageQueueView;
|
||||
import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue;
|
||||
|
||||
public class SendMessageActivity extends AbstractMessingActivity {
|
||||
|
||||
public SendMessageActivity(MessagingProcessor messagingProcessor,
|
||||
GrpcClientSettingsManager grpcClientSettingsManager) {
|
||||
super(messagingProcessor, grpcClientSettingsManager);
|
||||
}
|
||||
|
||||
public CompletableFuture<SendMessageResponse> sendMessage(Context ctx, SendMessageRequest request) {
|
||||
ProxyContext context = createContext(ctx);
|
||||
CompletableFuture<SendMessageResponse> future = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
if (request.getMessagesCount() <= 0) {
|
||||
throw new GrpcProxyException(Code.MESSAGE_CORRUPTED, "no message to send");
|
||||
}
|
||||
|
||||
List<Message> messageList = request.getMessagesList();
|
||||
Resource topic = messageList.get(0).getTopic();
|
||||
future = this.messagingProcessor.sendMessage(
|
||||
context,
|
||||
new SendMessageQueueSelector(request),
|
||||
GrpcConverter.wrapResourceWithNamespace(topic),
|
||||
buildMessage(context, request.getMessagesList(), topic)
|
||||
).thenApply(result -> convertToSendMessageResponse(context, request, result));
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected List<MessageExt> buildMessage(ProxyContext context, List<Message> protoMessageList, Resource topic) {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
List<MessageExt> messageExtList = new ArrayList<>();
|
||||
for (apache.rocketmq.v2.Message protoMessage : protoMessageList) {
|
||||
if (!protoMessage.getTopic().equals(topic)) {
|
||||
throw new GrpcProxyException(Code.MESSAGE_CORRUPTED, "topic in message is not same");
|
||||
}
|
||||
// here use topicName as producerGroup for transactional checker.
|
||||
messageExtList.add(buildMessage(protoMessage, topicName));
|
||||
}
|
||||
return messageExtList;
|
||||
}
|
||||
|
||||
protected MessageExt buildMessage(Message protoMessage, String producerGroup) {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(protoMessage.getTopic());
|
||||
|
||||
MessageExt messageExt = new MessageExt();
|
||||
messageExt.setTopic(topicName);
|
||||
messageExt.setBody(protoMessage.getBody().toByteArray());
|
||||
Map<String, String> messageProperty = this.buildMessageProperty(protoMessage, producerGroup);
|
||||
|
||||
// sysFlag (body encoding & message type)
|
||||
SystemProperties systemProperties = protoMessage.getSystemProperties();
|
||||
int sysFlag = 0;
|
||||
Encoding bodyEncoding = systemProperties.getBodyEncoding();
|
||||
if (bodyEncoding.equals(Encoding.GZIP)) {
|
||||
sysFlag |= MessageSysFlag.COMPRESSED_FLAG;
|
||||
}
|
||||
// transaction
|
||||
MessageType messageType = systemProperties.getMessageType();
|
||||
if (messageType.equals(MessageType.TRANSACTION)) {
|
||||
sysFlag |= MessageSysFlag.TRANSACTION_PREPARED_TYPE;
|
||||
}
|
||||
messageExt.setSysFlag(sysFlag);
|
||||
|
||||
MessageAccessor.setProperties(messageExt, messageProperty);
|
||||
return messageExt;
|
||||
}
|
||||
|
||||
protected Map<String, String> buildMessageProperty(Message message, String producerGroup) {
|
||||
org.apache.rocketmq.common.message.Message messageWithHeader = new org.apache.rocketmq.common.message.Message();
|
||||
// set user properties
|
||||
Map<String, String> userProperties = message.getUserPropertiesMap();
|
||||
for (String key : userProperties.keySet()) {
|
||||
if (MessageConst.STRING_HASH_SET.contains(key)) {
|
||||
throw new GrpcProxyException(Code.ILLEGAL_MESSAGE_PROPERTY_KEY, "property is used by system: " + key);
|
||||
}
|
||||
}
|
||||
MessageAccessor.setProperties(messageWithHeader, Maps.newHashMap(userProperties));
|
||||
|
||||
// set tag
|
||||
String tag = message.getSystemProperties().getTag();
|
||||
if (!"".equals(tag)) {
|
||||
messageWithHeader.setTags(tag);
|
||||
}
|
||||
|
||||
// set keys
|
||||
List<String> keysList = message.getSystemProperties().getKeysList();
|
||||
if (keysList.size() > 0) {
|
||||
messageWithHeader.setKeys(keysList);
|
||||
}
|
||||
|
||||
// set message id
|
||||
String messageId = message.getSystemProperties().getMessageId();
|
||||
if ("".equals(messageId)) {
|
||||
throw new GrpcProxyException(Code.ILLEGAL_MESSAGE_ID, "message id is empty");
|
||||
}
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, messageId);
|
||||
|
||||
// set transaction property
|
||||
MessageType messageType = message.getSystemProperties().getMessageType();
|
||||
if (messageType.equals(MessageType.TRANSACTION)) {
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_TRANSACTION_PREPARED, "true");
|
||||
|
||||
Duration transactionResolveDelay = message.getSystemProperties().getOrphanedTransactionRecoveryDuration();
|
||||
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_CHECK_IMMUNITY_TIME_IN_SECONDS,
|
||||
String.valueOf(Durations.toSeconds(transactionResolveDelay)));
|
||||
}
|
||||
|
||||
// set delay level or deliver timestamp
|
||||
if (message.getSystemProperties().hasDeliveryTimestamp()) {
|
||||
Timestamp deliveryTimestamp = message.getSystemProperties().getDeliveryTimestamp();
|
||||
String timestampString = String.valueOf(Timestamps.toMillis(deliveryTimestamp));
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_TIMER_DELIVER_MS, timestampString);
|
||||
}
|
||||
|
||||
// set reconsume times
|
||||
int reconsumeTimes = message.getSystemProperties().getDeliveryAttempt();
|
||||
MessageAccessor.setReconsumeTime(messageWithHeader, String.valueOf(reconsumeTimes));
|
||||
// set producer group
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_PRODUCER_GROUP, producerGroup);
|
||||
// set message group
|
||||
String messageGroup = message.getSystemProperties().getMessageGroup();
|
||||
if (!messageGroup.isEmpty()) {
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_SHARDING_KEY, messageGroup);
|
||||
}
|
||||
// set trace context
|
||||
String traceContext = message.getSystemProperties().getTraceContext();
|
||||
if (!traceContext.isEmpty()) {
|
||||
MessageAccessor.putProperty(messageWithHeader, MessageConst.PROPERTY_TRACE_CONTEXT, traceContext);
|
||||
}
|
||||
return messageWithHeader.getProperties();
|
||||
}
|
||||
|
||||
protected SendMessageResponse convertToSendMessageResponse(ProxyContext ctx, SendMessageRequest request,
|
||||
SendResult result) {
|
||||
if (result.getSendStatus() != SendStatus.SEND_OK) {
|
||||
return SendMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.INTERNAL_SERVER_ERROR, "send message failed, sendStatus=" + result.getSendStatus()))
|
||||
.build();
|
||||
}
|
||||
|
||||
List<SendReceipt> sendReceiptList = Lists.newArrayList();
|
||||
sendReceiptList.add(SendReceipt.newBuilder()
|
||||
.setMessageId(StringUtils.defaultString(result.getMsgId()))
|
||||
.setTransactionId(StringUtils.defaultString(result.getTransactionId()))
|
||||
.build());
|
||||
return SendMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.addAllReceipts(sendReceiptList)
|
||||
.build();
|
||||
}
|
||||
|
||||
protected static class SendMessageQueueSelector implements QueueSelector {
|
||||
|
||||
private final SendMessageRequest request;
|
||||
|
||||
public SendMessageQueueSelector(SendMessageRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectableMessageQueue select(ProxyContext ctx, MessageQueueView messageQueueView) {
|
||||
try {
|
||||
Message message = request.getMessages(0);
|
||||
String shardingKey = null;
|
||||
if (request.getMessagesCount() == 1) {
|
||||
shardingKey = message.getSystemProperties().getMessageGroup();
|
||||
}
|
||||
SelectableMessageQueue targetMessageQueue;
|
||||
if (StringUtils.isNotEmpty(shardingKey)) {
|
||||
// With shardingKey
|
||||
List<SelectableMessageQueue> writeQueues = messageQueueView.getWriteSelector().getQueues();
|
||||
int bucket = Hashing.consistentHash(shardingKey.hashCode(), writeQueues.size());
|
||||
targetMessageQueue = writeQueues.get(bucket);
|
||||
} else {
|
||||
targetMessageQueue = messageQueueView.getWriteSelector().selectOne(false);
|
||||
}
|
||||
return targetMessageQueue;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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.grpc.v2.route;
|
||||
|
||||
import apache.rocketmq.v2.Address;
|
||||
import apache.rocketmq.v2.AddressScheme;
|
||||
import apache.rocketmq.v2.Assignment;
|
||||
import apache.rocketmq.v2.Broker;
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.Endpoints;
|
||||
import apache.rocketmq.v2.MessageQueue;
|
||||
import apache.rocketmq.v2.Permission;
|
||||
import apache.rocketmq.v2.QueryAssignmentRequest;
|
||||
import apache.rocketmq.v2.QueryAssignmentResponse;
|
||||
import apache.rocketmq.v2.QueryRouteRequest;
|
||||
import apache.rocketmq.v2.QueryRouteResponse;
|
||||
import apache.rocketmq.v2.Resource;
|
||||
import com.google.common.net.HostAndPort;
|
||||
import io.grpc.Context;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.common.constant.PermName;
|
||||
import org.apache.rocketmq.common.protocol.route.QueueData;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.AbstractMessingActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcClientSettingsManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
|
||||
import org.apache.rocketmq.proxy.service.route.ProxyTopicRouteData;
|
||||
import org.apache.rocketmq.proxy.service.route.TopicRouteHelper;
|
||||
|
||||
public class RouteActivity extends AbstractMessingActivity {
|
||||
|
||||
public RouteActivity(MessagingProcessor messagingProcessor,
|
||||
|
||||
GrpcClientSettingsManager grpcClientSettingsManager) {
|
||||
super(messagingProcessor, grpcClientSettingsManager);
|
||||
}
|
||||
|
||||
public CompletableFuture<QueryRouteResponse> queryRoute(Context ctx, QueryRouteRequest request) {
|
||||
CompletableFuture<QueryRouteResponse> future = new CompletableFuture<>();
|
||||
try {
|
||||
ProxyContext context = createContext(ctx);
|
||||
List<org.apache.rocketmq.proxy.common.Address> addressList = this.convertToAddressList(request.getEndpoints());
|
||||
|
||||
ProxyTopicRouteData proxyTopicRouteData = this.messagingProcessor.getTopicRouteDataForProxy(
|
||||
context,
|
||||
addressList,
|
||||
GrpcConverter.wrapResourceWithNamespace(request.getTopic()));
|
||||
|
||||
List<MessageQueue> messageQueueList = new ArrayList<>();
|
||||
Map<String, Map<Long, Broker>> brokerMap = buildBrokerMap(proxyTopicRouteData.getBrokerDatas());
|
||||
|
||||
for (QueueData queueData : proxyTopicRouteData.getQueueDatas()) {
|
||||
String brokerName = queueData.getBrokerName();
|
||||
Map<Long, Broker> brokerIdMap = brokerMap.get(brokerName);
|
||||
if (brokerIdMap == null) {
|
||||
break;
|
||||
}
|
||||
for (Broker broker : brokerIdMap.values()) {
|
||||
messageQueueList.addAll(this.genMessageQueueFromQueueData(queueData, request.getTopic(), broker));
|
||||
}
|
||||
}
|
||||
|
||||
QueryRouteResponse response = QueryRouteResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.addAllMessageQueues(messageQueueList)
|
||||
.build();
|
||||
future.complete(response);
|
||||
} catch (Throwable t) {
|
||||
if (TopicRouteHelper.isTopicNotExistError(t)) {
|
||||
future.complete(QueryRouteResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.TOPIC_NOT_FOUND, t.getMessage()))
|
||||
.build());
|
||||
} else {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
public CompletableFuture<QueryAssignmentResponse> queryAssignment(Context ctx, QueryAssignmentRequest request) {
|
||||
CompletableFuture<QueryAssignmentResponse> future = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
ProxyContext context = createContext(ctx);
|
||||
List<org.apache.rocketmq.proxy.common.Address> addressList = this.convertToAddressList(request.getEndpoints());
|
||||
|
||||
ProxyTopicRouteData proxyTopicRouteData = this.messagingProcessor.getTopicRouteDataForProxy(
|
||||
context,
|
||||
addressList,
|
||||
GrpcConverter.wrapResourceWithNamespace(request.getTopic()));
|
||||
|
||||
List<Assignment> assignments = new ArrayList<>();
|
||||
Map<String, Map<Long, Broker>> brokerMap = buildBrokerMap(proxyTopicRouteData.getBrokerDatas());
|
||||
for (QueueData queueData : proxyTopicRouteData.getQueueDatas()) {
|
||||
Map<Long, Broker> brokerIdMap = brokerMap.get(queueData.getBrokerName());
|
||||
if (brokerIdMap != null) {
|
||||
for (Map.Entry<Long, Broker> brokerIdEntry : brokerIdMap.entrySet()) {
|
||||
MessageQueue defaultMessageQueue = MessageQueue.newBuilder()
|
||||
.setTopic(request.getTopic())
|
||||
.setId(-1)
|
||||
.setPermission(this.convertToPermission(queueData.getPerm()))
|
||||
.setBroker(brokerIdEntry.getValue())
|
||||
.build();
|
||||
|
||||
assignments.add(Assignment.newBuilder()
|
||||
.setMessageQueue(defaultMessageQueue)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
QueryAssignmentResponse response = QueryAssignmentResponse.newBuilder()
|
||||
.addAllAssignments(assignments)
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build();
|
||||
future.complete(response);
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected Permission convertToPermission(int perm) {
|
||||
boolean isReadable = PermName.isReadable(perm);
|
||||
boolean isWriteable = PermName.isWriteable(perm);
|
||||
if (isReadable && isWriteable) {
|
||||
return Permission.READ_WRITE;
|
||||
}
|
||||
if (isReadable) {
|
||||
return Permission.READ;
|
||||
}
|
||||
if (isWriteable) {
|
||||
return Permission.WRITE;
|
||||
}
|
||||
return Permission.NONE;
|
||||
}
|
||||
|
||||
protected List<org.apache.rocketmq.proxy.common.Address> convertToAddressList(Endpoints endpoints) {
|
||||
List<org.apache.rocketmq.proxy.common.Address> addressList = new ArrayList<>();
|
||||
for (Address address : endpoints.getAddressesList()) {
|
||||
addressList.add(new org.apache.rocketmq.proxy.common.Address(
|
||||
org.apache.rocketmq.proxy.common.Address.AddressScheme.valueOf(endpoints.getScheme().name()),
|
||||
HostAndPort.fromParts(address.getHost(), address.getPort()))
|
||||
);
|
||||
}
|
||||
return addressList;
|
||||
}
|
||||
|
||||
protected Map<String /*brokerName*/, Map<Long /*brokerID*/, Broker>> buildBrokerMap(
|
||||
List<ProxyTopicRouteData.ProxyBrokerData> brokerDataList) {
|
||||
Map<String, Map<Long, Broker>> brokerMap = new HashMap<>();
|
||||
for (ProxyTopicRouteData.ProxyBrokerData brokerData : brokerDataList) {
|
||||
Map<Long, Broker> brokerIdMap = new HashMap<>();
|
||||
String brokerName = brokerData.getBrokerName();
|
||||
for (Map.Entry<Long, List<org.apache.rocketmq.proxy.common.Address>> entry : brokerData.getBrokerAddrs().entrySet()) {
|
||||
Long brokerId = entry.getKey();
|
||||
List<Address> addressList = new ArrayList<>();
|
||||
AddressScheme addressScheme = AddressScheme.IPv4;
|
||||
for (org.apache.rocketmq.proxy.common.Address address : entry.getValue()) {
|
||||
addressScheme = AddressScheme.valueOf(address.getAddressScheme().name());
|
||||
addressList.add(Address.newBuilder()
|
||||
.setHost(address.getHostAndPort().getHost())
|
||||
.setPort(address.getHostAndPort().getPort())
|
||||
.build());
|
||||
}
|
||||
|
||||
Broker broker = Broker.newBuilder()
|
||||
.setName(brokerName)
|
||||
.setId(Math.toIntExact(brokerId))
|
||||
.setEndpoints(Endpoints.newBuilder()
|
||||
.setScheme(addressScheme)
|
||||
.addAllAddresses(addressList)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
brokerIdMap.put(brokerId, broker);
|
||||
}
|
||||
brokerMap.put(brokerName, brokerIdMap);
|
||||
}
|
||||
return brokerMap;
|
||||
}
|
||||
|
||||
protected List<MessageQueue> genMessageQueueFromQueueData(QueueData queueData, Resource topic, Broker broker) {
|
||||
List<MessageQueue> messageQueueList = new ArrayList<>();
|
||||
|
||||
int r = 0;
|
||||
int w = 0;
|
||||
int rw = 0;
|
||||
if (PermName.isWriteable(queueData.getPerm()) && PermName.isReadable(queueData.getPerm())) {
|
||||
rw = Math.min(queueData.getWriteQueueNums(), queueData.getReadQueueNums());
|
||||
r = queueData.getReadQueueNums() - rw;
|
||||
w = queueData.getWriteQueueNums() - rw;
|
||||
} else if (PermName.isWriteable(queueData.getPerm())) {
|
||||
w = queueData.getWriteQueueNums();
|
||||
} else if (PermName.isReadable(queueData.getPerm())) {
|
||||
r = queueData.getReadQueueNums();
|
||||
}
|
||||
|
||||
// r here means readOnly queue nums, w means writeOnly queue nums, while rw means both readable and writable queue nums.
|
||||
int queueIdIndex = 0;
|
||||
for (int i = 0; i < r; i++) {
|
||||
MessageQueue messageQueue = MessageQueue.newBuilder().setBroker(broker).setTopic(topic)
|
||||
.setId(queueIdIndex++)
|
||||
.setPermission(Permission.READ)
|
||||
.build();
|
||||
messageQueueList.add(messageQueue);
|
||||
}
|
||||
|
||||
for (int i = 0; i < w; i++) {
|
||||
MessageQueue messageQueue = MessageQueue.newBuilder().setBroker(broker).setTopic(topic)
|
||||
.setId(queueIdIndex++)
|
||||
.setPermission(Permission.WRITE)
|
||||
.build();
|
||||
messageQueueList.add(messageQueue);
|
||||
}
|
||||
|
||||
for (int i = 0; i < rw; i++) {
|
||||
MessageQueue messageQueue = MessageQueue.newBuilder().setBroker(broker).setTopic(topic)
|
||||
.setId(queueIdIndex++)
|
||||
.setPermission(Permission.READ_WRITE)
|
||||
.build();
|
||||
messageQueueList.add(messageQueue);
|
||||
}
|
||||
|
||||
return messageQueueList;
|
||||
}
|
||||
}
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service;
|
||||
|
||||
import apache.rocketmq.v2.Endpoints;
|
||||
import apache.rocketmq.v2.QueryAssignmentRequest;
|
||||
import apache.rocketmq.v2.QueryAssignmentResponse;
|
||||
import apache.rocketmq.v2.QueryRouteRequest;
|
||||
import apache.rocketmq.v2.QueryRouteResponse;
|
||||
import io.grpc.Context;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.proxy.common.ParameterConverter;
|
||||
import org.apache.rocketmq.proxy.connector.ConnectorManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseHook;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.cluster.AssignmentQueueSelector;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.cluster.DefaultAssignmentQueueSelector;
|
||||
|
||||
public abstract class AbstractRouteService extends BaseService {
|
||||
protected volatile ParameterConverter<Endpoints, Endpoints> queryRouteEndpointConverter;
|
||||
protected volatile ResponseHook<QueryRouteRequest, QueryRouteResponse> queryRouteHook;
|
||||
|
||||
protected volatile ParameterConverter<Endpoints, Endpoints> queryAssignmentEndpointConverter;
|
||||
protected volatile ResponseHook<QueryAssignmentRequest, QueryAssignmentResponse> queryAssignmentHook;
|
||||
protected volatile AssignmentQueueSelector assignmentQueueSelector;
|
||||
|
||||
protected final GrpcClientManager grpcClientManager;
|
||||
|
||||
public AbstractRouteService(ConnectorManager connectorManager, GrpcClientManager grpcClientManager) {
|
||||
super(connectorManager);
|
||||
this.grpcClientManager = grpcClientManager;
|
||||
this.queryRouteEndpointConverter = (ctx, parameter) -> parameter;
|
||||
this.queryAssignmentEndpointConverter = (ctx, parameter) -> parameter;
|
||||
this.assignmentQueueSelector = new DefaultAssignmentQueueSelector(this.connectorManager.getTopicRouteCache());
|
||||
}
|
||||
|
||||
public abstract CompletableFuture<QueryRouteResponse> queryRoute(Context ctx, QueryRouteRequest request);
|
||||
|
||||
public abstract CompletableFuture<QueryAssignmentResponse> queryAssignment(Context ctx, QueryAssignmentRequest request);
|
||||
|
||||
public ParameterConverter<Endpoints, Endpoints> getQueryRouteEndpointConverter() {
|
||||
return queryRouteEndpointConverter;
|
||||
}
|
||||
|
||||
public void setQueryRouteEndpointConverter(
|
||||
ParameterConverter<Endpoints, Endpoints> queryRouteEndpointConverter) {
|
||||
this.queryRouteEndpointConverter = queryRouteEndpointConverter;
|
||||
}
|
||||
|
||||
public ResponseHook<QueryRouteRequest, QueryRouteResponse> getQueryRouteHook() {
|
||||
return queryRouteHook;
|
||||
}
|
||||
|
||||
public void setQueryRouteHook(
|
||||
ResponseHook<QueryRouteRequest, QueryRouteResponse> queryRouteHook) {
|
||||
this.queryRouteHook = queryRouteHook;
|
||||
}
|
||||
|
||||
public ParameterConverter<Endpoints, Endpoints> getQueryAssignmentEndpointConverter() {
|
||||
return queryAssignmentEndpointConverter;
|
||||
}
|
||||
|
||||
public void setQueryAssignmentEndpointConverter(
|
||||
ParameterConverter<Endpoints, Endpoints> queryAssignmentEndpointConverter) {
|
||||
this.queryAssignmentEndpointConverter = queryAssignmentEndpointConverter;
|
||||
}
|
||||
|
||||
public AssignmentQueueSelector getAssignmentQueueSelector() {
|
||||
return assignmentQueueSelector;
|
||||
}
|
||||
|
||||
public void setAssignmentQueueSelector(
|
||||
AssignmentQueueSelector assignmentQueueSelector) {
|
||||
this.assignmentQueueSelector = assignmentQueueSelector;
|
||||
}
|
||||
|
||||
public ResponseHook<QueryAssignmentRequest, QueryAssignmentResponse> getQueryAssignmentHook() {
|
||||
return queryAssignmentHook;
|
||||
}
|
||||
|
||||
public void setQueryAssignmentHook(
|
||||
ResponseHook<QueryAssignmentRequest, QueryAssignmentResponse> queryAssignmentHook) {
|
||||
this.queryAssignmentHook = queryAssignmentHook;
|
||||
}
|
||||
}
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.Message;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.ReceiveMessageResponse;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.apache.rocketmq.client.consumer.PopStatus;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseHook;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseWriter;
|
||||
|
||||
public abstract class BaseReceiveMessageResponseStreamWriter {
|
||||
|
||||
protected final StreamObserver<ReceiveMessageResponse> streamObserver;
|
||||
protected final ResponseHook<ReceiveMessageRequest, ReceiveMessageResponse> receiveMessageHook;
|
||||
protected final ReceiveMessageResultFilter receiveMessageResultFilter;
|
||||
|
||||
public interface Builder {
|
||||
BaseReceiveMessageResponseStreamWriter build(
|
||||
StreamObserver<ReceiveMessageResponse> observer,
|
||||
ResponseHook<ReceiveMessageRequest, ReceiveMessageResponse> hook);
|
||||
}
|
||||
|
||||
public BaseReceiveMessageResponseStreamWriter(
|
||||
StreamObserver<ReceiveMessageResponse> observer,
|
||||
ResponseHook<ReceiveMessageRequest, ReceiveMessageResponse> hook,
|
||||
ReceiveMessageResultFilter messageResultFilter) {
|
||||
streamObserver = observer;
|
||||
receiveMessageHook = hook;
|
||||
receiveMessageResultFilter = messageResultFilter;
|
||||
}
|
||||
|
||||
public void write(Context ctx, ReceiveMessageRequest request, PopStatus status, List<MessageExt> messageFoundList) {
|
||||
ReceiveMessageResponseStreamObserver responseStreamObserver = new ReceiveMessageResponseStreamObserver(
|
||||
ctx,
|
||||
request,
|
||||
receiveMessageHook,
|
||||
streamObserver);
|
||||
try {
|
||||
switch (status) {
|
||||
case FOUND:
|
||||
List<Message> messageList = this.receiveMessageResultFilter.filterMessage(ctx, request, messageFoundList);
|
||||
if (messageList.isEmpty()) {
|
||||
responseStreamObserver.onNext(ReceiveMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, "no new message"))
|
||||
.build());
|
||||
} else {
|
||||
responseStreamObserver.onNext(ReceiveMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build());
|
||||
Iterator<Message> messageIterator = messageList.iterator();
|
||||
while (messageIterator.hasNext()) {
|
||||
Message curMessage = messageIterator.next();
|
||||
try {
|
||||
responseStreamObserver.onNext(ReceiveMessageResponse.newBuilder()
|
||||
.setMessage(curMessage)
|
||||
.build());
|
||||
} catch (Throwable t) {
|
||||
this.processThrowableWhenWriteMessage(t, ctx, request, curMessage);
|
||||
messageIterator.forEachRemaining(message ->
|
||||
this.processThrowableWhenWriteMessage(t, ctx, request, message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case POLLING_FULL:
|
||||
responseStreamObserver.onNext(ReceiveMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.TOO_MANY_REQUESTS, "polling full"))
|
||||
.build());
|
||||
break;
|
||||
case NO_NEW_MSG:
|
||||
case POLLING_NOT_FOUND:
|
||||
default:
|
||||
responseStreamObserver.onNext(ReceiveMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, "no new message"))
|
||||
.build());
|
||||
break;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
write(ctx, request, t);
|
||||
} finally {
|
||||
responseStreamObserver.onCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void processThrowableWhenWriteMessage(Throwable throwable,
|
||||
Context context, ReceiveMessageRequest request, Message message);
|
||||
|
||||
public void write(Context ctx, ReceiveMessageRequest request, Throwable throwable) {
|
||||
ReceiveMessageResponseStreamObserver responseStreamObserver = new ReceiveMessageResponseStreamObserver(
|
||||
ctx,
|
||||
request,
|
||||
receiveMessageHook,
|
||||
streamObserver);
|
||||
ResponseWriter.write(
|
||||
responseStreamObserver,
|
||||
ReceiveMessageResponse.newBuilder().setStatus(ResponseBuilder.buildStatus(throwable)).build()
|
||||
);
|
||||
}
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service;
|
||||
|
||||
import apache.rocketmq.v2.Message;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.Settings;
|
||||
import io.grpc.Context;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
|
||||
import org.apache.rocketmq.proxy.common.utils.FilterUtils;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
|
||||
public abstract class BaseReceiveMessageResultFilter implements ReceiveMessageResultFilter {
|
||||
|
||||
protected final GrpcClientManager grpcClientManager;
|
||||
|
||||
public BaseReceiveMessageResultFilter(GrpcClientManager manager) {
|
||||
grpcClientManager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Message> filterMessage(Context ctx, ReceiveMessageRequest request, List<MessageExt> messageExtList) {
|
||||
if (messageExtList == null || messageExtList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(request.getMessageQueue().getTopic());
|
||||
SubscriptionData subscriptionData = GrpcConverter.buildSubscriptionData(topicName, request.getFilterExpression());
|
||||
Settings settings = grpcClientManager.getClientSettings(ctx);
|
||||
int maxAttempts = settings.getBackoffPolicy().getMaxAttempts();
|
||||
List<Message> resMessageList = new ArrayList<>();
|
||||
for (MessageExt messageExt : messageExtList) {
|
||||
if (!FilterUtils.isTagMatched(subscriptionData.getTagsSet(), messageExt.getTags())) {
|
||||
processNoMatchMessage(ctx, request, messageExt);
|
||||
continue;
|
||||
}
|
||||
if (messageExt.getReconsumeTimes() >= maxAttempts) {
|
||||
processExceedMaxAttemptsMessage(ctx, request, messageExt, maxAttempts);
|
||||
continue;
|
||||
}
|
||||
resMessageList.add(GrpcConverter.buildMessage(messageExt));
|
||||
}
|
||||
return resMessageList;
|
||||
}
|
||||
|
||||
protected abstract void processNoMatchMessage(Context ctx, ReceiveMessageRequest request, MessageExt messageExt);
|
||||
|
||||
protected abstract void processExceedMaxAttemptsMessage(Context ctx, ReceiveMessageRequest request, MessageExt messageExt, int maxAttempts);
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.FilterExpression;
|
||||
import apache.rocketmq.v2.Resource;
|
||||
import io.grpc.Context;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.proxy.common.StartAndShutdown;
|
||||
import org.apache.rocketmq.proxy.connector.ConnectorManager;
|
||||
import org.apache.rocketmq.proxy.connector.route.TopicRouteCache;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ProxyException;
|
||||
|
||||
public abstract class BaseService implements StartAndShutdown {
|
||||
|
||||
protected final ConnectorManager connectorManager;
|
||||
|
||||
public BaseService(ConnectorManager connectorManager) {
|
||||
this.connectorManager = connectorManager;
|
||||
}
|
||||
|
||||
public static ReceiptHandle resolveReceiptHandle(Context ctx, String receiptHandleStr) {
|
||||
ReceiptHandle receiptHandle = ReceiptHandle.decode(receiptHandleStr);
|
||||
if (receiptHandle.isExpired()) {
|
||||
throw new ProxyException(Code.RECEIPT_HANDLE_EXPIRED, "handle has expired");
|
||||
}
|
||||
return receiptHandle;
|
||||
}
|
||||
|
||||
public static String getBrokerAddr(Context ctx, TopicRouteCache topicRouteCache, String brokerName) throws Exception {
|
||||
if (StringUtils.isBlank(brokerName)) {
|
||||
throw new ProxyException(Code.UNRECOGNIZED, "broker name is empty");
|
||||
}
|
||||
String addr = topicRouteCache.getBrokerAddr(brokerName);
|
||||
if (StringUtils.isBlank(addr)) {
|
||||
throw new ProxyException(Code.UNRECOGNIZED, brokerName + " not exist");
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
protected String getBrokerAddr(Context ctx, String brokerName) throws Exception {
|
||||
return getBrokerAddr(ctx, this.connectorManager.getTopicRouteCache(), brokerName);
|
||||
}
|
||||
|
||||
protected void checkSubscriptionData(Resource topic, FilterExpression filterExpression) {
|
||||
// for checking filterExpression.
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
GrpcConverter.buildSubscriptionData(topicName, filterExpression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service;
|
||||
|
||||
import apache.rocketmq.v2.Resource;
|
||||
import apache.rocketmq.v2.Settings;
|
||||
import apache.rocketmq.v2.TelemetryCommand;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import org.apache.rocketmq.proxy.channel.ChannelManager;
|
||||
import org.apache.rocketmq.proxy.common.TelemetryCommandManager;
|
||||
import org.apache.rocketmq.proxy.grpc.interceptor.InterceptorConstants;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.channel.GrpcClientChannel;
|
||||
|
||||
public class ClientSettingsService {
|
||||
|
||||
private final ChannelManager channelManager;
|
||||
private final GrpcClientManager grpcClientManager;
|
||||
private final TelemetryCommandManager telemetryCommandManager;
|
||||
|
||||
public ClientSettingsService(ChannelManager channelManager,
|
||||
GrpcClientManager grpcClientManager,
|
||||
TelemetryCommandManager telemetryCommandManager) {
|
||||
this.channelManager = channelManager;
|
||||
this.grpcClientManager = grpcClientManager;
|
||||
this.telemetryCommandManager = telemetryCommandManager;
|
||||
}
|
||||
|
||||
public TelemetryCommand processClientSettings(Context ctx, TelemetryCommand request, StreamObserver<TelemetryCommand> responseObserver) {
|
||||
String clientId = InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.CLIENT_ID);
|
||||
grpcClientManager.updateClientSettings(clientId, request.getSettings());
|
||||
Settings settings = grpcClientManager.getClientSettings(clientId);
|
||||
if (settings.hasPublishing()) {
|
||||
for (Resource topic : settings.getPublishing().getTopicsList()) {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
GrpcClientChannel producerChannel = GrpcClientChannel.create(channelManager, topicName, clientId, telemetryCommandManager);
|
||||
producerChannel.setClientObserver(responseObserver);
|
||||
}
|
||||
}
|
||||
if (settings.hasSubscription()) {
|
||||
String groupName = GrpcConverter.wrapResourceWithNamespace(settings.getSubscription().getGroup());
|
||||
GrpcClientChannel consumerChannel = GrpcClientChannel.create(channelManager, groupName, clientId, telemetryCommandManager);
|
||||
consumerChannel.setClientObserver(responseObserver);
|
||||
}
|
||||
return TelemetryCommand.newBuilder()
|
||||
.setSettings(settings)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
-183
@@ -1,183 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service;
|
||||
|
||||
import apache.rocketmq.v2.AckMessageRequest;
|
||||
import apache.rocketmq.v2.AckMessageResponse;
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationRequest;
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationResponse;
|
||||
import apache.rocketmq.v2.EndTransactionRequest;
|
||||
import apache.rocketmq.v2.EndTransactionResponse;
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueRequest;
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueResponse;
|
||||
import apache.rocketmq.v2.HeartbeatRequest;
|
||||
import apache.rocketmq.v2.HeartbeatResponse;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationRequest;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationResponse;
|
||||
import apache.rocketmq.v2.QueryAssignmentRequest;
|
||||
import apache.rocketmq.v2.QueryAssignmentResponse;
|
||||
import apache.rocketmq.v2.QueryRouteRequest;
|
||||
import apache.rocketmq.v2.QueryRouteResponse;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.ReceiveMessageResponse;
|
||||
import apache.rocketmq.v2.SendMessageRequest;
|
||||
import apache.rocketmq.v2.SendMessageResponse;
|
||||
import apache.rocketmq.v2.TelemetryCommand;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import org.apache.rocketmq.common.ThreadFactoryImpl;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.channel.ChannelManager;
|
||||
import org.apache.rocketmq.proxy.common.AbstractStartAndShutdown;
|
||||
import org.apache.rocketmq.proxy.common.StartAndShutdown;
|
||||
import org.apache.rocketmq.proxy.common.TelemetryCommandManager;
|
||||
import org.apache.rocketmq.proxy.connector.ConnectorManager;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionStateCheckRequest;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionStateChecker;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.cluster.ConsumerService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.cluster.ForwardClientService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.cluster.ProducerService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.cluster.RouteService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.cluster.TransactionService;
|
||||
|
||||
public class ClusterGrpcService extends AbstractStartAndShutdown implements GrpcForwardService {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
protected final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(
|
||||
new ThreadFactoryImpl("ClusterGrpcServiceScheduledThread")
|
||||
);
|
||||
|
||||
protected ChannelManager channelManager;
|
||||
protected ConnectorManager connectorManager;
|
||||
protected ProducerService producerService;
|
||||
protected ConsumerService consumerService;
|
||||
protected RouteService routeService;
|
||||
protected ForwardClientService clientService;
|
||||
protected TransactionService transactionService;
|
||||
protected TelemetryCommandManager pollCommandResponseManager;
|
||||
protected GrpcClientManager grpcClientManager;
|
||||
|
||||
public ClusterGrpcService() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
this.channelManager = new ChannelManager();
|
||||
this.grpcClientManager = new GrpcClientManager();
|
||||
this.pollCommandResponseManager = new TelemetryCommandManager();
|
||||
this.connectorManager = new ConnectorManager(new GrpcTransactionStateChecker());
|
||||
this.consumerService = new ConsumerService(connectorManager, grpcClientManager);
|
||||
this.producerService = new ProducerService(connectorManager);
|
||||
this.routeService = new RouteService(connectorManager, grpcClientManager);
|
||||
this.clientService = new ForwardClientService(connectorManager, scheduledExecutorService,
|
||||
channelManager, grpcClientManager, pollCommandResponseManager);
|
||||
this.transactionService = new TransactionService(connectorManager, channelManager);
|
||||
|
||||
this.appendStartAndShutdown(new ClusterGrpcServiceStartAndShutdown());
|
||||
this.appendStartAndShutdown(this.connectorManager);
|
||||
this.appendStartAndShutdown(this.consumerService);
|
||||
this.appendStartAndShutdown(this.producerService);
|
||||
this.appendStartAndShutdown(this.routeService);
|
||||
this.appendStartAndShutdown(this.clientService);
|
||||
this.appendStartAndShutdown(this.transactionService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<QueryRouteResponse> queryRoute(Context ctx, QueryRouteRequest request) {
|
||||
return routeService.queryRoute(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<HeartbeatResponse> heartbeat(Context ctx, HeartbeatRequest request) {
|
||||
return clientService.heartbeat(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SendMessageResponse> sendMessage(Context ctx, SendMessageRequest request) {
|
||||
return producerService.sendMessage(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<QueryAssignmentResponse> queryAssignment(Context ctx, QueryAssignmentRequest request) {
|
||||
return routeService.queryAssignment(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveMessage(Context ctx, ReceiveMessageRequest request,
|
||||
StreamObserver<ReceiveMessageResponse> responseObserver) {
|
||||
consumerService.receiveMessage(ctx, request, responseObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AckMessageResponse> ackMessage(Context ctx, AckMessageRequest request) {
|
||||
return consumerService.ackMessage(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ForwardMessageToDeadLetterQueueResponse> forwardMessageToDeadLetterQueue(Context ctx,
|
||||
ForwardMessageToDeadLetterQueueRequest request) {
|
||||
return producerService.forwardMessageToDeadLetterQueue(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<EndTransactionResponse> endTransaction(Context ctx, EndTransactionRequest request) {
|
||||
return transactionService.endTransaction(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<NotifyClientTerminationResponse> notifyClientTermination(Context ctx,
|
||||
NotifyClientTerminationRequest request) {
|
||||
return clientService.notifyClientTermination(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChangeInvisibleDurationResponse> changeInvisibleDuration(Context ctx,
|
||||
ChangeInvisibleDurationRequest request) {
|
||||
return consumerService.changeInvisibleDuration(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamObserver<TelemetryCommand> telemetry(Context ctx, StreamObserver<TelemetryCommand> responseObserver) {
|
||||
return clientService.telemetry(ctx, responseObserver);
|
||||
}
|
||||
|
||||
protected class ClusterGrpcServiceStartAndShutdown implements StartAndShutdown {
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws Exception {
|
||||
scheduledExecutorService.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
protected class GrpcTransactionStateChecker implements TransactionStateChecker {
|
||||
|
||||
@Override
|
||||
public void checkTransactionState(TransactionStateCheckRequest checkData) {
|
||||
transactionService.checkTransactionState(checkData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,565 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service;
|
||||
|
||||
import apache.rocketmq.v2.AckMessageEntry;
|
||||
import apache.rocketmq.v2.AckMessageRequest;
|
||||
import apache.rocketmq.v2.AckMessageResponse;
|
||||
import apache.rocketmq.v2.AckMessageResultEntry;
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationRequest;
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationResponse;
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.EndTransactionRequest;
|
||||
import apache.rocketmq.v2.EndTransactionResponse;
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueRequest;
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueResponse;
|
||||
import apache.rocketmq.v2.HeartbeatRequest;
|
||||
import apache.rocketmq.v2.HeartbeatResponse;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationRequest;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationResponse;
|
||||
import apache.rocketmq.v2.QueryAssignmentRequest;
|
||||
import apache.rocketmq.v2.QueryAssignmentResponse;
|
||||
import apache.rocketmq.v2.QueryRouteRequest;
|
||||
import apache.rocketmq.v2.QueryRouteResponse;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.ReceiveMessageResponse;
|
||||
import apache.rocketmq.v2.Resource;
|
||||
import apache.rocketmq.v2.SendMessageRequest;
|
||||
import apache.rocketmq.v2.SendMessageResponse;
|
||||
import apache.rocketmq.v2.Settings;
|
||||
import apache.rocketmq.v2.TelemetryCommand;
|
||||
import apache.rocketmq.v2.ThreadStackTrace;
|
||||
import apache.rocketmq.v2.VerifyMessageResult;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.broker.BrokerController;
|
||||
import org.apache.rocketmq.broker.client.ClientChannelInfo;
|
||||
import org.apache.rocketmq.broker.client.ConsumerGroupEvent;
|
||||
import org.apache.rocketmq.broker.client.ConsumerIdsChangeListener;
|
||||
import org.apache.rocketmq.broker.client.ProducerChangeListener;
|
||||
import org.apache.rocketmq.broker.client.ProducerGroupEvent;
|
||||
import org.apache.rocketmq.client.consumer.PopStatus;
|
||||
import org.apache.rocketmq.common.MQVersion;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.message.MessageBatch;
|
||||
import org.apache.rocketmq.common.message.MessageClientIDSetter;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.message.MessageQueue;
|
||||
import org.apache.rocketmq.common.protocol.RequestCode;
|
||||
import org.apache.rocketmq.common.protocol.ResponseCode;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeResponseHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.EndTransactionRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.UnregisterClientRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.HeartbeatData;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.channel.ChannelManager;
|
||||
import org.apache.rocketmq.proxy.channel.InvocationContext;
|
||||
import org.apache.rocketmq.proxy.channel.SimpleChannel;
|
||||
import org.apache.rocketmq.proxy.channel.SimpleChannelHandlerContext;
|
||||
import org.apache.rocketmq.proxy.common.AbstractStartAndShutdown;
|
||||
import org.apache.rocketmq.proxy.common.TelemetryCommandManager;
|
||||
import org.apache.rocketmq.proxy.common.TelemetryCommandRecord;
|
||||
import org.apache.rocketmq.proxy.connector.ConnectorManager;
|
||||
import org.apache.rocketmq.proxy.grpc.interceptor.InterceptorConstants;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseHook;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.channel.GrpcClientChannel;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.channel.ReceiveMessageChannel;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.channel.SendMessageChannel;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.handler.ReceiveMessageResponseHandler;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.handler.SendMessageResponseHandler;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.local.LocalReceiveMessageResponseStreamWriter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.local.LocalReceiveMessageResultFilter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.local.LocalWriteQueueSelector;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.local.RouteService;
|
||||
import org.apache.rocketmq.remoting.RemotingServer;
|
||||
import org.apache.rocketmq.remoting.netty.NettyRemotingAbstract;
|
||||
import org.apache.rocketmq.remoting.protocol.LanguageCode;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class LocalGrpcService extends AbstractStartAndShutdown implements GrpcForwardService {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
private final BrokerController brokerController;
|
||||
private final ChannelManager channelManager;
|
||||
private final TelemetryCommandManager telemetryCommandManager;
|
||||
private final GrpcClientManager grpcClientManager;
|
||||
private final RouteService routeService;
|
||||
private final ClientSettingsService clientSettingsService;
|
||||
private final LocalWriteQueueSelector localWriteQueueSelector;
|
||||
private final BaseReceiveMessageResponseStreamWriter.Builder streamWriterBuilder;
|
||||
|
||||
private volatile ResponseHook<ReceiveMessageRequest, ReceiveMessageResponse> receiveMessageHook;
|
||||
|
||||
public LocalGrpcService(BrokerController brokerController) {
|
||||
this(brokerController, new TelemetryCommandManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* For unit test
|
||||
* @param brokerController BrokerController works in local mode
|
||||
* @param telemetryCommandManager Used to manage telemetry command
|
||||
*/
|
||||
LocalGrpcService(BrokerController brokerController, TelemetryCommandManager telemetryCommandManager) {
|
||||
this.brokerController = brokerController;
|
||||
this.channelManager = new ChannelManager();
|
||||
// TransactionStateChecker is not used in Local mode.
|
||||
ConnectorManager connectorManager = new ConnectorManager(null);
|
||||
this.telemetryCommandManager = telemetryCommandManager;
|
||||
this.grpcClientManager = new GrpcClientManager();
|
||||
this.routeService = new RouteService(connectorManager, grpcClientManager);
|
||||
this.clientSettingsService = new ClientSettingsService(this.channelManager, this.grpcClientManager, this.telemetryCommandManager);
|
||||
this.localWriteQueueSelector = new LocalWriteQueueSelector(brokerController.getBrokerConfig().getBrokerName(),
|
||||
brokerController.getTopicConfigManager(), connectorManager.getTopicRouteCache());
|
||||
|
||||
this.brokerController.getConsumerManager().appendConsumerIdsChangeListener(new ConsumerIdsChangeListenerImpl());
|
||||
this.brokerController.getProducerManager().appendProducerChangeListener(new ProducerChangeListenerImpl());
|
||||
|
||||
this.streamWriterBuilder = (observer, hook) -> new LocalReceiveMessageResponseStreamWriter(
|
||||
observer,
|
||||
hook,
|
||||
channelManager,
|
||||
brokerController,
|
||||
new LocalReceiveMessageResultFilter(channelManager, brokerController, grpcClientManager)
|
||||
);
|
||||
|
||||
this.appendStartAndShutdown(connectorManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<QueryRouteResponse> queryRoute(Context ctx, QueryRouteRequest request) {
|
||||
return this.routeService.queryRoute(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<HeartbeatResponse> heartbeat(Context ctx, HeartbeatRequest request) {
|
||||
LanguageCode languageCode;
|
||||
String language = InterceptorConstants.METADATA.get(Context.current()).get(InterceptorConstants.LANGUAGE);
|
||||
String clientId = InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.CLIENT_ID);
|
||||
languageCode = LanguageCode.valueOf(language);
|
||||
|
||||
Settings clientSettings = grpcClientManager.getClientSettings(clientId);
|
||||
HeartbeatData heartbeatData = GrpcConverter.buildHeartbeatData(clientId, request, clientSettings);
|
||||
RemotingCommand command = RemotingCommand.createRequestCommand(RequestCode.HEART_BEAT, null);
|
||||
command.setLanguage(languageCode);
|
||||
command.setVersion(MQVersion.Version.V5_0_0.ordinal());
|
||||
command.setBody(heartbeatData.encode());
|
||||
command.makeCustomHeaderToNet();
|
||||
|
||||
CompletableFuture<HeartbeatResponse> future = new CompletableFuture<>();
|
||||
switch (clientSettings.getClientType()) {
|
||||
case PRODUCER: {
|
||||
for (Resource topic : clientSettings.getPublishing().getTopicsList()) {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
GrpcClientChannel channel = GrpcClientChannel.create(channelManager, topicName, clientId, telemetryCommandManager);
|
||||
SimpleChannelHandlerContext simpleChannelHandlerContext = new SimpleChannelHandlerContext(channel);
|
||||
|
||||
this.brokerController.getClientManageProcessor()
|
||||
.heartBeat(simpleChannelHandlerContext, command);
|
||||
}
|
||||
HeartbeatResponse heartbeatResponse = HeartbeatResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, "Producer heartbeat"))
|
||||
.build();
|
||||
future.complete(heartbeatResponse);
|
||||
break;
|
||||
}
|
||||
case PUSH_CONSUMER:
|
||||
case SIMPLE_CONSUMER: {
|
||||
String groupName = GrpcConverter.wrapResourceWithNamespace(request.getGroup());
|
||||
GrpcClientChannel channel = GrpcClientChannel.create(channelManager, groupName, clientId, telemetryCommandManager);
|
||||
SimpleChannelHandlerContext simpleChannelHandlerContext = new SimpleChannelHandlerContext(channel);
|
||||
|
||||
RemotingCommand response = this.brokerController.getClientManageProcessor()
|
||||
.heartBeat(simpleChannelHandlerContext, command);
|
||||
HeartbeatResponse heartbeatResponse = HeartbeatResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(response.getCode(), response.getRemark()))
|
||||
.build();
|
||||
future.complete(heartbeatResponse);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("ClientType not exist " + clientSettings.getClientType());
|
||||
}
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SendMessageResponse> sendMessage(Context ctx, SendMessageRequest request) {
|
||||
MessageQueue messageQueue = localWriteQueueSelector.selectQueue(ctx, request).getMessageQueue();
|
||||
String topicName = messageQueue.getTopic();
|
||||
SendMessageRequestHeader requestHeader = GrpcConverter.buildSendMessageRequestHeader(request, topicName, messageQueue.getQueueId());
|
||||
RemotingCommand command = RemotingCommand.createRequestCommand(RequestCode.SEND_MESSAGE, requestHeader);
|
||||
List<org.apache.rocketmq.common.message.Message> messageList = GrpcConverter.buildMessage(request.getMessagesList(), request.getMessages(0).getTopic());
|
||||
String messageId;
|
||||
if (messageList.size() == 1) {
|
||||
org.apache.rocketmq.common.message.Message message = messageList.get(0);
|
||||
command.setBody(message.getBody());
|
||||
messageId = MessageClientIDSetter.getUniqID(message);
|
||||
} else {
|
||||
MessageBatch messageBatch = MessageBatch.generateFromList(messageList);
|
||||
MessageClientIDSetter.setUniqID(messageBatch);
|
||||
messageBatch.setBody(messageBatch.encode());
|
||||
command.setBody(messageBatch.encode());
|
||||
messageId = MessageClientIDSetter.getUniqID(messageBatch);
|
||||
}
|
||||
command.makeCustomHeaderToNet();
|
||||
|
||||
SendMessageResponseHandler handler = new SendMessageResponseHandler(messageId, requestHeader.getSysFlag(), brokerController.getBrokerAddr());
|
||||
SendMessageChannel channel = channelManager.createChannel(ctx, context -> new SendMessageChannel(context, handler), SendMessageChannel.class);
|
||||
SimpleChannelHandlerContext channelHandlerContext = new SimpleChannelHandlerContext(channel);
|
||||
CompletableFuture<SendMessageResponse> future = new CompletableFuture<>();
|
||||
InvocationContext<SendMessageRequest, SendMessageResponse> context
|
||||
= new InvocationContext<>(request, future);
|
||||
channel.registerInvocationContext(command.getOpaque(), context);
|
||||
try {
|
||||
RemotingCommand response = brokerController.getSendMessageProcessor()
|
||||
.processRequest(channelHandlerContext, command);
|
||||
if (response != null) {
|
||||
handler.handle(response, context);
|
||||
channel.eraseInvocationContext(command.getOpaque());
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to process send message command", e);
|
||||
channel.eraseInvocationContext(command.getOpaque());
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<QueryAssignmentResponse> queryAssignment(Context ctx, QueryAssignmentRequest request) {
|
||||
return this.routeService.queryAssignment(ctx, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveMessage(Context ctx, ReceiveMessageRequest request, StreamObserver<ReceiveMessageResponse> responseObserver) {
|
||||
long pollTime = GrpcConverter.buildPollTimeFromContext(ctx);
|
||||
// TODO: get fifo config from subscriptionGroupManager
|
||||
boolean fifo = false;
|
||||
BaseReceiveMessageResponseStreamWriter writer = streamWriterBuilder.build(responseObserver, receiveMessageHook);
|
||||
ReceiveMessageResponseHandler handler = new ReceiveMessageResponseHandler(brokerController.getBrokerConfig().getBrokerName(), fifo);
|
||||
ReceiveMessageChannel channel = channelManager.createChannel(ctx, context -> new ReceiveMessageChannel(context, handler), ReceiveMessageChannel.class);
|
||||
CompletableFuture<List<MessageExt>> future = new CompletableFuture<>();
|
||||
InvocationContext<ReceiveMessageRequest, List<MessageExt>> context
|
||||
= new InvocationContext<>(request, future);
|
||||
SimpleChannelHandlerContext channelHandlerContext = new SimpleChannelHandlerContext(channel);
|
||||
PopMessageRequestHeader requestHeader = GrpcConverter.buildPopMessageRequestHeader(request, pollTime, fifo);
|
||||
RemotingCommand command = RemotingCommand.createRequestCommand(RequestCode.POP_MESSAGE, requestHeader);
|
||||
command.makeCustomHeaderToNet();
|
||||
channel.registerInvocationContext(command.getOpaque(), context);
|
||||
try {
|
||||
RemotingCommand response = brokerController.getPopMessageProcessor().processRequest(channelHandlerContext, command);
|
||||
if (response != null) {
|
||||
handler.handle(response, context);
|
||||
channel.eraseInvocationContext(command.getOpaque());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process pop message command", e);
|
||||
channel.eraseInvocationContext(command.getOpaque());
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
future.thenAccept(r -> writer.write(ctx, request, PopStatus.FOUND, r))
|
||||
.exceptionally(e -> {
|
||||
writer.write(ctx, request, e);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AckMessageResponse> ackMessage(Context ctx, AckMessageRequest request) {
|
||||
Channel channel = channelManager.createChannel(ctx);
|
||||
SimpleChannelHandlerContext channelHandlerContext = new SimpleChannelHandlerContext(channel);
|
||||
CompletableFuture<AckMessageResponse> future = new CompletableFuture<>();
|
||||
List<AckMessageResultEntry> ackMessageResultEntryList = new ArrayList<>();
|
||||
for (AckMessageEntry entry : request.getEntriesList()) {
|
||||
ReceiptHandle receiptHandle = ReceiptHandle.decode(entry.getReceiptHandle());
|
||||
if (receiptHandle.isExpired()) {
|
||||
ackMessageResultEntryList.add(AckMessageResultEntry.newBuilder()
|
||||
.setReceiptHandle(entry.getReceiptHandle())
|
||||
.setMessageId(entry.getMessageId())
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.RECEIPT_HANDLE_EXPIRED, "expired"))
|
||||
.build());
|
||||
continue;
|
||||
}
|
||||
AckMessageRequestHeader requestHeader = GrpcConverter.buildAckMessageRequestHeader(request, receiptHandle);
|
||||
RemotingCommand command = RemotingCommand.createRequestCommand(RequestCode.ACK_MESSAGE, requestHeader);
|
||||
command.makeCustomHeaderToNet();
|
||||
|
||||
try {
|
||||
RemotingCommand responseCommand = brokerController.getAckMessageProcessor()
|
||||
.processRequest(channelHandlerContext, command);
|
||||
ackMessageResultEntryList.add(AckMessageResultEntry.newBuilder()
|
||||
.setReceiptHandle(entry.getReceiptHandle())
|
||||
.setMessageId(entry.getMessageId())
|
||||
.setStatus(ResponseBuilder.buildStatus(responseCommand.getCode(), responseCommand.getRemark()))
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
log.error("Exception raised when ack message", e);
|
||||
ackMessageResultEntryList.add(AckMessageResultEntry.newBuilder()
|
||||
.setReceiptHandle(entry.getReceiptHandle())
|
||||
.setMessageId(entry.getMessageId())
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.INTERNAL_SERVER_ERROR, e.getMessage()))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
AckMessageResponse response = AckMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(ResponseCode.SUCCESS, "ok"))
|
||||
.addAllEntries(ackMessageResultEntryList)
|
||||
.build();
|
||||
future.complete(response);
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ForwardMessageToDeadLetterQueueResponse> forwardMessageToDeadLetterQueue(Context ctx,
|
||||
ForwardMessageToDeadLetterQueueRequest request) {
|
||||
SimpleChannel channel = channelManager.createChannel(ctx);
|
||||
SimpleChannelHandlerContext channelHandlerContext = new SimpleChannelHandlerContext(channel);
|
||||
|
||||
ConsumerSendMsgBackRequestHeader requestHeader = GrpcConverter.buildConsumerSendMsgBackRequestHeader(request);
|
||||
RemotingCommand command = RemotingCommand.createRequestCommand(RequestCode.CONSUMER_SEND_MSG_BACK, requestHeader);
|
||||
command.makeCustomHeaderToNet();
|
||||
|
||||
CompletableFuture<ForwardMessageToDeadLetterQueueResponse> future = new CompletableFuture<>();
|
||||
try {
|
||||
RemotingCommand response = brokerController.getSendMessageProcessor()
|
||||
.processRequest(channelHandlerContext, command);
|
||||
|
||||
future.complete(ForwardMessageToDeadLetterQueueResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(response.getCode(), response.getRemark()))
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
log.error("Exception raised when forwardMessageToDeadLetterQueue", e);
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<EndTransactionResponse> endTransaction(Context ctx, EndTransactionRequest request) {
|
||||
Channel channel = channelManager.createChannel(ctx);
|
||||
SimpleChannelHandlerContext channelHandlerContext = new SimpleChannelHandlerContext(channel);
|
||||
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic());
|
||||
EndTransactionRequestHeader requestHeader = GrpcConverter.buildEndTransactionRequestHeader(request, topicName);
|
||||
RemotingCommand command = RemotingCommand.createRequestCommand(RequestCode.END_TRANSACTION, requestHeader);
|
||||
command.makeCustomHeaderToNet();
|
||||
|
||||
CompletableFuture<EndTransactionResponse> future = new CompletableFuture<>();
|
||||
try {
|
||||
RemotingCommand responseCommand = brokerController.getEndTransactionProcessor()
|
||||
.processRequest(channelHandlerContext, command);
|
||||
EndTransactionResponse.Builder builder = EndTransactionResponse.newBuilder();
|
||||
if (null != responseCommand) {
|
||||
builder.setStatus(ResponseBuilder.buildStatus(responseCommand.getCode(), responseCommand.getRemark()));
|
||||
} else {
|
||||
builder.setStatus(ResponseBuilder.buildStatus(Code.INTERNAL_SERVER_ERROR, "Response command is null"));
|
||||
}
|
||||
EndTransactionResponse response = builder.build();
|
||||
future.complete(response);
|
||||
} catch (Exception e) {
|
||||
log.error("Exception raised while endTransaction", e);
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
public void reportThreadStackTrace(Context ctx, ThreadStackTrace request) {
|
||||
String nonce = request.getNonce();
|
||||
String threadStack = request.getThreadStackTrace();
|
||||
TelemetryCommandRecord pollCommandResponseFuture = telemetryCommandManager.getCommand(nonce);
|
||||
if (pollCommandResponseFuture != null) {
|
||||
Integer opaque = pollCommandResponseFuture.getOpaque();
|
||||
if (opaque != null) {
|
||||
RemotingServer remotingServer = this.brokerController.getRemotingServer();
|
||||
if (remotingServer instanceof NettyRemotingAbstract) {
|
||||
NettyRemotingAbstract nettyRemotingAbstract = (NettyRemotingAbstract) remotingServer;
|
||||
RemotingCommand remotingCommand = RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "From gRPC client");
|
||||
remotingCommand.setOpaque(pollCommandResponseFuture.getOpaque());
|
||||
ConsumerRunningInfo runningInfo = new ConsumerRunningInfo();
|
||||
runningInfo.setJstack(threadStack);
|
||||
remotingCommand.setBody(runningInfo.encode());
|
||||
nettyRemotingAbstract.processResponseCommand(new SimpleChannelHandlerContext(channelManager.createChannel(ctx)), remotingCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void reportVerifyMessageResult(Context ctx, VerifyMessageResult request) {
|
||||
String nonce = request.getNonce();
|
||||
TelemetryCommandRecord pollCommandResponseFuture = telemetryCommandManager.getCommand(nonce);
|
||||
if (pollCommandResponseFuture != null) {
|
||||
Integer opaque = pollCommandResponseFuture.getOpaque();
|
||||
if (opaque != null) {
|
||||
RemotingServer remotingServer = this.brokerController.getRemotingServer();
|
||||
if (remotingServer instanceof NettyRemotingAbstract) {
|
||||
NettyRemotingAbstract nettyRemotingAbstract = (NettyRemotingAbstract) remotingServer;
|
||||
RemotingCommand remotingCommand = RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "From gRPC client");
|
||||
remotingCommand.setOpaque(pollCommandResponseFuture.getOpaque());
|
||||
ConsumeMessageDirectlyResult result = GrpcConverter.buildConsumeMessageDirectlyResult(request);
|
||||
remotingCommand.setBody(result.encode());
|
||||
nettyRemotingAbstract.processResponseCommand(new SimpleChannelHandlerContext(channelManager.createChannel(ctx)), remotingCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<NotifyClientTerminationResponse> notifyClientTermination(Context ctx,
|
||||
NotifyClientTerminationRequest request) {
|
||||
Channel channel = channelManager.createChannel(ctx);
|
||||
SimpleChannelHandlerContext simpleChannelHandlerContext = new SimpleChannelHandlerContext(channel);
|
||||
String clientId = InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.CLIENT_ID);
|
||||
Settings clientSettings = grpcClientManager.getClientSettings(clientId);
|
||||
UnregisterClientRequestHeader header = GrpcConverter.buildUnregisterClientRequestHeader(clientId, clientSettings.getClientType(), request);
|
||||
|
||||
RemotingCommand remotingCommand = RemotingCommand.createRequestCommand(RequestCode.UNREGISTER_CLIENT, header);
|
||||
remotingCommand.makeCustomHeaderToNet();
|
||||
try {
|
||||
this.brokerController.getClientManageProcessor().unregisterClient(simpleChannelHandlerContext, remotingCommand);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return new CompletableFuture<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChangeInvisibleDurationResponse> changeInvisibleDuration(Context ctx,
|
||||
ChangeInvisibleDurationRequest request) {
|
||||
Channel channel = channelManager.createChannel(ctx);
|
||||
SimpleChannelHandlerContext channelHandlerContext = new SimpleChannelHandlerContext(channel);
|
||||
|
||||
ChangeInvisibleTimeRequestHeader requestHeader = GrpcConverter.buildChangeInvisibleTimeRequestHeader(request);
|
||||
ReceiptHandle receiptHandle = ReceiptHandle.decode(request.getReceiptHandle());
|
||||
RemotingCommand command = RemotingCommand.createRequestCommand(RequestCode.CHANGE_MESSAGE_INVISIBLETIME, requestHeader);
|
||||
command.makeCustomHeaderToNet();
|
||||
|
||||
CompletableFuture<ChangeInvisibleDurationResponse> future = new CompletableFuture<>();
|
||||
try {
|
||||
RemotingCommand responseCommand = brokerController.getChangeInvisibleTimeProcessor()
|
||||
.processRequest(channelHandlerContext, command);
|
||||
ChangeInvisibleTimeResponseHeader responseHeader = (ChangeInvisibleTimeResponseHeader) responseCommand.readCustomHeader();
|
||||
ChangeInvisibleDurationResponse.Builder builder = ChangeInvisibleDurationResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(responseCommand.getCode(), responseCommand.getRemark()));
|
||||
if (responseCommand.getCode() == ResponseCode.SUCCESS) {
|
||||
builder.setReceiptHandle(ReceiptHandle.builder()
|
||||
.startOffset(requestHeader.getOffset())
|
||||
.retrieveTime(responseHeader.getPopTime())
|
||||
.invisibleTime(responseHeader.getInvisibleTime())
|
||||
.reviveQueueId(responseHeader.getReviveQid())
|
||||
.topicType(receiptHandle.getTopicType())
|
||||
.brokerName(brokerController.getBrokerConfig().getBrokerName())
|
||||
.queueId(requestHeader.getQueueId())
|
||||
.offset(requestHeader.getOffset())
|
||||
.build()
|
||||
.encode());
|
||||
}
|
||||
|
||||
future.complete(builder.build());
|
||||
} catch (Exception e) {
|
||||
log.error("Exception raised while changeInvisibleDuration", e);
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamObserver<TelemetryCommand> telemetry(Context ctx, StreamObserver<TelemetryCommand> responseObserver) {
|
||||
String clientId = InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.CLIENT_ID);
|
||||
return new StreamObserver<TelemetryCommand>() {
|
||||
@Override
|
||||
public void onNext(TelemetryCommand request) {
|
||||
switch (request.getCommandCase()) {
|
||||
case SETTINGS: {
|
||||
responseObserver.onNext(clientSettingsService.processClientSettings(ctx, request, responseObserver));
|
||||
break;
|
||||
}
|
||||
case THREAD_STACK_TRACE: {
|
||||
reportThreadStackTrace(ctx, request.getThreadStackTrace());
|
||||
break;
|
||||
}
|
||||
case VERIFY_MESSAGE_RESULT: {
|
||||
reportVerifyMessageResult(ctx, request.getVerifyMessageResult());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("Request type is illegal");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected class ConsumerIdsChangeListenerImpl implements ConsumerIdsChangeListener {
|
||||
|
||||
@Override
|
||||
public void handle(ConsumerGroupEvent event, String group, Object... args) {
|
||||
if (event == ConsumerGroupEvent.CLIENT_UNREGISTER) {
|
||||
if (args == null || args.length < 1) {
|
||||
return;
|
||||
}
|
||||
if (args[0] instanceof ClientChannelInfo) {
|
||||
ClientChannelInfo clientChannelInfo = (ClientChannelInfo) args[0];
|
||||
channelManager.onClientOffline(clientChannelInfo.getClientId());
|
||||
grpcClientManager.removeClientSettings(clientChannelInfo.getClientId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected class ProducerChangeListenerImpl implements ProducerChangeListener {
|
||||
|
||||
@Override
|
||||
public void handle(ProducerGroupEvent event, String group, ClientChannelInfo clientChannelInfo) {
|
||||
if (event == ProducerGroupEvent.CLIENT_UNREGISTER) {
|
||||
channelManager.onClientOffline(clientChannelInfo.getClientId());
|
||||
grpcClientManager.removeClientSettings(clientChannelInfo.getClientId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service;
|
||||
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.ReceiveMessageResponse;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseHook;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseWriter;
|
||||
|
||||
public class ReceiveMessageResponseStreamObserver implements StreamObserver<ReceiveMessageResponse> {
|
||||
|
||||
private final Context context;
|
||||
private final ReceiveMessageRequest request;
|
||||
private final ResponseHook<ReceiveMessageRequest, ReceiveMessageResponse> receiveMessageHook;
|
||||
private final StreamObserver<ReceiveMessageResponse> observer;
|
||||
|
||||
public ReceiveMessageResponseStreamObserver(Context context, ReceiveMessageRequest request,
|
||||
ResponseHook<ReceiveMessageRequest, ReceiveMessageResponse> receiveMessageHook,
|
||||
StreamObserver<ReceiveMessageResponse> observer) {
|
||||
this.context = context;
|
||||
this.request = request;
|
||||
this.receiveMessageHook = receiveMessageHook;
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(ReceiveMessageResponse response) {
|
||||
if (receiveMessageHook != null) {
|
||||
receiveMessageHook.beforeResponse(context, request, response, null);
|
||||
}
|
||||
observer.onNext(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
if (receiveMessageHook != null) {
|
||||
receiveMessageHook.beforeResponse(context, request, null, throwable);
|
||||
}
|
||||
observer.onError(throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return ResponseWriter.isCancelled(observer);
|
||||
}
|
||||
}
|
||||
-283
@@ -1,283 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.cluster;
|
||||
|
||||
import apache.rocketmq.v2.AckMessageEntry;
|
||||
import apache.rocketmq.v2.AckMessageRequest;
|
||||
import apache.rocketmq.v2.AckMessageResponse;
|
||||
import apache.rocketmq.v2.AckMessageResultEntry;
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationRequest;
|
||||
import apache.rocketmq.v2.ChangeInvisibleDurationResponse;
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.ReceiveMessageResponse;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.client.consumer.AckStatus;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader;
|
||||
import org.apache.rocketmq.proxy.connector.ConnectorManager;
|
||||
import org.apache.rocketmq.proxy.connector.ForwardProducer;
|
||||
import org.apache.rocketmq.proxy.connector.ForwardReadConsumer;
|
||||
import org.apache.rocketmq.proxy.connector.ForwardWriteConsumer;
|
||||
import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ProxyException;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseHook;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.BaseService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.GrpcClientManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.BaseReceiveMessageResponseStreamWriter;
|
||||
|
||||
public class ConsumerService extends BaseService {
|
||||
protected final ForwardReadConsumer readConsumer;
|
||||
protected final ForwardWriteConsumer writeConsumer;
|
||||
/**
|
||||
* For sending messages back to broker.
|
||||
*/
|
||||
protected final ForwardProducer producer;
|
||||
protected final GrpcClientManager grpcClientManager;
|
||||
|
||||
private volatile ReadQueueSelector readQueueSelector;
|
||||
private volatile BaseReceiveMessageResponseStreamWriter.Builder receiveMessageWriterBuilder;
|
||||
|
||||
private volatile ResponseHook<ReceiveMessageRequest, ReceiveMessageResponse> receiveMessageHook;
|
||||
private volatile ResponseHook<AckMessageRequest, AckMessageResponse> ackMessageHook;
|
||||
private volatile ResponseHook<ChangeInvisibleDurationRequest, ChangeInvisibleDurationResponse> changeInvisibleDurationHook;
|
||||
|
||||
public ConsumerService(ConnectorManager connectorManager, GrpcClientManager grpcClientManager) {
|
||||
super(connectorManager);
|
||||
this.readConsumer = connectorManager.getForwardReadConsumer();
|
||||
this.writeConsumer = connectorManager.getForwardWriteConsumer();
|
||||
this.producer = connectorManager.getForwardProducer();
|
||||
this.grpcClientManager = grpcClientManager;
|
||||
|
||||
this.readQueueSelector = new DefaultReadQueueSelector(connectorManager.getTopicRouteCache());
|
||||
this.receiveMessageWriterBuilder = (observer, hook) -> new DefaultReceiveMessageResponseStreamWriter(
|
||||
observer,
|
||||
hook,
|
||||
writeConsumer,
|
||||
connectorManager.getTopicRouteCache(),
|
||||
new DefaultReceiveMessageResultFilter(
|
||||
producer, writeConsumer, grpcClientManager, connectorManager.getTopicRouteCache())
|
||||
);
|
||||
}
|
||||
|
||||
public void receiveMessage(Context ctx, ReceiveMessageRequest request,
|
||||
StreamObserver<ReceiveMessageResponse> responseObserver) {
|
||||
BaseReceiveMessageResponseStreamWriter writer = receiveMessageWriterBuilder.build(responseObserver, receiveMessageHook);
|
||||
try {
|
||||
PopMessageRequestHeader requestHeader = this.buildPopMessageRequestHeader(ctx, request);
|
||||
SelectableMessageQueue messageQueue = this.readQueueSelector.select(ctx, request, requestHeader);
|
||||
|
||||
if (messageQueue == null) {
|
||||
throw new ProxyException(Code.FORBIDDEN, "no readable topic route for topic " + requestHeader.getTopic());
|
||||
}
|
||||
|
||||
this.readConsumer.popMessage(
|
||||
ctx,
|
||||
messageQueue.getBrokerAddr(),
|
||||
messageQueue.getBrokerName(),
|
||||
requestHeader,
|
||||
requestHeader.getPollTime())
|
||||
.thenAccept(result -> writer.write(ctx, request, result.getPopStatus(), result.getMsgFoundList()))
|
||||
.exceptionally(e -> {
|
||||
writer.write(ctx, request, e);
|
||||
return null;
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
writer.write(ctx, request, t);
|
||||
}
|
||||
}
|
||||
|
||||
protected PopMessageRequestHeader buildPopMessageRequestHeader(Context ctx, ReceiveMessageRequest request) {
|
||||
checkSubscriptionData(request.getMessageQueue().getTopic(), request.getFilterExpression());
|
||||
boolean fifo = grpcClientManager.getClientSettings(ctx).getSubscription().getFifo();
|
||||
return GrpcConverter.buildPopMessageRequestHeader(request, GrpcConverter.buildPollTimeFromContext(ctx), fifo);
|
||||
}
|
||||
|
||||
public CompletableFuture<AckMessageResponse> ackMessage(Context ctx, AckMessageRequest request) {
|
||||
CompletableFuture<AckMessageResponse> future = new CompletableFuture<>();
|
||||
future.whenComplete((response, throwable) -> {
|
||||
if (ackMessageHook != null) {
|
||||
ackMessageHook.beforeResponse(ctx, request, response, throwable);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
CompletableFuture<AckMessageResultEntry>[] futures = new CompletableFuture[request.getEntriesCount()];
|
||||
for (int i = 0; i < request.getEntriesCount(); i++) {
|
||||
futures[i] = processAckMessage(ctx, request, request.getEntries(i));
|
||||
}
|
||||
CompletableFuture.allOf(futures).whenComplete((val, throwable) -> {
|
||||
if (throwable != null) {
|
||||
future.completeExceptionally(throwable);
|
||||
return;
|
||||
}
|
||||
List<AckMessageResultEntry> entryList = new ArrayList<>();
|
||||
for (CompletableFuture<AckMessageResultEntry> entryFuture : futures) {
|
||||
entryFuture.thenAccept(entryList::add);
|
||||
}
|
||||
AckMessageResponse.Builder responseBuilder = AckMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.addAllEntries(entryList);
|
||||
future.complete(responseBuilder.build());
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected CompletableFuture<AckMessageResultEntry> processAckMessage(Context ctx, AckMessageRequest request,
|
||||
AckMessageEntry ackMessageEntry) {
|
||||
CompletableFuture<AckMessageResultEntry> future = new CompletableFuture<>();
|
||||
AckMessageResultEntry.Builder failResult = AckMessageResultEntry.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.INTERNAL_SERVER_ERROR, "ack message failed"))
|
||||
.setMessageId(ackMessageEntry.getMessageId())
|
||||
.setReceiptHandle(ackMessageEntry.getReceiptHandle());
|
||||
|
||||
try {
|
||||
ReceiptHandle receiptHandle = resolveReceiptHandle(ctx, ackMessageEntry.getReceiptHandle());
|
||||
String brokerAddr = this.getBrokerAddr(ctx, receiptHandle.getBrokerName());
|
||||
|
||||
AckMessageRequestHeader requestHeader = this.buildAckMessageRequestHeader(ctx, request, receiptHandle);
|
||||
CompletableFuture<AckResult> ackResultFuture = this.writeConsumer.ackMessage(ctx, brokerAddr, ackMessageEntry.getMessageId(), requestHeader);
|
||||
ackResultFuture
|
||||
.thenAccept(result -> future.complete(convertToAckMessageResultEntry(ctx, ackMessageEntry, result)))
|
||||
.exceptionally(throwable -> {
|
||||
future.complete(failResult.setStatus(ResponseBuilder.buildStatus(throwable)).build());
|
||||
return null;
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
future.complete(failResult.setStatus(ResponseBuilder.buildStatus(t)).build());
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected AckMessageRequestHeader buildAckMessageRequestHeader(Context ctx, AckMessageRequest request,
|
||||
ReceiptHandle handle) {
|
||||
return GrpcConverter.buildAckMessageRequestHeader(request, handle);
|
||||
}
|
||||
|
||||
protected AckMessageResultEntry convertToAckMessageResultEntry(Context ctx, AckMessageEntry ackMessageEntry,
|
||||
AckResult ackResult) {
|
||||
if (AckStatus.OK.equals(ackResult.getStatus())) {
|
||||
return AckMessageResultEntry.newBuilder()
|
||||
.setMessageId(ackMessageEntry.getMessageId())
|
||||
.setReceiptHandle(ackMessageEntry.getReceiptHandle())
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build();
|
||||
}
|
||||
return AckMessageResultEntry.newBuilder()
|
||||
.setMessageId(ackMessageEntry.getMessageId())
|
||||
.setReceiptHandle(ackMessageEntry.getReceiptHandle())
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.INTERNAL_SERVER_ERROR, "ack failed: status is abnormal"))
|
||||
.build();
|
||||
}
|
||||
|
||||
public CompletableFuture<ChangeInvisibleDurationResponse> changeInvisibleDuration(Context ctx,
|
||||
ChangeInvisibleDurationRequest request) {
|
||||
CompletableFuture<ChangeInvisibleDurationResponse> future = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
ReceiptHandle receiptHandle = resolveReceiptHandle(ctx, request.getReceiptHandle());
|
||||
String brokerAddr = this.getBrokerAddr(ctx, receiptHandle.getBrokerName());
|
||||
|
||||
ChangeInvisibleTimeRequestHeader requestHeader = convertToChangeInvisibleTimeRequestHeader(ctx, request);
|
||||
future = this.writeConsumer.changeInvisibleTimeAsync(ctx, brokerAddr, receiptHandle.getBrokerName(),
|
||||
request.getMessageId(), requestHeader)
|
||||
.thenApply(result -> convertToChangeInvisibleDurationResponse(ctx, request, result));
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
future.whenComplete((response, throwable) -> {
|
||||
if (changeInvisibleDurationHook != null) {
|
||||
changeInvisibleDurationHook.beforeResponse(ctx, request, response, throwable);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
protected ChangeInvisibleTimeRequestHeader convertToChangeInvisibleTimeRequestHeader(Context ctx,
|
||||
ChangeInvisibleDurationRequest request) {
|
||||
return GrpcConverter.buildChangeInvisibleTimeRequestHeader(request);
|
||||
}
|
||||
|
||||
protected ChangeInvisibleDurationResponse convertToChangeInvisibleDurationResponse(Context ctx,
|
||||
ChangeInvisibleDurationRequest request, AckResult ackResult) {
|
||||
if (AckStatus.OK.equals(ackResult.getStatus())) {
|
||||
return ChangeInvisibleDurationResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.setReceiptHandle(ackResult.getExtraInfo())
|
||||
.build();
|
||||
}
|
||||
return ChangeInvisibleDurationResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.INTERNAL_SERVER_ERROR, "changeInvisibleDuration failed: status is abnormal"))
|
||||
.build();
|
||||
}
|
||||
|
||||
public ReadQueueSelector getReadQueueSelector() {
|
||||
return readQueueSelector;
|
||||
}
|
||||
|
||||
public void setReadQueueSelector(ReadQueueSelector readQueueSelector) {
|
||||
this.readQueueSelector = readQueueSelector;
|
||||
}
|
||||
|
||||
public BaseReceiveMessageResponseStreamWriter.Builder getReceiveMessageWriterBuilder() {
|
||||
return receiveMessageWriterBuilder;
|
||||
}
|
||||
|
||||
public void setReceiveMessageWriterBuilder(
|
||||
BaseReceiveMessageResponseStreamWriter.Builder receiveMessageWriterBuilder) {
|
||||
this.receiveMessageWriterBuilder = receiveMessageWriterBuilder;
|
||||
}
|
||||
|
||||
public ResponseHook<ReceiveMessageRequest, ReceiveMessageResponse> getReceiveMessageHook() {
|
||||
return receiveMessageHook;
|
||||
}
|
||||
|
||||
public void setReceiveMessageHook(
|
||||
ResponseHook<ReceiveMessageRequest, ReceiveMessageResponse> receiveMessageHook) {
|
||||
this.receiveMessageHook = receiveMessageHook;
|
||||
}
|
||||
|
||||
public ResponseHook<AckMessageRequest, AckMessageResponse> getAckMessageHook() {
|
||||
return ackMessageHook;
|
||||
}
|
||||
|
||||
public void setAckMessageHook(
|
||||
ResponseHook<AckMessageRequest, AckMessageResponse> ackMessageHook) {
|
||||
this.ackMessageHook = ackMessageHook;
|
||||
}
|
||||
|
||||
public ResponseHook<ChangeInvisibleDurationRequest, ChangeInvisibleDurationResponse> getChangeInvisibleDurationHook() {
|
||||
return changeInvisibleDurationHook;
|
||||
}
|
||||
|
||||
public void setChangeInvisibleDurationHook(
|
||||
ResponseHook<ChangeInvisibleDurationRequest, ChangeInvisibleDurationResponse> changeInvisibleDurationHook) {
|
||||
this.changeInvisibleDurationHook = changeInvisibleDurationHook;
|
||||
}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.cluster;
|
||||
|
||||
import apache.rocketmq.v2.QueryAssignmentRequest;
|
||||
import io.grpc.Context;
|
||||
import java.util.List;
|
||||
import org.apache.rocketmq.proxy.connector.route.MessageQueueWrapper;
|
||||
import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.connector.route.TopicRouteCache;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
|
||||
public class DefaultAssignmentQueueSelector implements AssignmentQueueSelector {
|
||||
|
||||
private final TopicRouteCache topicRouteCache;
|
||||
|
||||
public DefaultAssignmentQueueSelector(TopicRouteCache topicRouteCache) {
|
||||
this.topicRouteCache = topicRouteCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SelectableMessageQueue> getAssignment(Context ctx, QueryAssignmentRequest request) throws Exception {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic());
|
||||
MessageQueueWrapper messageQueueWrapper = topicRouteCache.getMessageQueue(topicName);
|
||||
return messageQueueWrapper.getReadSelector().getBrokerActingQueues();
|
||||
}
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.cluster;
|
||||
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import io.grpc.Context;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader;
|
||||
import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.connector.route.TopicRouteCache;
|
||||
|
||||
public class DefaultReadQueueSelector implements ReadQueueSelector {
|
||||
|
||||
private final TopicRouteCache topicRouteCache;
|
||||
|
||||
public DefaultReadQueueSelector(TopicRouteCache topicRouteCache) {
|
||||
this.topicRouteCache = topicRouteCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectableMessageQueue select(Context ctx, ReceiveMessageRequest request, PopMessageRequestHeader requestHeader) {
|
||||
SelectableMessageQueue messageQueue = null;
|
||||
try {
|
||||
String topic = requestHeader.getTopic();
|
||||
|
||||
if (request.hasMessageQueue() && request.getMessageQueue().hasBroker()) {
|
||||
String brokerName = request.getMessageQueue().getBroker().getName();
|
||||
if (StringUtils.isNotBlank(brokerName)) {
|
||||
messageQueue = topicRouteCache.selectReadBrokerByName(topic, brokerName);
|
||||
}
|
||||
}
|
||||
|
||||
if (messageQueue == null) {
|
||||
messageQueue = topicRouteCache.selectOneReadBroker(topic, null);
|
||||
}
|
||||
return messageQueue;
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.cluster;
|
||||
|
||||
import apache.rocketmq.v2.Message;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.ReceiveMessageResponse;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.time.Duration;
|
||||
import org.apache.rocketmq.client.consumer.AckStatus;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeRequestHeader;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.connector.ForwardWriteConsumer;
|
||||
import org.apache.rocketmq.proxy.connector.route.TopicRouteCache;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseHook;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.BaseService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.BaseReceiveMessageResponseStreamWriter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.ReceiveMessageResultFilter;
|
||||
|
||||
public class DefaultReceiveMessageResponseStreamWriter extends BaseReceiveMessageResponseStreamWriter {
|
||||
protected static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
protected static final long NACK_INVISIBLE_TIME = Duration.ofSeconds(1).toMillis();
|
||||
protected final ForwardWriteConsumer writeConsumer;
|
||||
protected final TopicRouteCache topicRouteCache;
|
||||
|
||||
public DefaultReceiveMessageResponseStreamWriter(
|
||||
StreamObserver<ReceiveMessageResponse> observer,
|
||||
ResponseHook<ReceiveMessageRequest, ReceiveMessageResponse> hook,
|
||||
ForwardWriteConsumer writeConsumer,
|
||||
TopicRouteCache topicRouteCache,
|
||||
ReceiveMessageResultFilter receiveMessageResultFilter) {
|
||||
super(observer, hook, receiveMessageResultFilter);
|
||||
this.writeConsumer = writeConsumer;
|
||||
this.topicRouteCache = topicRouteCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processThrowableWhenWriteMessage(Throwable throwable, Context context, ReceiveMessageRequest request,
|
||||
Message message) {
|
||||
this.nackFailToWriteMessage(context, request, message);
|
||||
}
|
||||
|
||||
protected void nackFailToWriteMessage(Context ctx, ReceiveMessageRequest request, Message message) {
|
||||
try {
|
||||
String receiptHandleStr = message.getSystemProperties().getReceiptHandle();
|
||||
ReceiptHandle handle = BaseService.resolveReceiptHandle(ctx, receiptHandleStr);
|
||||
String brokerAddr = BaseService.getBrokerAddr(ctx, this.topicRouteCache, handle.getBrokerName());
|
||||
|
||||
String groupName = GrpcConverter.wrapResourceWithNamespace(request.getGroup());
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(request.getMessageQueue().getTopic());
|
||||
ChangeInvisibleTimeRequestHeader changeInvisibleTimeRequestHeader = new ChangeInvisibleTimeRequestHeader();
|
||||
changeInvisibleTimeRequestHeader.setConsumerGroup(groupName);
|
||||
changeInvisibleTimeRequestHeader.setTopic(handle.getRealTopic(topicName, groupName));
|
||||
changeInvisibleTimeRequestHeader.setQueueId(handle.getQueueId());
|
||||
changeInvisibleTimeRequestHeader.setExtraInfo(handle.getReceiptHandle());
|
||||
changeInvisibleTimeRequestHeader.setOffset(handle.getOffset());
|
||||
changeInvisibleTimeRequestHeader.setInvisibleTime(NACK_INVISIBLE_TIME);
|
||||
|
||||
this.writeConsumer.changeInvisibleTimeAsync(
|
||||
ctx,
|
||||
brokerAddr,
|
||||
handle.getBrokerName(),
|
||||
message.getSystemProperties().getMessageId(),
|
||||
changeInvisibleTimeRequestHeader
|
||||
).whenComplete((ackResult, t) -> {
|
||||
if (t != null) {
|
||||
log.warn("err when nack message. request:{}, message:{}", request, message, t);
|
||||
} else if (!AckStatus.OK.equals(ackResult.getStatus())) {
|
||||
log.warn("nack failed. request:{}, message:{}, ackResult:{}", request, message, ackResult);
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
log.warn("err when nack message. request:{}, message:{}", request, message, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
-127
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.cluster;
|
||||
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import io.grpc.Context;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
|
||||
import org.apache.rocketmq.proxy.connector.ForwardProducer;
|
||||
import org.apache.rocketmq.proxy.connector.ForwardWriteConsumer;
|
||||
import org.apache.rocketmq.proxy.connector.route.TopicRouteCache;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseHook;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.BaseReceiveMessageResultFilter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.GrpcClientManager;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
import static org.apache.rocketmq.proxy.grpc.v2.service.BaseService.getBrokerAddr;
|
||||
|
||||
public class DefaultReceiveMessageResultFilter extends BaseReceiveMessageResultFilter {
|
||||
|
||||
protected final ForwardProducer producer;
|
||||
protected final ForwardWriteConsumer writeConsumer;
|
||||
protected final TopicRouteCache topicRouteCache;
|
||||
|
||||
private volatile ResponseHook<AckMessageRequestHeader, AckResult> ackNoMatchedMessageHook;
|
||||
private volatile ResponseHook<ConsumerSendMsgBackRequestHeader, RemotingCommand> forwardToDLQInRecvMessageHook;
|
||||
|
||||
public DefaultReceiveMessageResultFilter(ForwardProducer producer, ForwardWriteConsumer writeConsumer,
|
||||
GrpcClientManager grpcClientManager, TopicRouteCache topicRouteCache) {
|
||||
super(grpcClientManager);
|
||||
this.producer = producer;
|
||||
this.writeConsumer = writeConsumer;
|
||||
this.topicRouteCache = topicRouteCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processNoMatchMessage(Context ctx, ReceiveMessageRequest request, MessageExt messageExt) {
|
||||
CompletableFuture<AckResult> future = new CompletableFuture<>();
|
||||
|
||||
ReceiptHandle handle = ReceiptHandle.create(messageExt);
|
||||
if (handle == null) {
|
||||
return;
|
||||
}
|
||||
AckMessageRequestHeader ackMessageRequestHeader = GrpcConverter.buildAckMessageRequestHeader(request, handle);
|
||||
try {
|
||||
String brokerAddr = getBrokerAddr(ctx, topicRouteCache, handle.getBrokerName());
|
||||
future = this.writeConsumer.ackMessage(ctx, brokerAddr, messageExt.getMsgId(), ackMessageRequestHeader);
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
|
||||
future.whenComplete((ackResult, throwable) -> {
|
||||
if (ackNoMatchedMessageHook != null) {
|
||||
ackNoMatchedMessageHook.beforeResponse(ctx, ackMessageRequestHeader, ackResult, throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processExceedMaxAttemptsMessage(Context ctx, ReceiveMessageRequest request, MessageExt messageExt,
|
||||
int maxAttempts) {
|
||||
CompletableFuture<RemotingCommand> future = new CompletableFuture<>();
|
||||
ConsumerSendMsgBackRequestHeader consumerSendMsgBackRequestHeader = new ConsumerSendMsgBackRequestHeader();
|
||||
|
||||
try {
|
||||
ReceiptHandle handle = ReceiptHandle.create(messageExt);
|
||||
if (handle == null) {
|
||||
return;
|
||||
}
|
||||
String brokerAddr = getBrokerAddr(ctx, topicRouteCache, handle.getBrokerName());
|
||||
ConsumerSendMsgBackRequestHeader sendMsgBackRequestHeader = GrpcConverter.buildConsumerSendMsgBackRequestHeader(
|
||||
request,
|
||||
handle,
|
||||
messageExt.getMsgId(),
|
||||
maxAttempts);
|
||||
AckMessageRequestHeader ackMessageRequestHeader = GrpcConverter.buildAckMessageRequestHeader(request, handle);
|
||||
|
||||
future = this.producer.sendMessageBackThenAckOrg(ctx, brokerAddr, sendMsgBackRequestHeader, ackMessageRequestHeader);
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
|
||||
future.whenComplete((result, throwable) -> {
|
||||
if (forwardToDLQInRecvMessageHook != null) {
|
||||
forwardToDLQInRecvMessageHook.beforeResponse(ctx, consumerSendMsgBackRequestHeader, result, throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ResponseHook<AckMessageRequestHeader, AckResult> getAckNoMatchedMessageHook() {
|
||||
return ackNoMatchedMessageHook;
|
||||
}
|
||||
|
||||
public void setAckNoMatchedMessageHook(
|
||||
ResponseHook<AckMessageRequestHeader, AckResult> ackNoMatchedMessageHook) {
|
||||
this.ackNoMatchedMessageHook = ackNoMatchedMessageHook;
|
||||
}
|
||||
|
||||
public ResponseHook<ConsumerSendMsgBackRequestHeader, RemotingCommand> getForwardToDLQInRecvMessageHook() {
|
||||
return forwardToDLQInRecvMessageHook;
|
||||
}
|
||||
|
||||
public void setForwardToDLQInRecvMessageHook(
|
||||
ResponseHook<ConsumerSendMsgBackRequestHeader, RemotingCommand> forwardToDLQInRecvMessageHook) {
|
||||
this.forwardToDLQInRecvMessageHook = forwardToDLQInRecvMessageHook;
|
||||
}
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.cluster;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.Message;
|
||||
import apache.rocketmq.v2.SendMessageRequest;
|
||||
import io.grpc.Context;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.connector.route.TopicRouteCache;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ProxyException;
|
||||
|
||||
public class DefaultWriteQueueSelector implements WriteQueueSelector {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
protected final TopicRouteCache topicRouteCache;
|
||||
|
||||
public DefaultWriteQueueSelector(TopicRouteCache topicRouteCache) {
|
||||
this.topicRouteCache = topicRouteCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectableMessageQueue selectQueue(
|
||||
Context ctx,
|
||||
SendMessageRequest request
|
||||
) {
|
||||
try {
|
||||
if (request.getMessagesCount() <= 0) {
|
||||
throw new ProxyException(Code.MESSAGE_CORRUPTED, "no message to send");
|
||||
}
|
||||
Message message = request.getMessages(0);
|
||||
String topic = GrpcConverter.wrapResourceWithNamespace(message.getTopic());
|
||||
String shardingKey = null;
|
||||
if (request.getMessagesCount() == 1) {
|
||||
shardingKey = message.getSystemProperties().getMessageGroup();
|
||||
}
|
||||
SelectableMessageQueue targetMessageQueue;
|
||||
if (StringUtils.isNotEmpty(shardingKey)) {
|
||||
// With shardingKey
|
||||
targetMessageQueue = selectOrderQueue(topic, shardingKey);
|
||||
} else {
|
||||
targetMessageQueue = selectNormalQueue(topic);
|
||||
}
|
||||
return targetMessageQueue;
|
||||
} catch (Exception e) {
|
||||
log.error("error when select queue in DefaultMessageQueueSelector. request: {}", request, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected SelectableMessageQueue selectNormalQueue(String topic) throws Exception {
|
||||
return this.topicRouteCache.selectOneWriteQueue(topic, null);
|
||||
}
|
||||
|
||||
protected SelectableMessageQueue selectOrderQueue(String topic, String shardingKey) throws Exception {
|
||||
return this.topicRouteCache.selectOneWriteQueueByKey(topic, shardingKey);
|
||||
}
|
||||
}
|
||||
-266
@@ -1,266 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.cluster;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.HeartbeatRequest;
|
||||
import apache.rocketmq.v2.HeartbeatResponse;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationRequest;
|
||||
import apache.rocketmq.v2.NotifyClientTerminationResponse;
|
||||
import apache.rocketmq.v2.Resource;
|
||||
import apache.rocketmq.v2.Settings;
|
||||
import apache.rocketmq.v2.TelemetryCommand;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.rocketmq.broker.client.ClientChannelInfo;
|
||||
import org.apache.rocketmq.broker.client.ConsumerGroupEvent;
|
||||
import org.apache.rocketmq.broker.client.ConsumerIdsChangeListener;
|
||||
import org.apache.rocketmq.broker.client.ConsumerManager;
|
||||
import org.apache.rocketmq.broker.client.ProducerChangeListener;
|
||||
import org.apache.rocketmq.broker.client.ProducerGroupEvent;
|
||||
import org.apache.rocketmq.broker.client.ProducerManager;
|
||||
import org.apache.rocketmq.common.MQVersion;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.channel.ChannelManager;
|
||||
import org.apache.rocketmq.proxy.common.TelemetryCommandManager;
|
||||
import org.apache.rocketmq.proxy.connector.ConnectorManager;
|
||||
import org.apache.rocketmq.proxy.grpc.interceptor.InterceptorConstants;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ProxyException;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.channel.GrpcClientChannel;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.BaseService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.GrpcClientManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.ClientSettingsService;
|
||||
import org.apache.rocketmq.remoting.protocol.LanguageCode;
|
||||
|
||||
public class ForwardClientService extends BaseService {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
protected final ChannelManager channelManager;
|
||||
protected final GrpcClientManager grpcClientManager;
|
||||
protected final TelemetryCommandManager telemetryCommandManager;
|
||||
|
||||
protected ConsumerManager consumerManager;
|
||||
protected ProducerManager producerManager;
|
||||
protected ClientSettingsService clientSettingsService;
|
||||
|
||||
public ForwardClientService(
|
||||
ConnectorManager connectorManager,
|
||||
ScheduledExecutorService scheduledExecutorService,
|
||||
ChannelManager channelManager,
|
||||
GrpcClientManager grpcClientManager,
|
||||
TelemetryCommandManager telemetryCommandManager
|
||||
) {
|
||||
super(connectorManager);
|
||||
scheduledExecutorService.scheduleWithFixedDelay(
|
||||
this::scanNotActiveChannel,
|
||||
Duration.ofSeconds(10).toMillis(),
|
||||
Duration.ofSeconds(10).toMillis(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
this.channelManager = channelManager;
|
||||
this.grpcClientManager = grpcClientManager;
|
||||
this.telemetryCommandManager = telemetryCommandManager;
|
||||
|
||||
this.clientSettingsService = new ClientSettingsService(this.channelManager, this.grpcClientManager, this.telemetryCommandManager);
|
||||
this.consumerManager = new ConsumerManager(new ConsumerIdsChangeListenerImpl());
|
||||
this.producerManager = new ProducerManager();
|
||||
this.producerManager.appendProducerChangeListener(new ProducerChangeListenerImpl());
|
||||
}
|
||||
|
||||
protected class ConsumerIdsChangeListenerImpl implements ConsumerIdsChangeListener {
|
||||
|
||||
@Override
|
||||
public void handle(ConsumerGroupEvent event, String group, Object... args) {
|
||||
if (event == ConsumerGroupEvent.CLIENT_UNREGISTER) {
|
||||
if (args == null || args.length < 1) {
|
||||
return;
|
||||
}
|
||||
if (args[0] instanceof ClientChannelInfo) {
|
||||
ClientChannelInfo clientChannelInfo = (ClientChannelInfo) args[0];
|
||||
channelManager.onClientOffline(clientChannelInfo.getClientId());
|
||||
grpcClientManager.removeClientSettings(clientChannelInfo.getClientId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected class ProducerChangeListenerImpl implements ProducerChangeListener {
|
||||
|
||||
@Override
|
||||
public void handle(ProducerGroupEvent event, String group, ClientChannelInfo clientChannelInfo) {
|
||||
switch (event) {
|
||||
case GROUP_UNREGISTER:
|
||||
connectorManager.getTransactionHeartbeatRegisterService().onProducerGroupOffline(group);
|
||||
break;
|
||||
case CLIENT_UNREGISTER:
|
||||
channelManager.onClientOffline(clientChannelInfo.getClientId());
|
||||
grpcClientManager.removeClientSettings(clientChannelInfo.getClientId());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<HeartbeatResponse> heartbeat(Context ctx, HeartbeatRequest request) {
|
||||
CompletableFuture<HeartbeatResponse> future = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
String language = InterceptorConstants.METADATA.get(Context.current()).get(InterceptorConstants.LANGUAGE);
|
||||
String clientId = InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.CLIENT_ID);
|
||||
LanguageCode languageCode = LanguageCode.valueOf(language);
|
||||
|
||||
Settings clientSettings = grpcClientManager.getClientSettings(clientId);
|
||||
switch (clientSettings.getClientType()) {
|
||||
case PRODUCER: {
|
||||
for (Resource topic : clientSettings.getPublishing().getTopicsList()) {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
GrpcClientChannel channel = GrpcClientChannel.create(channelManager, topicName, clientId, telemetryCommandManager);
|
||||
ClientChannelInfo clientChannelInfo = new ClientChannelInfo(channel, clientId, languageCode, MQVersion.Version.V5_0_0.ordinal());
|
||||
// use topic name as producer group
|
||||
producerManager.registerProducer(topicName, clientChannelInfo);
|
||||
connectorManager.getTransactionHeartbeatRegisterService().addProducerGroup(topicName, topicName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PUSH_CONSUMER:
|
||||
case SIMPLE_CONSUMER: {
|
||||
if (!request.hasGroup()) {
|
||||
throw new ProxyException(Code.ILLEGAL_CONSUMER_GROUP, "group cannot be empty for consumer");
|
||||
}
|
||||
String consumerGroup = GrpcConverter.wrapResourceWithNamespace(request.getGroup());
|
||||
GrpcClientChannel channel = GrpcClientChannel.create(ctx, channelManager, consumerGroup, clientId, telemetryCommandManager);
|
||||
ClientChannelInfo clientChannelInfo = new ClientChannelInfo(channel, clientId, languageCode, MQVersion.Version.V5_0_0.ordinal());
|
||||
|
||||
consumerManager.registerConsumer(
|
||||
consumerGroup,
|
||||
clientChannelInfo,
|
||||
GrpcConverter.buildConsumeType(clientSettings.getClientType()),
|
||||
MessageModel.CLUSTERING,
|
||||
ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET,
|
||||
GrpcConverter.buildSubscriptionDataSet(clientSettings.getSubscription().getSubscriptionsList()),
|
||||
false
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("ClientType not exist " + clientSettings.getClientType());
|
||||
}
|
||||
}
|
||||
future.complete(HeartbeatResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build());
|
||||
return future;
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
public CompletableFuture<NotifyClientTerminationResponse> notifyClientTermination(Context ctx, NotifyClientTerminationRequest request) {
|
||||
CompletableFuture<NotifyClientTerminationResponse> future = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
String clientId = InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.CLIENT_ID);
|
||||
Settings clientSettings = grpcClientManager.getClientSettings(clientId);
|
||||
|
||||
switch (clientSettings.getClientType()) {
|
||||
case PRODUCER:
|
||||
for (Resource topic : clientSettings.getPublishing().getTopicsList()) {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
// user topic name as producer group
|
||||
GrpcClientChannel channel = GrpcClientChannel.removeChannel(channelManager, topicName, clientId);
|
||||
if (channel != null) {
|
||||
producerManager.doChannelCloseEvent(topicName, channel);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PUSH_CONSUMER:
|
||||
case SIMPLE_CONSUMER:
|
||||
if (!request.hasGroup()) {
|
||||
throw new ProxyException(Code.ILLEGAL_CONSUMER_GROUP, "group cannot be empty for consumer");
|
||||
}
|
||||
String consumerGroup = GrpcConverter.wrapResourceWithNamespace(request.getGroup());
|
||||
GrpcClientChannel channel = GrpcClientChannel.removeChannel(channelManager, consumerGroup, clientId);
|
||||
if (channel != null) {
|
||||
consumerManager.doChannelCloseEvent(consumerGroup, channel);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
future.complete(NotifyClientTerminationResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build());
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
public StreamObserver<TelemetryCommand> telemetry(Context ctx, StreamObserver<TelemetryCommand> responseObserver) {
|
||||
return new StreamObserver<TelemetryCommand>() {
|
||||
@Override
|
||||
public void onNext(TelemetryCommand request) {
|
||||
if (request.getCommandCase() == TelemetryCommand.CommandCase.SETTINGS) {
|
||||
responseObserver.onNext(clientSettingsService.processClientSettings(ctx, request, responseObserver));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void scanNotActiveChannel() {
|
||||
try {
|
||||
this.consumerManager.scanNotActiveChannel();
|
||||
this.producerManager.scanNotActiveChannel();
|
||||
} catch (Exception e) {
|
||||
log.error("error occurred when scan not active client channels.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ConsumerManager getConsumerManager() {
|
||||
return consumerManager;
|
||||
}
|
||||
|
||||
public ProducerManager getProducerManager() {
|
||||
return producerManager;
|
||||
}
|
||||
}
|
||||
-184
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.cluster;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueRequest;
|
||||
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueResponse;
|
||||
import apache.rocketmq.v2.Resource;
|
||||
import apache.rocketmq.v2.SendMessageRequest;
|
||||
import apache.rocketmq.v2.SendMessageResponse;
|
||||
import apache.rocketmq.v2.SendReceipt;
|
||||
import com.beust.jcommander.internal.Lists;
|
||||
import io.grpc.Context;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.client.producer.SendStatus;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader;
|
||||
import org.apache.rocketmq.proxy.connector.ConnectorManager;
|
||||
import org.apache.rocketmq.proxy.connector.ForwardProducer;
|
||||
import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ProxyException;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseHook;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.BaseService;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class ProducerService extends BaseService {
|
||||
|
||||
protected final ForwardProducer producer;
|
||||
private volatile WriteQueueSelector writeQueueSelector;
|
||||
private volatile ResponseHook<SendMessageRequest, SendMessageResponse> sendMessageHook;
|
||||
private volatile ResponseHook<ForwardMessageToDeadLetterQueueRequest, ForwardMessageToDeadLetterQueueResponse> forwardMessageToDLQHook;
|
||||
|
||||
public ProducerService(ConnectorManager connectorManager) {
|
||||
super(connectorManager);
|
||||
this.producer = connectorManager.getForwardProducer();
|
||||
|
||||
this.writeQueueSelector = new DefaultWriteQueueSelector(this.connectorManager.getTopicRouteCache());
|
||||
}
|
||||
|
||||
public CompletableFuture<SendMessageResponse> sendMessage(Context ctx, SendMessageRequest request) {
|
||||
CompletableFuture<SendMessageResponse> future = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
SelectableMessageQueue selectableMessageQueue = writeQueueSelector.selectQueue(ctx, request);
|
||||
if (selectableMessageQueue == null) {
|
||||
throw new ProxyException(Code.FORBIDDEN, "no writeable topic route");
|
||||
}
|
||||
|
||||
// send message to broker.
|
||||
future = this.producer.sendMessage(
|
||||
ctx,
|
||||
selectableMessageQueue.getBrokerAddr(),
|
||||
selectableMessageQueue.getBrokerName(),
|
||||
convertToMessageList(ctx, request),
|
||||
convertToSendMessageRequestHeader(ctx, request, selectableMessageQueue)
|
||||
).thenApply(result -> convertToSendMessageResponse(ctx, request, result));
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
|
||||
future.whenComplete((response, throwable) -> {
|
||||
if (sendMessageHook != null) {
|
||||
sendMessageHook.beforeResponse(ctx, request, response, throwable);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
protected SendMessageRequestHeader convertToSendMessageRequestHeader(Context ctx, SendMessageRequest request, SelectableMessageQueue selectableMessageQueue) {
|
||||
// use topic name as group
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(request.getMessages(0).getTopic());
|
||||
return GrpcConverter.buildSendMessageRequestHeader(request, topicName,
|
||||
selectableMessageQueue.getQueueId());
|
||||
}
|
||||
|
||||
protected List<org.apache.rocketmq.common.message.Message> convertToMessageList(Context ctx, SendMessageRequest request) {
|
||||
// use topic name as group
|
||||
Resource topic = request.getMessages(0).getTopic();
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(topic);
|
||||
return GrpcConverter.buildMessage(request.getMessagesList(), topic);
|
||||
}
|
||||
|
||||
protected SendMessageResponse convertToSendMessageResponse(Context ctx, SendMessageRequest request,
|
||||
SendResult result) {
|
||||
if (result.getSendStatus() != SendStatus.SEND_OK) {
|
||||
return SendMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.INTERNAL_SERVER_ERROR, "send message failed, sendStatus=" + result.getSendStatus()))
|
||||
.build();
|
||||
}
|
||||
|
||||
List<SendReceipt> sendReceiptList = Lists.newArrayList();
|
||||
sendReceiptList.add(SendReceipt.newBuilder()
|
||||
.setMessageId(StringUtils.defaultString(result.getMsgId()))
|
||||
.setTransactionId(StringUtils.defaultString(result.getTransactionId()))
|
||||
.build());
|
||||
return SendMessageResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.addAllReceipts(sendReceiptList)
|
||||
.build();
|
||||
}
|
||||
|
||||
public CompletableFuture<ForwardMessageToDeadLetterQueueResponse> forwardMessageToDeadLetterQueue(Context ctx,
|
||||
ForwardMessageToDeadLetterQueueRequest request) {
|
||||
CompletableFuture<ForwardMessageToDeadLetterQueueResponse> future = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
ReceiptHandle receiptHandle = resolveReceiptHandle(ctx, request.getReceiptHandle());
|
||||
String brokerAddr = this.getBrokerAddr(ctx, receiptHandle.getBrokerName());
|
||||
ConsumerSendMsgBackRequestHeader sendMsgBackRequestHeader = this.buildConsumerSendMsgBackRequestHeader(ctx, request);
|
||||
AckMessageRequestHeader ackMessageRequestHeader = GrpcConverter.buildAckMessageRequestHeader(
|
||||
request.getTopic(), request.getGroup(), receiptHandle);
|
||||
|
||||
future = this.producer.sendMessageBackThenAckOrg(ctx, brokerAddr, sendMsgBackRequestHeader, ackMessageRequestHeader)
|
||||
.thenApply(result -> convertToForwardMessageToDeadLetterQueueResponse(ctx, result));
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
future.whenComplete((response, throwable) -> {
|
||||
if (forwardMessageToDLQHook != null) {
|
||||
forwardMessageToDLQHook.beforeResponse(ctx, request, response, throwable);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
protected ConsumerSendMsgBackRequestHeader buildConsumerSendMsgBackRequestHeader(Context ctx,
|
||||
ForwardMessageToDeadLetterQueueRequest request) {
|
||||
return GrpcConverter.buildConsumerSendMsgBackRequestHeader(request);
|
||||
}
|
||||
|
||||
protected ForwardMessageToDeadLetterQueueResponse convertToForwardMessageToDeadLetterQueueResponse(Context ctx,
|
||||
RemotingCommand result) {
|
||||
return ForwardMessageToDeadLetterQueueResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(result.getCode(), result.getRemark()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public WriteQueueSelector getWriteQueueSelector() {
|
||||
return writeQueueSelector;
|
||||
}
|
||||
|
||||
public void setWriteQueueSelector(WriteQueueSelector writeQueueSelector) {
|
||||
this.writeQueueSelector = writeQueueSelector;
|
||||
}
|
||||
|
||||
public ResponseHook<SendMessageRequest, SendMessageResponse> getSendMessageHook() {
|
||||
return sendMessageHook;
|
||||
}
|
||||
|
||||
public void setSendMessageHook(
|
||||
ResponseHook<SendMessageRequest, SendMessageResponse> sendMessageHook) {
|
||||
this.sendMessageHook = sendMessageHook;
|
||||
}
|
||||
|
||||
public ResponseHook<ForwardMessageToDeadLetterQueueRequest, ForwardMessageToDeadLetterQueueResponse> getForwardMessageToDLQHook() {
|
||||
return forwardMessageToDLQHook;
|
||||
}
|
||||
|
||||
public void setForwardMessageToDLQHook(
|
||||
ResponseHook<ForwardMessageToDeadLetterQueueRequest, ForwardMessageToDeadLetterQueueResponse> forwardMessageToDLQHook) {
|
||||
this.forwardMessageToDLQHook = forwardMessageToDLQHook;
|
||||
}
|
||||
}
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.cluster;
|
||||
|
||||
import apache.rocketmq.v2.Assignment;
|
||||
import apache.rocketmq.v2.Broker;
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.Endpoints;
|
||||
import apache.rocketmq.v2.MessageQueue;
|
||||
import apache.rocketmq.v2.Permission;
|
||||
import apache.rocketmq.v2.QueryAssignmentRequest;
|
||||
import apache.rocketmq.v2.QueryAssignmentResponse;
|
||||
import apache.rocketmq.v2.QueryRouteRequest;
|
||||
import apache.rocketmq.v2.QueryRouteResponse;
|
||||
import io.grpc.Context;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.common.protocol.route.QueueData;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.proxy.connector.ConnectorManager;
|
||||
import org.apache.rocketmq.proxy.connector.route.MessageQueueWrapper;
|
||||
import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.connector.route.TopicRouteHelper;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.AbstractRouteService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.GrpcClientManager;
|
||||
|
||||
public class RouteService extends AbstractRouteService {
|
||||
public RouteService(ConnectorManager connectorManager, GrpcClientManager grpcClientManager) {
|
||||
super(connectorManager, grpcClientManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<QueryRouteResponse> queryRoute(Context ctx, QueryRouteRequest request) {
|
||||
CompletableFuture<QueryRouteResponse> future = new CompletableFuture<>();
|
||||
future.whenComplete((response, throwable) -> {
|
||||
if (queryRouteHook != null) {
|
||||
queryRouteHook.beforeResponse(ctx, request, response, throwable);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic());
|
||||
MessageQueueWrapper messageQueueWrapper = this.connectorManager.getTopicRouteCache().getMessageQueue(topicName);
|
||||
TopicRouteData topicRouteData = messageQueueWrapper.getTopicRouteData();
|
||||
List<QueueData> queueDataList = topicRouteData.getQueueDatas();
|
||||
|
||||
List<MessageQueue> messageQueueList = new ArrayList<>();
|
||||
Endpoints endpoints = request.getEndpoints();
|
||||
Endpoints resEndpoints = this.queryRouteEndpointConverter.convert(ctx, endpoints);
|
||||
if (resEndpoints == null || resEndpoints.getDefaultInstanceForType().equals(resEndpoints)) {
|
||||
future.complete(QueryRouteResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.ILLEGAL_ACCESS_POINT, "endpoint " +
|
||||
endpoints + " is invalidate"))
|
||||
.build());
|
||||
return future;
|
||||
}
|
||||
for (QueueData queueData : queueDataList) {
|
||||
Broker broker = Broker.newBuilder()
|
||||
.setName(queueData.getBrokerName())
|
||||
.setId(0)
|
||||
.setEndpoints(resEndpoints)
|
||||
.build();
|
||||
|
||||
messageQueueList.addAll(GrpcConverter.genMessageQueueFromQueueData(queueData, request.getTopic(), broker));
|
||||
}
|
||||
QueryRouteResponse response = QueryRouteResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.addAllMessageQueues(messageQueueList)
|
||||
.build();
|
||||
future.complete(response);
|
||||
} catch (Throwable t) {
|
||||
if (TopicRouteHelper.isTopicNotExistError(t)) {
|
||||
future.complete(QueryRouteResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.TOPIC_NOT_FOUND, t.getMessage()))
|
||||
.build());
|
||||
} else {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<QueryAssignmentResponse> queryAssignment(Context ctx, QueryAssignmentRequest request) {
|
||||
CompletableFuture<QueryAssignmentResponse> future = new CompletableFuture<>();
|
||||
future.whenComplete((response, throwable) -> {
|
||||
if (queryAssignmentHook != null) {
|
||||
queryAssignmentHook.beforeResponse(ctx, request, response, throwable);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
List<Assignment> assignments = new ArrayList<>();
|
||||
List<SelectableMessageQueue> messageQueueList = this.assignmentQueueSelector.getAssignment(ctx, request);
|
||||
Endpoints endpoints = request.getEndpoints();
|
||||
Endpoints resEndpoints = this.queryAssignmentEndpointConverter.convert(ctx, endpoints);
|
||||
if (resEndpoints == null || Endpoints.getDefaultInstance().equals(resEndpoints)) {
|
||||
future.complete(QueryAssignmentResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.ILLEGAL_ACCESS_POINT, "endpoint " +
|
||||
endpoints + " is invalidate"))
|
||||
.build());
|
||||
return future;
|
||||
}
|
||||
for (SelectableMessageQueue messageQueue : messageQueueList) {
|
||||
Broker broker = Broker.newBuilder()
|
||||
.setName(messageQueue.getBrokerName())
|
||||
.setId(0)
|
||||
.setEndpoints(resEndpoints)
|
||||
.build();
|
||||
|
||||
MessageQueue defaultMessageQueue = MessageQueue.newBuilder()
|
||||
.setTopic(request.getTopic())
|
||||
.setId(-1)
|
||||
.setPermission(Permission.READ_WRITE)
|
||||
.setBroker(broker)
|
||||
.build();
|
||||
|
||||
assignments.add(Assignment.newBuilder()
|
||||
.setMessageQueue(defaultMessageQueue)
|
||||
.build());
|
||||
}
|
||||
|
||||
QueryAssignmentResponse response = QueryAssignmentResponse.newBuilder()
|
||||
.addAllAssignments(assignments)
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build();
|
||||
future.complete(response);
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
}
|
||||
-139
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.cluster;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.EndTransactionRequest;
|
||||
import apache.rocketmq.v2.EndTransactionResponse;
|
||||
import apache.rocketmq.v2.Message;
|
||||
import apache.rocketmq.v2.RecoverOrphanedTransactionCommand;
|
||||
import apache.rocketmq.v2.TelemetryCommand;
|
||||
import io.grpc.Context;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.header.EndTransactionRequestHeader;
|
||||
import org.apache.rocketmq.proxy.channel.ChannelManager;
|
||||
import org.apache.rocketmq.proxy.connector.ConnectorManager;
|
||||
import org.apache.rocketmq.proxy.connector.ForwardProducer;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionId;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionStateCheckRequest;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionStateChecker;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseHook;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.channel.GrpcClientChannel;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.BaseService;
|
||||
import org.apache.rocketmq.remoting.common.RemotingHelper;
|
||||
|
||||
public class TransactionService extends BaseService implements TransactionStateChecker {
|
||||
|
||||
protected final ChannelManager channelManager;
|
||||
protected final ForwardProducer forwardProducer;
|
||||
|
||||
private volatile ResponseHook<TransactionStateCheckRequest, TelemetryCommand> checkTransactionStateHook;
|
||||
private volatile ResponseHook<EndTransactionRequest, EndTransactionResponse> endTransactionHook;
|
||||
|
||||
public TransactionService(ConnectorManager connectorManager, ChannelManager channelManager) {
|
||||
super(connectorManager);
|
||||
this.forwardProducer = connectorManager.getForwardProducer();
|
||||
this.channelManager = channelManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkTransactionState(TransactionStateCheckRequest checkData) {
|
||||
Context ctx = Context.current();
|
||||
try {
|
||||
List<String> clientIdList = this.channelManager.getClientIdList(checkData.getGroupId());
|
||||
if (CollectionUtils.isEmpty(clientIdList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String clientId = clientIdList.get(ThreadLocalRandom.current().nextInt(clientIdList.size()));
|
||||
GrpcClientChannel channel = GrpcClientChannel.getChannel(this.channelManager, checkData.getGroupId(), clientId);
|
||||
|
||||
String transactionId = checkData.getTransactionId().getProxyTransactionId();
|
||||
MessageExt messageExt = checkData.getMessageExt();
|
||||
Message message = GrpcConverter.buildMessage(messageExt);
|
||||
TelemetryCommand response = TelemetryCommand.newBuilder()
|
||||
.setRecoverOrphanedTransactionCommand(
|
||||
RecoverOrphanedTransactionCommand.newBuilder()
|
||||
.setOrphanedTransactionalMessage(message)
|
||||
.setTransactionId(transactionId)
|
||||
.setMessageQueue(GrpcConverter.buildMessageQueue(messageExt, checkData.getBrokerName()))
|
||||
.build()
|
||||
).build();
|
||||
|
||||
channel.writeAndFlush(response);
|
||||
if (this.checkTransactionStateHook != null) {
|
||||
this.checkTransactionStateHook.beforeResponse(ctx, checkData, response, null);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
if (this.checkTransactionStateHook != null) {
|
||||
this.checkTransactionStateHook.beforeResponse(ctx, checkData, null, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<EndTransactionResponse> endTransaction(Context ctx, EndTransactionRequest request) {
|
||||
CompletableFuture<EndTransactionResponse> future = new CompletableFuture<>();
|
||||
future.whenComplete((response, throwable) -> {
|
||||
if (endTransactionHook != null) {
|
||||
endTransactionHook.beforeResponse(ctx, request, response, throwable);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
TransactionId handle = TransactionId.decode(request.getTransactionId());
|
||||
String brokerAddr = RemotingHelper.parseSocketAddressAddr(handle.getBrokerAddr());
|
||||
EndTransactionRequestHeader requestHeader = this.toEndTransactionRequestHeader(ctx, request);
|
||||
this.forwardProducer.endTransaction(ctx, brokerAddr, requestHeader);
|
||||
future.complete(EndTransactionResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build());
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected EndTransactionRequestHeader toEndTransactionRequestHeader(Context ctx, EndTransactionRequest request) {
|
||||
String topic = GrpcConverter.wrapResourceWithNamespace(request.getTopic());
|
||||
// use topic name as producerGroup
|
||||
return GrpcConverter.buildEndTransactionRequestHeader(request, topic);
|
||||
}
|
||||
|
||||
public ResponseHook<TransactionStateCheckRequest, TelemetryCommand> getCheckTransactionStateHook() {
|
||||
return checkTransactionStateHook;
|
||||
}
|
||||
|
||||
public void setCheckTransactionStateHook(
|
||||
ResponseHook<TransactionStateCheckRequest, TelemetryCommand> checkTransactionStateHook) {
|
||||
this.checkTransactionStateHook = checkTransactionStateHook;
|
||||
}
|
||||
|
||||
public ResponseHook<EndTransactionRequest, EndTransactionResponse> getEndTransactionHook() {
|
||||
return endTransactionHook;
|
||||
}
|
||||
|
||||
public void setEndTransactionHook(
|
||||
ResponseHook<EndTransactionRequest, EndTransactionResponse> endTransactionHook) {
|
||||
this.endTransactionHook = endTransactionHook;
|
||||
}
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.local;
|
||||
|
||||
import apache.rocketmq.v2.Message;
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import apache.rocketmq.v2.ReceiveMessageResponse;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import io.netty.channel.Channel;
|
||||
import org.apache.rocketmq.broker.BrokerController;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.protocol.RequestCode;
|
||||
import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeRequestHeader;
|
||||
import org.apache.rocketmq.proxy.channel.ChannelManager;
|
||||
import org.apache.rocketmq.proxy.channel.SimpleChannelHandlerContext;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseHook;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.BaseReceiveMessageResponseStreamWriter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.ReceiveMessageResultFilter;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LocalReceiveMessageResponseStreamWriter extends BaseReceiveMessageResponseStreamWriter {
|
||||
private final static Logger log = LoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
private final ChannelManager channelManager;
|
||||
private final BrokerController brokerController;
|
||||
|
||||
public LocalReceiveMessageResponseStreamWriter(
|
||||
StreamObserver<ReceiveMessageResponse> observer,
|
||||
ResponseHook<ReceiveMessageRequest, ReceiveMessageResponse> hook,
|
||||
ChannelManager channelManager,
|
||||
BrokerController brokerController,
|
||||
ReceiveMessageResultFilter receiveMessageResultFilter) {
|
||||
super(observer, hook, receiveMessageResultFilter);
|
||||
this.channelManager = channelManager;
|
||||
this.brokerController = brokerController;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processThrowableWhenWriteMessage(Throwable throwable, Context context, ReceiveMessageRequest request,
|
||||
Message message) {
|
||||
this.changeInvisibleTime(context, request, ReceiptHandle.decode(message.getSystemProperties().getReceiptHandle()));
|
||||
}
|
||||
|
||||
private void changeInvisibleTime(Context ctx, ReceiveMessageRequest request, ReceiptHandle handle) {
|
||||
Channel channel = channelManager.createChannel(ctx);
|
||||
SimpleChannelHandlerContext simpleChannelHandlerContext = new SimpleChannelHandlerContext(channel);
|
||||
ChangeInvisibleTimeRequestHeader requestHeader = GrpcConverter.buildChangeInvisibleTimeRequestHeader(request, handle);
|
||||
RemotingCommand command = RemotingCommand.createRequestCommand(RequestCode.CHANGE_MESSAGE_INVISIBLETIME, requestHeader);
|
||||
command.makeCustomHeaderToNet();
|
||||
try {
|
||||
brokerController.getChangeInvisibleTimeProcessor().processRequest(simpleChannelHandlerContext, command);
|
||||
} catch (RemotingCommandException e) {
|
||||
log.error("ChangeInvisibleTime error when write response", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.local;
|
||||
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import io.grpc.Context;
|
||||
import io.netty.channel.Channel;
|
||||
import org.apache.rocketmq.broker.BrokerController;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.RequestCode;
|
||||
import org.apache.rocketmq.common.protocol.ResponseCode;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
|
||||
import org.apache.rocketmq.proxy.channel.ChannelManager;
|
||||
import org.apache.rocketmq.proxy.channel.SimpleChannelHandlerContext;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.BaseReceiveMessageResultFilter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.GrpcClientManager;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LocalReceiveMessageResultFilter extends BaseReceiveMessageResultFilter {
|
||||
private final static Logger log = LoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
private final ChannelManager channelManager;
|
||||
private final BrokerController brokerController;
|
||||
|
||||
public LocalReceiveMessageResultFilter(ChannelManager channelManager, BrokerController brokerController, GrpcClientManager grpcClientManager) {
|
||||
super(grpcClientManager);
|
||||
this.channelManager = channelManager;
|
||||
this.brokerController = brokerController;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processNoMatchMessage(Context ctx, ReceiveMessageRequest request, MessageExt messageExt) {
|
||||
ReceiptHandle handle = ReceiptHandle.create(messageExt);
|
||||
if (handle == null) {
|
||||
return;
|
||||
}
|
||||
Channel channel = channelManager.createChannel(ctx);
|
||||
AckMessageRequestHeader requestHeader = GrpcConverter.buildAckMessageRequestHeader(request, handle);
|
||||
RemotingCommand command = RemotingCommand.createRequestCommand(RequestCode.ACK_MESSAGE, requestHeader);
|
||||
command.makeCustomHeaderToNet();
|
||||
try {
|
||||
brokerController.getAckMessageProcessor().processRequest(new SimpleChannelHandlerContext(channel), command);
|
||||
} catch (RemotingCommandException e) {
|
||||
log.error("AckMessage failed in filterMessage", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processExceedMaxAttemptsMessage(Context ctx, ReceiveMessageRequest request, MessageExt messageExt,
|
||||
int maxAttempts) {
|
||||
try {
|
||||
ReceiptHandle handle = ReceiptHandle.create(messageExt);
|
||||
if (handle == null) {
|
||||
return;
|
||||
}
|
||||
Channel channel = channelManager.createChannel(ctx);
|
||||
SimpleChannelHandlerContext simpleChannelHandlerContext = new SimpleChannelHandlerContext(channel);
|
||||
ConsumerSendMsgBackRequestHeader consumerSendMsgBackRequestHeader = GrpcConverter.buildConsumerSendMsgBackRequestHeader(request, handle, messageExt.getMsgId(), maxAttempts);
|
||||
RemotingCommand command = RemotingCommand.createRequestCommand(RequestCode.CONSUMER_SEND_MSG_BACK, consumerSendMsgBackRequestHeader);
|
||||
command.makeCustomHeaderToNet();
|
||||
RemotingCommand response = brokerController.getSendMessageProcessor().processRequest(simpleChannelHandlerContext, command);
|
||||
if (response.getCode() == ResponseCode.SUCCESS) {
|
||||
AckMessageRequestHeader ackMessageRequestHeader = GrpcConverter.buildAckMessageRequestHeader(request, handle);
|
||||
command = RemotingCommand.createRequestCommand(RequestCode.ACK_MESSAGE, ackMessageRequestHeader);
|
||||
command.makeCustomHeaderToNet();
|
||||
brokerController.getAckMessageProcessor().processRequest(simpleChannelHandlerContext, command);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("ForwardMessageToDLQ failed in filterMessage", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.local;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.Message;
|
||||
import apache.rocketmq.v2.SendMessageRequest;
|
||||
import com.google.common.hash.Hashing;
|
||||
import io.grpc.Context;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.broker.topic.TopicConfigManager;
|
||||
import org.apache.rocketmq.common.TopicConfig;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.message.MessageQueue;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.connector.route.TopicRouteCache;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ProxyException;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.cluster.DefaultWriteQueueSelector;
|
||||
|
||||
public class LocalWriteQueueSelector extends DefaultWriteQueueSelector {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
private final String brokerName;
|
||||
private final AtomicInteger queueIndex;
|
||||
private final TopicConfigManager topicConfigManager;
|
||||
|
||||
public LocalWriteQueueSelector(String brokerName, TopicConfigManager topicConfigManager, TopicRouteCache topicRouteCache) {
|
||||
super(topicRouteCache);
|
||||
this.brokerName = brokerName;
|
||||
this.queueIndex = new AtomicInteger(Math.abs(new Random().nextInt()));
|
||||
this.topicConfigManager = topicConfigManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectableMessageQueue selectQueue(
|
||||
Context ctx,
|
||||
SendMessageRequest request
|
||||
) {
|
||||
try {
|
||||
if (request.getMessagesCount() <= 0) {
|
||||
throw new ProxyException(Code.MESSAGE_CORRUPTED, "no message to send");
|
||||
}
|
||||
Message message = request.getMessages(0);
|
||||
String topic = GrpcConverter.wrapResourceWithNamespace(message.getTopic());
|
||||
String shardingKey = null;
|
||||
if (request.getMessagesCount() == 1) {
|
||||
shardingKey = message.getSystemProperties().getMessageGroup();
|
||||
}
|
||||
SelectableMessageQueue targetMessageQueue;
|
||||
if (StringUtils.isNotEmpty(shardingKey)) {
|
||||
// With shardingKey
|
||||
targetMessageQueue = selectOrderQueue(topic, shardingKey);
|
||||
} else {
|
||||
targetMessageQueue = selectNormalQueue(topic);
|
||||
}
|
||||
return targetMessageQueue;
|
||||
} catch (Exception e) {
|
||||
log.error("error when select queue in DefaultMessageQueueSelector. request: {}", request, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected SelectableMessageQueue selectNormalQueue(String topic) throws Exception {
|
||||
TopicConfig topicConfig = topicConfigManager.selectTopicConfig(topic);
|
||||
if (topicConfig == null) {
|
||||
throw new ProxyException(Code.TOPIC_NOT_FOUND, topic);
|
||||
}
|
||||
int selectQueueId = queueIndex.getAndIncrement() % topicConfig.getWriteQueueNums();
|
||||
MessageQueue mq = new MessageQueue(topic, brokerName, selectQueueId);
|
||||
return new SelectableMessageQueue(mq, null);
|
||||
}
|
||||
|
||||
protected SelectableMessageQueue selectOrderQueue(String topic, String shardingKey) throws Exception {
|
||||
TopicConfig topicConfig = topicConfigManager.selectTopicConfig(topic);
|
||||
if (topicConfig == null) {
|
||||
throw new ProxyException(Code.TOPIC_NOT_FOUND, topic);
|
||||
}
|
||||
int selectQueueId = Hashing.consistentHash(shardingKey.hashCode(), topicConfig.getWriteQueueNums());
|
||||
MessageQueue mq = new MessageQueue(topic, brokerName, selectQueueId);
|
||||
return new SelectableMessageQueue(mq, null);
|
||||
}
|
||||
}
|
||||
-175
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* 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.grpc.v2.service.local;
|
||||
|
||||
import apache.rocketmq.v2.Address;
|
||||
import apache.rocketmq.v2.AddressScheme;
|
||||
import apache.rocketmq.v2.Assignment;
|
||||
import apache.rocketmq.v2.Broker;
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.Endpoints;
|
||||
import apache.rocketmq.v2.MessageQueue;
|
||||
import apache.rocketmq.v2.Permission;
|
||||
import apache.rocketmq.v2.QueryAssignmentRequest;
|
||||
import apache.rocketmq.v2.QueryAssignmentResponse;
|
||||
import apache.rocketmq.v2.QueryRouteRequest;
|
||||
import apache.rocketmq.v2.QueryRouteResponse;
|
||||
import com.google.common.net.HostAndPort;
|
||||
import io.grpc.Context;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.common.protocol.route.QueueData;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.proxy.config.ConfigurationManager;
|
||||
import org.apache.rocketmq.proxy.connector.ConnectorManager;
|
||||
import org.apache.rocketmq.proxy.connector.route.MessageQueueWrapper;
|
||||
import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.connector.route.TopicRouteHelper;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.AbstractRouteService;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.service.GrpcClientManager;
|
||||
|
||||
public class RouteService extends AbstractRouteService {
|
||||
public RouteService(ConnectorManager connectorManager, GrpcClientManager grpcClientManager) {
|
||||
super(connectorManager, grpcClientManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<QueryRouteResponse> queryRoute(Context ctx, QueryRouteRequest request) {
|
||||
CompletableFuture<QueryRouteResponse> future = new CompletableFuture<>();
|
||||
future.whenComplete((response, throwable) -> {
|
||||
if (queryRouteHook != null) {
|
||||
queryRouteHook.beforeResponse(ctx, request, response, throwable);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic());
|
||||
MessageQueueWrapper messageQueueWrapper = this.connectorManager.getTopicRouteCache().getMessageQueue(topicName);
|
||||
TopicRouteData topicRouteData = messageQueueWrapper.getTopicRouteData();
|
||||
List<QueueData> queueDataList = topicRouteData.getQueueDatas();
|
||||
List<BrokerData> brokerDataList = topicRouteData.getBrokerDatas();
|
||||
|
||||
List<MessageQueue> messageQueueList = new ArrayList<>();
|
||||
Map<String, Map<Long, Broker>> brokerMap = buildBrokerMap(brokerDataList);
|
||||
|
||||
for (QueueData queueData : queueDataList) {
|
||||
String brokerName = queueData.getBrokerName();
|
||||
Map<Long, Broker> brokerIdMap = brokerMap.get(brokerName);
|
||||
if (brokerIdMap == null) {
|
||||
break;
|
||||
}
|
||||
for (Broker broker : brokerIdMap.values()) {
|
||||
messageQueueList.addAll(GrpcConverter.genMessageQueueFromQueueData(queueData, request.getTopic(), broker));
|
||||
}
|
||||
}
|
||||
|
||||
QueryRouteResponse response = QueryRouteResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.addAllMessageQueues(messageQueueList)
|
||||
.build();
|
||||
future.complete(response);
|
||||
} catch (Throwable t) {
|
||||
if (TopicRouteHelper.isTopicNotExistError(t)) {
|
||||
future.complete(QueryRouteResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.TOPIC_NOT_FOUND, t.getMessage()))
|
||||
.build());
|
||||
} else {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<QueryAssignmentResponse> queryAssignment(Context ctx, QueryAssignmentRequest request) {
|
||||
CompletableFuture<QueryAssignmentResponse> future = new CompletableFuture<>();
|
||||
future.whenComplete((response, throwable) -> {
|
||||
if (queryAssignmentHook != null) {
|
||||
queryAssignmentHook.beforeResponse(ctx, request, response, throwable);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
List<Assignment> assignments = new ArrayList<>();
|
||||
List<SelectableMessageQueue> messageQueueList = this.assignmentQueueSelector.getAssignment(ctx, request);
|
||||
String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic());
|
||||
MessageQueueWrapper messageQueueWrapper = this.connectorManager.getTopicRouteCache().getMessageQueue(topicName);
|
||||
TopicRouteData topicRouteData = messageQueueWrapper.getTopicRouteData();
|
||||
Map<String, Map<Long, Broker>> brokerMap = buildBrokerMap(topicRouteData.getBrokerDatas());
|
||||
for (SelectableMessageQueue messageQueue : messageQueueList) {
|
||||
Map<Long, Broker> brokerIdMap = brokerMap.get(messageQueue.getBrokerName());
|
||||
if (brokerIdMap != null) {
|
||||
Broker broker = brokerIdMap.get(0L);
|
||||
|
||||
MessageQueue defaultMessageQueue = MessageQueue.newBuilder()
|
||||
.setTopic(request.getTopic())
|
||||
.setId(-1)
|
||||
.setPermission(Permission.READ_WRITE)
|
||||
.setBroker(broker)
|
||||
.build();
|
||||
|
||||
assignments.add(Assignment.newBuilder()
|
||||
.setMessageQueue(defaultMessageQueue)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
QueryAssignmentResponse response = QueryAssignmentResponse.newBuilder()
|
||||
.addAllAssignments(assignments)
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build();
|
||||
future.complete(response);
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
private Map<String/*brokerName*/, Map<Long/*brokerID*/, Broker>> buildBrokerMap(List<BrokerData> brokerDataList) {
|
||||
Map<String, Map<Long, Broker>> brokerMap = new HashMap<>();
|
||||
for (BrokerData brokerData : brokerDataList) {
|
||||
Map<Long, Broker> brokerIdMap = new HashMap<>();
|
||||
String brokerName = brokerData.getBrokerName();
|
||||
for (Map.Entry<Long, String> entry : brokerData.getBrokerAddrs().entrySet()) {
|
||||
Long brokerId = entry.getKey();
|
||||
HostAndPort hostAndPort = HostAndPort.fromString(entry.getValue());
|
||||
Broker broker = Broker.newBuilder()
|
||||
.setName(brokerName)
|
||||
.setId(Math.toIntExact(brokerId))
|
||||
.setEndpoints(Endpoints.newBuilder()
|
||||
.setScheme(AddressScheme.IPv4)
|
||||
.addAddresses(
|
||||
Address.newBuilder()
|
||||
.setPort(ConfigurationManager.getProxyConfig().getGrpcServerPort())
|
||||
.setHost(hostAndPort.getHost())
|
||||
)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
brokerIdMap.put(brokerId, broker);
|
||||
}
|
||||
brokerMap.put(brokerName, brokerIdMap);
|
||||
}
|
||||
return brokerMap;
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.grpc.v2.transaction;
|
||||
|
||||
import apache.rocketmq.v2.Code;
|
||||
import apache.rocketmq.v2.EndTransactionRequest;
|
||||
import apache.rocketmq.v2.EndTransactionResponse;
|
||||
import apache.rocketmq.v2.TransactionResolution;
|
||||
import apache.rocketmq.v2.TransactionSource;
|
||||
import io.grpc.Context;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.AbstractMessingActivity;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcClientSettingsManager;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.GrpcConverter;
|
||||
import org.apache.rocketmq.proxy.grpc.v2.common.ResponseBuilder;
|
||||
import org.apache.rocketmq.proxy.processor.MessagingProcessor;
|
||||
import org.apache.rocketmq.proxy.processor.TransactionStatus;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionId;
|
||||
|
||||
public class EndTransactionActivity extends AbstractMessingActivity {
|
||||
|
||||
public EndTransactionActivity(MessagingProcessor messagingProcessor,
|
||||
GrpcClientSettingsManager grpcClientSettingsManager) {
|
||||
super(messagingProcessor, grpcClientSettingsManager);
|
||||
}
|
||||
|
||||
public CompletableFuture<EndTransactionResponse> endTransaction(Context ctx, EndTransactionRequest request) {
|
||||
CompletableFuture<EndTransactionResponse> future = new CompletableFuture<>();
|
||||
try {
|
||||
ProxyContext context = createContext(ctx);
|
||||
TransactionId transactionId = TransactionId.decode(request.getTransactionId());
|
||||
TransactionStatus transactionStatus = TransactionStatus.UNKNOWN;
|
||||
TransactionResolution transactionResolution = request.getResolution();
|
||||
switch (transactionResolution) {
|
||||
case COMMIT:
|
||||
transactionStatus = TransactionStatus.COMMIT;
|
||||
break;
|
||||
case ROLLBACK:
|
||||
transactionStatus = TransactionStatus.ROLLBACK;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.messagingProcessor.endTransaction(
|
||||
context,
|
||||
transactionId,
|
||||
request.getMessageId(),
|
||||
GrpcConverter.wrapResourceWithNamespace(request.getTopic()),
|
||||
transactionStatus,
|
||||
request.getSource().equals(TransactionSource.SOURCE_SERVER_CHECK));
|
||||
future.complete(EndTransactionResponse.newBuilder()
|
||||
.setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name()))
|
||||
.build());
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.proxy.common.ProxyException;
|
||||
import org.apache.rocketmq.proxy.common.ProxyExceptionCode;
|
||||
import org.apache.rocketmq.proxy.service.ServiceManager;
|
||||
|
||||
public abstract class AbstractProcessor {
|
||||
|
||||
protected MessagingProcessor messagingProcessor;
|
||||
protected ServiceManager serviceManager;
|
||||
|
||||
public AbstractProcessor(MessagingProcessor messagingProcessor,
|
||||
ServiceManager serviceManager) {
|
||||
this.messagingProcessor = messagingProcessor;
|
||||
this.serviceManager = serviceManager;
|
||||
}
|
||||
|
||||
protected void checkReceiptHandle(ReceiptHandle handle) {
|
||||
if (handle.isExpired()) {
|
||||
throw new ProxyException(ProxyExceptionCode.RECEIPT_HANDLE_EXPIRED, "receipt handle is expired");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.processor;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.Set;
|
||||
import org.apache.rocketmq.broker.client.ClientChannelInfo;
|
||||
import org.apache.rocketmq.broker.client.ConsumerIdsChangeListener;
|
||||
import org.apache.rocketmq.broker.client.ProducerChangeListener;
|
||||
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.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.service.ServiceManager;
|
||||
|
||||
public class ClientProcessor extends AbstractProcessor {
|
||||
|
||||
public ClientProcessor(MessagingProcessor messagingProcessor,
|
||||
ServiceManager serviceManager) {
|
||||
super(messagingProcessor, serviceManager);
|
||||
}
|
||||
|
||||
public void registerProducer(
|
||||
ProxyContext ctx,
|
||||
String producerGroup,
|
||||
ClientChannelInfo clientChannelInfo
|
||||
) {
|
||||
this.serviceManager.getProducerManager().registerProducer(producerGroup, clientChannelInfo);
|
||||
}
|
||||
|
||||
public void unRegisterProducer(
|
||||
ProxyContext ctx,
|
||||
String producerGroup,
|
||||
ClientChannelInfo clientChannelInfo
|
||||
) {
|
||||
this.serviceManager.getProducerManager().unregisterProducer(producerGroup, clientChannelInfo);
|
||||
}
|
||||
|
||||
public Channel findProducerChannel(
|
||||
ProxyContext ctx,
|
||||
String producerGroup,
|
||||
String clientId
|
||||
) {
|
||||
return this.serviceManager.getProducerManager().findChannel(clientId);
|
||||
}
|
||||
|
||||
public void registerProducerChangeListener(ProducerChangeListener listener) {
|
||||
this.serviceManager.getProducerManager().appendProducerChangeListener(listener);
|
||||
}
|
||||
|
||||
public void registerConsumer(
|
||||
ProxyContext ctx,
|
||||
String consumerGroup,
|
||||
ClientChannelInfo clientChannelInfo,
|
||||
ConsumeType consumeType,
|
||||
MessageModel messageModel,
|
||||
ConsumeFromWhere consumeFromWhere,
|
||||
Set<SubscriptionData> subList
|
||||
) {
|
||||
this.serviceManager.getConsumerManager().registerConsumer(
|
||||
consumerGroup,
|
||||
clientChannelInfo,
|
||||
consumeType,
|
||||
messageModel,
|
||||
consumeFromWhere,
|
||||
subList,
|
||||
false);
|
||||
}
|
||||
|
||||
public ClientChannelInfo findConsumerChannel(
|
||||
ProxyContext ctx,
|
||||
String consumerGroup,
|
||||
String clientId
|
||||
) {
|
||||
return this.serviceManager.getConsumerManager().findChannel(consumerGroup, clientId);
|
||||
}
|
||||
|
||||
public void unRegisterConsumer(
|
||||
ProxyContext ctx,
|
||||
String consumerGroup,
|
||||
ClientChannelInfo clientChannelInfo
|
||||
) {
|
||||
this.serviceManager.getConsumerManager().unregisterConsumer(consumerGroup, clientChannelInfo, false);
|
||||
}
|
||||
|
||||
public void registerConsumerIdsChangeListener(ConsumerIdsChangeListener listener) {
|
||||
this.serviceManager.getConsumerManager().appendConsumerIdsChangeListener(listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.client.consumer.PopResult;
|
||||
import org.apache.rocketmq.client.consumer.PopStatus;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.common.ProxyException;
|
||||
import org.apache.rocketmq.proxy.common.ProxyExceptionCode;
|
||||
import org.apache.rocketmq.proxy.common.utils.ProxyUtils;
|
||||
import org.apache.rocketmq.proxy.service.ServiceManager;
|
||||
import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue;
|
||||
|
||||
public class ConsumerProcessor extends AbstractProcessor {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
public ConsumerProcessor(MessagingProcessor messagingProcessor,
|
||||
ServiceManager serviceManager) {
|
||||
super(messagingProcessor, serviceManager);
|
||||
}
|
||||
|
||||
public CompletableFuture<PopResult> popMessage(
|
||||
ProxyContext ctx,
|
||||
QueueSelector queueSelector,
|
||||
String consumerGroup,
|
||||
String topic,
|
||||
int maxMsgNums,
|
||||
long invisibleTime,
|
||||
long pollTime,
|
||||
int initMode,
|
||||
SubscriptionData subscriptionData,
|
||||
boolean fifo,
|
||||
PopMessageResultFilter popMessageResultFilter,
|
||||
long timeoutMillis
|
||||
) {
|
||||
CompletableFuture<PopResult> future = new CompletableFuture<>();
|
||||
try {
|
||||
SelectableMessageQueue messageQueue = queueSelector.select(ctx, this.serviceManager.getTopicRouteService().getCurrentMessageQueueView(topic));
|
||||
if (messageQueue == null) {
|
||||
throw new ProxyException(ProxyExceptionCode.FORBIDDEN, "no readable queue");
|
||||
}
|
||||
|
||||
if (maxMsgNums > ProxyUtils.MAX_MSG_NUMS_FOR_POP_REQUEST) {
|
||||
log.warn("change maxNums from {} to {} for pop request, with info: topic:{}, group:{}",
|
||||
maxMsgNums, ProxyUtils.MAX_MSG_NUMS_FOR_POP_REQUEST, topic, consumerGroup);
|
||||
maxMsgNums = ProxyUtils.MAX_MSG_NUMS_FOR_POP_REQUEST;
|
||||
}
|
||||
|
||||
PopMessageRequestHeader requestHeader = new PopMessageRequestHeader();
|
||||
requestHeader.setConsumerGroup(consumerGroup);
|
||||
requestHeader.setTopic(topic);
|
||||
requestHeader.setQueueId(messageQueue.getQueueId());
|
||||
requestHeader.setMaxMsgNums(maxMsgNums);
|
||||
requestHeader.setInvisibleTime(invisibleTime);
|
||||
requestHeader.setPollTime(pollTime);
|
||||
requestHeader.setInitMode(initMode);
|
||||
requestHeader.setExpType(subscriptionData.getExpressionType());
|
||||
requestHeader.setExp(subscriptionData.getSubString());
|
||||
requestHeader.setOrder(fifo);
|
||||
|
||||
return this.serviceManager.getMessageService().popMessage(
|
||||
ctx,
|
||||
messageQueue,
|
||||
requestHeader,
|
||||
timeoutMillis)
|
||||
.thenApply(popResult -> {
|
||||
if (PopStatus.FOUND.equals(popResult.getPopStatus()) &&
|
||||
popResult.getMsgFoundList() != null &&
|
||||
!popResult.getMsgFoundList().isEmpty() &&
|
||||
popMessageResultFilter != null) {
|
||||
|
||||
List<MessageExt> messageExtList = new ArrayList<>();
|
||||
for (MessageExt messageExt : popResult.getMsgFoundList()) {
|
||||
try {
|
||||
PopMessageResultFilter.FilterResult filterResult =
|
||||
popMessageResultFilter.filterMessage(ctx, consumerGroup, subscriptionData, messageExt);
|
||||
switch (filterResult) {
|
||||
case NO_MATCH:
|
||||
this.messagingProcessor.ackMessage(
|
||||
ctx,
|
||||
ReceiptHandle.create(messageExt),
|
||||
messageExt.getMsgId(),
|
||||
consumerGroup,
|
||||
topic,
|
||||
MessagingProcessor.DEFAULT_TIMEOUT_MILLS);
|
||||
break;
|
||||
case TO_DLQ:
|
||||
this.messagingProcessor.forwardMessageToDeadLetterQueue(
|
||||
ctx,
|
||||
ReceiptHandle.create(messageExt),
|
||||
messageExt.getMsgId(),
|
||||
consumerGroup,
|
||||
topic,
|
||||
MessagingProcessor.DEFAULT_TIMEOUT_MILLS);
|
||||
break;
|
||||
case MATCH:
|
||||
default:
|
||||
messageExtList.add(messageExt);
|
||||
break;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.error("process filterMessage failed. requestHeader:{}, msg:{}", requestHeader, messageExt, t);
|
||||
messageExtList.add(messageExt);
|
||||
}
|
||||
}
|
||||
popResult.setMsgFoundList(messageExtList);
|
||||
}
|
||||
return popResult;
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
public CompletableFuture<AckResult> ackMessage(
|
||||
ProxyContext ctx,
|
||||
ReceiptHandle handle,
|
||||
String messageId,
|
||||
String consumerGroup,
|
||||
String topic,
|
||||
long timeoutMillis
|
||||
) {
|
||||
CompletableFuture<AckResult> future = new CompletableFuture<>();
|
||||
try {
|
||||
this.checkReceiptHandle(handle);
|
||||
|
||||
AckMessageRequestHeader ackMessageRequestHeader = new AckMessageRequestHeader();
|
||||
ackMessageRequestHeader.setConsumerGroup(consumerGroup);
|
||||
ackMessageRequestHeader.setTopic(handle.getRealTopic(topic, consumerGroup));
|
||||
ackMessageRequestHeader.setQueueId(handle.getQueueId());
|
||||
ackMessageRequestHeader.setExtraInfo(handle.getReceiptHandle());
|
||||
ackMessageRequestHeader.setOffset(handle.getOffset());
|
||||
|
||||
return this.serviceManager.getMessageService().ackMessage(
|
||||
ctx,
|
||||
handle,
|
||||
messageId,
|
||||
ackMessageRequestHeader,
|
||||
timeoutMillis);
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
public CompletableFuture<AckResult> changeInvisibleTime(ProxyContext ctx, ReceiptHandle handle,
|
||||
String messageId, String groupName, String topicName, long invisibleTime, long timeoutMillis) {
|
||||
CompletableFuture<AckResult> future = new CompletableFuture<>();
|
||||
try {
|
||||
this.checkReceiptHandle(handle);
|
||||
|
||||
ChangeInvisibleTimeRequestHeader changeInvisibleTimeRequestHeader = new ChangeInvisibleTimeRequestHeader();
|
||||
changeInvisibleTimeRequestHeader.setConsumerGroup(groupName);
|
||||
changeInvisibleTimeRequestHeader.setTopic(handle.getRealTopic(topicName, groupName));
|
||||
changeInvisibleTimeRequestHeader.setQueueId(handle.getQueueId());
|
||||
changeInvisibleTimeRequestHeader.setExtraInfo(handle.getReceiptHandle());
|
||||
changeInvisibleTimeRequestHeader.setOffset(handle.getOffset());
|
||||
changeInvisibleTimeRequestHeader.setInvisibleTime(invisibleTime);
|
||||
|
||||
return this.serviceManager.getMessageService().changeInvisibleTime(
|
||||
ctx,
|
||||
handle,
|
||||
messageId,
|
||||
changeInvisibleTimeRequestHeader,
|
||||
timeoutMillis);
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.broker.client.ClientChannelInfo;
|
||||
import org.apache.rocketmq.broker.client.ConsumerIdsChangeListener;
|
||||
import org.apache.rocketmq.broker.client.ProducerChangeListener;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.client.consumer.PopResult;
|
||||
import org.apache.rocketmq.client.exception.MQBrokerException;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
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.proxy.common.Address;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.service.ServiceManager;
|
||||
import org.apache.rocketmq.proxy.service.out.ProxyOutService;
|
||||
import org.apache.rocketmq.proxy.service.route.ProxyTopicRouteData;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionId;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class DefaultMessagingProcessor implements MessagingProcessor {
|
||||
|
||||
private final ServiceManager serviceManager;
|
||||
private final ProducerProcessor producerProcessor;
|
||||
private final ConsumerProcessor consumerProcessor;
|
||||
private final TransactionProcessor transactionProcessor;
|
||||
private final ClientProcessor clientProcessor;
|
||||
|
||||
public DefaultMessagingProcessor(ServiceManager serviceManager) {
|
||||
this.serviceManager = serviceManager;
|
||||
this.producerProcessor = new ProducerProcessor(this, serviceManager);
|
||||
this.consumerProcessor = new ConsumerProcessor(this, serviceManager);
|
||||
this.transactionProcessor = new TransactionProcessor(this, serviceManager);
|
||||
this.clientProcessor = new ClientProcessor(this, serviceManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyTopicRouteData getTopicRouteDataForProxy(ProxyContext ctx, List<Address> requestHostAndPortList,
|
||||
String topicName) throws Exception {
|
||||
return this.serviceManager.getTopicRouteService().getTopicRouteForProxy(requestHostAndPortList, topicName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SendResult> sendMessage(ProxyContext ctx, QueueSelector queueSelector,
|
||||
String producerGroup, List<MessageExt> msg, long timeoutMillis) {
|
||||
return this.producerProcessor.sendMessage(ctx, queueSelector, producerGroup, msg, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<RemotingCommand> forwardMessageToDeadLetterQueue(ProxyContext ctx, ReceiptHandle handle,
|
||||
String messageId, String groupName, String topicName, long timeoutMillis) {
|
||||
return this.producerProcessor.forwardMessageToDeadLetterQueue(ctx, handle, messageId, groupName, topicName, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTransaction(ProxyContext ctx, TransactionId transactionId, String messageId,
|
||||
String producerGroup, TransactionStatus transactionStatus, boolean fromTransactionCheck, long timeoutMillis) throws MQBrokerException, RemotingException, InterruptedException {
|
||||
this.transactionProcessor.endTransaction(ctx, transactionId, messageId, producerGroup, transactionStatus, fromTransactionCheck, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<PopResult> popMessage(
|
||||
ProxyContext ctx,
|
||||
QueueSelector queueSelector,
|
||||
String consumerGroup,
|
||||
String topic,
|
||||
int maxMsgNums,
|
||||
long invisibleTime,
|
||||
long pollTime,
|
||||
int initMode,
|
||||
SubscriptionData subscriptionData,
|
||||
boolean fifo,
|
||||
PopMessageResultFilter popMessageResultFilter,
|
||||
long timeoutMillis
|
||||
) {
|
||||
return this.consumerProcessor.popMessage(ctx, queueSelector, consumerGroup, topic, maxMsgNums,
|
||||
invisibleTime, pollTime, initMode, subscriptionData, fifo, popMessageResultFilter, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AckResult> ackMessage(ProxyContext ctx, ReceiptHandle handle, String messageId,
|
||||
String consumerGroup, String topic, long timeoutMillis) {
|
||||
return this.consumerProcessor.ackMessage(ctx, handle, messageId, consumerGroup, topic, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AckResult> changeInvisibleTime(ProxyContext ctx, ReceiptHandle handle, String messageId,
|
||||
String groupName, String topicName, long invisibleTime, long timeoutMillis) {
|
||||
return this.consumerProcessor.changeInvisibleTime(ctx, handle, messageId, groupName, topicName, invisibleTime, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerProducer(ProxyContext ctx, String producerGroup, ClientChannelInfo clientChannelInfo) {
|
||||
this.clientProcessor.registerProducer(ctx, producerGroup, clientChannelInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unRegisterProducer(ProxyContext ctx, String producerGroup, ClientChannelInfo clientChannelInfo) {
|
||||
this.clientProcessor.unRegisterProducer(ctx, producerGroup, clientChannelInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel findProducerChannel(ProxyContext ctx, String producerGroup, String clientId) {
|
||||
return this.clientProcessor.findProducerChannel(ctx, producerGroup, clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerProducerListener(ProducerChangeListener producerChangeListener) {
|
||||
this.clientProcessor.registerProducerChangeListener(producerChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerConsumer(ProxyContext ctx, String consumerGroup, ClientChannelInfo clientChannelInfo,
|
||||
ConsumeType consumeType, MessageModel messageModel, ConsumeFromWhere consumeFromWhere,
|
||||
Set<SubscriptionData> subList) {
|
||||
this.clientProcessor.registerConsumer(ctx, consumerGroup, clientChannelInfo, consumeType, messageModel, consumeFromWhere, subList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientChannelInfo findConsumerChannel(ProxyContext ctx, String consumerGroup, String clientId) {
|
||||
return this.clientProcessor.findConsumerChannel(ctx, consumerGroup, clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unRegisterConsumer(ProxyContext ctx, String consumerGroup, ClientChannelInfo clientChannelInfo) {
|
||||
this.clientProcessor.unRegisterConsumer(ctx, consumerGroup, clientChannelInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerConsumerListener(ConsumerIdsChangeListener consumerIdsChangeListener) {
|
||||
this.clientProcessor.registerConsumerIdsChangeListener(consumerIdsChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTransactionSubscription(ProxyContext ctx, String producerGroup, String topic) {
|
||||
this.transactionProcessor.addTransactionSubscription(ctx, producerGroup, topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyOutService getProxyOutService() {
|
||||
return this.serviceManager.getProxyOutService();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.broker.client.ClientChannelInfo;
|
||||
import org.apache.rocketmq.broker.client.ConsumerIdsChangeListener;
|
||||
import org.apache.rocketmq.broker.client.ProducerChangeListener;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.client.consumer.PopResult;
|
||||
import org.apache.rocketmq.client.exception.MQBrokerException;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
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.proxy.common.Address;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.service.out.ProxyOutService;
|
||||
import org.apache.rocketmq.proxy.service.route.ProxyTopicRouteData;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionId;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public interface MessagingProcessor {
|
||||
|
||||
long DEFAULT_TIMEOUT_MILLS = Duration.ofSeconds(2).toMillis();
|
||||
|
||||
ProxyTopicRouteData getTopicRouteDataForProxy(
|
||||
ProxyContext ctx,
|
||||
List<Address> requestHostAndPortList,
|
||||
String topicName
|
||||
) throws Exception;
|
||||
|
||||
default CompletableFuture<SendResult> sendMessage(
|
||||
ProxyContext ctx,
|
||||
QueueSelector queueSelector,
|
||||
String producerGroup,
|
||||
List<MessageExt> msg
|
||||
) {
|
||||
return sendMessage(ctx, queueSelector, producerGroup, msg, DEFAULT_TIMEOUT_MILLS);
|
||||
}
|
||||
|
||||
CompletableFuture<SendResult> sendMessage(
|
||||
ProxyContext ctx,
|
||||
QueueSelector queueSelector,
|
||||
String producerGroup,
|
||||
List<MessageExt> msg,
|
||||
long timeoutMillis
|
||||
);
|
||||
|
||||
default CompletableFuture<RemotingCommand> forwardMessageToDeadLetterQueue(
|
||||
ProxyContext ctx,
|
||||
ReceiptHandle handle,
|
||||
String messageId,
|
||||
String groupName,
|
||||
String topicName
|
||||
) {
|
||||
return forwardMessageToDeadLetterQueue(ctx, handle, messageId, groupName, topicName, DEFAULT_TIMEOUT_MILLS);
|
||||
}
|
||||
|
||||
CompletableFuture<RemotingCommand> forwardMessageToDeadLetterQueue(
|
||||
ProxyContext ctx,
|
||||
ReceiptHandle handle,
|
||||
String messageId,
|
||||
String groupName,
|
||||
String topicName,
|
||||
long timeoutMillis
|
||||
);
|
||||
|
||||
default void endTransaction(
|
||||
ProxyContext ctx,
|
||||
TransactionId transactionId,
|
||||
String messageId,
|
||||
String producerGroup,
|
||||
TransactionStatus transactionStatus,
|
||||
boolean fromTransactionCheck
|
||||
) throws MQBrokerException, RemotingException, InterruptedException {
|
||||
endTransaction(ctx, transactionId, messageId, producerGroup, transactionStatus, fromTransactionCheck, DEFAULT_TIMEOUT_MILLS);
|
||||
}
|
||||
|
||||
void endTransaction(
|
||||
ProxyContext ctx,
|
||||
TransactionId transactionId,
|
||||
String messageId,
|
||||
String producerGroup,
|
||||
TransactionStatus transactionStatus,
|
||||
boolean fromTransactionCheck,
|
||||
long timeoutMillis
|
||||
) throws MQBrokerException, RemotingException, InterruptedException;
|
||||
|
||||
CompletableFuture<PopResult> popMessage(
|
||||
ProxyContext ctx,
|
||||
QueueSelector queueSelector,
|
||||
String consumerGroup,
|
||||
String topic,
|
||||
int maxMsgNums,
|
||||
long invisibleTime,
|
||||
long pollTime,
|
||||
int initMode,
|
||||
SubscriptionData subscriptionData,
|
||||
boolean fifo,
|
||||
PopMessageResultFilter popMessageResultFilter,
|
||||
long timeoutMillis
|
||||
);
|
||||
|
||||
default CompletableFuture<AckResult> ackMessage(
|
||||
ProxyContext ctx,
|
||||
ReceiptHandle handle,
|
||||
String messageId,
|
||||
String consumerGroup,
|
||||
String topic
|
||||
) {
|
||||
return ackMessage(ctx, handle, messageId, consumerGroup, topic, DEFAULT_TIMEOUT_MILLS);
|
||||
}
|
||||
|
||||
CompletableFuture<AckResult> ackMessage(
|
||||
ProxyContext ctx,
|
||||
ReceiptHandle handle,
|
||||
String messageId,
|
||||
String consumerGroup,
|
||||
String topic,
|
||||
long timeoutMillis
|
||||
);
|
||||
|
||||
default CompletableFuture<AckResult> changeInvisibleTime(
|
||||
ProxyContext ctx,
|
||||
ReceiptHandle handle,
|
||||
String messageId,
|
||||
String groupName,
|
||||
String topicName,
|
||||
long invisibleTime
|
||||
) {
|
||||
return changeInvisibleTime(ctx, handle, messageId, groupName, topicName, invisibleTime, DEFAULT_TIMEOUT_MILLS);
|
||||
}
|
||||
|
||||
CompletableFuture<AckResult> changeInvisibleTime(
|
||||
ProxyContext ctx,
|
||||
ReceiptHandle handle,
|
||||
String messageId,
|
||||
String groupName,
|
||||
String topicName,
|
||||
long invisibleTime,
|
||||
long timeoutMillis
|
||||
);
|
||||
|
||||
void registerProducer(
|
||||
ProxyContext ctx,
|
||||
String producerGroup,
|
||||
ClientChannelInfo clientChannelInfo
|
||||
);
|
||||
|
||||
void unRegisterProducer(
|
||||
ProxyContext ctx,
|
||||
String producerGroup,
|
||||
ClientChannelInfo clientChannelInfo
|
||||
);
|
||||
|
||||
Channel findProducerChannel(
|
||||
ProxyContext ctx,
|
||||
String producerGroup,
|
||||
String clientId
|
||||
);
|
||||
|
||||
void registerProducerListener(
|
||||
ProducerChangeListener producerChangeListener
|
||||
);
|
||||
|
||||
void registerConsumer(
|
||||
ProxyContext ctx,
|
||||
String consumerGroup,
|
||||
ClientChannelInfo clientChannelInfo,
|
||||
ConsumeType consumeType,
|
||||
MessageModel messageModel,
|
||||
ConsumeFromWhere consumeFromWhere,
|
||||
Set<SubscriptionData> subList
|
||||
);
|
||||
|
||||
ClientChannelInfo findConsumerChannel(
|
||||
ProxyContext ctx,
|
||||
String consumerGroup,
|
||||
String clientId
|
||||
);
|
||||
|
||||
void unRegisterConsumer(
|
||||
ProxyContext ctx,
|
||||
String consumerGroup,
|
||||
ClientChannelInfo clientChannelInfo
|
||||
);
|
||||
|
||||
void registerConsumerListener(
|
||||
ConsumerIdsChangeListener consumerIdsChangeListener
|
||||
);
|
||||
|
||||
void addTransactionSubscription(
|
||||
ProxyContext ctx,
|
||||
String producerGroup,
|
||||
String topic
|
||||
);
|
||||
|
||||
ProxyOutService getProxyOutService();
|
||||
}
|
||||
+13
-7
@@ -14,14 +14,20 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.grpc.v2.service.cluster;
|
||||
package org.apache.rocketmq.proxy.processor;
|
||||
|
||||
import apache.rocketmq.v2.ReceiveMessageRequest;
|
||||
import io.grpc.Context;
|
||||
import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader;
|
||||
import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
|
||||
public interface ReadQueueSelector {
|
||||
public interface PopMessageResultFilter {
|
||||
|
||||
SelectableMessageQueue select(Context ctx, ReceiveMessageRequest request, PopMessageRequestHeader requestHeader);
|
||||
enum FilterResult {
|
||||
TO_DLQ,
|
||||
NO_MATCH,
|
||||
MATCH
|
||||
}
|
||||
|
||||
FilterResult filterMessage(ProxyContext ctx, String consumerGroup, SubscriptionData subscriptionData,
|
||||
MessageExt messageExt);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
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.protocol.ResponseCode;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.common.ProxyException;
|
||||
import org.apache.rocketmq.proxy.common.ProxyExceptionCode;
|
||||
import org.apache.rocketmq.proxy.service.ServiceManager;
|
||||
import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class ProducerProcessor extends AbstractProcessor {
|
||||
|
||||
public ProducerProcessor(MessagingProcessor messagingProcessor,
|
||||
ServiceManager serviceManager) {
|
||||
super(messagingProcessor, serviceManager);
|
||||
}
|
||||
|
||||
public CompletableFuture<SendResult> sendMessage(ProxyContext ctx, QueueSelector queueSelector,
|
||||
String producerGroup, List<MessageExt> messageExtList, long timeoutMillis) {
|
||||
CompletableFuture<SendResult> future = new CompletableFuture<>();
|
||||
try {
|
||||
String topic = messageExtList.get(0).getTopic();
|
||||
SelectableMessageQueue messageQueue = queueSelector.select(ctx,
|
||||
this.serviceManager.getTopicRouteService().getCurrentMessageQueueView(topic));
|
||||
if (messageQueue == null) {
|
||||
throw new ProxyException(ProxyExceptionCode.FORBIDDEN, "no writable queue");
|
||||
}
|
||||
|
||||
SendMessageRequestHeader requestHeader = buildSendMessageRequestHeader(messageExtList, producerGroup, messageQueue.getQueueId());
|
||||
|
||||
return this.serviceManager.getMessageService().sendMessage(
|
||||
ctx,
|
||||
messageQueue,
|
||||
messageExtList,
|
||||
requestHeader,
|
||||
timeoutMillis);
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected SendMessageRequestHeader buildSendMessageRequestHeader(List<MessageExt> messageExtList,
|
||||
String producerGroup, int queueId) {
|
||||
SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
|
||||
|
||||
MessageExt message = messageExtList.get(0);
|
||||
|
||||
requestHeader.setProducerGroup(producerGroup);
|
||||
requestHeader.setTopic(message.getTopic());
|
||||
requestHeader.setDefaultTopic("");
|
||||
requestHeader.setDefaultTopicQueueNums(0);
|
||||
requestHeader.setQueueId(queueId);
|
||||
requestHeader.setSysFlag(message.getSysFlag());
|
||||
requestHeader.setBornTimestamp(System.currentTimeMillis());
|
||||
requestHeader.setFlag(message.getFlag());
|
||||
requestHeader.setProperties(MessageDecoder.messageProperties2String(message.getProperties()));
|
||||
requestHeader.setReconsumeTimes(0);
|
||||
if (messageExtList.size() > 1) {
|
||||
requestHeader.setBatch(true);
|
||||
}
|
||||
if (requestHeader.getTopic().startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {
|
||||
String reconsumeTimes = MessageAccessor.getReconsumeTime(message);
|
||||
if (reconsumeTimes != null) {
|
||||
requestHeader.setReconsumeTimes(Integer.valueOf(reconsumeTimes));
|
||||
MessageAccessor.clearProperty(message, MessageConst.PROPERTY_RECONSUME_TIME);
|
||||
}
|
||||
|
||||
String maxReconsumeTimes = MessageAccessor.getMaxReconsumeTimes(message);
|
||||
if (maxReconsumeTimes != null) {
|
||||
requestHeader.setMaxReconsumeTimes(Integer.valueOf(maxReconsumeTimes));
|
||||
MessageAccessor.clearProperty(message, MessageConst.PROPERTY_MAX_RECONSUME_TIMES);
|
||||
}
|
||||
}
|
||||
|
||||
return requestHeader;
|
||||
}
|
||||
|
||||
public CompletableFuture<RemotingCommand> forwardMessageToDeadLetterQueue(ProxyContext ctx, ReceiptHandle handle,
|
||||
String messageId, String groupName, String topicName, long timeoutMillis) {
|
||||
CompletableFuture<RemotingCommand> future = new CompletableFuture<>();
|
||||
try {
|
||||
ConsumerSendMsgBackRequestHeader consumerSendMsgBackRequestHeader = new ConsumerSendMsgBackRequestHeader();
|
||||
consumerSendMsgBackRequestHeader.setOffset(handle.getCommitLogOffset());
|
||||
consumerSendMsgBackRequestHeader.setGroup(groupName);
|
||||
consumerSendMsgBackRequestHeader.setDelayLevel(-1);
|
||||
consumerSendMsgBackRequestHeader.setOriginMsgId(messageId);
|
||||
consumerSendMsgBackRequestHeader.setOriginTopic(handle.getRealTopic(topicName, groupName));
|
||||
consumerSendMsgBackRequestHeader.setMaxReconsumeTimes(0);
|
||||
|
||||
return this.serviceManager.getMessageService().sendMessageBack(
|
||||
ctx,
|
||||
handle,
|
||||
messageId,
|
||||
consumerSendMsgBackRequestHeader,
|
||||
timeoutMillis
|
||||
).whenComplete((remotingCommand, t) -> {
|
||||
if (t == null && remotingCommand.getCode() == ResponseCode.SUCCESS) {
|
||||
this.messagingProcessor.ackMessage(ctx, handle, messageId,
|
||||
groupName, topicName, timeoutMillis);
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
}
|
||||
+6
-7
@@ -14,14 +14,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.grpc.v2.service.cluster;
|
||||
package org.apache.rocketmq.proxy.processor;
|
||||
|
||||
import apache.rocketmq.v2.QueryAssignmentRequest;
|
||||
import io.grpc.Context;
|
||||
import java.util.List;
|
||||
import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.service.route.MessageQueueView;
|
||||
import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue;
|
||||
|
||||
public interface AssignmentQueueSelector {
|
||||
public interface QueueSelector {
|
||||
|
||||
List<SelectableMessageQueue> getAssignment(Context ctx, QueryAssignmentRequest request) throws Exception;
|
||||
SelectableMessageQueue select(ProxyContext ctx, MessageQueueView messageQueueView);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import org.apache.rocketmq.client.exception.MQBrokerException;
|
||||
import org.apache.rocketmq.common.protocol.header.EndTransactionRequestHeader;
|
||||
import org.apache.rocketmq.common.sysflag.MessageSysFlag;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.service.ServiceManager;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionId;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
|
||||
public class TransactionProcessor extends AbstractProcessor {
|
||||
|
||||
public TransactionProcessor(MessagingProcessor messagingProcessor,
|
||||
ServiceManager serviceManager) {
|
||||
super(messagingProcessor, serviceManager);
|
||||
}
|
||||
|
||||
void endTransaction(ProxyContext ctx, TransactionId transactionId, String messageId,String producerGroup,
|
||||
TransactionStatus transactionStatus, boolean fromTransactionCheck, long timeoutMillis) throws MQBrokerException, RemotingException, InterruptedException {
|
||||
|
||||
EndTransactionRequestHeader requestHeader = buildEndTransactionRequestHeader(transactionId, messageId,
|
||||
producerGroup, transactionStatus, fromTransactionCheck);
|
||||
this.serviceManager.getMessageService().endTransactionOneway(
|
||||
ctx,
|
||||
transactionId,
|
||||
requestHeader,
|
||||
timeoutMillis
|
||||
);
|
||||
}
|
||||
|
||||
protected EndTransactionRequestHeader buildEndTransactionRequestHeader(TransactionId transactionId, String messageId,String producerGroup,
|
||||
TransactionStatus transactionStatus, boolean fromTransactionCheck) {
|
||||
long transactionStateTableOffset = transactionId.getTranStateTableOffset();
|
||||
long commitLogOffset = transactionId.getCommitLogOffset();
|
||||
|
||||
int commitOrRollback;
|
||||
switch (transactionStatus) {
|
||||
case COMMIT:
|
||||
commitOrRollback = MessageSysFlag.TRANSACTION_COMMIT_TYPE;
|
||||
break;
|
||||
case ROLLBACK:
|
||||
commitOrRollback = MessageSysFlag.TRANSACTION_ROLLBACK_TYPE;
|
||||
break;
|
||||
default:
|
||||
commitOrRollback = MessageSysFlag.TRANSACTION_NOT_TYPE;
|
||||
break;
|
||||
}
|
||||
|
||||
EndTransactionRequestHeader endTransactionRequestHeader = new EndTransactionRequestHeader();
|
||||
endTransactionRequestHeader.setProducerGroup(producerGroup);
|
||||
endTransactionRequestHeader.setMsgId(messageId);
|
||||
endTransactionRequestHeader.setTransactionId(transactionId.getBrokerTransactionId());
|
||||
endTransactionRequestHeader.setTranStateTableOffset(transactionStateTableOffset);
|
||||
endTransactionRequestHeader.setCommitLogOffset(commitLogOffset);
|
||||
endTransactionRequestHeader.setCommitOrRollback(commitOrRollback);
|
||||
endTransactionRequestHeader.setFromTransactionCheck(fromTransactionCheck);
|
||||
|
||||
return endTransactionRequestHeader;
|
||||
}
|
||||
|
||||
public void addTransactionSubscription(ProxyContext ctx, String producerGroup, String topic) {
|
||||
this.serviceManager.getTransactionService().addTransactionSubscription(producerGroup, topic);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
public enum TransactionStatus {
|
||||
UNKNOWN,
|
||||
COMMIT,
|
||||
ROLLBACK
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.rocketmq.broker.client.ClientChannelInfo;
|
||||
import org.apache.rocketmq.broker.client.ConsumerGroupEvent;
|
||||
import org.apache.rocketmq.broker.client.ConsumerIdsChangeListener;
|
||||
import org.apache.rocketmq.broker.client.ConsumerManager;
|
||||
import org.apache.rocketmq.broker.client.ProducerChangeListener;
|
||||
import org.apache.rocketmq.broker.client.ProducerGroupEvent;
|
||||
import org.apache.rocketmq.broker.client.ProducerManager;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.config.ConfigurationManager;
|
||||
import org.apache.rocketmq.proxy.config.ProxyConfig;
|
||||
import org.apache.rocketmq.proxy.service.mqclient.MQClientAPIFactory;
|
||||
import org.apache.rocketmq.proxy.service.message.ClusterMessageService;
|
||||
import org.apache.rocketmq.proxy.service.message.MessageService;
|
||||
import org.apache.rocketmq.proxy.service.mqclient.DoNothingClientRemotingProcessor;
|
||||
import org.apache.rocketmq.proxy.service.out.ClusterProxyOutService;
|
||||
import org.apache.rocketmq.proxy.service.out.ProxyOutService;
|
||||
import org.apache.rocketmq.proxy.service.route.ClusterTopicRouteService;
|
||||
import org.apache.rocketmq.proxy.service.route.TopicRouteService;
|
||||
import org.apache.rocketmq.proxy.service.transaction.ClusterTransactionService;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionService;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
|
||||
public class ClusterServiceManager extends ServiceManager {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
private final ClusterTransactionService clusterTransactionService;
|
||||
private final ProducerManager producerManager;
|
||||
private final ConsumerManager consumerManager;
|
||||
private final TopicRouteService topicRouteService;
|
||||
private final MessageService messageService;
|
||||
private final ProxyOutService proxyOutService;
|
||||
|
||||
private final ScheduledExecutorService scheduledExecutorService;
|
||||
private final MQClientAPIFactory mqClientAPIFactory;
|
||||
|
||||
public ClusterServiceManager(RPCHook rpcHook) {
|
||||
this.scheduledExecutorService = Executors.newScheduledThreadPool(3);
|
||||
this.producerManager = new ProducerManager();
|
||||
this.consumerManager = new ConsumerManager(new ConsumerIdsChangeListenerImpl());
|
||||
|
||||
ProxyConfig proxyConfig = ConfigurationManager.getProxyConfig();
|
||||
double workerFactor = proxyConfig.getRocketmqMQClientWorkerFactor();
|
||||
int threadCount = (int) Math.ceil(Runtime.getRuntime().availableProcessors() * workerFactor);
|
||||
this.mqClientAPIFactory = new MQClientAPIFactory(
|
||||
"CLUSTER_MQ_CLIENT_",
|
||||
proxyConfig.getRocketmqMQClientNum(),
|
||||
threadCount,
|
||||
new DoNothingClientRemotingProcessor(null),
|
||||
rpcHook,
|
||||
scheduledExecutorService);
|
||||
|
||||
this.topicRouteService = new ClusterTopicRouteService(rpcHook);
|
||||
this.messageService = new ClusterMessageService(this.topicRouteService, this.mqClientAPIFactory);
|
||||
this.clusterTransactionService = new ClusterTransactionService(this.topicRouteService, this.producerManager, rpcHook);
|
||||
this.proxyOutService = new ClusterProxyOutService();
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
this.producerManager.appendProducerChangeListener(new ProducerChangeListenerImpl());
|
||||
|
||||
this.scheduledExecutorService.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
producerManager.scanNotActiveChannel();
|
||||
consumerManager.scanNotActiveChannel();
|
||||
} catch (Throwable e) {
|
||||
log.error("Error occurred when scan not active client channels.", e);
|
||||
}
|
||||
}, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS);
|
||||
|
||||
this.appendShutdown(scheduledExecutorService::shutdown);
|
||||
this.appendStartAndShutdown(this.mqClientAPIFactory);
|
||||
this.appendStartAndShutdown(this.topicRouteService);
|
||||
this.appendStartAndShutdown(this.clusterTransactionService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageService getMessageService() {
|
||||
return this.messageService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicRouteService getTopicRouteService() {
|
||||
return topicRouteService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProducerManager getProducerManager() {
|
||||
return this.producerManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumerManager getConsumerManager() {
|
||||
return this.consumerManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionService getTransactionService() {
|
||||
return this.clusterTransactionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyOutService getProxyOutService() {
|
||||
return this.proxyOutService;
|
||||
}
|
||||
|
||||
protected static class ConsumerIdsChangeListenerImpl implements ConsumerIdsChangeListener {
|
||||
|
||||
@Override
|
||||
public void handle(ConsumerGroupEvent event, String group, Object... args) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected class ProducerChangeListenerImpl implements ProducerChangeListener {
|
||||
@Override
|
||||
public void handle(ProducerGroupEvent event, String group, ClientChannelInfo clientChannelInfo) {
|
||||
if (event == ProducerGroupEvent.GROUP_UNREGISTER) {
|
||||
getTransactionService().unSubscribeAllTransactionTopic(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.service;
|
||||
|
||||
import org.apache.rocketmq.broker.BrokerController;
|
||||
import org.apache.rocketmq.broker.client.ConsumerManager;
|
||||
import org.apache.rocketmq.broker.client.ProducerManager;
|
||||
import org.apache.rocketmq.proxy.service.message.LocalMessageService;
|
||||
import org.apache.rocketmq.proxy.service.message.MessageService;
|
||||
import org.apache.rocketmq.proxy.service.out.LocalProxyOutService;
|
||||
import org.apache.rocketmq.proxy.service.out.ProxyOutService;
|
||||
import org.apache.rocketmq.proxy.service.route.LocalTopicRouteService;
|
||||
import org.apache.rocketmq.proxy.service.route.TopicRouteService;
|
||||
import org.apache.rocketmq.proxy.service.transaction.LocalTransactionService;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionService;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
|
||||
public class LocalServiceManager extends ServiceManager {
|
||||
|
||||
private final BrokerController brokerController;
|
||||
private final TopicRouteService topicRouteService;
|
||||
private final MessageService messageService;
|
||||
private final TransactionService transactionService;
|
||||
private final ProxyOutService proxyOutService;
|
||||
|
||||
public LocalServiceManager(BrokerController brokerController, RPCHook rpcHook) {
|
||||
this.brokerController = brokerController;
|
||||
this.messageService = new LocalMessageService(brokerController, rpcHook);
|
||||
this.topicRouteService = new LocalTopicRouteService(brokerController, rpcHook);
|
||||
this.transactionService = new LocalTransactionService();
|
||||
this.proxyOutService = new LocalProxyOutService(brokerController);
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
this.appendStartAndShutdown(this.topicRouteService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageService getMessageService() {
|
||||
return this.messageService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicRouteService getTopicRouteService() {
|
||||
return this.topicRouteService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProducerManager getProducerManager() {
|
||||
return this.brokerController.getProducerManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumerManager getConsumerManager() {
|
||||
return this.brokerController.getConsumerManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionService getTransactionService() {
|
||||
return this.transactionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyOutService getProxyOutService() {
|
||||
return this.proxyOutService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import org.apache.rocketmq.broker.BrokerController;
|
||||
import org.apache.rocketmq.broker.client.ConsumerManager;
|
||||
import org.apache.rocketmq.broker.client.ProducerManager;
|
||||
import org.apache.rocketmq.proxy.common.AbstractStartAndShutdown;
|
||||
import org.apache.rocketmq.proxy.service.message.MessageService;
|
||||
import org.apache.rocketmq.proxy.service.out.ProxyOutService;
|
||||
import org.apache.rocketmq.proxy.service.route.TopicRouteService;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionService;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
|
||||
public abstract class ServiceManager extends AbstractStartAndShutdown {
|
||||
|
||||
public static ServiceManager createForLocalMode(BrokerController brokerController) {
|
||||
return createForLocalMode(brokerController, null);
|
||||
}
|
||||
|
||||
public static ServiceManager createForLocalMode(BrokerController brokerController, RPCHook rpcHook) {
|
||||
return new LocalServiceManager(brokerController, rpcHook);
|
||||
}
|
||||
|
||||
public static ServiceManager createForClusterMode() {
|
||||
return createForClusterMode(null);
|
||||
}
|
||||
|
||||
public static ServiceManager createForClusterMode(RPCHook rpcHook) {
|
||||
return new ClusterServiceManager(rpcHook);
|
||||
}
|
||||
|
||||
public abstract MessageService getMessageService();
|
||||
|
||||
public abstract TopicRouteService getTopicRouteService();
|
||||
|
||||
public abstract ProducerManager getProducerManager();
|
||||
|
||||
public abstract ConsumerManager getConsumerManager();
|
||||
|
||||
public abstract TransactionService getTransactionService();
|
||||
|
||||
public abstract ProxyOutService getProxyOutService();
|
||||
}
|
||||
+44
@@ -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.proxy.service.message;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.client.producer.SendStatus;
|
||||
import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.sysflag.MessageSysFlag;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionId;
|
||||
|
||||
public abstract class AbstractMessageService implements MessageService {
|
||||
|
||||
protected CompletableFuture<SendResult> processSendMessageResponseFuture(
|
||||
String brokerName,
|
||||
SendMessageRequestHeader requestHeader,
|
||||
CompletableFuture<SendResult> future) {
|
||||
return future.thenApply(sendResult -> {
|
||||
int tranType = MessageSysFlag.getTransactionValue(requestHeader.getSysFlag());
|
||||
if (SendStatus.SEND_OK.equals(sendResult.getSendStatus()) &&
|
||||
tranType == MessageSysFlag.TRANSACTION_PREPARED_TYPE &&
|
||||
StringUtils.isNotBlank(sendResult.getTransactionId())) {
|
||||
TransactionId transactionId = TransactionId.genByBrokerTransactionId(brokerName, sendResult);
|
||||
sendResult.setTransactionId(transactionId.getProxyTransactionId());
|
||||
}
|
||||
return sendResult;
|
||||
});
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.service.message;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.client.consumer.PopResult;
|
||||
import org.apache.rocketmq.client.exception.MQBrokerException;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.message.Message;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.EndTransactionRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.common.ProxyException;
|
||||
import org.apache.rocketmq.proxy.common.ProxyExceptionCode;
|
||||
import org.apache.rocketmq.proxy.service.mqclient.MQClientAPIFactory;
|
||||
import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.service.route.TopicRouteService;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionId;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class ClusterMessageService extends AbstractMessageService {
|
||||
|
||||
private final TopicRouteService topicRouteService;
|
||||
private final MQClientAPIFactory mqClientAPIFactory;
|
||||
|
||||
public ClusterMessageService(TopicRouteService topicRouteService, MQClientAPIFactory mqClientAPIFactory) {
|
||||
this.topicRouteService = topicRouteService;
|
||||
this.mqClientAPIFactory = mqClientAPIFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SendResult> sendMessage(ProxyContext ctx, SelectableMessageQueue messageQueue,
|
||||
List<? extends Message> msgList, SendMessageRequestHeader requestHeader, long timeoutMillis) {
|
||||
CompletableFuture<SendResult> future;
|
||||
if (msgList.size() == 1) {
|
||||
future = this.mqClientAPIFactory.getClient().sendMessageAsync(
|
||||
messageQueue.getBrokerAddr(),
|
||||
messageQueue.getBrokerName(), msgList.get(0), requestHeader, timeoutMillis);
|
||||
} else {
|
||||
future = this.mqClientAPIFactory.getClient().sendMessageAsync(
|
||||
messageQueue.getBrokerAddr(),
|
||||
messageQueue.getBrokerName(), msgList, requestHeader, timeoutMillis);
|
||||
}
|
||||
return processSendMessageResponseFuture(messageQueue.getBrokerName(), requestHeader, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<RemotingCommand> sendMessageBack(ProxyContext ctx, ReceiptHandle handle, String messageId,
|
||||
ConsumerSendMsgBackRequestHeader requestHeader, long timeoutMillis) {
|
||||
return this.mqClientAPIFactory.getClient().sendMessageBackAsync(
|
||||
this.resolveBrokerAddr(handle),
|
||||
requestHeader,
|
||||
timeoutMillis
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTransactionOneway(ProxyContext ctx, TransactionId transactionId,
|
||||
EndTransactionRequestHeader requestHeader, long timeoutMillis) throws MQBrokerException, RemotingException, InterruptedException {
|
||||
this.mqClientAPIFactory.getClient().endTransactionOneway(
|
||||
this.resolveBrokerAddr(transactionId.getBrokerName()),
|
||||
requestHeader,
|
||||
"end transaction from proxy",
|
||||
timeoutMillis
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<PopResult> popMessage(ProxyContext ctx, SelectableMessageQueue messageQueue,
|
||||
PopMessageRequestHeader requestHeader, long timeoutMillis) {
|
||||
return this.mqClientAPIFactory.getClient().popMessageAsync(
|
||||
messageQueue.getBrokerAddr(),
|
||||
messageQueue.getBrokerName(),
|
||||
requestHeader,
|
||||
timeoutMillis
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AckResult> changeInvisibleTime(ProxyContext ctx, ReceiptHandle handle, String messageId,
|
||||
ChangeInvisibleTimeRequestHeader requestHeader, long timeoutMillis) {
|
||||
return this.mqClientAPIFactory.getClient().changeInvisibleTimeAsync(
|
||||
this.resolveBrokerAddr(handle),
|
||||
handle.getBrokerName(),
|
||||
requestHeader,
|
||||
timeoutMillis
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AckResult> ackMessage(ProxyContext ctx, ReceiptHandle handle, String messageId,
|
||||
AckMessageRequestHeader requestHeader, long timeoutMillis) {
|
||||
return this.mqClientAPIFactory.getClient().ackMessageAsync(
|
||||
this.resolveBrokerAddr(handle),
|
||||
requestHeader,
|
||||
timeoutMillis
|
||||
);
|
||||
}
|
||||
|
||||
protected String resolveBrokerAddr(ReceiptHandle handle) {
|
||||
return resolveBrokerAddr(handle.getBrokerName());
|
||||
}
|
||||
|
||||
protected String resolveBrokerAddr(String brokerName) {
|
||||
try {
|
||||
return this.topicRouteService.getBrokerAddr(brokerName);
|
||||
} catch (Throwable t) {
|
||||
throw new ProxyException(ProxyExceptionCode.INVALID_BROKER_NAME, "broker " + brokerName + " cannot find", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.service.message;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.broker.BrokerController;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.client.consumer.PopResult;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.message.Message;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.EndTransactionRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionId;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class LocalMessageService extends AbstractMessageService {
|
||||
|
||||
private BrokerController brokerController;
|
||||
|
||||
public LocalMessageService(BrokerController brokerController, RPCHook rpcHook) {
|
||||
this.brokerController = brokerController;
|
||||
}
|
||||
|
||||
@Override public CompletableFuture<SendResult> sendMessage(ProxyContext ctx, SelectableMessageQueue messageQueue,
|
||||
List<? extends Message> msgList, SendMessageRequestHeader requestHeader, long timeoutMillis) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<RemotingCommand> sendMessageBack(ProxyContext ctx, ReceiptHandle handle, String messageId,
|
||||
ConsumerSendMsgBackRequestHeader requestHeader, long timeoutMillis) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public void endTransactionOneway(ProxyContext ctx, TransactionId transactionId,
|
||||
EndTransactionRequestHeader requestHeader, long timeoutMillis) {
|
||||
|
||||
}
|
||||
|
||||
@Override public CompletableFuture<PopResult> popMessage(ProxyContext ctx, SelectableMessageQueue messageQueue,
|
||||
PopMessageRequestHeader requestHeader, long timeoutMillis) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AckResult> changeInvisibleTime(ProxyContext ctx, ReceiptHandle handle, String messageId,
|
||||
ChangeInvisibleTimeRequestHeader requestHeader, long timeoutMillis) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public CompletableFuture<AckResult> ackMessage(ProxyContext ctx, ReceiptHandle handle, String messageId,
|
||||
AckMessageRequestHeader requestHeader, long timeoutMillis) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+14
-10
@@ -14,21 +14,25 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.connector.factory;
|
||||
package org.apache.rocketmq.proxy.service.message;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import org.apache.rocketmq.client.impl.ClientRemotingProcessor;
|
||||
import org.apache.rocketmq.proxy.connector.processor.DoNothingClientRemotingProcessor;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
import org.apache.rocketmq.remoting.CommandCustomHeader;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class MQClientFactory extends AbstractMQClientFactory {
|
||||
public class LocalRemotingCommand extends RemotingCommand {
|
||||
|
||||
public MQClientFactory(ScheduledExecutorService scheduledExecutorService, RPCHook rpcHook) {
|
||||
super(scheduledExecutorService, rpcHook);
|
||||
public static LocalRemotingCommand createRequestCommand(int code, CommandCustomHeader customHeader) {
|
||||
LocalRemotingCommand cmd = new LocalRemotingCommand();
|
||||
cmd.setCode(code);
|
||||
cmd.writeCustomHeader(customHeader);
|
||||
setCmdVersion(cmd);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientRemotingProcessor createClientRemotingProcessor() {
|
||||
return new DoNothingClientRemotingProcessor(null);
|
||||
public <T extends CommandCustomHeader> T decodeCommandCustomHeader(
|
||||
Class<T> classHeader) throws RemotingCommandException {
|
||||
return classHeader.cast(readCustomHeader());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.service.message;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.client.consumer.AckResult;
|
||||
import org.apache.rocketmq.client.consumer.PopResult;
|
||||
import org.apache.rocketmq.client.exception.MQBrokerException;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.common.consumer.ReceiptHandle;
|
||||
import org.apache.rocketmq.common.message.Message;
|
||||
import org.apache.rocketmq.common.protocol.header.AckMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.EndTransactionRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader;
|
||||
import org.apache.rocketmq.proxy.common.ProxyContext;
|
||||
import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionId;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public interface MessageService {
|
||||
|
||||
CompletableFuture<SendResult> sendMessage(
|
||||
ProxyContext ctx,
|
||||
SelectableMessageQueue messageQueue,
|
||||
List<? extends Message> msgList,
|
||||
SendMessageRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
);
|
||||
|
||||
CompletableFuture<RemotingCommand> sendMessageBack(
|
||||
ProxyContext ctx,
|
||||
ReceiptHandle handle,
|
||||
String messageId,
|
||||
ConsumerSendMsgBackRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
);
|
||||
|
||||
void endTransactionOneway(
|
||||
ProxyContext ctx,
|
||||
TransactionId transactionId,
|
||||
EndTransactionRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
) throws MQBrokerException, RemotingException, InterruptedException;
|
||||
|
||||
CompletableFuture<PopResult> popMessage(
|
||||
ProxyContext ctx,
|
||||
SelectableMessageQueue messageQueue,
|
||||
PopMessageRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
);
|
||||
|
||||
CompletableFuture<AckResult> changeInvisibleTime(
|
||||
ProxyContext ctx,
|
||||
ReceiptHandle handle,
|
||||
String messageId,
|
||||
ChangeInvisibleTimeRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
);
|
||||
|
||||
CompletableFuture<AckResult> ackMessage(
|
||||
ProxyContext ctx,
|
||||
ReceiptHandle handle,
|
||||
String messageId,
|
||||
AckMessageRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
);
|
||||
}
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.connector.processor;
|
||||
package org.apache.rocketmq.proxy.service.mqclient;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import org.apache.rocketmq.client.impl.ClientRemotingProcessor;
|
||||
+17
-2
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.connector.client;
|
||||
package org.apache.rocketmq.proxy.service.mqclient;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
@@ -60,7 +60,11 @@ import org.apache.rocketmq.common.protocol.heartbeat.HeartbeatData;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingConnectException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException;
|
||||
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
|
||||
import org.apache.rocketmq.remoting.netty.ResponseFuture;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
@@ -102,6 +106,17 @@ public class MQClientAPIExt extends MQClientAPIImpl {
|
||||
return ex;
|
||||
}
|
||||
|
||||
public void sendHeartbeatOneway(
|
||||
String brokerAddr,
|
||||
HeartbeatData heartbeatData,
|
||||
long timeoutMillis
|
||||
) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, InterruptedException, RemotingTooMuchRequestException {
|
||||
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.HEART_BEAT, null);
|
||||
request.setLanguage(clientConfig.getLanguage());
|
||||
request.setBody(heartbeatData.encode());
|
||||
this.getRemotingClient().invokeOneway(brokerAddr, request, timeoutMillis);
|
||||
}
|
||||
|
||||
public CompletableFuture<Integer> sendHeartbeatAsync(
|
||||
String brokerAddr,
|
||||
HeartbeatData heartbeatData,
|
||||
@@ -164,7 +179,7 @@ public class MQClientAPIExt extends MQClientAPIImpl {
|
||||
public CompletableFuture<SendResult> sendMessageAsync(
|
||||
String brokerAddr,
|
||||
String brokerName,
|
||||
List<Message> msgList,
|
||||
List<? extends Message> msgList,
|
||||
SendMessageRequestHeader requestHeader,
|
||||
long timeoutMillis
|
||||
) {
|
||||
+115
@@ -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.service.mqclient;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.client.ClientConfig;
|
||||
import org.apache.rocketmq.client.impl.ClientRemotingProcessor;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
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.remoting.RPCHook;
|
||||
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
|
||||
|
||||
public class MQClientAPIFactory implements StartAndShutdown {
|
||||
|
||||
private MQClientAPIExt[] clients;
|
||||
private final String namePrefix;
|
||||
private final int clientNum;
|
||||
private final int bootstrapWorkerThreads;
|
||||
private final ClientRemotingProcessor clientRemotingProcessor;
|
||||
private final RPCHook rpcHook;
|
||||
private final ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
public MQClientAPIFactory(String namePrefix, int clientNum, int bootstrapWorkerThreads,
|
||||
ClientRemotingProcessor clientRemotingProcessor,
|
||||
RPCHook rpcHook, ScheduledExecutorService scheduledExecutorService) {
|
||||
this.namePrefix = namePrefix;
|
||||
this.clientNum = clientNum;
|
||||
this.bootstrapWorkerThreads = bootstrapWorkerThreads;
|
||||
this.clientRemotingProcessor = clientRemotingProcessor;
|
||||
this.rpcHook = rpcHook;
|
||||
this.scheduledExecutorService = scheduledExecutorService;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
System.setProperty(ClientConfig.SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, "false");
|
||||
ProxyConfig proxyConfig = ConfigurationManager.getProxyConfig();
|
||||
if (StringUtils.isEmpty(proxyConfig.getNameSrvDomain())) {
|
||||
System.setProperty(MixAll.NAMESRV_ADDR_PROPERTY, proxyConfig.getNameSrvAddr());
|
||||
} else {
|
||||
System.setProperty("rocketmq.namesrv.domain", proxyConfig.getNameSrvDomain());
|
||||
System.setProperty("rocketmq.namesrv.domain.subgroup", proxyConfig.getNameSrvDomainSubgroup());
|
||||
}
|
||||
}
|
||||
|
||||
public MQClientAPIExt getClient() {
|
||||
if (clients.length == 1) {
|
||||
return this.clients[0];
|
||||
}
|
||||
int index = ThreadLocalRandom.current().nextInt(this.clients.length);
|
||||
return this.clients[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
this.clients = new MQClientAPIExt[this.clientNum];
|
||||
|
||||
for (int i = 0; i < this.clientNum; i++) {
|
||||
clients[i] = createAndStart(this.namePrefix + "N_" + i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws Exception {
|
||||
for (int i = 0; i < this.clientNum; i++) {
|
||||
clients[i].shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
protected MQClientAPIExt createAndStart(String instanceName) {
|
||||
ClientConfig clientConfig = new ClientConfig();
|
||||
clientConfig.setInstanceName(instanceName);
|
||||
|
||||
NettyClientConfig nettyClientConfig = new NettyClientConfig();
|
||||
nettyClientConfig.setBootstrapWorkerThreads(bootstrapWorkerThreads);
|
||||
nettyClientConfig.setDisableNettyWorkerGroup(true);
|
||||
nettyClientConfig.setDisableCallbackExecutor(true);
|
||||
|
||||
MQClientAPIExt mqClientAPIExt = new MQClientAPIExt(clientConfig, nettyClientConfig,
|
||||
clientRemotingProcessor,
|
||||
rpcHook);
|
||||
|
||||
if (!mqClientAPIExt.updateNameServerAddressList()) {
|
||||
this.scheduledExecutorService.scheduleAtFixedRate(
|
||||
mqClientAPIExt::fetchNameServerAddr,
|
||||
Duration.ofSeconds(10).toMillis(),
|
||||
Duration.ofMinutes(2).toMillis(),
|
||||
TimeUnit.MILLISECONDS
|
||||
);
|
||||
}
|
||||
mqClientAPIExt.start();
|
||||
return mqClientAPIExt;
|
||||
}
|
||||
}
|
||||
+10
-26
@@ -14,28 +14,26 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.connector.processor;
|
||||
package org.apache.rocketmq.proxy.service.mqclient;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import java.nio.ByteBuffer;
|
||||
import org.apache.rocketmq.broker.client.ProducerManager;
|
||||
import org.apache.rocketmq.client.impl.ClientRemotingProcessor;
|
||||
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.protocol.RequestCode;
|
||||
import org.apache.rocketmq.common.protocol.header.CheckTransactionStateRequestHeader;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionId;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionStateCheckRequest;
|
||||
import org.apache.rocketmq.proxy.connector.transaction.TransactionStateChecker;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class ProxyClientRemotingProcessor extends ClientRemotingProcessor {
|
||||
private final TransactionStateChecker transactionStateChecker;
|
||||
private final ProducerManager producerManager;
|
||||
|
||||
public ProxyClientRemotingProcessor(TransactionStateChecker transactionStateChecker) {
|
||||
public ProxyClientRemotingProcessor(ProducerManager producerManager) {
|
||||
super(null);
|
||||
this.transactionStateChecker = transactionStateChecker;
|
||||
this.producerManager = producerManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,30 +46,16 @@ public class ProxyClientRemotingProcessor extends ClientRemotingProcessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemotingCommand checkTransactionState(ChannelHandlerContext ctx, RemotingCommand request)
|
||||
throws RemotingCommandException {
|
||||
final CheckTransactionStateRequestHeader requestHeader =
|
||||
(CheckTransactionStateRequestHeader) request.decodeCommandCustomHeader(CheckTransactionStateRequestHeader.class);
|
||||
public RemotingCommand checkTransactionState(ChannelHandlerContext ctx, RemotingCommand request) throws RemotingCommandException {
|
||||
final ByteBuffer byteBuffer = ByteBuffer.wrap(request.getBody());
|
||||
final MessageExt messageExt = MessageDecoder.decode(byteBuffer, true, false, false);
|
||||
if (messageExt != null) {
|
||||
final String group = messageExt.getProperty(MessageConst.PROPERTY_PRODUCER_GROUP);
|
||||
if (group != null) {
|
||||
transactionStateChecker.checkTransactionState(
|
||||
new TransactionStateCheckRequest(
|
||||
group,
|
||||
requestHeader.getTranStateTableOffset(),
|
||||
requestHeader.getCommitLogOffset(),
|
||||
requestHeader.getMsgId(),
|
||||
TransactionId.genByBrokerTransactionId(
|
||||
ctx.channel().remoteAddress(),
|
||||
requestHeader.getTransactionId(),
|
||||
requestHeader.getCommitLogOffset(),
|
||||
requestHeader.getTranStateTableOffset()),
|
||||
requestHeader.getBrokerName(),
|
||||
messageExt
|
||||
)
|
||||
);
|
||||
CheckTransactionStateRequestHeader requestHeader =
|
||||
request.decodeCommandCustomHeader(CheckTransactionStateRequestHeader.class);
|
||||
request.writeCustomHeader(requestHeader);
|
||||
this.producerManager.getAvailableChannel(group).writeAndFlush(request);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.service.out;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumeMessageDirectlyResultRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.GetConsumerRunningInfoRequestHeader;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
/**
|
||||
* not implement yet
|
||||
*/
|
||||
public class ClusterProxyOutService implements ProxyOutService {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ConsumerRunningInfo> processGetConsumerRunningInfo(RemotingCommand command,
|
||||
GetConsumerRunningInfoRequestHeader header) {
|
||||
return new CompletableFuture<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ConsumeMessageDirectlyResult> processConsumeMessageDirectly(RemotingCommand command,
|
||||
ConsumeMessageDirectlyResultRequestHeader header) {
|
||||
return new CompletableFuture<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.service.out;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.broker.BrokerController;
|
||||
import org.apache.rocketmq.common.protocol.ResponseCode;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumeMessageDirectlyResultRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.GetConsumerRunningInfoRequestHeader;
|
||||
import org.apache.rocketmq.remoting.RemotingServer;
|
||||
import org.apache.rocketmq.remoting.netty.NettyRemotingAbstract;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public class LocalProxyOutService implements ProxyOutService {
|
||||
|
||||
private final BrokerController brokerController;
|
||||
|
||||
public LocalProxyOutService(BrokerController brokerController) {
|
||||
this.brokerController = brokerController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ConsumerRunningInfo> processGetConsumerRunningInfo(RemotingCommand command,
|
||||
GetConsumerRunningInfoRequestHeader header) {
|
||||
CompletableFuture<ConsumerRunningInfo> future = new CompletableFuture<>();
|
||||
future.thenAccept(consumerRunningInfo -> {
|
||||
RemotingServer remotingServer = this.brokerController.getRemotingServer();
|
||||
if (remotingServer instanceof NettyRemotingAbstract) {
|
||||
NettyRemotingAbstract nettyRemotingAbstract = (NettyRemotingAbstract) remotingServer;
|
||||
RemotingCommand remotingCommand = RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "from gRPC client");
|
||||
remotingCommand.setOpaque(command.getOpaque());
|
||||
ConsumerRunningInfo runningInfo = new ConsumerRunningInfo();
|
||||
runningInfo.setJstack(consumerRunningInfo.getJstack());
|
||||
remotingCommand.setBody(runningInfo.encode());
|
||||
|
||||
// nettyRemotingAbstract.processResponseCommand(new SimpleChannelHandlerContext(channelManager.createChannel(ctx)), remotingCommand);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ConsumeMessageDirectlyResult> processConsumeMessageDirectly(RemotingCommand command,
|
||||
ConsumeMessageDirectlyResultRequestHeader header) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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.service.out;
|
||||
|
||||
import io.netty.channel.AbstractChannel;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelId;
|
||||
import io.netty.channel.ChannelMetadata;
|
||||
import io.netty.channel.ChannelOutboundBuffer;
|
||||
import io.netty.channel.DefaultChannelPromise;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.common.constant.LoggerName;
|
||||
import org.apache.rocketmq.common.message.MessageDecoder;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.RequestCode;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
|
||||
import org.apache.rocketmq.common.protocol.header.CheckTransactionStateRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumeMessageDirectlyResultRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.GetConsumerRunningInfoRequestHeader;
|
||||
import org.apache.rocketmq.logging.InternalLogger;
|
||||
import org.apache.rocketmq.logging.InternalLoggerFactory;
|
||||
import org.apache.rocketmq.proxy.service.transaction.TransactionId;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public abstract class ProxyChannel extends AbstractChannel {
|
||||
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME);
|
||||
|
||||
protected final ProxyOutService proxyOutService;
|
||||
|
||||
protected ProxyChannel(ProxyOutService proxyOutService, Channel parent) {
|
||||
super(parent);
|
||||
this.proxyOutService = proxyOutService;
|
||||
}
|
||||
|
||||
protected ProxyChannel(ProxyOutService proxyOutService, Channel parent, ChannelId id) {
|
||||
super(parent, id);
|
||||
this.proxyOutService = proxyOutService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object msg) {
|
||||
CompletableFuture<Void> processFuture = new CompletableFuture<>();
|
||||
|
||||
try {
|
||||
if (msg instanceof RemotingCommand) {
|
||||
RemotingCommand command = (RemotingCommand) msg;
|
||||
switch (command.getCode()) {
|
||||
case RequestCode.CHECK_TRANSACTION_STATE: {
|
||||
CheckTransactionStateRequestHeader header = (CheckTransactionStateRequestHeader) command.readCustomHeader();
|
||||
MessageExt messageExt = MessageDecoder.decode(ByteBuffer.wrap(command.getBody()), true, false, false);
|
||||
TransactionId transactionId = TransactionId.genByBrokerTransactionId(header.getBrokerName(),
|
||||
header.getTransactionId(), messageExt.getCommitLogOffset(), messageExt.getQueueOffset());
|
||||
processFuture = this.processCheckTransaction(header, messageExt, transactionId);
|
||||
break;
|
||||
}
|
||||
case RequestCode.GET_CONSUMER_RUNNING_INFO: {
|
||||
GetConsumerRunningInfoRequestHeader header = (GetConsumerRunningInfoRequestHeader) command.readCustomHeader();
|
||||
processFuture = this.processGetConsumerRunningInfo(command, header,
|
||||
this.proxyOutService.processGetConsumerRunningInfo(command, header));
|
||||
break;
|
||||
}
|
||||
case RequestCode.CONSUME_MESSAGE_DIRECTLY: {
|
||||
ConsumeMessageDirectlyResultRequestHeader header = (ConsumeMessageDirectlyResultRequestHeader) command.readCustomHeader();
|
||||
MessageExt messageExt = MessageDecoder.decode(ByteBuffer.wrap(command.getBody()), true, false, false);
|
||||
processFuture = this.processConsumeMessageDirectly(command, header, messageExt,
|
||||
this.proxyOutService.processConsumeMessageDirectly(command, header));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
processFuture = processOtherMessage(msg);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.error("process failed. msg:{}", msg, t);
|
||||
processFuture.completeExceptionally(t);
|
||||
}
|
||||
|
||||
DefaultChannelPromise promise = new DefaultChannelPromise(this, GlobalEventExecutor.INSTANCE);
|
||||
processFuture.thenAccept(ignore -> promise.setSuccess())
|
||||
.exceptionally(t -> {
|
||||
promise.setFailure(t);
|
||||
return null;
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
protected abstract CompletableFuture<Void> processOtherMessage(Object msg);
|
||||
|
||||
protected abstract CompletableFuture<Void> processCheckTransaction(CheckTransactionStateRequestHeader header,
|
||||
MessageExt messageExt, TransactionId transactionId);
|
||||
|
||||
protected abstract CompletableFuture<Void> processGetConsumerRunningInfo(
|
||||
RemotingCommand command,
|
||||
GetConsumerRunningInfoRequestHeader header,
|
||||
CompletableFuture<ConsumerRunningInfo> responseFuture);
|
||||
|
||||
protected abstract CompletableFuture<Void> processConsumeMessageDirectly(
|
||||
RemotingCommand command,
|
||||
ConsumeMessageDirectlyResultRequestHeader header,
|
||||
MessageExt messageExt,
|
||||
CompletableFuture<ConsumeMessageDirectlyResult> responseFuture);
|
||||
|
||||
@Override
|
||||
public ChannelConfig config() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelMetadata metadata() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractUnsafe newUnsafe() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCompatible(EventLoop loop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDisconnect() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBeginRead() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.service.out;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
|
||||
import org.apache.rocketmq.common.protocol.header.ConsumeMessageDirectlyResultRequestHeader;
|
||||
import org.apache.rocketmq.common.protocol.header.GetConsumerRunningInfoRequestHeader;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
|
||||
public interface ProxyOutService {
|
||||
|
||||
CompletableFuture<ConsumerRunningInfo> processGetConsumerRunningInfo(
|
||||
RemotingCommand command,
|
||||
GetConsumerRunningInfoRequestHeader header
|
||||
);
|
||||
|
||||
CompletableFuture<ConsumeMessageDirectlyResult> processConsumeMessageDirectly(
|
||||
RemotingCommand command,
|
||||
ConsumeMessageDirectlyResultRequestHeader header
|
||||
);
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.service.route;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.proxy.common.Address;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
|
||||
public class ClusterTopicRouteService extends TopicRouteService {
|
||||
|
||||
public ClusterTopicRouteService(RPCHook rpcHook) {
|
||||
super(rpcHook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageQueueView getCurrentMessageQueueView(String topicName) throws Exception {
|
||||
return getAllMessageQueueView(topicName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyTopicRouteData getTopicRouteForProxy(List<Address> requestHostAndPortList,
|
||||
String topicName) throws Exception {
|
||||
TopicRouteData topicRouteData = getAllMessageQueueView(topicName).getTopicRouteData();
|
||||
|
||||
ProxyTopicRouteData proxyTopicRouteData = new ProxyTopicRouteData();
|
||||
proxyTopicRouteData.setQueueDatas(topicRouteData.getQueueDatas());
|
||||
|
||||
for (BrokerData brokerData : topicRouteData.getBrokerDatas()) {
|
||||
ProxyTopicRouteData.ProxyBrokerData proxyBrokerData = new ProxyTopicRouteData.ProxyBrokerData();
|
||||
proxyBrokerData.setCluster(brokerData.getCluster());
|
||||
proxyBrokerData.setBrokerName(brokerData.getBrokerName());
|
||||
for (Long brokerId : brokerData.getBrokerAddrs().keySet()) {
|
||||
proxyBrokerData.getBrokerAddrs().put(brokerId, requestHostAndPortList);
|
||||
}
|
||||
proxyTopicRouteData.getBrokerDatas().add(proxyBrokerData);
|
||||
}
|
||||
|
||||
return proxyTopicRouteData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBrokerAddr(String brokerName) throws Exception {
|
||||
List<BrokerData> brokerDataList = getAllMessageQueueView(brokerName).getTopicRouteData().getBrokerDatas();
|
||||
if (brokerDataList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return brokerDataList.get(0).getBrokerAddrs().get(MixAll.MASTER_ID);
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.service.route;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.net.HostAndPort;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.apache.rocketmq.broker.BrokerController;
|
||||
import org.apache.rocketmq.common.BrokerConfig;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.TopicConfig;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.common.protocol.route.QueueData;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.proxy.common.Address;
|
||||
import org.apache.rocketmq.proxy.config.ConfigurationManager;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
|
||||
public class LocalTopicRouteService extends TopicRouteService {
|
||||
|
||||
private final BrokerController brokerController;
|
||||
private final List<BrokerData> brokerDataList;
|
||||
private final int grpcPort;
|
||||
|
||||
public LocalTopicRouteService(BrokerController brokerController, RPCHook rpcHook) {
|
||||
super(rpcHook);
|
||||
this.brokerController = brokerController;
|
||||
BrokerConfig brokerConfig = this.brokerController.getBrokerConfig();
|
||||
HashMap<Long, String> brokerAddrs = new HashMap<>();
|
||||
brokerAddrs.put(MixAll.MASTER_ID, this.brokerController.getBrokerAddr());
|
||||
this.brokerDataList = Lists.newArrayList(
|
||||
new BrokerData(brokerConfig.getBrokerClusterName(), brokerConfig.getBrokerName(), brokerAddrs)
|
||||
);
|
||||
this.grpcPort = ConfigurationManager.getProxyConfig().getGrpcServerPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageQueueView getCurrentMessageQueueView(String topic) throws Exception {
|
||||
TopicConfig topicConfig = this.brokerController.getTopicConfigManager().getTopicConfigTable().get(topic);
|
||||
return new MessageQueueView(topic, toTopicRouteData(topicConfig));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyTopicRouteData getTopicRouteForProxy(List<Address> requestHostAndPortList, String topicName) throws Exception {
|
||||
MessageQueueView messageQueueView = getAllMessageQueueView(topicName);
|
||||
TopicRouteData topicRouteData = messageQueueView.getTopicRouteData();
|
||||
|
||||
ProxyTopicRouteData proxyTopicRouteData = new ProxyTopicRouteData();
|
||||
proxyTopicRouteData.setQueueDatas(topicRouteData.getQueueDatas());
|
||||
|
||||
for (BrokerData brokerData : topicRouteData.getBrokerDatas()) {
|
||||
ProxyTopicRouteData.ProxyBrokerData proxyBrokerData = new ProxyTopicRouteData.ProxyBrokerData();
|
||||
proxyBrokerData.setCluster(brokerData.getCluster());
|
||||
proxyBrokerData.setBrokerName(brokerData.getBrokerName());
|
||||
for (Long brokerId : brokerData.getBrokerAddrs().keySet()) {
|
||||
String brokerAddr = brokerData.getBrokerAddrs().get(brokerId);
|
||||
HostAndPort brokerHostAndPort = HostAndPort.fromString(brokerAddr);
|
||||
HostAndPort grpcHostAndPort = HostAndPort.fromParts(brokerHostAndPort.getHost(), grpcPort);
|
||||
|
||||
proxyBrokerData.getBrokerAddrs().put(brokerId, Lists.newArrayList(new Address(Address.AddressScheme.IPv4, grpcHostAndPort)));
|
||||
}
|
||||
proxyTopicRouteData.getBrokerDatas().add(proxyBrokerData);
|
||||
}
|
||||
|
||||
return proxyTopicRouteData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBrokerAddr(String brokerName) throws Exception {
|
||||
return this.brokerController.getBrokerAddr();
|
||||
}
|
||||
|
||||
protected TopicRouteData toTopicRouteData(TopicConfig topicConfig) {
|
||||
TopicRouteData topicRouteData = new TopicRouteData();
|
||||
topicRouteData.setBrokerDatas(brokerDataList);
|
||||
|
||||
QueueData queueData = new QueueData();
|
||||
queueData.setPerm(topicConfig.getPerm());
|
||||
queueData.setReadQueueNums(topicConfig.getReadQueueNums());
|
||||
queueData.setWriteQueueNums(topicConfig.getWriteQueueNums());
|
||||
queueData.setTopicSysFlag(topicConfig.getTopicSysFlag());
|
||||
queueData.setBrokerName(this.brokerController.getBrokerConfig().getBrokerName());
|
||||
topicRouteData.setQueueDatas(Lists.newArrayList(queueData));
|
||||
|
||||
return topicRouteData;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.proxy.connector.route;
|
||||
package org.apache.rocketmq.proxy.service.route;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user