diff --git a/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java b/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java index edb30b576d..df22fb1f8e 100644 --- a/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java +++ b/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java @@ -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 diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageBatch.java b/common/src/main/java/org/apache/rocketmq/common/message/MessageBatch.java index a6b801edab..e3104f1656 100644 --- a/common/src/main/java/org/apache/rocketmq/common/message/MessageBatch.java +++ b/common/src/main/java/org/apache/rocketmq/common/message/MessageBatch.java @@ -39,7 +39,7 @@ public class MessageBatch extends Message implements Iterable { return messages.iterator(); } - public static MessageBatch generateFromList(Collection messages) { + public static MessageBatch generateFromList(Collection messages) { assert messages != null; assert messages.size() > 0; List messageList = new ArrayList(messages.size()); diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/ProxyMode.java b/proxy/src/main/java/org/apache/rocketmq/proxy/ProxyMode.java new file mode 100644 index 0000000000..3cc36425b0 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/ProxyMode.java @@ -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); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/ProxyStartup.java b/proxy/src/main/java/org/apache/rocketmq/proxy/ProxyStartup.java index 1ba8feb444..fe09ef38cc 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/ProxyStartup.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/ProxyStartup.java @@ -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() { diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/channel/ChannelManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/channel/ChannelManager.java deleted file mode 100644 index e09180c5b7..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/channel/ChannelManager.java +++ /dev/null @@ -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 clientIdChannelMap = new ConcurrentHashMap<>(); - protected final ConcurrentMap/* 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 createChannel(Context ctx, Function creator, Class clazz) { - return createChannel(ctx, anonymousChannelId(ctx, clazz.getName()), creator, clazz); - } - - public T createChannel(Context ctx, String clientId, Function creator, Class 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 getChannel(String clientId, Class clazz) { - SimpleChannel channel = clientIdChannelMap.get(clientId); - if (channel == null) { - return null; - } - return clazz.cast(channel); - } - - public void setChannel(String clientId, T channel) { - clientIdChannelMap.put(clientId, channel); - } - - public T removeChannel(String clientId, Class 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 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; - }); - } - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/channel/InvocationChannel.java b/proxy/src/main/java/org/apache/rocketmq/proxy/channel/InvocationChannel.java deleted file mode 100644 index 2ae543f254..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/channel/InvocationChannel.java +++ /dev/null @@ -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 extends SimpleChannel implements Cleaner { - protected final ConcurrentMap> inFlightRequestMap; - protected final ResponseHandler handler; - - public InvocationChannel(Context ctx, ResponseHandler 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 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 context) { - inFlightRequestMap.put(opaque, context); - } - - public void eraseInvocationContext(int opaque) { - inFlightRequestMap.remove(opaque); - } - - @Override - public void clean() { - Iterator>> iterator = inFlightRequestMap.entrySet().iterator(); - int count = 0; - while (iterator.hasNext()) { - Map.Entry> 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); - } - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/common/TelemetryCommandManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/common/Address.java similarity index 52% rename from proxy/src/main/java/org/apache/rocketmq/proxy/common/TelemetryCommandManager.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/common/Address.java index 99461ed39b..7480fb2fa0 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/common/TelemetryCommandManager.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/common/Address.java @@ -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 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; } } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/common/ProxyContext.java b/proxy/src/main/java/org/apache/rocketmq/proxy/common/ProxyContext.java new file mode 100644 index 0000000000..be0f6d4b00 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/common/ProxyContext.java @@ -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 value = new HashMap<>(); + + public static ProxyContext create() { + return new ProxyContext(); + } + + public Map getValue() { + return this.value; + } + + public ProxyContext withVal(String key, Object val) { + this.value.put(key, val); + return this; + } + + public T getVal(String key) { + return (T) this.value.get(key); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/common/TelemetryCommandRecord.java b/proxy/src/main/java/org/apache/rocketmq/proxy/common/ProxyException.java similarity index 66% rename from proxy/src/main/java/org/apache/rocketmq/proxy/common/TelemetryCommandRecord.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/common/ProxyException.java index adc074113f..af528329fd 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/common/TelemetryCommandRecord.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/common/ProxyException.java @@ -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; } } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionStateChecker.java b/proxy/src/main/java/org/apache/rocketmq/proxy/common/ProxyExceptionCode.java similarity index 82% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionStateChecker.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/common/ProxyExceptionCode.java index 506aa0154c..95b3a93f69 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionStateChecker.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/common/ProxyExceptionCode.java @@ -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 } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/common/utils/ExceptionUtils.java b/proxy/src/main/java/org/apache/rocketmq/proxy/common/utils/ExceptionUtils.java new file mode 100644 index 0000000000..78b32e4cb0 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/common/utils/ExceptionUtils.java @@ -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; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/config/ProxyConfig.java b/proxy/src/main/java/org/apache/rocketmq/proxy/config/ProxyConfig.java index f3f2f6b2b6..43a11bab4d 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/config/ProxyConfig.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/config/ProxyConfig.java @@ -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; } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/AbstractForwardClient.java b/proxy/src/main/java/org/apache/rocketmq/proxy/connector/AbstractForwardClient.java deleted file mode 100644 index 2dce57491e..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/AbstractForwardClient.java +++ /dev/null @@ -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 { - - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ConnectorManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ConnectorManager.java deleted file mode 100644 index e0495c319e..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ConnectorManager.java +++ /dev/null @@ -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; - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/DefaultForwardClient.java b/proxy/src/main/java/org/apache/rocketmq/proxy/connector/DefaultForwardClient.java deleted file mode 100644 index ee7b1e45c7..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/DefaultForwardClient.java +++ /dev/null @@ -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> 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 getMaxOffset(Context ctx, String brokerAddr, String topic, int queueId) { - return this.getMaxOffset(ctx, brokerAddr, topic, queueId, DEFAULT_MQ_CLIENT_TIMEOUT); - } - - public CompletableFuture getMaxOffset( - Context ctx, - String brokerAddr, - String topic, - int queueId, - long timeoutMillis - ) { - return this.getClient().getMaxOffsetAsync(brokerAddr, topic, queueId, timeoutMillis); - } - - public CompletableFuture 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 searchOffset( - Context ctx, - String brokerAddr, - String topic, - int queueId, - long timestamp, - long timeoutMillis - ) { - return this.getClient().searchOffsetAsync(brokerAddr, topic, queueId, timestamp, timeoutMillis); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ForwardProducer.java b/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ForwardProducer.java deleted file mode 100644 index 12a7a8c1d2..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ForwardProducer.java +++ /dev/null @@ -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 heartBeat(Context ctx, String brokerAddr, HeartbeatData heartbeatData) throws Exception { - return this.heartBeat(ctx, brokerAddr, heartbeatData, DEFAULT_MQ_CLIENT_TIMEOUT); - } - public CompletableFuture 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 sendMessage( - Context ctx, - String address, - String brokerName, - List msg, - SendMessageRequestHeader requestHeader - ) { - return this.sendMessage(ctx, address, brokerName, msg, requestHeader, DEFAULT_MQ_CLIENT_TIMEOUT); - } - - public CompletableFuture sendMessage( - Context ctx, - String address, - String brokerName, - List msg, - SendMessageRequestHeader requestHeader, - long timeoutMillis - ) { - CompletableFuture 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 processSendMessageResponseFuture( - Context ctx, - String address, - SendMessageRequestHeader requestHeader, - CompletableFuture 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 sendMessageBackThenAckOrg(Context ctx, String brokerAddr, ConsumerSendMsgBackRequestHeader sendMsgBackRequestHeader, - AckMessageRequestHeader ackMessageRequestHeader) { - return sendMessageBackThenAckOrg(ctx, brokerAddr, sendMsgBackRequestHeader, ackMessageRequestHeader, DEFAULT_MQ_CLIENT_TIMEOUT, - future -> { - }); - } - - public CompletableFuture sendMessageBackThenAckOrg(Context ctx, String brokerAddr, ConsumerSendMsgBackRequestHeader sendMsgBackRequestHeader, - AckMessageRequestHeader ackMessageRequestHeader, long timeoutMillis, - Consumer> 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 sendMessageBack(Context ctx, String brokerAddr, ConsumerSendMsgBackRequestHeader requestHeader) { - return this.sendMessageBack(ctx, brokerAddr, requestHeader, DEFAULT_MQ_CLIENT_TIMEOUT); - } - - public CompletableFuture sendMessageBack(Context ctx, String brokerAddr, ConsumerSendMsgBackRequestHeader requestHeader, long timeoutMillis) { - return this.getClient().sendMessageBackAsync(brokerAddr, requestHeader, timeoutMillis); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ForwardReadConsumer.java b/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ForwardReadConsumer.java deleted file mode 100644 index 13973e8882..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ForwardReadConsumer.java +++ /dev/null @@ -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 popMessage(Context ctx, String address, String brokerName, - PopMessageRequestHeader requestHeader) { - return this.popMessage(ctx, address, brokerName, requestHeader, DEFAULT_MQ_CLIENT_TIMEOUT); - } - - public CompletableFuture popMessage( - Context ctx, - String address, - String brokerName, - PopMessageRequestHeader requestHeader, - long timeoutMillis - ) { - return this.getClient().popMessageAsync(address, brokerName, requestHeader, timeoutMillis); - } - - public CompletableFuture pullMessage(Context ctx, String address, PullMessageRequestHeader requestHeader) { - return this.pullMessage(ctx, address, requestHeader, MAX_CONSUMER_TIMEOUT_MILLIS); - } - - public CompletableFuture pullMessage(Context ctx, String address, PullMessageRequestHeader requestHeader, - long timeoutMillis) { - return this.getClient().pullMessageAsync(address, requestHeader, timeoutMillis); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ForwardWriteConsumer.java b/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ForwardWriteConsumer.java deleted file mode 100644 index 5404c77a96..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/ForwardWriteConsumer.java +++ /dev/null @@ -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 ackMessage(Context ctx, String address, String messageId, AckMessageRequestHeader requestHeader) { - return this.ackMessage(ctx, address, messageId, requestHeader, DEFAULT_MQ_CLIENT_TIMEOUT); - } - - public CompletableFuture ackMessage( - Context ctx, - String address, - String messageId, - AckMessageRequestHeader requestHeader, - long timeoutMillis - ) { - return this.getClient().ackMessageAsync(address, requestHeader, timeoutMillis); - } - - public CompletableFuture 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 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); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/AbstractClientManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/AbstractClientManager.java deleted file mode 100644 index 35e4f696bf..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/AbstractClientManager.java +++ /dev/null @@ -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 { - private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME); - - protected final ScheduledExecutorService scheduledExecutorService; - protected Map 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); - } - }); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/AbstractMQClientFactory.java b/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/AbstractMQClientFactory.java deleted file mode 100644 index 37a0cedabc..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/AbstractMQClientFactory.java +++ /dev/null @@ -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 { - - 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(); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/ForwardClientManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/ForwardClientManager.java deleted file mode 100644 index af75583b0c..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/ForwardClientManager.java +++ /dev/null @@ -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(); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/TransactionProducerFactory.java b/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/TransactionProducerFactory.java deleted file mode 100644 index 60234b3f84..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/TransactionProducerFactory.java +++ /dev/null @@ -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); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionStateCheckRequest.java b/proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionStateCheckRequest.java deleted file mode 100644 index c5269e64f9..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionStateCheckRequest.java +++ /dev/null @@ -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; - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/interceptor/AuthenticationInterceptor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/interceptor/AuthenticationInterceptor.java index d3c3063252..409c2c8c16 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/interceptor/AuthenticationInterceptor.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/interceptor/AuthenticationInterceptor.java @@ -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 accessValidatorList; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/interceptor/RequestMapping.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/interceptor/RequestMapping.java new file mode 100644 index 0000000000..a9674d1837 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/interceptor/RequestMapping.java @@ -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 REQUEST_MAP = new HashMap() { + { + // 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; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/AbstractMessingActivity.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/AbstractMessingActivity.java new file mode 100644 index 0000000000..925d36ae37 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/AbstractMessingActivity.java @@ -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))); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/DefaultGrpcMessingActivity.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/DefaultGrpcMessingActivity.java new file mode 100644 index 0000000000..57f5390080 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/DefaultGrpcMessingActivity.java @@ -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 queryRoute(Context ctx, QueryRouteRequest request) { + return this.routeActivity.queryRoute(ctx, request); + } + + @Override + public CompletableFuture heartbeat(Context ctx, HeartbeatRequest request) { + return this.clientActivity.heartbeat(ctx, request); + } + + @Override + public CompletableFuture sendMessage(Context ctx, SendMessageRequest request) { + return this.sendMessageActivity.sendMessage(ctx, request); + } + + @Override + public CompletableFuture queryAssignment(Context ctx, QueryAssignmentRequest request) { + return this.routeActivity.queryAssignment(ctx, request); + } + + @Override + public void receiveMessage(Context ctx, ReceiveMessageRequest request, StreamObserver responseObserver) { + this.receiveMessageActivity.receiveMessage(ctx, request, responseObserver); + } + + @Override + public CompletableFuture ackMessage(Context ctx, AckMessageRequest request) { + return this.ackMessageActivity.ackMessage(ctx, request); + } + + @Override + public CompletableFuture forwardMessageToDeadLetterQueue(Context ctx, + ForwardMessageToDeadLetterQueueRequest request) { + return this.forwardMessageToDLQActivity.forwardMessageToDeadLetterQueue(ctx, request); + } + + @Override + public CompletableFuture endTransaction(Context ctx, EndTransactionRequest request) { + return this.endTransactionActivity.endTransaction(ctx, request); + } + + @Override + public CompletableFuture notifyClientTermination(Context ctx, + NotifyClientTerminationRequest request) { + return this.clientActivity.notifyClientTermination(ctx, request); + } + + @Override + public CompletableFuture changeInvisibleDuration(Context ctx, + ChangeInvisibleDurationRequest request) { + return this.changeInvisibleDurationActivity.changeInvisibleDuration(ctx, request); + } + + @Override + public StreamObserver telemetry(Context ctx, StreamObserver responseObserver) { + return this.clientActivity.telemetry(ctx, responseObserver); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/WriteQueueSelector.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcContextConstants.java similarity index 71% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/WriteQueueSelector.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcContextConstants.java index 90648fe2cd..061766c0eb 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/WriteQueueSelector.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcContextConstants.java @@ -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"; } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcMessagingProcessor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcMessagingApplication.java similarity index 78% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcMessagingProcessor.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcMessagingApplication.java index 89666c1a80..6449259802 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcMessagingProcessor.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcMessagingApplication.java @@ -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 responseObserver) { - CompletableFuture future = grpcForwardService.queryRoute(Context.current(), request); + CompletableFuture 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 responseObserver) { - CompletableFuture future = grpcForwardService.heartbeat(Context.current(), request); + CompletableFuture 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 responseObserver) { - CompletableFuture future = grpcForwardService.sendMessage(Context.current(), request); + CompletableFuture 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 responseObserver) { - CompletableFuture future = grpcForwardService.queryAssignment(Context.current(), request); + CompletableFuture 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 responseObserver) { - grpcForwardService.receiveMessage(Context.current(), request, responseObserver); + grpcMessingActivity.receiveMessage(Context.current(), request, responseObserver); } @Override public void ackMessage(AckMessageRequest request, StreamObserver responseObserver) { - CompletableFuture future = grpcForwardService.ackMessage(Context.current(), request); + CompletableFuture 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 responseObserver) { - CompletableFuture future = grpcForwardService.forwardMessageToDeadLetterQueue(Context.current(), request); + CompletableFuture 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 responseObserver) { - CompletableFuture future = grpcForwardService.endTransaction(Context.current(), request); + CompletableFuture 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 responseObserver) { - CompletableFuture future = grpcForwardService.notifyClientTermination(Context.current(), request); + CompletableFuture 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 responseObserver) { - CompletableFuture future = grpcForwardService.changeInvisibleDuration(Context.current(), request); + CompletableFuture 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 telemetry(StreamObserver 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(); } } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/GrpcForwardService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcMessingActivity.java similarity index 96% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/GrpcForwardService.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcMessingActivity.java index 3f0b459612..68337cba1c 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/GrpcForwardService.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/GrpcMessingActivity.java @@ -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 queryRoute(Context ctx, QueryRouteRequest request); diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/GrpcConverter.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/GrpcConverter.java deleted file mode 100644 index 52fb47d950..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/GrpcConverter.java +++ /dev/null @@ -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 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 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 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 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 buildMessageProperty(Message message, String producerGroup) { - org.apache.rocketmq.common.message.Message messageWithHeader = new org.apache.rocketmq.common.message.Message(); - // set user properties - Map 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 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 buildMessage(List protoMessageList, - Resource topic) { - String topicName = wrapResourceWithNamespace(topic); - List 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 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 buildSubscriptionDataSet(List subscriptionEntryList) { - Set 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 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 buildUserAttributes(MessageExt messageExt) { - Map userAttributes = new HashMap<>(); - Map properties = messageExt.getProperties(); - - for (Map.Entry 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 genMessageQueueFromQueueData(QueueData queueData, Resource topic, Broker broker) { - List 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; - } - -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/channel/GrpcClientChannel.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/channel/GrpcClientChannel.java deleted file mode 100644 index a14dd4e3f8..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/channel/GrpcClientChannel.java +++ /dev/null @@ -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> 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 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} - *

- * Case {@link RequestCode#CHECK_TRANSACTION_STATE} - * @see org.apache.rocketmq.broker.client.net.Broker2Client#checkProducerTransactionState - *

- *

- * Case {@link RequestCode#GET_CONSUMER_RUNNING_INFO} - * @see org.apache.rocketmq.broker.processor.AdminBrokerProcessor - * getConsumerRunningInfo - *

- */ - @Override - public ChannelFuture writeAndFlush(Object msg) { - StreamObserver 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; - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/channel/GrpcChannelManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/channel/GrpcChannelManager.java new file mode 100644 index 0000000000..1824104f6c --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/channel/GrpcChannelManager.java @@ -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/* clientId */> groupClientIdChannelMap = new ConcurrentHashMap<>(); + + protected final AtomicLong nonceIdGenerator = new AtomicLong(0); + protected final ConcurrentMap> 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 clientIdChannelMap = this.groupClientIdChannelMap.get(group); + if (clientIdChannelMap == null) { + return null; + } + return clientIdChannelMap.get(clientId); + } + + public GrpcClientChannel removeChannel(String group, String clientId) { + AtomicReference 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 CompletableFuture getAndRemoveResponseFuture(String nonce) { + return (CompletableFuture) this.resultNonceFutureMap.remove(nonce); + } + + protected String nextNonce() { + return String.valueOf(this.nonceIdGenerator.getAndIncrement()); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/channel/GrpcClientChannel.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/channel/GrpcClientChannel.java new file mode 100644 index 0000000000..1ad4422a0c --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/channel/GrpcClientChannel.java @@ -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> 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 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 processOtherMessage(Object msg) { + if (msg instanceof TelemetryCommand) { + TelemetryCommand response = (TelemetryCommand) msg; + this.getTelemetryCommandStreamObserver().onNext(response); + } + return CompletableFuture.completedFuture(null); + } + + @Override + protected CompletableFuture 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 processGetConsumerRunningInfo(RemotingCommand command, + GetConsumerRunningInfoRequestHeader header, + CompletableFuture 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 processConsumeMessageDirectly(RemotingCommand command, + ConsumeMessageDirectlyResultRequestHeader header, + MessageExt messageExt, CompletableFuture 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 getTelemetryCommandStreamObserver() { + return this.telemetryCommandRef.get(); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/client/ClientActivity.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/client/ClientActivity.java new file mode 100644 index 0000000000..8e7c9699b7 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/client/ClientActivity.java @@ -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 heartbeat(Context ctx, HeartbeatRequest request) { + CompletableFuture 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 notifyClientTermination(Context ctx, NotifyClientTerminationRequest request) { + CompletableFuture 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 telemetry(Context ctx, StreamObserver responseObserver) { + return new StreamObserver() { + @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 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 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 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 buildSubscriptionDataSet(List subscriptionEntryList) { + Set 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()); + } + } + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/GrpcClientManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/GrpcClientSettingsManager.java similarity index 89% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/GrpcClientManager.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/GrpcClientSettingsManager.java index e0088cd8b8..b5f8bde099 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/GrpcClientManager.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/GrpcClientSettingsManager.java @@ -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 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); } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/GrpcConverter.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/GrpcConverter.java new file mode 100644 index 0000000000..810708a216 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/GrpcConverter.java @@ -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 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 buildUserAttributes(MessageExt messageExt) { + Map userAttributes = new HashMap<>(); + Map properties = messageExt.getProperties(); + + for (Map.Entry 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(); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/GrpcProxyException.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/GrpcProxyException.java new file mode 100644 index 0000000000..702ca2c75d --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/GrpcProxyException.java @@ -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 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; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ResponseBuilder.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/ResponseBuilder.java similarity index 88% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ResponseBuilder.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/ResponseBuilder.java index 0344f28193..c0c275f5b5 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ResponseBuilder.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/ResponseBuilder.java @@ -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"); } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ResponseWriter.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/ResponseWriter.java similarity index 98% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ResponseWriter.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/ResponseWriter.java index 90f1bfaa77..1f63734932 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ResponseWriter.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/common/ResponseWriter.java @@ -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; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/AckMessageActivity.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/AckMessageActivity.java new file mode 100644 index 0000000000..7226717a73 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/AckMessageActivity.java @@ -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 ackMessage(Context ctx, AckMessageRequest request) { + ProxyContext proxyContext = createContext(ctx); + CompletableFuture future = new CompletableFuture<>(); + + try { + CompletableFuture[] 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 entryList = new ArrayList<>(); + for (CompletableFuture 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 processAckMessage(ProxyContext ctx, AckMessageRequest request, + AckMessageEntry ackMessageEntry) { + CompletableFuture 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 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(); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/ChangeInvisibleDurationActivity.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/ChangeInvisibleDurationActivity.java new file mode 100644 index 0000000000..fc8fb405f7 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/ChangeInvisibleDurationActivity.java @@ -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 changeInvisibleDuration(Context ctx, + ChangeInvisibleDurationRequest request) { + ProxyContext context = createContext(ctx); + CompletableFuture 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(); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/PopMessageResultFilterImpl.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/PopMessageResultFilterImpl.java new file mode 100644 index 0000000000..23842c6fab --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/PopMessageResultFilterImpl.java @@ -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; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/ReceiveMessageActivity.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/ReceiveMessageActivity.java new file mode 100644 index 0000000000..bac4e3cc44 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/ReceiveMessageActivity.java @@ -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 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; + } + } + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/ReceiveMessageResponseStreamWriter.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/ReceiveMessageResponseStreamWriter.java new file mode 100644 index 0000000000..6a94f49848 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/consumer/ReceiveMessageResponseStreamWriter.java @@ -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 streamObserver; + + public ReceiveMessageResponseStreamWriter( + MessagingProcessor messagingProcessor, + StreamObserver observer) { + this.messagingProcessor = messagingProcessor; + this.streamObserver = observer; + } + + public void write(ProxyContext ctx, ReceiveMessageRequest request, PopResult popResult) { + PopStatus status = popResult.getPopStatus(); + List 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 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() + ); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/producer/ForwardMessageToDLQActivity.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/producer/ForwardMessageToDLQActivity.java new file mode 100644 index 0000000000..d6133ded44 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/producer/ForwardMessageToDLQActivity.java @@ -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 forwardMessageToDeadLetterQueue(Context ctx, + ForwardMessageToDeadLetterQueueRequest request) { + CompletableFuture 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(); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/producer/SendMessageActivity.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/producer/SendMessageActivity.java new file mode 100644 index 0000000000..49edbcaba8 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/producer/SendMessageActivity.java @@ -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 sendMessage(Context ctx, SendMessageRequest request) { + ProxyContext context = createContext(ctx); + CompletableFuture future = new CompletableFuture<>(); + + try { + if (request.getMessagesCount() <= 0) { + throw new GrpcProxyException(Code.MESSAGE_CORRUPTED, "no message to send"); + } + + List 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 buildMessage(ProxyContext context, List protoMessageList, Resource topic) { + String topicName = GrpcConverter.wrapResourceWithNamespace(topic); + List 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 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 buildMessageProperty(Message message, String producerGroup) { + org.apache.rocketmq.common.message.Message messageWithHeader = new org.apache.rocketmq.common.message.Message(); + // set user properties + Map 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 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 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 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; + } + } + } + +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/route/RouteActivity.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/route/RouteActivity.java new file mode 100644 index 0000000000..dd4f2471eb --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/route/RouteActivity.java @@ -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 queryRoute(Context ctx, QueryRouteRequest request) { + CompletableFuture future = new CompletableFuture<>(); + try { + ProxyContext context = createContext(ctx); + List addressList = this.convertToAddressList(request.getEndpoints()); + + ProxyTopicRouteData proxyTopicRouteData = this.messagingProcessor.getTopicRouteDataForProxy( + context, + addressList, + GrpcConverter.wrapResourceWithNamespace(request.getTopic())); + + List messageQueueList = new ArrayList<>(); + Map> brokerMap = buildBrokerMap(proxyTopicRouteData.getBrokerDatas()); + + for (QueueData queueData : proxyTopicRouteData.getQueueDatas()) { + String brokerName = queueData.getBrokerName(); + Map 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 queryAssignment(Context ctx, QueryAssignmentRequest request) { + CompletableFuture future = new CompletableFuture<>(); + + try { + ProxyContext context = createContext(ctx); + List addressList = this.convertToAddressList(request.getEndpoints()); + + ProxyTopicRouteData proxyTopicRouteData = this.messagingProcessor.getTopicRouteDataForProxy( + context, + addressList, + GrpcConverter.wrapResourceWithNamespace(request.getTopic())); + + List assignments = new ArrayList<>(); + Map> brokerMap = buildBrokerMap(proxyTopicRouteData.getBrokerDatas()); + for (QueueData queueData : proxyTopicRouteData.getQueueDatas()) { + Map brokerIdMap = brokerMap.get(queueData.getBrokerName()); + if (brokerIdMap != null) { + for (Map.Entry 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 convertToAddressList(Endpoints endpoints) { + List 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> buildBrokerMap( + List brokerDataList) { + Map> brokerMap = new HashMap<>(); + for (ProxyTopicRouteData.ProxyBrokerData brokerData : brokerDataList) { + Map brokerIdMap = new HashMap<>(); + String brokerName = brokerData.getBrokerName(); + for (Map.Entry> entry : brokerData.getBrokerAddrs().entrySet()) { + Long brokerId = entry.getKey(); + List
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 genMessageQueueFromQueueData(QueueData queueData, Resource topic, Broker broker) { + List 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; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/AbstractRouteService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/AbstractRouteService.java deleted file mode 100644 index 307d9cea42..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/AbstractRouteService.java +++ /dev/null @@ -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 queryRouteEndpointConverter; - protected volatile ResponseHook queryRouteHook; - - protected volatile ParameterConverter queryAssignmentEndpointConverter; - protected volatile ResponseHook 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 queryRoute(Context ctx, QueryRouteRequest request); - - public abstract CompletableFuture queryAssignment(Context ctx, QueryAssignmentRequest request); - - public ParameterConverter getQueryRouteEndpointConverter() { - return queryRouteEndpointConverter; - } - - public void setQueryRouteEndpointConverter( - ParameterConverter queryRouteEndpointConverter) { - this.queryRouteEndpointConverter = queryRouteEndpointConverter; - } - - public ResponseHook getQueryRouteHook() { - return queryRouteHook; - } - - public void setQueryRouteHook( - ResponseHook queryRouteHook) { - this.queryRouteHook = queryRouteHook; - } - - public ParameterConverter getQueryAssignmentEndpointConverter() { - return queryAssignmentEndpointConverter; - } - - public void setQueryAssignmentEndpointConverter( - ParameterConverter queryAssignmentEndpointConverter) { - this.queryAssignmentEndpointConverter = queryAssignmentEndpointConverter; - } - - public AssignmentQueueSelector getAssignmentQueueSelector() { - return assignmentQueueSelector; - } - - public void setAssignmentQueueSelector( - AssignmentQueueSelector assignmentQueueSelector) { - this.assignmentQueueSelector = assignmentQueueSelector; - } - - public ResponseHook getQueryAssignmentHook() { - return queryAssignmentHook; - } - - public void setQueryAssignmentHook( - ResponseHook queryAssignmentHook) { - this.queryAssignmentHook = queryAssignmentHook; - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/BaseReceiveMessageResponseStreamWriter.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/BaseReceiveMessageResponseStreamWriter.java deleted file mode 100644 index 1f1cadb746..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/BaseReceiveMessageResponseStreamWriter.java +++ /dev/null @@ -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 streamObserver; - protected final ResponseHook receiveMessageHook; - protected final ReceiveMessageResultFilter receiveMessageResultFilter; - - public interface Builder { - BaseReceiveMessageResponseStreamWriter build( - StreamObserver observer, - ResponseHook hook); - } - - public BaseReceiveMessageResponseStreamWriter( - StreamObserver observer, - ResponseHook hook, - ReceiveMessageResultFilter messageResultFilter) { - streamObserver = observer; - receiveMessageHook = hook; - receiveMessageResultFilter = messageResultFilter; - } - - public void write(Context ctx, ReceiveMessageRequest request, PopStatus status, List messageFoundList) { - ReceiveMessageResponseStreamObserver responseStreamObserver = new ReceiveMessageResponseStreamObserver( - ctx, - request, - receiveMessageHook, - streamObserver); - try { - switch (status) { - case FOUND: - List 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 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() - ); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/BaseReceiveMessageResultFilter.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/BaseReceiveMessageResultFilter.java deleted file mode 100644 index 350335f1fd..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/BaseReceiveMessageResultFilter.java +++ /dev/null @@ -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 filterMessage(Context ctx, ReceiveMessageRequest request, List 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 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); -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/BaseService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/BaseService.java deleted file mode 100644 index 8d2a923323..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/BaseService.java +++ /dev/null @@ -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 { - - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ClientSettingsService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ClientSettingsService.java deleted file mode 100644 index 40897b4ea6..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ClientSettingsService.java +++ /dev/null @@ -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 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(); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ClusterGrpcService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ClusterGrpcService.java deleted file mode 100644 index f47dcc9071..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ClusterGrpcService.java +++ /dev/null @@ -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 queryRoute(Context ctx, QueryRouteRequest request) { - return routeService.queryRoute(ctx, request); - } - - @Override - public CompletableFuture heartbeat(Context ctx, HeartbeatRequest request) { - return clientService.heartbeat(ctx, request); - } - - @Override - public CompletableFuture sendMessage(Context ctx, SendMessageRequest request) { - return producerService.sendMessage(ctx, request); - } - - @Override - public CompletableFuture queryAssignment(Context ctx, QueryAssignmentRequest request) { - return routeService.queryAssignment(ctx, request); - } - - @Override - public void receiveMessage(Context ctx, ReceiveMessageRequest request, - StreamObserver responseObserver) { - consumerService.receiveMessage(ctx, request, responseObserver); - } - - @Override - public CompletableFuture ackMessage(Context ctx, AckMessageRequest request) { - return consumerService.ackMessage(ctx, request); - } - - @Override - public CompletableFuture forwardMessageToDeadLetterQueue(Context ctx, - ForwardMessageToDeadLetterQueueRequest request) { - return producerService.forwardMessageToDeadLetterQueue(ctx, request); - } - - @Override - public CompletableFuture endTransaction(Context ctx, EndTransactionRequest request) { - return transactionService.endTransaction(ctx, request); - } - - @Override - public CompletableFuture notifyClientTermination(Context ctx, - NotifyClientTerminationRequest request) { - return clientService.notifyClientTermination(ctx, request); - } - - @Override - public CompletableFuture changeInvisibleDuration(Context ctx, - ChangeInvisibleDurationRequest request) { - return consumerService.changeInvisibleDuration(ctx, request); - } - - @Override - public StreamObserver telemetry(Context ctx, StreamObserver 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); - } - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/LocalGrpcService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/LocalGrpcService.java deleted file mode 100644 index dbcf3bd638..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/LocalGrpcService.java +++ /dev/null @@ -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 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 queryRoute(Context ctx, QueryRouteRequest request) { - return this.routeService.queryRoute(ctx, request); - } - - @Override - public CompletableFuture 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 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 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 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 future = new CompletableFuture<>(); - InvocationContext 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 queryAssignment(Context ctx, QueryAssignmentRequest request) { - return this.routeService.queryAssignment(ctx, request); - } - - @Override - public void receiveMessage(Context ctx, ReceiveMessageRequest request, StreamObserver 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> future = new CompletableFuture<>(); - InvocationContext> 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 ackMessage(Context ctx, AckMessageRequest request) { - Channel channel = channelManager.createChannel(ctx); - SimpleChannelHandlerContext channelHandlerContext = new SimpleChannelHandlerContext(channel); - CompletableFuture future = new CompletableFuture<>(); - List 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 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 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 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 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 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 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 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 telemetry(Context ctx, StreamObserver responseObserver) { - String clientId = InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.CLIENT_ID); - return new StreamObserver() { - @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()); - } - } - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ReceiveMessageResponseStreamObserver.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ReceiveMessageResponseStreamObserver.java deleted file mode 100644 index 16a2f29d43..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ReceiveMessageResponseStreamObserver.java +++ /dev/null @@ -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 { - - private final Context context; - private final ReceiveMessageRequest request; - private final ResponseHook receiveMessageHook; - private final StreamObserver observer; - - public ReceiveMessageResponseStreamObserver(Context context, ReceiveMessageRequest request, - ResponseHook receiveMessageHook, - StreamObserver 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); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ConsumerService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ConsumerService.java deleted file mode 100644 index 474ed29f15..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ConsumerService.java +++ /dev/null @@ -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 receiveMessageHook; - private volatile ResponseHook ackMessageHook; - private volatile ResponseHook 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 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 ackMessage(Context ctx, AckMessageRequest request) { - CompletableFuture future = new CompletableFuture<>(); - future.whenComplete((response, throwable) -> { - if (ackMessageHook != null) { - ackMessageHook.beforeResponse(ctx, request, response, throwable); - } - }); - - try { - CompletableFuture[] 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 entryList = new ArrayList<>(); - for (CompletableFuture 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 processAckMessage(Context ctx, AckMessageRequest request, - AckMessageEntry ackMessageEntry) { - CompletableFuture 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 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 changeInvisibleDuration(Context ctx, - ChangeInvisibleDurationRequest request) { - CompletableFuture 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 getReceiveMessageHook() { - return receiveMessageHook; - } - - public void setReceiveMessageHook( - ResponseHook receiveMessageHook) { - this.receiveMessageHook = receiveMessageHook; - } - - public ResponseHook getAckMessageHook() { - return ackMessageHook; - } - - public void setAckMessageHook( - ResponseHook ackMessageHook) { - this.ackMessageHook = ackMessageHook; - } - - public ResponseHook getChangeInvisibleDurationHook() { - return changeInvisibleDurationHook; - } - - public void setChangeInvisibleDurationHook( - ResponseHook changeInvisibleDurationHook) { - this.changeInvisibleDurationHook = changeInvisibleDurationHook; - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultAssignmentQueueSelector.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultAssignmentQueueSelector.java deleted file mode 100644 index a27ed2a74f..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultAssignmentQueueSelector.java +++ /dev/null @@ -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 getAssignment(Context ctx, QueryAssignmentRequest request) throws Exception { - String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic()); - MessageQueueWrapper messageQueueWrapper = topicRouteCache.getMessageQueue(topicName); - return messageQueueWrapper.getReadSelector().getBrokerActingQueues(); - } -} \ No newline at end of file diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReadQueueSelector.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReadQueueSelector.java deleted file mode 100644 index 97b878b871..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReadQueueSelector.java +++ /dev/null @@ -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; - } - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReceiveMessageResponseStreamWriter.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReceiveMessageResponseStreamWriter.java deleted file mode 100644 index 10dfda6fcc..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReceiveMessageResponseStreamWriter.java +++ /dev/null @@ -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 observer, - ResponseHook 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); - } - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReceiveMessageResultFilter.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReceiveMessageResultFilter.java deleted file mode 100644 index adf662e87b..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReceiveMessageResultFilter.java +++ /dev/null @@ -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 ackNoMatchedMessageHook; - private volatile ResponseHook 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 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 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 getAckNoMatchedMessageHook() { - return ackNoMatchedMessageHook; - } - - public void setAckNoMatchedMessageHook( - ResponseHook ackNoMatchedMessageHook) { - this.ackNoMatchedMessageHook = ackNoMatchedMessageHook; - } - - public ResponseHook getForwardToDLQInRecvMessageHook() { - return forwardToDLQInRecvMessageHook; - } - - public void setForwardToDLQInRecvMessageHook( - ResponseHook forwardToDLQInRecvMessageHook) { - this.forwardToDLQInRecvMessageHook = forwardToDLQInRecvMessageHook; - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultWriteQueueSelector.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultWriteQueueSelector.java deleted file mode 100644 index 0e11112912..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultWriteQueueSelector.java +++ /dev/null @@ -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); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ForwardClientService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ForwardClientService.java deleted file mode 100644 index d3898bee4d..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ForwardClientService.java +++ /dev/null @@ -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 heartbeat(Context ctx, HeartbeatRequest request) { - CompletableFuture 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 notifyClientTermination(Context ctx, NotifyClientTerminationRequest request) { - CompletableFuture 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 telemetry(Context ctx, StreamObserver responseObserver) { - return new StreamObserver() { - @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; - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ProducerService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ProducerService.java deleted file mode 100644 index 9fd4ea8e44..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ProducerService.java +++ /dev/null @@ -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 sendMessageHook; - private volatile ResponseHook forwardMessageToDLQHook; - - public ProducerService(ConnectorManager connectorManager) { - super(connectorManager); - this.producer = connectorManager.getForwardProducer(); - - this.writeQueueSelector = new DefaultWriteQueueSelector(this.connectorManager.getTopicRouteCache()); - } - - public CompletableFuture sendMessage(Context ctx, SendMessageRequest request) { - CompletableFuture 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 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 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 forwardMessageToDeadLetterQueue(Context ctx, - ForwardMessageToDeadLetterQueueRequest request) { - CompletableFuture 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 getSendMessageHook() { - return sendMessageHook; - } - - public void setSendMessageHook( - ResponseHook sendMessageHook) { - this.sendMessageHook = sendMessageHook; - } - - public ResponseHook getForwardMessageToDLQHook() { - return forwardMessageToDLQHook; - } - - public void setForwardMessageToDLQHook( - ResponseHook forwardMessageToDLQHook) { - this.forwardMessageToDLQHook = forwardMessageToDLQHook; - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/RouteService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/RouteService.java deleted file mode 100644 index b238335f2b..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/RouteService.java +++ /dev/null @@ -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 queryRoute(Context ctx, QueryRouteRequest request) { - CompletableFuture 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 queueDataList = topicRouteData.getQueueDatas(); - - List 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 queryAssignment(Context ctx, QueryAssignmentRequest request) { - CompletableFuture future = new CompletableFuture<>(); - future.whenComplete((response, throwable) -> { - if (queryAssignmentHook != null) { - queryAssignmentHook.beforeResponse(ctx, request, response, throwable); - } - }); - - try { - List assignments = new ArrayList<>(); - List 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; - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/TransactionService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/TransactionService.java deleted file mode 100644 index 9600d7acc2..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/TransactionService.java +++ /dev/null @@ -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 checkTransactionStateHook; - private volatile ResponseHook 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 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 endTransaction(Context ctx, EndTransactionRequest request) { - CompletableFuture 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 getCheckTransactionStateHook() { - return checkTransactionStateHook; - } - - public void setCheckTransactionStateHook( - ResponseHook checkTransactionStateHook) { - this.checkTransactionStateHook = checkTransactionStateHook; - } - - public ResponseHook getEndTransactionHook() { - return endTransactionHook; - } - - public void setEndTransactionHook( - ResponseHook endTransactionHook) { - this.endTransactionHook = endTransactionHook; - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriter.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriter.java deleted file mode 100644 index 0ec9913e22..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriter.java +++ /dev/null @@ -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 observer, - ResponseHook 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); - } - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResultFilter.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResultFilter.java deleted file mode 100644 index c69aab2567..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResultFilter.java +++ /dev/null @@ -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); - } - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalWriteQueueSelector.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalWriteQueueSelector.java deleted file mode 100644 index 8880b83e9c..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalWriteQueueSelector.java +++ /dev/null @@ -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); - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/RouteService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/RouteService.java deleted file mode 100644 index 7083f0684a..0000000000 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/local/RouteService.java +++ /dev/null @@ -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 queryRoute(Context ctx, QueryRouteRequest request) { - CompletableFuture 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 queueDataList = topicRouteData.getQueueDatas(); - List brokerDataList = topicRouteData.getBrokerDatas(); - - List messageQueueList = new ArrayList<>(); - Map> brokerMap = buildBrokerMap(brokerDataList); - - for (QueueData queueData : queueDataList) { - String brokerName = queueData.getBrokerName(); - Map 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 queryAssignment(Context ctx, QueryAssignmentRequest request) { - CompletableFuture future = new CompletableFuture<>(); - future.whenComplete((response, throwable) -> { - if (queryAssignmentHook != null) { - queryAssignmentHook.beforeResponse(ctx, request, response, throwable); - } - }); - - try { - List assignments = new ArrayList<>(); - List messageQueueList = this.assignmentQueueSelector.getAssignment(ctx, request); - String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic()); - MessageQueueWrapper messageQueueWrapper = this.connectorManager.getTopicRouteCache().getMessageQueue(topicName); - TopicRouteData topicRouteData = messageQueueWrapper.getTopicRouteData(); - Map> brokerMap = buildBrokerMap(topicRouteData.getBrokerDatas()); - for (SelectableMessageQueue messageQueue : messageQueueList) { - Map 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> buildBrokerMap(List brokerDataList) { - Map> brokerMap = new HashMap<>(); - for (BrokerData brokerData : brokerDataList) { - Map brokerIdMap = new HashMap<>(); - String brokerName = brokerData.getBrokerName(); - for (Map.Entry 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; - } -} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/transaction/EndTransactionActivity.java b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/transaction/EndTransactionActivity.java new file mode 100644 index 0000000000..35c6a02218 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/transaction/EndTransactionActivity.java @@ -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 endTransaction(Context ctx, EndTransactionRequest request) { + CompletableFuture 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; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/AbstractProcessor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/AbstractProcessor.java new file mode 100644 index 0000000000..bf47d2d116 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/AbstractProcessor.java @@ -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"); + } + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ClientProcessor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ClientProcessor.java new file mode 100644 index 0000000000..ea6f253f8e --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ClientProcessor.java @@ -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 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); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ConsumerProcessor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ConsumerProcessor.java new file mode 100644 index 0000000000..6794615544 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ConsumerProcessor.java @@ -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 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 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 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 ackMessage( + ProxyContext ctx, + ReceiptHandle handle, + String messageId, + String consumerGroup, + String topic, + long timeoutMillis + ) { + CompletableFuture 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 changeInvisibleTime(ProxyContext ctx, ReceiptHandle handle, + String messageId, String groupName, String topicName, long invisibleTime, long timeoutMillis) { + CompletableFuture 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; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/DefaultMessagingProcessor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/DefaultMessagingProcessor.java new file mode 100644 index 0000000000..e89330ed76 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/DefaultMessagingProcessor.java @@ -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
requestHostAndPortList, + String topicName) throws Exception { + return this.serviceManager.getTopicRouteService().getTopicRouteForProxy(requestHostAndPortList, topicName); + } + + @Override + public CompletableFuture sendMessage(ProxyContext ctx, QueueSelector queueSelector, + String producerGroup, List msg, long timeoutMillis) { + return this.producerProcessor.sendMessage(ctx, queueSelector, producerGroup, msg, timeoutMillis); + } + + @Override + public CompletableFuture 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 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 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 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 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(); + } + +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/MessagingProcessor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/MessagingProcessor.java new file mode 100644 index 0000000000..9fdb8afc4b --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/MessagingProcessor.java @@ -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
requestHostAndPortList, + String topicName + ) throws Exception; + + default CompletableFuture sendMessage( + ProxyContext ctx, + QueueSelector queueSelector, + String producerGroup, + List msg + ) { + return sendMessage(ctx, queueSelector, producerGroup, msg, DEFAULT_TIMEOUT_MILLS); + } + + CompletableFuture sendMessage( + ProxyContext ctx, + QueueSelector queueSelector, + String producerGroup, + List msg, + long timeoutMillis + ); + + default CompletableFuture forwardMessageToDeadLetterQueue( + ProxyContext ctx, + ReceiptHandle handle, + String messageId, + String groupName, + String topicName + ) { + return forwardMessageToDeadLetterQueue(ctx, handle, messageId, groupName, topicName, DEFAULT_TIMEOUT_MILLS); + } + + CompletableFuture 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 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 ackMessage( + ProxyContext ctx, + ReceiptHandle handle, + String messageId, + String consumerGroup, + String topic + ) { + return ackMessage(ctx, handle, messageId, consumerGroup, topic, DEFAULT_TIMEOUT_MILLS); + } + + CompletableFuture ackMessage( + ProxyContext ctx, + ReceiptHandle handle, + String messageId, + String consumerGroup, + String topic, + long timeoutMillis + ); + + default CompletableFuture 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 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 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(); +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ReadQueueSelector.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/PopMessageResultFilter.java similarity index 62% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ReadQueueSelector.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/processor/PopMessageResultFilter.java index 3eb936437a..328ae94e30 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ReadQueueSelector.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/PopMessageResultFilter.java @@ -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); } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ProducerProcessor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ProducerProcessor.java new file mode 100644 index 0000000000..dfd6395e59 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ProducerProcessor.java @@ -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 sendMessage(ProxyContext ctx, QueueSelector queueSelector, + String producerGroup, List messageExtList, long timeoutMillis) { + CompletableFuture 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 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 forwardMessageToDeadLetterQueue(ProxyContext ctx, ReceiptHandle handle, + String messageId, String groupName, String topicName, long timeoutMillis) { + CompletableFuture 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; + } + +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/AssignmentQueueSelector.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/QueueSelector.java similarity index 67% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/AssignmentQueueSelector.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/processor/QueueSelector.java index 5151225904..ae6d66d9bb 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/AssignmentQueueSelector.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/QueueSelector.java @@ -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 getAssignment(Context ctx, QueryAssignmentRequest request) throws Exception; + SelectableMessageQueue select(ProxyContext ctx, MessageQueueView messageQueueView); } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/TransactionProcessor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/TransactionProcessor.java new file mode 100644 index 0000000000..d548271f9d --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/TransactionProcessor.java @@ -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); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/TransactionStatus.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/TransactionStatus.java new file mode 100644 index 0000000000..e456a6061a --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/TransactionStatus.java @@ -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 +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/ClusterServiceManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/ClusterServiceManager.java new file mode 100644 index 0000000000..4a548883f2 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/ClusterServiceManager.java @@ -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); + } + } + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/LocalServiceManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/LocalServiceManager.java new file mode 100644 index 0000000000..97549a6eb0 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/LocalServiceManager.java @@ -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; + } + +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/ServiceManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/ServiceManager.java new file mode 100644 index 0000000000..820d99444b --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/ServiceManager.java @@ -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(); +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/AbstractMessageService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/AbstractMessageService.java new file mode 100644 index 0000000000..b7aa35ae84 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/AbstractMessageService.java @@ -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 processSendMessageResponseFuture( + String brokerName, + SendMessageRequestHeader requestHeader, + CompletableFuture 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; + }); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/ClusterMessageService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/ClusterMessageService.java new file mode 100644 index 0000000000..b48cade6da --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/ClusterMessageService.java @@ -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 sendMessage(ProxyContext ctx, SelectableMessageQueue messageQueue, + List msgList, SendMessageRequestHeader requestHeader, long timeoutMillis) { + CompletableFuture 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 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 popMessage(ProxyContext ctx, SelectableMessageQueue messageQueue, + PopMessageRequestHeader requestHeader, long timeoutMillis) { + return this.mqClientAPIFactory.getClient().popMessageAsync( + messageQueue.getBrokerAddr(), + messageQueue.getBrokerName(), + requestHeader, + timeoutMillis + ); + } + + @Override + public CompletableFuture 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 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); + } + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/LocalMessageService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/LocalMessageService.java new file mode 100644 index 0000000000..e8ff4a353c --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/LocalMessageService.java @@ -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 sendMessage(ProxyContext ctx, SelectableMessageQueue messageQueue, + List msgList, SendMessageRequestHeader requestHeader, long timeoutMillis) { + return null; + } + + @Override + public CompletableFuture 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 popMessage(ProxyContext ctx, SelectableMessageQueue messageQueue, + PopMessageRequestHeader requestHeader, long timeoutMillis) { + return null; + } + + @Override + public CompletableFuture changeInvisibleTime(ProxyContext ctx, ReceiptHandle handle, String messageId, + ChangeInvisibleTimeRequestHeader requestHeader, long timeoutMillis) { + return null; + } + + @Override public CompletableFuture ackMessage(ProxyContext ctx, ReceiptHandle handle, String messageId, + AckMessageRequestHeader requestHeader, long timeoutMillis) { + return null; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/MQClientFactory.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/LocalRemotingCommand.java similarity index 51% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/MQClientFactory.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/message/LocalRemotingCommand.java index 450c611bd7..8f833d4c99 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/factory/MQClientFactory.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/LocalRemotingCommand.java @@ -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 decodeCommandCustomHeader( + Class classHeader) throws RemotingCommandException { + return classHeader.cast(readCustomHeader()); } } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/MessageService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/MessageService.java new file mode 100644 index 0000000000..4d42c317cb --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/message/MessageService.java @@ -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 sendMessage( + ProxyContext ctx, + SelectableMessageQueue messageQueue, + List msgList, + SendMessageRequestHeader requestHeader, + long timeoutMillis + ); + + CompletableFuture 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 popMessage( + ProxyContext ctx, + SelectableMessageQueue messageQueue, + PopMessageRequestHeader requestHeader, + long timeoutMillis + ); + + CompletableFuture changeInvisibleTime( + ProxyContext ctx, + ReceiptHandle handle, + String messageId, + ChangeInvisibleTimeRequestHeader requestHeader, + long timeoutMillis + ); + + CompletableFuture ackMessage( + ProxyContext ctx, + ReceiptHandle handle, + String messageId, + AckMessageRequestHeader requestHeader, + long timeoutMillis + ); +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/processor/DoNothingClientRemotingProcessor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/DoNothingClientRemotingProcessor.java similarity index 96% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/processor/DoNothingClientRemotingProcessor.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/DoNothingClientRemotingProcessor.java index 05d2e19deb..5d2be52ab3 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/processor/DoNothingClientRemotingProcessor.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/DoNothingClientRemotingProcessor.java @@ -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; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/client/MQClientAPIExt.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/MQClientAPIExt.java similarity index 95% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/client/MQClientAPIExt.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/MQClientAPIExt.java index 47e204e579..8826c188a1 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/client/MQClientAPIExt.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/MQClientAPIExt.java @@ -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 sendHeartbeatAsync( String brokerAddr, HeartbeatData heartbeatData, @@ -164,7 +179,7 @@ public class MQClientAPIExt extends MQClientAPIImpl { public CompletableFuture sendMessageAsync( String brokerAddr, String brokerName, - List msgList, + List msgList, SendMessageRequestHeader requestHeader, long timeoutMillis ) { diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/MQClientAPIFactory.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/MQClientAPIFactory.java new file mode 100644 index 0000000000..b9e17d228b --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/MQClientAPIFactory.java @@ -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; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/processor/ProxyClientRemotingProcessor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/ProxyClientRemotingProcessor.java similarity index 59% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/processor/ProxyClientRemotingProcessor.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/ProxyClientRemotingProcessor.java index 3c03488916..ca3edf3ef7 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/processor/ProxyClientRemotingProcessor.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/mqclient/ProxyClientRemotingProcessor.java @@ -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; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/ClusterProxyOutService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/ClusterProxyOutService.java new file mode 100644 index 0000000000..4e4e6e36c6 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/ClusterProxyOutService.java @@ -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 processGetConsumerRunningInfo(RemotingCommand command, + GetConsumerRunningInfoRequestHeader header) { + return new CompletableFuture<>(); + } + + @Override + public CompletableFuture processConsumeMessageDirectly(RemotingCommand command, + ConsumeMessageDirectlyResultRequestHeader header) { + return new CompletableFuture<>(); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/LocalProxyOutService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/LocalProxyOutService.java new file mode 100644 index 0000000000..1b3d5baed8 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/LocalProxyOutService.java @@ -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 processGetConsumerRunningInfo(RemotingCommand command, + GetConsumerRunningInfoRequestHeader header) { + CompletableFuture 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 processConsumeMessageDirectly(RemotingCommand command, + ConsumeMessageDirectlyResultRequestHeader header) { + return null; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/ProxyChannel.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/ProxyChannel.java new file mode 100644 index 0000000000..a085013096 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/ProxyChannel.java @@ -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 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 processOtherMessage(Object msg); + + protected abstract CompletableFuture processCheckTransaction(CheckTransactionStateRequestHeader header, + MessageExt messageExt, TransactionId transactionId); + + protected abstract CompletableFuture processGetConsumerRunningInfo( + RemotingCommand command, + GetConsumerRunningInfoRequestHeader header, + CompletableFuture responseFuture); + + protected abstract CompletableFuture processConsumeMessageDirectly( + RemotingCommand command, + ConsumeMessageDirectlyResultRequestHeader header, + MessageExt messageExt, + CompletableFuture 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 { + + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/ProxyOutService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/ProxyOutService.java new file mode 100644 index 0000000000..923c410681 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/out/ProxyOutService.java @@ -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 processGetConsumerRunningInfo( + RemotingCommand command, + GetConsumerRunningInfoRequestHeader header + ); + + CompletableFuture processConsumeMessageDirectly( + RemotingCommand command, + ConsumeMessageDirectlyResultRequestHeader header + ); +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/ClusterTopicRouteService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/ClusterTopicRouteService.java new file mode 100644 index 0000000000..de058db13c --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/ClusterTopicRouteService.java @@ -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
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 brokerDataList = getAllMessageQueueView(brokerName).getTopicRouteData().getBrokerDatas(); + if (brokerDataList.isEmpty()) { + return null; + } + return brokerDataList.get(0).getBrokerAddrs().get(MixAll.MASTER_ID); + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/LocalTopicRouteService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/LocalTopicRouteService.java new file mode 100644 index 0000000000..87d9078997 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/LocalTopicRouteService.java @@ -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 brokerDataList; + private final int grpcPort; + + public LocalTopicRouteService(BrokerController brokerController, RPCHook rpcHook) { + super(rpcHook); + this.brokerController = brokerController; + BrokerConfig brokerConfig = this.brokerController.getBrokerConfig(); + HashMap 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
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; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/MessageQueueSelector.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/MessageQueueSelector.java similarity index 99% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/MessageQueueSelector.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/route/MessageQueueSelector.java index 6e40325ad7..f37155e319 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/MessageQueueSelector.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/MessageQueueSelector.java @@ -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; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/MessageQueueWrapper.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/MessageQueueView.java similarity index 85% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/MessageQueueWrapper.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/route/MessageQueueView.java index 7a4a8f9788..303dfe2a77 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/MessageQueueWrapper.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/MessageQueueView.java @@ -14,18 +14,18 @@ * 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 org.apache.rocketmq.common.protocol.route.TopicRouteData; -public class MessageQueueWrapper { - public static final MessageQueueWrapper WRAPPED_EMPTY_QUEUE = new MessageQueueWrapper("", new TopicRouteData()); +public class MessageQueueView { + public static final MessageQueueView WRAPPED_EMPTY_QUEUE = new MessageQueueView("", new TopicRouteData()); private final MessageQueueSelector readSelector; private final MessageQueueSelector writeSelector; private final TopicRouteWrapper topicRouteWrapper; - public MessageQueueWrapper(String topic, TopicRouteData topicRouteData) { + public MessageQueueView(String topic, TopicRouteData topicRouteData) { this.topicRouteWrapper = new TopicRouteWrapper(topicRouteData, topic); this.readSelector = new MessageQueueSelector(topicRouteWrapper, true); @@ -54,7 +54,7 @@ public class MessageQueueWrapper { @Override public String toString() { - return "MessageQueueWrapper{" + + return "MessageQueueView{" + "readSelector=" + readSelector + ", writeSelector=" + writeSelector + ", topicRouteWrapper=" + topicRouteWrapper + diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/ProxyTopicRouteData.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/ProxyTopicRouteData.java new file mode 100644 index 0000000000..40bd844f4e --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/ProxyTopicRouteData.java @@ -0,0 +1,77 @@ +/* + * 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.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.rocketmq.common.protocol.route.QueueData; +import org.apache.rocketmq.proxy.common.Address; + +public class ProxyTopicRouteData { + + public static class ProxyBrokerData { + private String cluster; + private String brokerName; + private Map/* broker address */> brokerAddrs = new HashMap<>(); + + public String getCluster() { + return cluster; + } + + public void setCluster(String cluster) { + this.cluster = cluster; + } + + public String getBrokerName() { + return brokerName; + } + + public void setBrokerName(String brokerName) { + this.brokerName = brokerName; + } + + public Map> getBrokerAddrs() { + return brokerAddrs; + } + + public void setBrokerAddrs(Map> brokerAddrs) { + this.brokerAddrs = brokerAddrs; + } + } + + private List queueDatas = new ArrayList<>(); + private List brokerDatas = new ArrayList<>(); + + public List getQueueDatas() { + return queueDatas; + } + + public void setQueueDatas(List queueDatas) { + this.queueDatas = queueDatas; + } + + public List getBrokerDatas() { + return brokerDatas; + } + + public void setBrokerDatas( + List brokerDatas) { + this.brokerDatas = brokerDatas; + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/SelectableMessageQueue.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/SelectableMessageQueue.java similarity index 97% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/SelectableMessageQueue.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/route/SelectableMessageQueue.java index 78a388214d..88446c7195 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/SelectableMessageQueue.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/SelectableMessageQueue.java @@ -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.Objects; import org.apache.rocketmq.common.message.MessageQueue; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/TopicRouteHelper.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/TopicRouteHelper.java similarity index 97% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/TopicRouteHelper.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/route/TopicRouteHelper.java index 97b345d703..2df41255df 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/TopicRouteHelper.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/TopicRouteHelper.java @@ -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 org.apache.rocketmq.client.common.ClientErrorCode; import org.apache.rocketmq.client.exception.MQBrokerException; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/TopicRouteCache.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/TopicRouteService.java similarity index 55% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/TopicRouteCache.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/route/TopicRouteService.java index ff6a653160..f1d3df58e0 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/TopicRouteCache.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/TopicRouteService.java @@ -14,40 +14,48 @@ * 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 com.google.common.cache.CacheBuilder; import com.google.common.cache.LoadingCache; -import com.google.common.hash.Hashing; +import java.time.Duration; import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.rocketmq.client.exception.MQClientException; -import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.ThreadFactoryImpl; import org.apache.rocketmq.common.constant.LoggerName; import org.apache.rocketmq.common.protocol.ResponseCode; -import org.apache.rocketmq.common.protocol.route.BrokerData; import org.apache.rocketmq.common.protocol.route.TopicRouteData; import org.apache.rocketmq.common.thread.ThreadPoolMonitor; import org.apache.rocketmq.logging.InternalLogger; import org.apache.rocketmq.logging.InternalLoggerFactory; import org.apache.rocketmq.proxy.common.AbstractCacheLoader; +import org.apache.rocketmq.proxy.common.AbstractStartAndShutdown; +import org.apache.rocketmq.proxy.common.Address; import org.apache.rocketmq.proxy.config.ConfigurationManager; import org.apache.rocketmq.proxy.config.ProxyConfig; -import org.apache.rocketmq.proxy.connector.DefaultForwardClient; +import org.apache.rocketmq.proxy.service.mqclient.DoNothingClientRemotingProcessor; +import org.apache.rocketmq.proxy.service.mqclient.MQClientAPIFactory; +import org.apache.rocketmq.remoting.RPCHook; -public class TopicRouteCache { +public abstract class TopicRouteService extends AbstractStartAndShutdown { private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME); - private final LoadingCache topicCache; + private final LoadingCache topicCache; + + private final MQClientAPIFactory mqClientAPIFactory; + private final ScheduledExecutorService scheduledExecutorService; private final ThreadPoolExecutor cacheRefreshExecutor; - private final DefaultForwardClient defaultClient; - - public TopicRouteCache(DefaultForwardClient defaultClient) { + public TopicRouteService(RPCHook rpcHook) { ProxyConfig config = ConfigurationManager.getProxyConfig(); - this.defaultClient = defaultClient; + this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor( + new ThreadFactoryImpl("TopicRouteService_") + ); this.cacheRefreshExecutor = ThreadPoolMonitor.createAndMonitor( config.getTopicRouteThreadPoolNums(), config.getTopicRouteThreadPoolNums(), @@ -56,55 +64,39 @@ public class TopicRouteCache { "TopicRouteCacheRefresh", config.getTopicRouteThreadPoolQueueCapacity() ); + this.mqClientAPIFactory = new MQClientAPIFactory( + "TopicRouteServiceClient_", + 1, + 1, + new DoNothingClientRemotingProcessor(null), + rpcHook, + this.scheduledExecutorService + ); this.topicCache = CacheBuilder.newBuilder() .maximumSize(config.getTopicRouteCacheMaxNum()) .refreshAfterWrite(config.getTopicRouteCacheExpiredInSeconds(), TimeUnit.SECONDS) .build(new TopicRouteCacheLoader()); + + this.init(); } - public MessageQueueWrapper getMessageQueue(String topicName) throws Exception { + protected void init() { + this.appendShutdown(this.scheduledExecutorService::shutdown); + this.appendStartAndShutdown(this.mqClientAPIFactory); + } + + public MessageQueueView getAllMessageQueueView(String topicName) throws Exception { return getCacheMessageQueueWrapper(this.topicCache, topicName); } - public String getBrokerAddr(String brokerName) throws Exception { - List brokerDataList = getMessageQueue(brokerName).getTopicRouteData().getBrokerDatas(); - if (brokerDataList.isEmpty()) { - return null; - } - return brokerDataList.get(0).getBrokerAddrs().get(MixAll.MASTER_ID); - } + public abstract MessageQueueView getCurrentMessageQueueView(String topicName) throws Exception; - public SelectableMessageQueue selectOneWriteQueue(String topic, SelectableMessageQueue last) throws Exception { - if (last == null) { - return getMessageQueue(topic).getWriteSelector().selectOne(false); - } - return getMessageQueue(topic).getWriteSelector().selectNextQueue(last); - } + public abstract ProxyTopicRouteData getTopicRouteForProxy(List
requestHostAndPortList, String topicName) throws Exception; - public SelectableMessageQueue selectOneWriteQueue(String topic, String brokerName, int queueId) throws Exception { - return getMessageQueue(topic).getWriteSelector() - .selectOne(brokerName, queueId); - } + public abstract String getBrokerAddr(String brokerName) throws Exception; - public SelectableMessageQueue selectOneWriteQueueByKey(String topic, String shardingKey) throws Exception { - List writeQueues = getMessageQueue(topic).getWriteSelector().getQueues(); - int bucket = Hashing.consistentHash(shardingKey.hashCode(), writeQueues.size()); - return writeQueues.get(bucket); - } - - public SelectableMessageQueue selectReadBrokerByName(String topic, String brokerName) throws Exception { - return getMessageQueue(topic).getReadSelector().getQueueByBrokerName(brokerName); - } - - public SelectableMessageQueue selectOneReadBroker(String topic, SelectableMessageQueue last) throws Exception { - if (last == null) { - return getMessageQueue(topic).getReadSelector().selectOne(true); - } - return getMessageQueue(topic).getReadSelector().selectNextQueue(last); - } - - protected static MessageQueueWrapper getCacheMessageQueueWrapper(LoadingCache topicCache, String key) throws Exception { - MessageQueueWrapper res = topicCache.get(key); + protected static MessageQueueView getCacheMessageQueueWrapper(LoadingCache topicCache, String key) throws Exception { + MessageQueueView res = topicCache.get(key); if (res.isEmptyCachedQueue()) { throw new MQClientException(ResponseCode.TOPIC_NOT_EXIST, "No topic route info in name server for the topic: " + key); @@ -117,30 +109,28 @@ public class TopicRouteCache { && routeData.getBrokerDatas() != null && !routeData.getBrokerDatas().isEmpty(); } - protected abstract class AbstractTopicRouteCacheLoader extends AbstractCacheLoader { + protected abstract class AbstractTopicRouteCacheLoader extends AbstractCacheLoader { public AbstractTopicRouteCacheLoader() { super(cacheRefreshExecutor); } - protected abstract String loaderName(); - protected abstract TopicRouteData loadTopicRouteData(String topic) throws Exception; @Override - public MessageQueueWrapper getDirectly(String topic) throws Exception { + public MessageQueueView getDirectly(String topic) throws Exception { try { TopicRouteData topicRouteData = loadTopicRouteData(topic); if (isTopicRouteValid(topicRouteData)) { - MessageQueueWrapper tmp = new MessageQueueWrapper(topic, topicRouteData); - log.info("load {} from namesrv. topic: {}, queue: {}", loaderName(), topic, tmp); + MessageQueueView tmp = new MessageQueueView(topic, topicRouteData); + log.info("load topic route from namesrv. topic: {}, queue: {}", topic, tmp); return tmp; } - return MessageQueueWrapper.WRAPPED_EMPTY_QUEUE; + return MessageQueueView.WRAPPED_EMPTY_QUEUE; } catch (Exception e) { if (TopicRouteHelper.isTopicNotExistError(e)) { - return MessageQueueWrapper.WRAPPED_EMPTY_QUEUE; + return MessageQueueView.WRAPPED_EMPTY_QUEUE; } throw e; } @@ -148,20 +138,15 @@ public class TopicRouteCache { @Override protected void onErr(String key, Exception e) { - log.error("load {} from namesrv failed. topic:{}", loaderName(), key, e); + log.error("load topic route from namesrv failed. topic:{}", key, e); } } protected class TopicRouteCacheLoader extends AbstractTopicRouteCacheLoader { - @Override - protected String loaderName() { - return "topicRoute"; - } - @Override protected TopicRouteData loadTopicRouteData(String topic) throws Exception { - return defaultClient.getTopicRouteInfoFromNameServer(topic); + return mqClientAPIFactory.getClient().getTopicRouteInfoFromNameServer(topic, Duration.ofSeconds(3).toMillis()); } } } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/TopicRouteWrapper.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/TopicRouteWrapper.java similarity index 98% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/TopicRouteWrapper.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/route/TopicRouteWrapper.java index 5b566dbc13..3950d92a1d 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/route/TopicRouteWrapper.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/route/TopicRouteWrapper.java @@ -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.HashMap; import java.util.List; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionHeartbeatRegisterService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/ClusterTransactionService.java similarity index 80% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionHeartbeatRegisterService.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/ClusterTransactionService.java index 92c7d63964..be5e3b0171 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionHeartbeatRegisterService.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/ClusterTransactionService.java @@ -14,10 +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.service.transaction; import com.google.common.collect.Sets; -import io.grpc.Context; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -26,9 +26,13 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import org.apache.rocketmq.broker.client.ProducerManager; import org.apache.rocketmq.common.ServiceThread; +import org.apache.rocketmq.common.ThreadFactoryImpl; import org.apache.rocketmq.common.constant.LoggerName; import org.apache.rocketmq.common.protocol.heartbeat.HeartbeatData; import org.apache.rocketmq.common.protocol.heartbeat.ProducerData; @@ -39,34 +43,47 @@ import org.apache.rocketmq.logging.InternalLoggerFactory; import org.apache.rocketmq.proxy.common.StartAndShutdown; import org.apache.rocketmq.proxy.config.ConfigurationManager; import org.apache.rocketmq.proxy.config.ProxyConfig; -import org.apache.rocketmq.proxy.connector.ForwardProducer; -import org.apache.rocketmq.proxy.connector.route.MessageQueueWrapper; -import org.apache.rocketmq.proxy.connector.route.TopicRouteCache; +import org.apache.rocketmq.proxy.service.mqclient.MQClientAPIFactory; +import org.apache.rocketmq.proxy.service.mqclient.ProxyClientRemotingProcessor; +import org.apache.rocketmq.proxy.service.route.MessageQueueView; +import org.apache.rocketmq.proxy.service.route.TopicRouteService; +import org.apache.rocketmq.remoting.RPCHook; -public class TransactionHeartbeatRegisterService implements StartAndShutdown { +public class ClusterTransactionService implements StartAndShutdown, TransactionService { private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME); private static final String TRANS_HEARTBEAT_CLIENT_ID = "rmq-proxy-producer-client"; - private final ForwardProducer forwardProducer; - private final TopicRouteCache topicRouteCache; + private final MQClientAPIFactory mqClientAPIFactory; + private final TopicRouteService topicRouteService; + private final ScheduledExecutorService scheduledExecutorService; private ThreadPoolExecutor heartbeatExecutors; private final Map/* cluster list */> groupClusterData = new ConcurrentHashMap<>(); private TxHeartbeatServiceThread txHeartbeatServiceThread; - public TransactionHeartbeatRegisterService(ForwardProducer forwardProducer, TopicRouteCache topicRouteCache) { - this.forwardProducer = forwardProducer; - this.topicRouteCache = topicRouteCache; + public ClusterTransactionService(TopicRouteService topicRouteService, ProducerManager producerManager, RPCHook rpcHook) { + this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor( + new ThreadFactoryImpl("ClusterTransactionScheduledThread_")); + this.topicRouteService = topicRouteService; + this.mqClientAPIFactory = new MQClientAPIFactory( + "ClusterTransaction_", + 1, + 1, + new ProxyClientRemotingProcessor(producerManager), + rpcHook, + scheduledExecutorService); } - public void addProducerGroup(String group, List topicList) { + @Override + public void addTransactionSubscription(String group, List topicList) { for (String topic : topicList) { - addProducerGroup(group, topic); + addTransactionSubscription(group, topic); } } - public void addProducerGroup(String group, String topic) { + @Override + public void addTransactionSubscription(String group, String topic) { try { groupClusterData.compute(group, (groupName, clusterDataSet) -> { if (clusterDataSet == null) { @@ -80,7 +97,8 @@ public class TransactionHeartbeatRegisterService implements StartAndShutdown { } } - public void replaceProducerGroup(String group, List topicList) { + @Override + public void replaceTransactionSubscription(String group, List topicList) { Set clusterDataSet = new HashSet<>(); for (String topic : topicList) { clusterDataSet.addAll(getClusterDataFromTopic(topic)); @@ -90,7 +108,7 @@ public class TransactionHeartbeatRegisterService implements StartAndShutdown { private Set getClusterDataFromTopic(String topic) { try { - MessageQueueWrapper messageQueue = this.topicRouteCache.getMessageQueue(topic); + MessageQueueView messageQueue = this.topicRouteService.getAllMessageQueueView(topic); List brokerDataList = messageQueue.getTopicRouteData().getBrokerDatas(); if (brokerDataList == null) { @@ -107,7 +125,8 @@ public class TransactionHeartbeatRegisterService implements StartAndShutdown { return Collections.emptySet(); } - public void onProducerGroupOffline(String group) { + @Override + public void unSubscribeAllTransactionTopic(String group) { groupClusterData.remove(group); } @@ -175,8 +194,7 @@ public class TransactionHeartbeatRegisterService implements StartAndShutdown { protected void sendHeartBeatToCluster(String clusterName, HeartbeatData heartbeatData) { try { - Context ctx = Context.current(); - MessageQueueWrapper messageQueue = this.topicRouteCache.getMessageQueue(clusterName); + MessageQueueView messageQueue = this.topicRouteService.getAllMessageQueueView(clusterName); List brokerDataList = messageQueue.getTopicRouteData().getBrokerDatas(); if (brokerDataList == null) { return; @@ -185,7 +203,7 @@ public class TransactionHeartbeatRegisterService implements StartAndShutdown { heartbeatExecutors.submit(() -> { String brokerAddr = brokerData.selectBrokerAddr(); try { - this.forwardProducer.heartBeat(ctx, brokerAddr, heartbeatData); + this.mqClientAPIFactory.getClient().sendHeartbeatOneway(brokerAddr, heartbeatData, Duration.ofSeconds(3).toMillis()); } catch (Exception e) { log.error("Send transactionHeartbeat to broker err. brokerAddr: {}", brokerAddr, e); } @@ -247,6 +265,7 @@ public class TransactionHeartbeatRegisterService implements StartAndShutdown { ProxyConfig proxyConfig = ConfigurationManager.getProxyConfig(); txHeartbeatServiceThread = new TxHeartbeatServiceThread(); + mqClientAPIFactory.start(); txHeartbeatServiceThread.start(); heartbeatExecutors = ThreadPoolMonitor.createAndMonitor( proxyConfig.getTransactionHeartbeatThreadPoolNums(), @@ -261,5 +280,6 @@ public class TransactionHeartbeatRegisterService implements StartAndShutdown { public void shutdown() throws Exception { txHeartbeatServiceThread.shutdown(); heartbeatExecutors.shutdown(); + mqClientAPIFactory.shutdown(); } } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/LocalTransactionService.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/LocalTransactionService.java new file mode 100644 index 0000000000..c465520a98 --- /dev/null +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/LocalTransactionService.java @@ -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.transaction; + +import java.util.List; + +public class LocalTransactionService implements TransactionService { + @Override public void addTransactionSubscription(String group, List topicList) { + + } + + @Override public void addTransactionSubscription(String group, String topic) { + + } + + @Override public void replaceTransactionSubscription(String group, List topicList) { + + } + + @Override public void unSubscribeAllTransactionTopic(String group) { + + } +} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionId.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/TransactionId.java similarity index 79% rename from proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionId.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/TransactionId.java index e0614c2636..3a22da078e 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/connector/transaction/TransactionId.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/TransactionId.java @@ -14,11 +14,8 @@ * 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.service.transaction; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -28,29 +25,27 @@ import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.common.UtilAll; 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.message.MessageId; import org.apache.rocketmq.logging.InternalLogger; import org.apache.rocketmq.logging.InternalLoggerFactory; -import org.apache.rocketmq.remoting.common.RemotingUtil; public class TransactionId { private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME); - private SocketAddress brokerAddr; + private String brokerName; private String brokerTransactionId; private long commitLogOffset; private long tranStateTableOffset; private String proxyTransactionId; public TransactionId( - SocketAddress brokerAddr, + String brokerName, String brokerTransactionId, long commitLogOffset, long tranStateTableOffset, String proxyTransactionId ) { - this.brokerAddr = brokerAddr; + this.brokerName = brokerName; this.brokerTransactionId = brokerTransactionId; this.commitLogOffset = commitLogOffset; this.tranStateTableOffset = tranStateTableOffset; @@ -60,7 +55,7 @@ public class TransactionId { public TransactionId() { } - public static TransactionId genByBrokerTransactionId(String brokerAddr, SendResult sendResult) { + public static TransactionId genByBrokerTransactionId(String brokerName, SendResult sendResult) { long commitLogOffset = 0L; try { if (sendResult.getOffsetMsgId() != null) { @@ -69,14 +64,14 @@ public class TransactionId { commitLogOffset = generateCommitLogOffset(sendResult.getMsgId()); } } catch (Exception e) { - log.warn("genFromBrokerTransactionId failed. brokerAddr: {}, sendResult: {}", brokerAddr, sendResult, e); + log.warn("genFromBrokerTransactionId failed. brokerName: {}, sendResult: {}", brokerName, sendResult, e); } - return genByBrokerTransactionId(RemotingUtil.string2SocketAddress(brokerAddr), sendResult.getTransactionId(), + return genByBrokerTransactionId(brokerName, sendResult.getTransactionId(), commitLogOffset, sendResult.getQueueOffset()); } public static TransactionId genByBrokerTransactionId( - SocketAddress brokerAddr, + String brokerName, String orgTransactionId, long commitLogOffset, long tranStateTableOffset @@ -85,10 +80,12 @@ public class TransactionId { if (StringUtils.isNotBlank(orgTransactionId)) { orgTransactionIdByte = orgTransactionId.getBytes(StandardCharsets.UTF_8); } + byte[] brokerNameByte = brokerName.getBytes(StandardCharsets.UTF_8); - ByteBuffer byteBuffer = ByteBuffer.allocate(8 + 4 + orgTransactionIdByte.length + 8 + 8); - byteBuffer.put(MessageExt.socketAddress2ByteBuffer(brokerAddr)); + ByteBuffer byteBuffer = ByteBuffer.allocate(4 + brokerNameByte.length + 4 + orgTransactionIdByte.length + 8 + 8); + byteBuffer.putInt(brokerNameByte.length); + byteBuffer.put(brokerNameByte); byteBuffer.putInt(orgTransactionIdByte.length); byteBuffer.put(orgTransactionIdByte); byteBuffer.putLong(commitLogOffset); @@ -97,7 +94,7 @@ public class TransactionId { String gatewayTransactionId = UtilAll.bytes2string(byteBuffer.array()); return TransactionId.builder() - .brokerAddr(brokerAddr) + .brokerName(brokerName) .brokerTransactionId(orgTransactionId) .commitLogOffset(commitLogOffset) .tranStateTableOffset(tranStateTableOffset) @@ -108,10 +105,9 @@ public class TransactionId { public static TransactionId decode(String transactionId) throws UnknownHostException { ByteBuffer byteBuffer = ByteBuffer.wrap(UtilAll.string2bytes(transactionId)); - byte[] ip = new byte[4]; - byteBuffer.get(ip); - int port = byteBuffer.getInt(); - SocketAddress brokerAddr = new InetSocketAddress(InetAddress.getByAddress(ip), port); + int brokerNameLen = byteBuffer.getInt(); + byte[] brokerNameByte = new byte[brokerNameLen]; + byteBuffer.get(brokerNameByte); int orgTransactionIdLen = byteBuffer.getInt(); byte[] orgTransactionIdByte = new byte[0]; @@ -124,7 +120,7 @@ public class TransactionId { long tranStateTableOffset = byteBuffer.getLong(); return TransactionId.builder() - .brokerAddr(brokerAddr) + .brokerName(new String(brokerNameByte, StandardCharsets.UTF_8)) .brokerTransactionId(new String(orgTransactionIdByte, StandardCharsets.UTF_8)) .commitLogOffset(commitLogOffset) .tranStateTableOffset(tranStateTableOffset) @@ -150,54 +146,56 @@ public class TransactionId { return false; } TransactionId id = (TransactionId) o; - return commitLogOffset == id.commitLogOffset && tranStateTableOffset == id.tranStateTableOffset && Objects.equals(brokerAddr, id.brokerAddr) && Objects.equals(brokerTransactionId, id.brokerTransactionId) && Objects.equals(proxyTransactionId, id.proxyTransactionId); + return commitLogOffset == id.commitLogOffset && tranStateTableOffset == id.tranStateTableOffset && + Objects.equals(brokerName, id.brokerName) && Objects.equals(brokerTransactionId, id.brokerTransactionId) && + Objects.equals(proxyTransactionId, id.proxyTransactionId); } @Override public int hashCode() { - return Objects.hash(brokerAddr, brokerTransactionId, commitLogOffset, tranStateTableOffset, proxyTransactionId); + return Objects.hash(brokerName, brokerTransactionId, commitLogOffset, tranStateTableOffset, proxyTransactionId); } public static TransactionIdBuilder builder() { return new TransactionIdBuilder(); } - public SocketAddress getBrokerAddr() { - return this.brokerAddr; + public String getBrokerName() { + return brokerName; + } + + public void setBrokerName(String brokerName) { + this.brokerName = brokerName; } public String getBrokerTransactionId() { - return this.brokerTransactionId; - } - - public long getCommitLogOffset() { - return this.commitLogOffset; - } - - public long getTranStateTableOffset() { - return this.tranStateTableOffset; - } - - public String getProxyTransactionId() { - return this.proxyTransactionId; - } - - public void setBrokerAddr(SocketAddress brokerAddr) { - this.brokerAddr = brokerAddr; + return brokerTransactionId; } public void setBrokerTransactionId(String brokerTransactionId) { this.brokerTransactionId = brokerTransactionId; } + public long getCommitLogOffset() { + return commitLogOffset; + } + public void setCommitLogOffset(long commitLogOffset) { this.commitLogOffset = commitLogOffset; } + public long getTranStateTableOffset() { + return tranStateTableOffset; + } + public void setTranStateTableOffset(long tranStateTableOffset) { this.tranStateTableOffset = tranStateTableOffset; } + public String getProxyTransactionId() { + return proxyTransactionId; + } + public void setProxyTransactionId(String proxyTransactionId) { this.proxyTransactionId = proxyTransactionId; } @@ -205,7 +203,7 @@ public class TransactionId { @Override public String toString() { return "TransactionId{" + - "brokerAddr=" + brokerAddr + + "brokerName=" + brokerName + ", brokerTransactionId='" + brokerTransactionId + '\'' + ", commitLogOffset=" + commitLogOffset + ", tranStateTableOffset=" + tranStateTableOffset + @@ -214,7 +212,7 @@ public class TransactionId { } public static class TransactionIdBuilder { - private SocketAddress brokerAddr; + private String brokerName; private String brokerTransactionId; private long commitLogOffset; private long tranStateTableOffset; @@ -223,8 +221,8 @@ public class TransactionId { TransactionIdBuilder() { } - public TransactionIdBuilder brokerAddr(SocketAddress brokerAddr) { - this.brokerAddr = brokerAddr; + public TransactionIdBuilder brokerName(String brokerName) { + this.brokerName = brokerName; return this; } @@ -249,13 +247,13 @@ public class TransactionId { } public TransactionId build() { - return new TransactionId(brokerAddr, brokerTransactionId, commitLogOffset, tranStateTableOffset, proxyTransactionId); + return new TransactionId(brokerName, brokerTransactionId, commitLogOffset, tranStateTableOffset, proxyTransactionId); } @Override public String toString() { return "TransactionId.TransactionIdBuilder{" + - "brokerAddr=" + brokerAddr + + "brokerName=" + brokerName + ", brokerTransactionId='" + brokerTransactionId + '\'' + ", commitLogOffset=" + commitLogOffset + ", tranStateTableOffset=" + tranStateTableOffset + diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ReceiveMessageResultFilter.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/TransactionService.java similarity index 69% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ReceiveMessageResultFilter.java rename to proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/TransactionService.java index 398dc5d790..75b83e98ac 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/service/ReceiveMessageResultFilter.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/transaction/TransactionService.java @@ -14,16 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.rocketmq.proxy.service.transaction; -package org.apache.rocketmq.proxy.grpc.v2.service; - -import apache.rocketmq.v2.Message; -import apache.rocketmq.v2.ReceiveMessageRequest; -import io.grpc.Context; import java.util.List; -import org.apache.rocketmq.common.message.MessageExt; -public interface ReceiveMessageResultFilter { +public interface TransactionService { - List filterMessage(Context ctx, ReceiveMessageRequest request, List messageExtList); + void addTransactionSubscription(String group, List topicList); + + void addTransactionSubscription(String group, String topic); + + void replaceTransactionSubscription(String group, List topicList); + + void unSubscribeAllTransactionTopic(String group); } diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationManagerTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationManagerTest.java index 9d3b572bf8..ca36f5f20f 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationManagerTest.java +++ b/proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationManagerTest.java @@ -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; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ForwardClientServiceTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ForwardClientServiceTest.java deleted file mode 100644 index 2cb06da6ab..0000000000 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ForwardClientServiceTest.java +++ /dev/null @@ -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.grpc.v2.service.cluster; - -import apache.rocketmq.v2.ClientType; -import apache.rocketmq.v2.FilterExpression; -import apache.rocketmq.v2.FilterType; -import apache.rocketmq.v2.HeartbeatRequest; -import apache.rocketmq.v2.NotifyClientTerminationRequest; -import apache.rocketmq.v2.Publishing; -import apache.rocketmq.v2.Resource; -import apache.rocketmq.v2.Settings; -import apache.rocketmq.v2.Subscription; -import apache.rocketmq.v2.SubscriptionEntry; -import io.grpc.Context; -import io.netty.channel.Channel; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executors; -import org.apache.rocketmq.broker.client.ClientChannelInfo; -import org.apache.rocketmq.broker.client.ConsumerGroupInfo; -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.proxy.channel.ChannelManager; -import org.apache.rocketmq.proxy.common.TelemetryCommandManager; -import org.apache.rocketmq.proxy.grpc.v2.adapter.channel.GrpcClientChannel; -import org.apache.rocketmq.remoting.protocol.LanguageCode; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; - -public class ForwardClientServiceTest extends BaseServiceTest { - - private ChannelManager channelManager = new ChannelManager(); - private TelemetryCommandManager telemetryCommandManager = new TelemetryCommandManager(); - private ForwardClientService clientService; - - @Override - public void beforeEach() throws Throwable { - clientService = new ForwardClientService( - this.connectorManager, - Executors.newSingleThreadScheduledExecutor(), - this.channelManager, - this.grpcClientManager, - this.telemetryCommandManager); - clientService.start(); - } - - @Test - public void testProducerHeartbeat() { - Settings clientSettings = Settings.newBuilder() - .setClientType(ClientType.PRODUCER) - .setPublishing(Publishing.newBuilder() - .addTopics(Resource.newBuilder() - .setName("topic1") - .build()) - .addTopics(Resource.newBuilder() - .setName("topic2") - .build()) - .build()) - .build(); - when(grpcClientManager.getClientSettings(anyString())).thenReturn(clientSettings); - - clientService.heartbeat(Context.current(), HeartbeatRequest.newBuilder().build()); - - assertEquals(2, clientService.getProducerManager().getGroupChannelTable().size()); - Channel channel = clientService.getProducerManager().findChannel(CLIENT_ID); - assertNotNull(channel); - assertTrue(channel instanceof GrpcClientChannel); - - clientService.notifyClientTermination(Context.current(), NotifyClientTerminationRequest.newBuilder().build()); - assertTrue(clientService.getProducerManager().getGroupChannelTable().isEmpty()); - } - - @Test - public void testConsumerHeartbeat() { - List subscriptionEntryList = new ArrayList<>(); - subscriptionEntryList.add(SubscriptionEntry.newBuilder() - .setTopic(Resource.newBuilder() - .setName("topic") - .build()) - .setExpression(FilterExpression.newBuilder() - .setExpression("*") - .setType(FilterType.TAG) - .build()) - .build()); - - Settings clientSettings = Settings.newBuilder() - .setClientType(ClientType.PUSH_CONSUMER) - .setSubscription(Subscription.newBuilder() - .addAllSubscriptions(subscriptionEntryList) - .build()) - .build(); - when(grpcClientManager.getClientSettings(anyString())).thenReturn(clientSettings); - - clientService.heartbeat(Context.current(), HeartbeatRequest.newBuilder() - .setGroup(Resource.newBuilder() - .setName("consumerGroup") - .build()) - .build()); - - ClientChannelInfo clientChannelInfo = clientService.getConsumerManager().findChannel("consumerGroup", CLIENT_ID); - assertNotNull(clientChannelInfo); - assertEquals(LanguageCode.JAVA, clientChannelInfo.getLanguage()); - assertEquals(CLIENT_ID, clientChannelInfo.getClientId()); - assertTrue(clientChannelInfo.getChannel() instanceof GrpcClientChannel); - ConsumerGroupInfo consumerGroupInfo = clientService.getConsumerManager().getConsumerGroupInfo("consumerGroup"); - assertEquals(MessageModel.CLUSTERING, consumerGroupInfo.getMessageModel()); - assertEquals(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET, consumerGroupInfo.getConsumeFromWhere()); - assertEquals(ConsumeType.CONSUME_PASSIVELY, consumerGroupInfo.getConsumeType()); - assertEquals("TAG", consumerGroupInfo.getSubscriptionTable().get("topic").getExpressionType()); - assertEquals("*", consumerGroupInfo.getSubscriptionTable().get("topic").getSubString()); - - - clientService.notifyClientTermination(Context.current(), NotifyClientTerminationRequest.newBuilder() - .setGroup(Resource.newBuilder() - .setName("consumerGroup") - .build()) - .build()); - assertNull(clientService.getConsumerManager().getConsumerGroupInfo("consumerGroup")); - } -} \ No newline at end of file diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/TransactionServiceTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/TransactionServiceTest.java deleted file mode 100644 index eafc3a4325..0000000000 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/TransactionServiceTest.java +++ /dev/null @@ -1,105 +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.RecoverOrphanedTransactionCommand; -import apache.rocketmq.v2.TelemetryCommand; -import io.grpc.Context; -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.transaction.TransactionId; -import org.apache.rocketmq.proxy.connector.transaction.TransactionStateCheckRequest; -import org.apache.rocketmq.proxy.grpc.v2.adapter.channel.GrpcClientChannel; -import org.apache.rocketmq.remoting.common.RemotingHelper; -import org.assertj.core.util.Lists; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class TransactionServiceTest extends BaseServiceTest { - - private TransactionService transactionService; - @Mock - private ChannelManager channelManager; - - @Override - public void beforeEach() throws Throwable { - transactionService = new TransactionService(this.connectorManager, this.channelManager); - } - - @Test - public void testCheckTransactionState() { - GrpcClientChannel channel = mock(GrpcClientChannel.class); - - when(channelManager.getClientIdList(anyString())).thenReturn(Lists.newArrayList("clientId")); - when(channelManager.getChannel(anyString(), any())).thenReturn(channel); - ArgumentCaptor flushDataCaptor = ArgumentCaptor.forClass(Object.class); - when(channel.writeAndFlush(flushDataCaptor.capture())).thenReturn(null); - - TransactionId transactionId = TransactionId.genByBrokerTransactionId( - RemotingHelper.string2SocketAddress("127.0.0.1:8080"), - "71F99B78B6E261357FA259CCA6456118", 1234, 5678); - transactionService.checkTransactionState(new TransactionStateCheckRequest( - "group", - 1L, - 2L, - "msgId", - transactionId, - "brokerName", - createMessageExt("msgId", "msgId") - )); - - Object flushData = flushDataCaptor.getValue(); - assertTrue(flushData instanceof TelemetryCommand); - TelemetryCommand response = (TelemetryCommand) flushData; - RecoverOrphanedTransactionCommand command = response.getRecoverOrphanedTransactionCommand(); - assertEquals(transactionId.getProxyTransactionId(), command.getTransactionId()); - assertEquals("brokerName", command.getMessageQueue().getBroker().getName()); - } - - @Test - public void testEndTransaction() throws Exception { - TransactionId transactionId = TransactionId.genByBrokerTransactionId( - RemotingHelper.string2SocketAddress("127.0.0.1:8080"), - "71F99B78B6E261357FA259CCA6456118", 1234, 5678); - ArgumentCaptor brokerAddrCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor headerCaptor = ArgumentCaptor.forClass(EndTransactionRequestHeader.class); - doNothing().when(producerClient) - .endTransaction(any(), brokerAddrCaptor.capture(), headerCaptor.capture()); - - EndTransactionResponse response = transactionService.endTransaction(Context.current(), EndTransactionRequest.newBuilder() - .setTransactionId(transactionId.getProxyTransactionId()) - .build() - ).get(); - - assertEquals(Code.OK, response.getStatus().getCode()); - assertEquals(transactionId.getBrokerTransactionId(), headerCaptor.getValue().getTransactionId()); - assertEquals("127.0.0.1:8080", brokerAddrCaptor.getValue()); - } -} \ No newline at end of file diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriterTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriterTest.java deleted file mode 100644 index a560673503..0000000000 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriterTest.java +++ /dev/null @@ -1,170 +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.ReceiveMessageRequest; -import apache.rocketmq.v2.ReceiveMessageResponse; -import io.grpc.Context; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; -import io.grpc.stub.ServerCallStreamObserver; -import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import org.apache.rocketmq.broker.BrokerController; -import org.apache.rocketmq.broker.processor.ChangeInvisibleTimeProcessor; -import org.apache.rocketmq.client.consumer.PopStatus; -import org.apache.rocketmq.common.consumer.ReceiptHandle; -import org.apache.rocketmq.common.message.MessageConst; -import org.apache.rocketmq.common.message.MessageExt; -import org.apache.rocketmq.proxy.channel.ChannelManager; -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.service.ReceiveMessageResultFilter; -import org.apache.rocketmq.remoting.exception.RemotingCommandException; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; - -import static org.assertj.core.api.Assertions.assertThat; - -@RunWith(MockitoJUnitRunner.class) -public class LocalReceiveMessageResponseStreamWriterTest { - @Mock - private ServerCallStreamObserver streamObserverMock; - @Mock - private ChannelManager channelManagerMock; - @Mock - private BrokerController brokerControllerMock; - @Mock - private ReceiveMessageResultFilter receiveMessageResultFilterMock; - @Mock - private ChangeInvisibleTimeProcessor changeInvisibleTimeProcessorMock; - - private LocalReceiveMessageResponseStreamWriter localReceiveMessageResponseStreamWriter; - - @Before - public void setup() { - Mockito.when(receiveMessageResultFilterMock.filterMessage(Mockito.any(), Mockito.any(), Mockito.anyList())).thenAnswer((Answer>) invocation -> { - List messageList = new ArrayList<>(); - List messageExtList = invocation.getArgument(2); - for (MessageExt messageExt : messageExtList) { - messageList.add(GrpcConverter.buildMessage(messageExt)); - } - return messageList; - }); - localReceiveMessageResponseStreamWriter - = new LocalReceiveMessageResponseStreamWriter(streamObserverMock, null, channelManagerMock, brokerControllerMock, receiveMessageResultFilterMock); - } - - @Test - public void testWrite() { - MessageExt messageExt = new MessageExt(); - messageExt.setTopic("topic"); - messageExt.setQueueOffset(0L); - messageExt.setBornHost(new InetSocketAddress("127.0.0.1", 10911)); - messageExt.setStoreHost(new InetSocketAddress("127.0.0.1", 10911)); - messageExt.setBody("body".getBytes(StandardCharsets.UTF_8)); - messageExt.putUserProperty("key", "value"); - messageExt.getProperties().put(MessageConst.PROPERTY_POP_CK, ReceiptHandle.builder() - .startOffset(0L) - .retrieveTime(0L) - .invisibleTime(1000L) - .nextVisibleTime(1000L) - .reviveQueueId(0) - .topicType("0") - .brokerName("brokerName") - .queueId(0) - .offset(0L) - .build().encode()); - messageExt.putUserProperty("key", "value"); - List messageExtList = new ArrayList<>(); - messageExtList.add(messageExt); - localReceiveMessageResponseStreamWriter.write(Context.current(), ReceiveMessageRequest.newBuilder().build(), PopStatus.FOUND, messageExtList); - ArgumentCaptor argument = ArgumentCaptor.forClass(ReceiveMessageResponse.class); - Mockito.verify(streamObserverMock, Mockito.times(2)).onNext(argument.capture()); - assertThat(argument.getAllValues().get(0)).isEqualTo(ReceiveMessageResponse.newBuilder() - .setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name())).build()); - assertThat(argument.getAllValues().get(1)).isEqualTo(ReceiveMessageResponse.newBuilder() - .setMessage(GrpcConverter.buildMessage(messageExt)).build()); - } - - @Test - public void testWriteWhenNoMessage() { - localReceiveMessageResponseStreamWriter.write(Context.current(), ReceiveMessageRequest.newBuilder().build(), PopStatus.FOUND, new ArrayList<>()); - Mockito.verify(streamObserverMock, Mockito.times(1)).onNext(Mockito.eq(ReceiveMessageResponse.newBuilder() - .setStatus(ResponseBuilder.buildStatus(Code.OK, "no new message")).build())); - } - - @Test - public void testWriteWhenCancel() throws RemotingCommandException { - AtomicInteger onNextCallTimes = new AtomicInteger(0); - Mockito.doAnswer(mock -> { - if (onNextCallTimes.get() <=0) { - onNextCallTimes.incrementAndGet(); - return null; - } - throw new StatusRuntimeException(Status.CANCELLED); - }).when(streamObserverMock).onNext(Mockito.any()); - Mockito.when(brokerControllerMock.getChangeInvisibleTimeProcessor()).thenReturn(changeInvisibleTimeProcessorMock); - MessageExt messageExt = new MessageExt(); - messageExt.setTopic("topic"); - messageExt.setQueueOffset(0L); - messageExt.setBornHost(new InetSocketAddress("127.0.0.1", 10911)); - messageExt.setStoreHost(new InetSocketAddress("127.0.0.1", 10911)); - messageExt.setBody("body".getBytes(StandardCharsets.UTF_8)); - messageExt.putUserProperty("key", "value"); - messageExt.getProperties().put(MessageConst.PROPERTY_POP_CK, ReceiptHandle.builder() - .startOffset(0L) - .retrieveTime(0L) - .invisibleTime(1000L) - .nextVisibleTime(1000L) - .reviveQueueId(0) - .topicType("0") - .brokerName("brokerName") - .queueId(0) - .offset(0L) - .build().encode()); - messageExt.putUserProperty("key", "value"); - List messageExtList = new ArrayList<>(); - messageExtList.add(messageExt); - localReceiveMessageResponseStreamWriter.write(Context.current(), ReceiveMessageRequest.newBuilder().build(), PopStatus.FOUND, messageExtList); - Mockito.verify(streamObserverMock, Mockito.times(1)).onNext(Mockito.eq(ReceiveMessageResponse.newBuilder() - .setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name())).build())); - Mockito.verify(changeInvisibleTimeProcessorMock, Mockito.times(1)).processRequest(Mockito.any(), Mockito.any()); - } - - @Test - public void testWriteError() { - String info = "error"; - localReceiveMessageResponseStreamWriter.write(Context.current(), ReceiveMessageRequest.newBuilder().build(), new ProxyException(Code.ILLEGAL_MESSAGE, info)); - Mockito.verify(streamObserverMock, Mockito.times(1)).onNext(Mockito.eq(ReceiveMessageResponse.newBuilder() - .setStatus(ResponseBuilder.buildStatus(Code.ILLEGAL_MESSAGE, info)).build())); - } -} \ No newline at end of file diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResultFilterTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResultFilterTest.java deleted file mode 100644 index 20dbce41a7..0000000000 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalReceiveMessageResultFilterTest.java +++ /dev/null @@ -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.grpc.v2.service.local; - -import apache.rocketmq.v2.FilterExpression; -import apache.rocketmq.v2.ReceiveMessageRequest; -import apache.rocketmq.v2.Settings; -import io.grpc.Context; -import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import org.apache.rocketmq.broker.BrokerController; -import org.apache.rocketmq.broker.processor.AckMessageProcessor; -import org.apache.rocketmq.broker.processor.SendMessageProcessor; -import org.apache.rocketmq.common.consumer.ReceiptHandle; -import org.apache.rocketmq.common.message.MessageConst; -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.proxy.channel.ChannelManager; -import org.apache.rocketmq.proxy.channel.SimpleChannel; -import org.apache.rocketmq.proxy.config.InitConfigAndLoggerTest; -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.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class LocalReceiveMessageResultFilterTest extends InitConfigAndLoggerTest { - @Mock - private ChannelManager channelManagerMock; - @Mock - private BrokerController brokerControllerMock; - @Mock - private GrpcClientManager grpcClientManagerMock; - @Mock - private AckMessageProcessor ackMessageProcessorMock; - @Mock - private SendMessageProcessor sendMessageProcessorMock; - - private String topic = "topic"; - - @Test - public void testFilterMessageWhenNotMatch() throws RemotingCommandException { - RemotingCommand remotingCommand = RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, ""); - Mockito.when(channelManagerMock.createChannel(Mockito.any(Context.class))).thenReturn(new SimpleChannel(null, null, null, 0)); - Mockito.when(brokerControllerMock.getAckMessageProcessor()).thenReturn(ackMessageProcessorMock); - Mockito.when(ackMessageProcessorMock.processRequest(Mockito.any(), Mockito.any())).thenReturn(remotingCommand); - Mockito.when(grpcClientManagerMock.getClientSettings(Mockito.any(Context.class))).thenReturn(Settings.newBuilder().build()); - List messageExtList = new ArrayList<>(); - MessageExt messageExt = new MessageExt(); - messageExt.setTopic(topic); - messageExt.setQueueOffset(0L); - messageExt.setBornHost(new InetSocketAddress("127.0.0.1", 10911)); - messageExt.setStoreHost(new InetSocketAddress("127.0.0.1", 10911)); - messageExt.setBody("body".getBytes(StandardCharsets.UTF_8)); - messageExt.putUserProperty("key", "value"); - messageExt.setTags("b"); - messageExt.getProperties().put(MessageConst.PROPERTY_POP_CK, ReceiptHandle.builder() - .startOffset(0L) - .retrieveTime(0L) - .invisibleTime(1000L) - .nextVisibleTime(1000L) - .reviveQueueId(0) - .topicType("0") - .brokerName("brokerName") - .queueId(0) - .offset(0L) - .build().encode()); - messageExtList.add(messageExt); - LocalReceiveMessageResultFilter localReceiveMessageResultFilter = new LocalReceiveMessageResultFilter(channelManagerMock, brokerControllerMock, grpcClientManagerMock); - localReceiveMessageResultFilter.filterMessage(Context.current(), ReceiveMessageRequest.newBuilder() - .setFilterExpression(FilterExpression.newBuilder() - .setExpression("a").build()).build(), messageExtList); - Mockito.verify(ackMessageProcessorMock, Mockito.times(1)).processRequest(Mockito.any(), Mockito.argThat(r -> r.getCode() == RequestCode.ACK_MESSAGE)); - } - - @Test - public void testFilterMessageWhenDLQ() throws RemotingCommandException { - RemotingCommand remotingCommand = RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, ""); - Mockito.when(channelManagerMock.createChannel(Mockito.any(Context.class))).thenReturn(new SimpleChannel(null, null, null, 0)); - Mockito.when(brokerControllerMock.getSendMessageProcessor()).thenReturn(sendMessageProcessorMock); - Mockito.when(sendMessageProcessorMock.processRequest(Mockito.any(), Mockito.any())).thenReturn(remotingCommand); - Mockito.when(brokerControllerMock.getAckMessageProcessor()).thenReturn(ackMessageProcessorMock); - Mockito.when(ackMessageProcessorMock.processRequest(Mockito.any(), Mockito.any())).thenReturn(remotingCommand); - Mockito.when(grpcClientManagerMock.getClientSettings(Mockito.any(Context.class))).thenReturn(Settings.newBuilder().build()); - List messageExtList = new ArrayList<>(); - MessageExt messageExt = new MessageExt(); - messageExt.setTopic(topic); - messageExt.setQueueOffset(0L); - messageExt.setBornHost(new InetSocketAddress("127.0.0.1", 10911)); - messageExt.setStoreHost(new InetSocketAddress("127.0.0.1", 10911)); - messageExt.setBody("body".getBytes(StandardCharsets.UTF_8)); - messageExt.putUserProperty("key", "value"); - messageExt.setTags("a"); - messageExt.getProperties().put(MessageConst.PROPERTY_POP_CK, ReceiptHandle.builder() - .startOffset(0L) - .retrieveTime(0L) - .invisibleTime(1000L) - .nextVisibleTime(1000L) - .reviveQueueId(0) - .topicType("0") - .brokerName("brokerName") - .queueId(0) - .offset(0L) - .build().encode()); - messageExtList.add(messageExt); - LocalReceiveMessageResultFilter localReceiveMessageResultFilter = new LocalReceiveMessageResultFilter(channelManagerMock, brokerControllerMock, grpcClientManagerMock); - localReceiveMessageResultFilter.filterMessage(Context.current(), ReceiveMessageRequest.newBuilder() - .setFilterExpression(FilterExpression.newBuilder() - .setExpression("a").build()).build(), messageExtList); - Mockito.verify(sendMessageProcessorMock, Mockito.times(1)).processRequest(Mockito.any(), Mockito.argThat(r -> r.getCode() == RequestCode.CONSUMER_SEND_MSG_BACK)); - Mockito.verify(ackMessageProcessorMock, Mockito.times(1)).processRequest(Mockito.any(), Mockito.argThat(r -> r.getCode() == RequestCode.ACK_MESSAGE)); - } -} \ No newline at end of file diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/processor/BaseProcessorTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/processor/BaseProcessorTest.java new file mode 100644 index 0000000000..152b66c82a --- /dev/null +++ b/proxy/src/test/java/org/apache/rocketmq/proxy/processor/BaseProcessorTest.java @@ -0,0 +1,103 @@ +/* + * 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.util.internal.MathUtil; +import java.nio.charset.StandardCharsets; +import java.util.Random; +import java.util.UUID; +import org.apache.rocketmq.broker.client.ConsumerManager; +import org.apache.rocketmq.broker.client.ProducerManager; +import org.apache.rocketmq.common.consumer.ReceiptHandle; +import org.apache.rocketmq.common.message.MessageAccessor; +import org.apache.rocketmq.common.message.MessageClientIDSetter; +import org.apache.rocketmq.common.message.MessageConst; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.protocol.header.ExtraInfoUtil; +import org.apache.rocketmq.proxy.common.ProxyContext; +import org.apache.rocketmq.proxy.config.InitConfigAndLoggerTest; +import org.apache.rocketmq.proxy.service.ServiceManager; +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.junit.Ignore; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.Mockito.when; + +@Ignore +@RunWith(MockitoJUnitRunner.Silent.class) +public class BaseProcessorTest extends InitConfigAndLoggerTest { + protected static final Random RANDOM = new Random(); + + @Mock + protected MessagingProcessor messagingProcessor; + @Mock + protected ServiceManager serviceManager; + @Mock + protected MessageService messageService; + @Mock + protected TopicRouteService topicRouteService; + @Mock + protected ProducerManager producerManager; + @Mock + protected ConsumerManager consumerManager; + @Mock + protected TransactionService transactionService; + @Mock + protected ProxyOutService proxyOutService; + @Mock + protected ProducerProcessor producerProcessor; + @Mock + protected ConsumerProcessor consumerProcessor; + @Mock + protected TransactionProcessor transactionProcessor; + @Mock + protected ClientProcessor clientProcessor; + + public void before() throws Throwable { + super.before(); + when(serviceManager.getMessageService()).thenReturn(messageService); + when(serviceManager.getTopicRouteService()).thenReturn(topicRouteService); + when(serviceManager.getProducerManager()).thenReturn(producerManager); + when(serviceManager.getConsumerManager()).thenReturn(consumerManager); + when(serviceManager.getTransactionService()).thenReturn(transactionService); + when(serviceManager.getProxyOutService()).thenReturn(proxyOutService); + } + + protected static ProxyContext createContext() { + return ProxyContext.create(); + } + + protected static MessageExt createMessageExt(String topic, String tags, int reconsumeTimes, long invisibleTime) { + MessageExt messageExt = new MessageExt(); + messageExt.setTopic(topic); + messageExt.setTags(tags); + messageExt.setReconsumeTimes(reconsumeTimes); + messageExt.setBody(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); + messageExt.setMsgId(MessageClientIDSetter.createUniqID()); + messageExt.setCommitLogOffset(RANDOM.nextInt()); + MessageAccessor.putProperty(messageExt, MessageConst.PROPERTY_POP_CK, + ExtraInfoUtil.buildExtraInfo(RANDOM.nextInt(), System.currentTimeMillis(), invisibleTime, + RANDOM.nextInt(), topic, "mockBroker", RANDOM.nextInt(), RANDOM.nextInt())); + return messageExt; + } +} diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/processor/ConsumerProcessorTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/processor/ConsumerProcessorTest.java new file mode 100644 index 0000000000..f2547a8b78 --- /dev/null +++ b/proxy/src/test/java/org/apache/rocketmq/proxy/processor/ConsumerProcessorTest.java @@ -0,0 +1,174 @@ +/* + * 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.time.Duration; +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.client.consumer.PopResult; +import org.apache.rocketmq.client.consumer.PopStatus; +import org.apache.rocketmq.common.KeyBuilder; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.constant.ConsumeInitMode; +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.MessageClientIDSetter; +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.proxy.common.utils.ProxyUtils; +import org.apache.rocketmq.proxy.service.route.MessageQueueView; +import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; +import org.apache.rocketmq.remoting.protocol.RemotingCommand; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ConsumerProcessorTest extends BaseProcessorTest { + + private static final String CONSUMER_GROUP = "consumerGroup"; + private static final String TOPIC = "topic"; + + private ConsumerProcessor consumerProcessor; + + @Before + public void before() throws Throwable { + super.before(); + this.consumerProcessor = new ConsumerProcessor(this.messagingProcessor, this.serviceManager); + } + + @Test + public void testPopMessage() throws Throwable { + final String tag = "tag"; + final long invisibleTime = Duration.ofSeconds(15).toMillis(); + ArgumentCaptor messageQueueArgumentCaptor = ArgumentCaptor.forClass(SelectableMessageQueue.class); + ArgumentCaptor requestHeaderArgumentCaptor = ArgumentCaptor.forClass(PopMessageRequestHeader.class); + + List messageExtList = new ArrayList<>(); + messageExtList.add(createMessageExt(TOPIC, "noMatch", 0, invisibleTime)); + messageExtList.add(createMessageExt(TOPIC, tag, 0, invisibleTime)); + messageExtList.add(createMessageExt(TOPIC, tag, 1, invisibleTime)); + PopResult innerPopResult = new PopResult(PopStatus.FOUND, messageExtList); + when(this.messageService.popMessage(any(), messageQueueArgumentCaptor.capture(), requestHeaderArgumentCaptor.capture(), anyLong())) + .thenReturn(CompletableFuture.completedFuture(innerPopResult)); + + when(this.topicRouteService.getCurrentMessageQueueView(anyString())) + .thenReturn(mock(MessageQueueView.class)); + + ArgumentCaptor ackMessageIdArgumentCaptor = ArgumentCaptor.forClass(String.class); + when(this.messagingProcessor.ackMessage(any(), any(), ackMessageIdArgumentCaptor.capture(), anyString(), anyString(), anyLong())) + .thenReturn(CompletableFuture.completedFuture(mock(AckResult.class))); + + ArgumentCaptor toDLQMessageIdArgumentCaptor = ArgumentCaptor.forClass(String.class); + when(this.messagingProcessor.forwardMessageToDeadLetterQueue(any(), any(), toDLQMessageIdArgumentCaptor.capture(), anyString(), anyString(), anyLong())) + .thenReturn(CompletableFuture.completedFuture(mock(RemotingCommand.class))); + + SelectableMessageQueue messageQueue = mock(SelectableMessageQueue.class); + PopResult popResult = this.consumerProcessor.popMessage( + createContext(), + (ctx, messageQueueView) -> messageQueue, + CONSUMER_GROUP, + TOPIC, + 60, + invisibleTime, + Duration.ofSeconds(3).toMillis(), + ConsumeInitMode.MAX, + FilterAPI.build(TOPIC, tag, ExpressionType.TAG), + false, + (ctx, consumerGroup, subscriptionData, messageExt) -> { + if (!messageExt.getTags().equals(tag)) { + return PopMessageResultFilter.FilterResult.NO_MATCH; + } + if (messageExt.getReconsumeTimes() > 0) { + return PopMessageResultFilter.FilterResult.TO_DLQ; + } + return PopMessageResultFilter.FilterResult.MATCH; + }, + Duration.ofSeconds(3).toMillis() + ).get(); + + assertSame(messageQueue, messageQueueArgumentCaptor.getValue()); + assertEquals(CONSUMER_GROUP, requestHeaderArgumentCaptor.getValue().getConsumerGroup()); + assertEquals(TOPIC, requestHeaderArgumentCaptor.getValue().getTopic()); + assertEquals(ProxyUtils.MAX_MSG_NUMS_FOR_POP_REQUEST, requestHeaderArgumentCaptor.getValue().getMaxMsgNums()); + assertEquals(tag, requestHeaderArgumentCaptor.getValue().getExp()); + assertEquals(ExpressionType.TAG, requestHeaderArgumentCaptor.getValue().getExpType()); + + assertEquals(PopStatus.FOUND, popResult.getPopStatus()); + assertEquals(1, popResult.getMsgFoundList().size()); + assertEquals(messageExtList.get(1), popResult.getMsgFoundList().get(0)); + + assertEquals(messageExtList.get(0).getMsgId(), ackMessageIdArgumentCaptor.getValue()); + assertEquals(messageExtList.get(2).getMsgId(), toDLQMessageIdArgumentCaptor.getValue()); + } + + @Test + public void testAckMessage() throws Throwable { + ReceiptHandle handle = ReceiptHandle.create(createMessageExt(MixAll.RETRY_GROUP_TOPIC_PREFIX + TOPIC, "", 0, 3000)); + assertNotNull(handle); + + ArgumentCaptor requestHeaderArgumentCaptor = ArgumentCaptor.forClass(AckMessageRequestHeader.class); + AckResult innerAckResult = new AckResult(); + innerAckResult.setStatus(AckStatus.OK); + when(this.messageService.ackMessage(any(), any(), anyString(), requestHeaderArgumentCaptor.capture(), anyLong())) + .thenReturn(CompletableFuture.completedFuture(innerAckResult)); + + AckResult ackResult = this.consumerProcessor.ackMessage(createContext(), handle, MessageClientIDSetter.createUniqID(), + CONSUMER_GROUP, TOPIC, 3000).get(); + + assertEquals(AckStatus.OK, ackResult.getStatus()); + assertEquals(KeyBuilder.buildPopRetryTopic(TOPIC, CONSUMER_GROUP), requestHeaderArgumentCaptor.getValue().getTopic()); + assertEquals(CONSUMER_GROUP, requestHeaderArgumentCaptor.getValue().getConsumerGroup()); + assertEquals(handle.getReceiptHandle(), requestHeaderArgumentCaptor.getValue().getExtraInfo()); + } + + @Test + public void testChangeInvisibleTime() throws Throwable { + ReceiptHandle handle = ReceiptHandle.create(createMessageExt(MixAll.RETRY_GROUP_TOPIC_PREFIX + TOPIC, "", 0, 3000)); + assertNotNull(handle); + + ArgumentCaptor requestHeaderArgumentCaptor = ArgumentCaptor.forClass(ChangeInvisibleTimeRequestHeader.class); + AckResult innerAckResult = new AckResult(); + innerAckResult.setStatus(AckStatus.OK); + when(this.messageService.changeInvisibleTime(any(), any(), anyString(), requestHeaderArgumentCaptor.capture(), anyLong())) + .thenReturn(CompletableFuture.completedFuture(innerAckResult)); + + AckResult ackResult = this.consumerProcessor.changeInvisibleTime(createContext(), handle, MessageClientIDSetter.createUniqID(), + CONSUMER_GROUP, TOPIC, 1000, 3000).get(); + + assertEquals(AckStatus.OK, ackResult.getStatus()); + assertEquals(KeyBuilder.buildPopRetryTopic(TOPIC, CONSUMER_GROUP), requestHeaderArgumentCaptor.getValue().getTopic()); + assertEquals(CONSUMER_GROUP, requestHeaderArgumentCaptor.getValue().getConsumerGroup()); + assertEquals(1000, requestHeaderArgumentCaptor.getValue().getInvisibleTime().longValue()); + assertEquals(handle.getReceiptHandle(), requestHeaderArgumentCaptor.getValue().getExtraInfo()); + } +} \ No newline at end of file diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/processor/ProducerProcessorTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/processor/ProducerProcessorTest.java new file mode 100644 index 0000000000..6d4e45c6ce --- /dev/null +++ b/proxy/src/test/java/org/apache/rocketmq/proxy/processor/ProducerProcessorTest.java @@ -0,0 +1,24 @@ +/* + * 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 static org.junit.Assert.*; + +public class ProducerProcessorTest extends BaseProcessorTest { + +} \ No newline at end of file diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/connector/transaction/TransactionIdTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/service/transaction/TransactionIdTest.java similarity index 81% rename from proxy/src/test/java/org/apache/rocketmq/proxy/connector/transaction/TransactionIdTest.java rename to proxy/src/test/java/org/apache/rocketmq/proxy/service/transaction/TransactionIdTest.java index 8636cf5954..9e1887918a 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/connector/transaction/TransactionIdTest.java +++ b/proxy/src/test/java/org/apache/rocketmq/proxy/service/transaction/TransactionIdTest.java @@ -14,26 +14,27 @@ * 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.service.transaction; import java.net.UnknownHostException; -import org.apache.rocketmq.remoting.common.RemotingHelper; import org.junit.Test; import static org.junit.Assert.assertEquals; public class TransactionIdTest { + private static final String BROKER_NAME = "brokerName"; + @Test public void test() throws UnknownHostException { TransactionId transactionId = TransactionId.genByBrokerTransactionId( - RemotingHelper.string2SocketAddress("127.0.0.1:8080"), + BROKER_NAME, "71F99B78B6E261357FA259CCA6456118", 1234, 5678); TransactionId decodeTransactionId = TransactionId.decode(transactionId.getProxyTransactionId()); assertEquals(transactionId.getBrokerTransactionId(), decodeTransactionId.getBrokerTransactionId()); - assertEquals(transactionId.getBrokerAddr().toString(), decodeTransactionId.getBrokerAddr().toString()); + assertEquals(transactionId.getBrokerName(), decodeTransactionId.getBrokerName()); assertEquals(transactionId.getCommitLogOffset(), decodeTransactionId.getCommitLogOffset()); assertEquals(transactionId.getTranStateTableOffset(), decodeTransactionId.getTranStateTableOffset()); } @@ -41,13 +42,13 @@ public class TransactionIdTest { @Test public void testEmptyTransactionId() throws UnknownHostException { TransactionId transactionId = TransactionId.genByBrokerTransactionId( - RemotingHelper.string2SocketAddress("127.0.0.1:8080"), + BROKER_NAME, "", 1234, 5678); TransactionId decodeTransactionId = TransactionId.decode(transactionId.getProxyTransactionId()); assertEquals(transactionId.getBrokerTransactionId(), decodeTransactionId.getBrokerTransactionId()); - assertEquals(transactionId.getBrokerAddr().toString(), decodeTransactionId.getBrokerAddr().toString()); + assertEquals(transactionId.getBrokerName(), decodeTransactionId.getBrokerName()); assertEquals(transactionId.getCommitLogOffset(), decodeTransactionId.getCommitLogOffset()); assertEquals(transactionId.getTranStateTableOffset(), decodeTransactionId.getTranStateTableOffset()); } @@ -55,13 +56,13 @@ public class TransactionIdTest { @Test public void testNullTransactionId() throws UnknownHostException { TransactionId transactionId = TransactionId.genByBrokerTransactionId( - RemotingHelper.string2SocketAddress("127.0.0.1:8080"), + BROKER_NAME, null, 1234, 5678); TransactionId decodeTransactionId = TransactionId.decode(transactionId.getProxyTransactionId()); assertEquals("", decodeTransactionId.getBrokerTransactionId()); - assertEquals(transactionId.getBrokerAddr().toString(), decodeTransactionId.getBrokerAddr().toString()); + assertEquals(transactionId.getBrokerName(), decodeTransactionId.getBrokerName()); assertEquals(transactionId.getCommitLogOffset(), decodeTransactionId.getCommitLogOffset()); assertEquals(transactionId.getTranStateTableOffset(), decodeTransactionId.getTranStateTableOffset()); } diff --git a/proxy/src/test/resources/backup/channel/InvocationChannel.java b/proxy/src/test/resources/backup/channel/InvocationChannel.java new file mode 100644 index 0000000000..4d537f740b --- /dev/null +++ b/proxy/src/test/resources/backup/channel/InvocationChannel.java @@ -0,0 +1,83 @@ +///* +// * 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 extends SimpleChannel implements Cleaner { +// protected final ConcurrentMap> inFlightRequestMap; +// protected final ResponseHandler handler; +// +// public InvocationChannel(Context ctx, ResponseHandler 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 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 context) { +// inFlightRequestMap.put(opaque, context); +// } +// +// public void eraseInvocationContext(int opaque) { +// inFlightRequestMap.remove(opaque); +// } +// +// @Override +// public void clean() { +// Iterator>> iterator = inFlightRequestMap.entrySet().iterator(); +// int count = 0; +// while (iterator.hasNext()) { +// Map.Entry> 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); +// } +// } +//} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/channel/InvocationContext.java b/proxy/src/test/resources/backup/channel/InvocationContext.java similarity index 100% rename from proxy/src/main/java/org/apache/rocketmq/proxy/channel/InvocationContext.java rename to proxy/src/test/resources/backup/channel/InvocationContext.java diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/channel/SimpleChannel.java b/proxy/src/test/resources/backup/channel/SimpleChannel.java similarity index 99% rename from proxy/src/main/java/org/apache/rocketmq/proxy/channel/SimpleChannel.java rename to proxy/src/test/resources/backup/channel/SimpleChannel.java index cd3a5a9bed..da9c7fb47e 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/channel/SimpleChannel.java +++ b/proxy/src/test/resources/backup/channel/SimpleChannel.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.rocketmq.proxy.channel; +package backup.channel; import com.google.common.base.Strings; import io.netty.channel.AbstractChannel; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/channel/SimpleChannelHandlerContext.java b/proxy/src/test/resources/backup/channel/SimpleChannelHandlerContext.java similarity index 99% rename from proxy/src/main/java/org/apache/rocketmq/proxy/channel/SimpleChannelHandlerContext.java rename to proxy/src/test/resources/backup/channel/SimpleChannelHandlerContext.java index a78c5727f7..1811996a94 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/channel/SimpleChannelHandlerContext.java +++ b/proxy/src/test/resources/backup/channel/SimpleChannelHandlerContext.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.rocketmq.proxy.channel; +package backup.channel; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.Channel; diff --git a/proxy/src/test/resources/backup/grpc/v2/adapter/GrpcConverter.java b/proxy/src/test/resources/backup/grpc/v2/adapter/GrpcConverter.java new file mode 100644 index 0000000000..e21753dcd2 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/adapter/GrpcConverter.java @@ -0,0 +1,733 @@ +///* +// * 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.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.service.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 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 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 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 GrpcProxyException(Code.MESSAGE_CORRUPTED, "no message to send"); +// } +// if (request.getMessagesCount() > 1) { +// requestHeader.setBatch(true); +// } +// Message message = request.getMessages(0); +// SystemProperties systemProperties = message.getSystemProperties(); +// +// Map 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 GrpcProxyException(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 buildMessageProperty(Message message, String producerGroup) { +// org.apache.rocketmq.common.message.Message messageWithHeader = new org.apache.rocketmq.common.message.Message(); +// // set user properties +// Map 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 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(); +// } +// +// public static List buildMessage(List protoMessageList, +// Resource topic) { +// String topicName = wrapResourceWithNamespace(topic); +// List messages = new ArrayList<>(); +// for (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. +// 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 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 buildSubscriptionDataSet(List subscriptionEntryList) { +// Set 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 GrpcProxyException(Code.ILLEGAL_FILTER_EXPRESSION, "expression format is not correct", e); +// } +// } +// +// public static Message buildMessage(MessageExt messageExt) { +// Map 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 buildUserAttributes(MessageExt messageExt) { +// Map userAttributes = new HashMap<>(); +// Map properties = messageExt.getProperties(); +// +// for (Map.Entry 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 genMessageQueueFromQueueData(QueueData queueData, Resource topic, Broker broker) { +// List 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; +// } +// +//} diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ProxyException.java b/proxy/src/test/resources/backup/grpc/v2/adapter/ProxyException.java similarity index 100% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ProxyException.java rename to proxy/src/test/resources/backup/grpc/v2/adapter/ProxyException.java diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ProxyMode.java b/proxy/src/test/resources/backup/grpc/v2/adapter/ProxyMode.java similarity index 100% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ProxyMode.java rename to proxy/src/test/resources/backup/grpc/v2/adapter/ProxyMode.java diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/RequestMapping.java b/proxy/src/test/resources/backup/grpc/v2/adapter/RequestMapping.java similarity index 100% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/RequestMapping.java rename to proxy/src/test/resources/backup/grpc/v2/adapter/RequestMapping.java diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ResponseHook.java b/proxy/src/test/resources/backup/grpc/v2/adapter/ResponseHook.java similarity index 87% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ResponseHook.java rename to proxy/src/test/resources/backup/grpc/v2/adapter/ResponseHook.java index 45fe954bda..d01abaf2ad 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/ResponseHook.java +++ b/proxy/src/test/resources/backup/grpc/v2/adapter/ResponseHook.java @@ -16,9 +16,9 @@ */ package org.apache.rocketmq.proxy.grpc.v2.adapter; -import io.grpc.Context; +import org.apache.rocketmq.proxy.common.ProxyContext; public interface ResponseHook { - void beforeResponse(Context ctx, T request, R response, Throwable t); + void beforeResponse(ProxyContext ctx, T request, R response, Throwable t); } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/channel/ReceiveMessageChannel.java b/proxy/src/test/resources/backup/grpc/v2/adapter/channel/ReceiveMessageChannel.java similarity index 100% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/channel/ReceiveMessageChannel.java rename to proxy/src/test/resources/backup/grpc/v2/adapter/channel/ReceiveMessageChannel.java diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/channel/SendMessageChannel.java b/proxy/src/test/resources/backup/grpc/v2/adapter/channel/SendMessageChannel.java similarity index 100% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/channel/SendMessageChannel.java rename to proxy/src/test/resources/backup/grpc/v2/adapter/channel/SendMessageChannel.java diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/handler/ReceiveMessageResponseHandler.java b/proxy/src/test/resources/backup/grpc/v2/adapter/handler/ReceiveMessageResponseHandler.java similarity index 100% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/handler/ReceiveMessageResponseHandler.java rename to proxy/src/test/resources/backup/grpc/v2/adapter/handler/ReceiveMessageResponseHandler.java diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/handler/ResponseHandler.java b/proxy/src/test/resources/backup/grpc/v2/adapter/handler/ResponseHandler.java similarity index 100% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/handler/ResponseHandler.java rename to proxy/src/test/resources/backup/grpc/v2/adapter/handler/ResponseHandler.java diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/handler/SendMessageResponseHandler.java b/proxy/src/test/resources/backup/grpc/v2/adapter/handler/SendMessageResponseHandler.java similarity index 98% rename from proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/handler/SendMessageResponseHandler.java rename to proxy/src/test/resources/backup/grpc/v2/adapter/handler/SendMessageResponseHandler.java index 0cbcd56a24..9ce3ddcccc 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/v2/adapter/handler/SendMessageResponseHandler.java +++ b/proxy/src/test/resources/backup/grpc/v2/adapter/handler/SendMessageResponseHandler.java @@ -28,7 +28,7 @@ import org.apache.rocketmq.common.sysflag.MessageSysFlag; import org.apache.rocketmq.logging.InternalLogger; import org.apache.rocketmq.logging.InternalLoggerFactory; import org.apache.rocketmq.proxy.channel.InvocationContext; -import org.apache.rocketmq.proxy.connector.transaction.TransactionId; +import org.apache.rocketmq.proxy.service.transaction.TransactionId; import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseBuilder; import org.apache.rocketmq.remoting.common.RemotingUtil; import org.apache.rocketmq.remoting.protocol.RemotingCommand; diff --git a/proxy/src/test/resources/backup/grpc/v2/service/AbstractRouteService.java b/proxy/src/test/resources/backup/grpc/v2/service/AbstractRouteService.java new file mode 100644 index 0000000000..3807409816 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/AbstractRouteService.java @@ -0,0 +1,99 @@ +///* +// * 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.service.ServiceManager; +//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 queryRouteEndpointConverter; +// protected volatile ResponseHook queryRouteHook; +// +// protected volatile ParameterConverter queryAssignmentEndpointConverter; +// protected volatile ResponseHook queryAssignmentHook; +// protected volatile AssignmentQueueSelector assignmentQueueSelector; +// +// protected final GrpcClientManager grpcClientManager; +// +// public AbstractRouteService(ServiceManager serviceManager, GrpcClientManager grpcClientManager) { +// super(serviceManager); +// this.grpcClientManager = grpcClientManager; +// this.queryRouteEndpointConverter = (ctx, parameter) -> parameter; +// this.queryAssignmentEndpointConverter = (ctx, parameter) -> parameter; +// this.assignmentQueueSelector = new DefaultAssignmentQueueSelector(this.serviceManager.getTopicRouteService()); +// } +// +// public abstract CompletableFuture queryRoute(Context ctx, QueryRouteRequest request); +// +// public abstract CompletableFuture queryAssignment(Context ctx, QueryAssignmentRequest request); +// +// public ParameterConverter getQueryRouteEndpointConverter() { +// return queryRouteEndpointConverter; +// } +// +// public void setQueryRouteEndpointConverter( +// ParameterConverter queryRouteEndpointConverter) { +// this.queryRouteEndpointConverter = queryRouteEndpointConverter; +// } +// +// public ResponseHook getQueryRouteHook() { +// return queryRouteHook; +// } +// +// public void setQueryRouteHook( +// ResponseHook queryRouteHook) { +// this.queryRouteHook = queryRouteHook; +// } +// +// public ParameterConverter getQueryAssignmentEndpointConverter() { +// return queryAssignmentEndpointConverter; +// } +// +// public void setQueryAssignmentEndpointConverter( +// ParameterConverter queryAssignmentEndpointConverter) { +// this.queryAssignmentEndpointConverter = queryAssignmentEndpointConverter; +// } +// +// public AssignmentQueueSelector getAssignmentQueueSelector() { +// return assignmentQueueSelector; +// } +// +// public void setAssignmentQueueSelector( +// AssignmentQueueSelector assignmentQueueSelector) { +// this.assignmentQueueSelector = assignmentQueueSelector; +// } +// +// public ResponseHook getQueryAssignmentHook() { +// return queryAssignmentHook; +// } +// +// public void setQueryAssignmentHook( +// ResponseHook queryAssignmentHook) { +// this.queryAssignmentHook = queryAssignmentHook; +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/BaseReceiveMessageResponseStreamWriter.java b/proxy/src/test/resources/backup/grpc/v2/service/BaseReceiveMessageResponseStreamWriter.java new file mode 100644 index 0000000000..6a65830fca --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/BaseReceiveMessageResponseStreamWriter.java @@ -0,0 +1,122 @@ +///* +// * 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 streamObserver; +// protected final ResponseHook receiveMessageHook; +// protected final ReceiveMessageResultFilter receiveMessageResultFilter; +// +// public interface Builder { +// BaseReceiveMessageResponseStreamWriter build( +// StreamObserver observer, +// ResponseHook hook); +// } +// +// public BaseReceiveMessageResponseStreamWriter( +// StreamObserver observer, +// ResponseHook hook, +// ReceiveMessageResultFilter messageResultFilter) { +// streamObserver = observer; +// receiveMessageHook = hook; +// receiveMessageResultFilter = messageResultFilter; +// } +// +// public void write(Context ctx, ReceiveMessageRequest request, PopStatus status, List messageFoundList) { +// ReceiveMessageResponseStreamObserver responseStreamObserver = new ReceiveMessageResponseStreamObserver( +// ctx, +// request, +// receiveMessageHook, +// streamObserver); +// try { +// switch (status) { +// case FOUND: +// List 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 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() +// ); +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/BaseReceiveMessageResultFilter.java b/proxy/src/test/resources/backup/grpc/v2/service/BaseReceiveMessageResultFilter.java new file mode 100644 index 0000000000..cf40b63772 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/BaseReceiveMessageResultFilter.java @@ -0,0 +1,67 @@ +///* +// * 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 filterMessage(Context ctx, ReceiveMessageRequest request, List 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 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); +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/BaseService.java b/proxy/src/test/resources/backup/grpc/v2/service/BaseService.java new file mode 100644 index 0000000000..a1e2a904d3 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/BaseService.java @@ -0,0 +1,77 @@ +///* +// * 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.service.ServiceManager; +//import org.apache.rocketmq.proxy.service.route.TopicRouteService; +//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 ServiceManager serviceManager; +// +// public BaseService(ServiceManager serviceManager) { +// this.serviceManager = serviceManager; +// } +// +// 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, TopicRouteService topicRouteService, String brokerName) throws Exception { +// if (StringUtils.isBlank(brokerName)) { +// throw new ProxyException(Code.UNRECOGNIZED, "broker name is empty"); +// } +// String addr = topicRouteService.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.serviceManager.getTopicRouteService(), 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 { +// +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/ClientSettingsService.java b/proxy/src/test/resources/backup/grpc/v2/service/ClientSettingsService.java new file mode 100644 index 0000000000..966e4e3570 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/ClientSettingsService.java @@ -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.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.grpc.v2.common.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 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(); +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/ClusterGrpcActivity.java b/proxy/src/test/resources/backup/grpc/v2/service/ClusterGrpcActivity.java new file mode 100644 index 0000000000..0c223d86bb --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/ClusterGrpcActivity.java @@ -0,0 +1,184 @@ +///* +// * 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.grpc.v2.common.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.service.ServiceManager; +//import org.apache.rocketmq.proxy.service.transaction.TransactionStateCheckRequest; +//import org.apache.rocketmq.proxy.service.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; +//import org.apache.rocketmq.proxy.grpc2.v2.GrpcMessingActivity; +// +//public class ClusterGrpcActivity extends AbstractStartAndShutdown implements GrpcMessingActivity { +// private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME); +// +// protected final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor( +// new ThreadFactoryImpl("ClusterGrpcServiceScheduledThread") +// ); +// +// protected ChannelManager channelManager; +// protected ServiceManager serviceManager; +// protected ProducerService producerService; +// protected ConsumerService consumerService; +// protected RouteService routeService; +// protected ForwardClientService clientService; +// protected TransactionService transactionService; +// protected TelemetryCommandManager pollCommandResponseManager; +// protected GrpcClientManager grpcClientManager; +// +// public ClusterGrpcActivity() { +// this.init(); +// } +// +// protected void init() { +// this.channelManager = new ChannelManager(); +// this.grpcClientManager = new GrpcClientManager(); +// this.pollCommandResponseManager = new TelemetryCommandManager(); +// this.serviceManager = new ServiceManager(new GrpcTransactionStateChecker()); +// this.consumerService = new ConsumerService(serviceManager, grpcClientManager); +// this.producerService = new ProducerService(serviceManager); +// this.routeService = new RouteService(serviceManager, grpcClientManager); +// this.clientService = new ForwardClientService(serviceManager, scheduledExecutorService, +// channelManager, grpcClientManager, pollCommandResponseManager); +// this.transactionService = new TransactionService(serviceManager, channelManager); +// +// this.appendStartAndShutdown(new ClusterGrpcServiceStartAndShutdown()); +// this.appendStartAndShutdown(this.serviceManager); +// this.appendStartAndShutdown(this.consumerService); +// this.appendStartAndShutdown(this.producerService); +// this.appendStartAndShutdown(this.routeService); +// this.appendStartAndShutdown(this.clientService); +// this.appendStartAndShutdown(this.transactionService); +// } +// +// @Override +// public CompletableFuture queryRoute(Context ctx, QueryRouteRequest request) { +// return routeService.queryRoute(ctx, request); +// } +// +// @Override +// public CompletableFuture heartbeat(Context ctx, HeartbeatRequest request) { +// return clientService.heartbeat(ctx, request); +// } +// +// @Override +// public CompletableFuture sendMessage(Context ctx, SendMessageRequest request) { +// return producerService.sendMessage(ctx, request); +// } +// +// @Override +// public CompletableFuture queryAssignment(Context ctx, QueryAssignmentRequest request) { +// return routeService.queryAssignment(ctx, request); +// } +// +// @Override +// public void receiveMessage(Context ctx, ReceiveMessageRequest request, +// StreamObserver responseObserver) { +// consumerService.receiveMessage(ctx, request, responseObserver); +// } +// +// @Override +// public CompletableFuture ackMessage(Context ctx, AckMessageRequest request) { +// return consumerService.ackMessage(ctx, request); +// } +// +// @Override +// public CompletableFuture forwardMessageToDeadLetterQueue(Context ctx, +// ForwardMessageToDeadLetterQueueRequest request) { +// return producerService.forwardMessageToDeadLetterQueue(ctx, request); +// } +// +// @Override +// public CompletableFuture endTransaction(Context ctx, EndTransactionRequest request) { +// return transactionService.endTransaction(ctx, request); +// } +// +// @Override +// public CompletableFuture notifyClientTermination(Context ctx, +// NotifyClientTerminationRequest request) { +// return clientService.notifyClientTermination(ctx, request); +// } +// +// @Override +// public CompletableFuture changeInvisibleDuration(Context ctx, +// ChangeInvisibleDurationRequest request) { +// return consumerService.changeInvisibleDuration(ctx, request); +// } +// +// @Override +// public StreamObserver telemetry(Context ctx, StreamObserver 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); +// } +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/LocalGrpcActivity.java b/proxy/src/test/resources/backup/grpc/v2/service/LocalGrpcActivity.java new file mode 100644 index 0000000000..daa2977ab6 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/LocalGrpcActivity.java @@ -0,0 +1,566 @@ +///* +// * 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.grpc.v2.common.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.service.ServiceManager; +//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.proxy.grpc2.v2.GrpcMessingActivity; +//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 LocalGrpcActivity extends AbstractStartAndShutdown implements GrpcMessingActivity { +// 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 receiveMessageHook; +// +// public LocalGrpcActivity(BrokerController brokerController) { +// this(brokerController, new TelemetryCommandManager()); +// } +// +// /** +// * For unit test +// * @param brokerController BrokerController works in local mode +// * @param telemetryCommandManager Used to manage telemetry command +// */ +// LocalGrpcActivity(BrokerController brokerController, TelemetryCommandManager telemetryCommandManager) { +// this.brokerController = brokerController; +// this.channelManager = new ChannelManager(); +// // TransactionStateChecker is not used in Local mode. +// ServiceManager serviceManager = new ServiceManager(null); +// this.telemetryCommandManager = telemetryCommandManager; +// this.grpcClientManager = new GrpcClientManager(); +// this.routeService = new RouteService(serviceManager, grpcClientManager); +// this.clientSettingsService = new ClientSettingsService(this.channelManager, this.grpcClientManager, this.telemetryCommandManager); +// this.localWriteQueueSelector = new LocalWriteQueueSelector(brokerController.getBrokerConfig().getBrokerName(), +// brokerController.getTopicConfigManager(), serviceManager.getTopicRouteService()); +// +// 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(serviceManager); +// } +// +// @Override +// public CompletableFuture queryRoute(Context ctx, QueryRouteRequest request) { +// return this.routeService.queryRoute(ctx, request); +// } +// +// @Override +// public CompletableFuture 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 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 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 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 future = new CompletableFuture<>(); +// InvocationContext 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 queryAssignment(Context ctx, QueryAssignmentRequest request) { +// return this.routeService.queryAssignment(ctx, request); +// } +// +// @Override +// public void receiveMessage(Context ctx, ReceiveMessageRequest request, StreamObserver 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> future = new CompletableFuture<>(); +// InvocationContext> 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 ackMessage(Context ctx, AckMessageRequest request) { +// Channel channel = channelManager.createChannel(ctx); +// SimpleChannelHandlerContext channelHandlerContext = new SimpleChannelHandlerContext(channel); +// CompletableFuture future = new CompletableFuture<>(); +// List 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 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 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 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 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 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 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 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 telemetry(Context ctx, StreamObserver responseObserver) { +// String clientId = InterceptorConstants.METADATA.get(ctx).get(InterceptorConstants.CLIENT_ID); +// return new StreamObserver() { +// @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()); +// } +// } +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/ReceiveMessageResponseStreamObserver.java b/proxy/src/test/resources/backup/grpc/v2/service/ReceiveMessageResponseStreamObserver.java new file mode 100644 index 0000000000..2f36081448 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/ReceiveMessageResponseStreamObserver.java @@ -0,0 +1,67 @@ +///* +// * 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.stub.StreamObserver; +//import org.apache.rocketmq.proxy.common.ProxyContext; +//import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseHook; +//import org.apache.rocketmq.proxy.grpc.v2.adapter.ResponseWriter; +// +//public class ReceiveMessageResponseStreamObserver implements StreamObserver { +// +// private final ProxyContext context; +// private final ReceiveMessageRequest request; +// private final ResponseHook receiveMessageHook; +// private final StreamObserver observer; +// +// public ReceiveMessageResponseStreamObserver(ProxyContext context, ReceiveMessageRequest request, +// ResponseHook receiveMessageHook, +// StreamObserver 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); +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/ReceiveMessageResultFilter.java b/proxy/src/test/resources/backup/grpc/v2/service/ReceiveMessageResultFilter.java new file mode 100644 index 0000000000..f837861968 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/ReceiveMessageResultFilter.java @@ -0,0 +1,29 @@ +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package org.apache.rocketmq.proxy.grpc.v2.service; +// +//import apache.rocketmq.v2.Message; +//import apache.rocketmq.v2.ReceiveMessageRequest; +//import io.grpc.Context; +//import java.util.List; +//import org.apache.rocketmq.common.message.MessageExt; +// +//public interface ReceiveMessageResultFilter { +// +// List filterMessage(Context ctx, ReceiveMessageRequest request, List messageExtList); +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/AssignmentQueueSelector.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/AssignmentQueueSelector.java new file mode 100644 index 0000000000..d065a9cf1d --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/AssignmentQueueSelector.java @@ -0,0 +1,27 @@ +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package org.apache.rocketmq.proxy.grpc.v2.service.cluster; +// +//import apache.rocketmq.v2.QueryAssignmentRequest; +//import io.grpc.Context; +//import java.util.List; +//import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; +// +//public interface AssignmentQueueSelector { +// +// List getAssignment(Context ctx, QueryAssignmentRequest request) throws Exception; +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/ConsumerService.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/ConsumerService.java new file mode 100644 index 0000000000..2db4f9b1b7 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/ConsumerService.java @@ -0,0 +1,283 @@ +///* +// * 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.service.ServiceManager; +//import org.apache.rocketmq.proxy.service.ForwardProducer; +//import org.apache.rocketmq.proxy.service.ForwardReadConsumer; +//import org.apache.rocketmq.proxy.service.ForwardWriteConsumer; +//import org.apache.rocketmq.proxy.service.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 receiveMessageHook; +// private volatile ResponseHook ackMessageHook; +// private volatile ResponseHook changeInvisibleDurationHook; +// +// public ConsumerService(ServiceManager serviceManager, GrpcClientManager grpcClientManager) { +// super(serviceManager); +// this.readConsumer = serviceManager.getForwardReadConsumer(); +// this.writeConsumer = serviceManager.getForwardWriteConsumer(); +// this.producer = serviceManager.getForwardProducer(); +// this.grpcClientManager = grpcClientManager; +// +// this.readQueueSelector = new DefaultReadQueueSelector(serviceManager.getTopicRouteService()); +// this.receiveMessageWriterBuilder = (observer, hook) -> new DefaultReceiveMessageResponseStreamWriter( +// observer, +// hook, +// writeConsumer, +// serviceManager.getTopicRouteService(), +// new DefaultReceiveMessageResultFilter( +// producer, writeConsumer, grpcClientManager, serviceManager.getTopicRouteService()) +// ); +// } +// +// public void receiveMessage(Context ctx, ReceiveMessageRequest request, +// StreamObserver 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 ackMessage(Context ctx, AckMessageRequest request) { +// CompletableFuture future = new CompletableFuture<>(); +// future.whenComplete((response, throwable) -> { +// if (ackMessageHook != null) { +// ackMessageHook.beforeResponse(ctx, request, response, throwable); +// } +// }); +// +// try { +// CompletableFuture[] 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 entryList = new ArrayList<>(); +// for (CompletableFuture 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 processAckMessage(Context ctx, AckMessageRequest request, +// AckMessageEntry ackMessageEntry) { +// CompletableFuture 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 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 changeInvisibleDuration(Context ctx, +// ChangeInvisibleDurationRequest request) { +// CompletableFuture 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 getReceiveMessageHook() { +// return receiveMessageHook; +// } +// +// public void setReceiveMessageHook( +// ResponseHook receiveMessageHook) { +// this.receiveMessageHook = receiveMessageHook; +// } +// +// public ResponseHook getAckMessageHook() { +// return ackMessageHook; +// } +// +// public void setAckMessageHook( +// ResponseHook ackMessageHook) { +// this.ackMessageHook = ackMessageHook; +// } +// +// public ResponseHook getChangeInvisibleDurationHook() { +// return changeInvisibleDurationHook; +// } +// +// public void setChangeInvisibleDurationHook( +// ResponseHook changeInvisibleDurationHook) { +// this.changeInvisibleDurationHook = changeInvisibleDurationHook; +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultAssignmentQueueSelector.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultAssignmentQueueSelector.java new file mode 100644 index 0000000000..87bc8cc0ad --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultAssignmentQueueSelector.java @@ -0,0 +1,41 @@ +///* +// * 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.service.route.MessageQueueView; +//import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; +//import org.apache.rocketmq.proxy.service.route.TopicRouteService; +//import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter; +// +//public class DefaultAssignmentQueueSelector implements AssignmentQueueSelector { +// +// private final TopicRouteService topicRouteService; +// +// public DefaultAssignmentQueueSelector(TopicRouteService topicRouteService) { +// this.topicRouteService = topicRouteService; +// } +// +// @Override +// public List getAssignment(Context ctx, QueryAssignmentRequest request) throws Exception { +// String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic()); +// MessageQueueView messageQueueView = topicRouteService.getAllMessageQueueView(topicName); +// return messageQueueView.getReadSelector().getBrokerActingQueues(); +// } +//} \ No newline at end of file diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultReadQueueSelector.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultReadQueueSelector.java new file mode 100644 index 0000000000..ecc4b2988c --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultReadQueueSelector.java @@ -0,0 +1,55 @@ +///* +// * 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.service.route.SelectableMessageQueue; +//import org.apache.rocketmq.proxy.service.route.TopicRouteService; +// +//public class DefaultReadQueueSelector implements ReadQueueSelector { +// +// private final TopicRouteService topicRouteService; +// +// public DefaultReadQueueSelector(TopicRouteService topicRouteService) { +// this.topicRouteService = topicRouteService; +// } +// +// @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 = topicRouteService.selectReadBrokerByName(topic, brokerName); +// } +// } +// +// if (messageQueue == null) { +// messageQueue = topicRouteService.selectOneReadBroker(topic, null); +// } +// return messageQueue; +// } catch (Throwable t) { +// return null; +// } +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultReceiveMessageResponseStreamWriter.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultReceiveMessageResponseStreamWriter.java new file mode 100644 index 0000000000..09c57539ba --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultReceiveMessageResponseStreamWriter.java @@ -0,0 +1,96 @@ +///* +// * 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.service.ForwardWriteConsumer; +//import org.apache.rocketmq.proxy.service.route.TopicRouteService; +//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 TopicRouteService topicRouteService; +// +// public DefaultReceiveMessageResponseStreamWriter( +// StreamObserver observer, +// ResponseHook hook, +// ForwardWriteConsumer writeConsumer, +// TopicRouteService topicRouteService, +// ReceiveMessageResultFilter receiveMessageResultFilter) { +// super(observer, hook, receiveMessageResultFilter); +// this.writeConsumer = writeConsumer; +// this.topicRouteService = topicRouteService; +// } +// +// @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.topicRouteService, 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); +// } +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultReceiveMessageResultFilter.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultReceiveMessageResultFilter.java new file mode 100644 index 0000000000..15c8bfe864 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultReceiveMessageResultFilter.java @@ -0,0 +1,127 @@ +///* +// * 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.service.ForwardProducer; +//import org.apache.rocketmq.proxy.service.ForwardWriteConsumer; +//import org.apache.rocketmq.proxy.service.route.TopicRouteService; +//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 TopicRouteService topicRouteService; +// +// private volatile ResponseHook ackNoMatchedMessageHook; +// private volatile ResponseHook forwardToDLQInRecvMessageHook; +// +// public DefaultReceiveMessageResultFilter(ForwardProducer producer, ForwardWriteConsumer writeConsumer, +// GrpcClientManager grpcClientManager, TopicRouteService topicRouteService) { +// super(grpcClientManager); +// this.producer = producer; +// this.writeConsumer = writeConsumer; +// this.topicRouteService = topicRouteService; +// } +// +// @Override +// protected void processNoMatchMessage(Context ctx, ReceiveMessageRequest request, MessageExt messageExt) { +// CompletableFuture future = new CompletableFuture<>(); +// +// ReceiptHandle handle = ReceiptHandle.create(messageExt); +// if (handle == null) { +// return; +// } +// AckMessageRequestHeader ackMessageRequestHeader = GrpcConverter.buildAckMessageRequestHeader(request, handle); +// try { +// String brokerAddr = getBrokerAddr(ctx, topicRouteService, 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 future = new CompletableFuture<>(); +// ConsumerSendMsgBackRequestHeader consumerSendMsgBackRequestHeader = new ConsumerSendMsgBackRequestHeader(); +// +// try { +// ReceiptHandle handle = ReceiptHandle.create(messageExt); +// if (handle == null) { +// return; +// } +// String brokerAddr = getBrokerAddr(ctx, topicRouteService, 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 getAckNoMatchedMessageHook() { +// return ackNoMatchedMessageHook; +// } +// +// public void setAckNoMatchedMessageHook( +// ResponseHook ackNoMatchedMessageHook) { +// this.ackNoMatchedMessageHook = ackNoMatchedMessageHook; +// } +// +// public ResponseHook getForwardToDLQInRecvMessageHook() { +// return forwardToDLQInRecvMessageHook; +// } +// +// public void setForwardToDLQInRecvMessageHook( +// ResponseHook forwardToDLQInRecvMessageHook) { +// this.forwardToDLQInRecvMessageHook = forwardToDLQInRecvMessageHook; +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultWriteQueueSelector.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultWriteQueueSelector.java new file mode 100644 index 0000000000..ab48b6fdac --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/DefaultWriteQueueSelector.java @@ -0,0 +1,77 @@ +///* +// * 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.service.route.SelectableMessageQueue; +//import org.apache.rocketmq.proxy.service.route.TopicRouteService; +//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 TopicRouteService topicRouteService; +// +// public DefaultWriteQueueSelector(TopicRouteService topicRouteService) { +// this.topicRouteService = topicRouteService; +// } +// +// @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.topicRouteService.selectOneWriteQueue(topic, null); +// } +// +// protected SelectableMessageQueue selectOrderQueue(String topic, String shardingKey) throws Exception { +// return this.topicRouteService.selectOneWriteQueueByKey(topic, shardingKey); +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/ForwardClientService.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/ForwardClientService.java new file mode 100644 index 0000000000..d5dcbbdd75 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/ForwardClientService.java @@ -0,0 +1,266 @@ +///* +// * 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.grpc.v2.common.ChannelManager; +//import org.apache.rocketmq.proxy.common.TelemetryCommandManager; +//import org.apache.rocketmq.proxy.service.ServiceManager; +//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( +// ServiceManager serviceManager, +// ScheduledExecutorService scheduledExecutorService, +// ChannelManager channelManager, +// GrpcClientManager grpcClientManager, +// TelemetryCommandManager telemetryCommandManager +// ) { +// super(serviceManager); +// 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: +// serviceManager.getTransactionHeartbeatRegisterService().onProducerGroupOffline(group); +// break; +// case CLIENT_UNREGISTER: +// channelManager.onClientOffline(clientChannelInfo.getClientId()); +// grpcClientManager.removeClientSettings(clientChannelInfo.getClientId()); +// break; +// default: +// break; +// } +// } +// } +// +// public CompletableFuture heartbeat(Context ctx, HeartbeatRequest request) { +// CompletableFuture 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); +// serviceManager.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 notifyClientTermination(Context ctx, NotifyClientTerminationRequest request) { +// CompletableFuture 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 telemetry(Context ctx, StreamObserver responseObserver) { +// return new StreamObserver() { +// @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; +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/ProducerService.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/ProducerService.java new file mode 100644 index 0000000000..a6d851bb10 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/ProducerService.java @@ -0,0 +1,184 @@ +///* +// * 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.service.ServiceManager; +//import org.apache.rocketmq.proxy.service.ForwardProducer; +//import org.apache.rocketmq.proxy.service.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 sendMessageHook; +// private volatile ResponseHook forwardMessageToDLQHook; +// +// public ProducerService(ServiceManager serviceManager) { +// super(serviceManager); +// this.producer = serviceManager.getForwardProducer(); +// +// this.writeQueueSelector = new DefaultWriteQueueSelector(this.serviceManager.getTopicRouteService()); +// } +// +// public CompletableFuture sendMessage(Context ctx, SendMessageRequest request) { +// CompletableFuture 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 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 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 forwardMessageToDeadLetterQueue(Context ctx, +// ForwardMessageToDeadLetterQueueRequest request) { +// CompletableFuture 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 getSendMessageHook() { +// return sendMessageHook; +// } +// +// public void setSendMessageHook( +// ResponseHook sendMessageHook) { +// this.sendMessageHook = sendMessageHook; +// } +// +// public ResponseHook getForwardMessageToDLQHook() { +// return forwardMessageToDLQHook; +// } +// +// public void setForwardMessageToDLQHook( +// ResponseHook forwardMessageToDLQHook) { +// this.forwardMessageToDLQHook = forwardMessageToDLQHook; +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/ReadQueueSelector.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/ReadQueueSelector.java new file mode 100644 index 0000000000..ea0e078b84 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/ReadQueueSelector.java @@ -0,0 +1,27 @@ +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package org.apache.rocketmq.proxy.grpc.v2.service.cluster; +// +//import apache.rocketmq.v2.ReceiveMessageRequest; +//import io.grpc.Context; +//import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader; +//import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; +// +//public interface ReadQueueSelector { +// +// SelectableMessageQueue select(Context ctx, ReceiveMessageRequest request, PopMessageRequestHeader requestHeader); +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/RouteService.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/RouteService.java new file mode 100644 index 0000000000..6a65c036f8 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/RouteService.java @@ -0,0 +1,150 @@ +///* +// * 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.service.ServiceManager; +//import org.apache.rocketmq.proxy.service.route.MessageQueueView; +//import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; +//import org.apache.rocketmq.proxy.service.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(ServiceManager serviceManager, GrpcClientManager grpcClientManager) { +// super(serviceManager, grpcClientManager); +// } +// +// @Override +// public CompletableFuture queryRoute(Context ctx, QueryRouteRequest request) { +// CompletableFuture future = new CompletableFuture<>(); +// future.whenComplete((response, throwable) -> { +// if (queryRouteHook != null) { +// queryRouteHook.beforeResponse(ctx, request, response, throwable); +// } +// }); +// +// try { +// String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic()); +// MessageQueueView messageQueueView = this.serviceManager.getTopicRouteService().getAllMessageQueueView(topicName); +// TopicRouteData topicRouteData = messageQueueView.getTopicRouteData(); +// List queueDataList = topicRouteData.getQueueDatas(); +// +// List 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 queryAssignment(Context ctx, QueryAssignmentRequest request) { +// CompletableFuture future = new CompletableFuture<>(); +// future.whenComplete((response, throwable) -> { +// if (queryAssignmentHook != null) { +// queryAssignmentHook.beforeResponse(ctx, request, response, throwable); +// } +// }); +// +// try { +// List assignments = new ArrayList<>(); +// List 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; +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/TransactionService.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/TransactionService.java new file mode 100644 index 0000000000..92cf16b0ad --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/TransactionService.java @@ -0,0 +1,139 @@ +///* +// * 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.grpc.v2.common.ChannelManager; +//import org.apache.rocketmq.proxy.service.ServiceManager; +//import org.apache.rocketmq.proxy.service.ForwardProducer; +//import org.apache.rocketmq.proxy.service.transaction.TransactionId; +//import org.apache.rocketmq.proxy.service.transaction.TransactionStateCheckRequest; +//import org.apache.rocketmq.proxy.service.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 checkTransactionStateHook; +// private volatile ResponseHook endTransactionHook; +// +// public TransactionService(ServiceManager serviceManager, ChannelManager channelManager) { +// super(serviceManager); +// this.forwardProducer = serviceManager.getForwardProducer(); +// this.channelManager = channelManager; +// } +// +// @Override +// public void checkTransactionState(TransactionStateCheckRequest checkData) { +// Context ctx = Context.current(); +// try { +// List 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 endTransaction(Context ctx, EndTransactionRequest request) { +// CompletableFuture 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 getCheckTransactionStateHook() { +// return checkTransactionStateHook; +// } +// +// public void setCheckTransactionStateHook( +// ResponseHook checkTransactionStateHook) { +// this.checkTransactionStateHook = checkTransactionStateHook; +// } +// +// public ResponseHook getEndTransactionHook() { +// return endTransactionHook; +// } +// +// public void setEndTransactionHook( +// ResponseHook endTransactionHook) { +// this.endTransactionHook = endTransactionHook; +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/cluster/WriteQueueSelector.java b/proxy/src/test/resources/backup/grpc/v2/service/cluster/WriteQueueSelector.java new file mode 100644 index 0000000000..853bb1df2a --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/cluster/WriteQueueSelector.java @@ -0,0 +1,26 @@ +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package org.apache.rocketmq.proxy.grpc.v2.service.cluster; +// +//import apache.rocketmq.v2.SendMessageRequest; +//import io.grpc.Context; +//import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; +// +//public interface WriteQueueSelector { +// +// SelectableMessageQueue selectQueue(Context ctx, SendMessageRequest request); +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriter.java b/proxy/src/test/resources/backup/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriter.java new file mode 100644 index 0000000000..4f32b0aed2 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriter.java @@ -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.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.grpc.v2.common.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 observer, +// ResponseHook 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); +// } +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/local/LocalReceiveMessageResultFilter.java b/proxy/src/test/resources/backup/grpc/v2/service/local/LocalReceiveMessageResultFilter.java new file mode 100644 index 0000000000..2b62522856 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/local/LocalReceiveMessageResultFilter.java @@ -0,0 +1,94 @@ +///* +// * 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.grpc.v2.common.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); +// } +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/local/LocalWriteQueueSelector.java b/proxy/src/test/resources/backup/grpc/v2/service/local/LocalWriteQueueSelector.java new file mode 100644 index 0000000000..0553ba39da --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/local/LocalWriteQueueSelector.java @@ -0,0 +1,101 @@ +///* +// * 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.service.route.SelectableMessageQueue; +//import org.apache.rocketmq.proxy.service.route.TopicRouteService; +//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, TopicRouteService topicRouteService) { +// super(topicRouteService); +// 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); +// } +//} diff --git a/proxy/src/test/resources/backup/grpc/v2/service/local/RouteService.java b/proxy/src/test/resources/backup/grpc/v2/service/local/RouteService.java new file mode 100644 index 0000000000..2be0fedb23 --- /dev/null +++ b/proxy/src/test/resources/backup/grpc/v2/service/local/RouteService.java @@ -0,0 +1,175 @@ +///* +// * 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.service.ServiceManager; +//import org.apache.rocketmq.proxy.service.route.MessageQueueView; +//import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; +//import org.apache.rocketmq.proxy.service.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(ServiceManager serviceManager, GrpcClientManager grpcClientManager) { +// super(serviceManager, grpcClientManager); +// } +// +// @Override +// public CompletableFuture queryRoute(Context ctx, QueryRouteRequest request) { +// CompletableFuture future = new CompletableFuture<>(); +// future.whenComplete((response, throwable) -> { +// if (queryRouteHook != null) { +// queryRouteHook.beforeResponse(ctx, request, response, throwable); +// } +// }); +// +// try { +// String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic()); +// MessageQueueView messageQueueView = this.serviceManager.getTopicRouteService().getAllMessageQueueView(topicName); +// TopicRouteData topicRouteData = messageQueueView.getTopicRouteData(); +// List queueDataList = topicRouteData.getQueueDatas(); +// List brokerDataList = topicRouteData.getBrokerDatas(); +// +// List messageQueueList = new ArrayList<>(); +// Map> brokerMap = buildBrokerMap(brokerDataList); +// +// for (QueueData queueData : queueDataList) { +// String brokerName = queueData.getBrokerName(); +// Map 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 queryAssignment(Context ctx, QueryAssignmentRequest request) { +// CompletableFuture future = new CompletableFuture<>(); +// future.whenComplete((response, throwable) -> { +// if (queryAssignmentHook != null) { +// queryAssignmentHook.beforeResponse(ctx, request, response, throwable); +// } +// }); +// +// try { +// List assignments = new ArrayList<>(); +// List messageQueueList = this.assignmentQueueSelector.getAssignment(ctx, request); +// String topicName = GrpcConverter.wrapResourceWithNamespace(request.getTopic()); +// MessageQueueView messageQueueView = this.serviceManager.getTopicRouteService().getAllMessageQueueView(topicName); +// TopicRouteData topicRouteData = messageQueueView.getTopicRouteData(); +// Map> brokerMap = buildBrokerMap(topicRouteData.getBrokerDatas()); +// for (SelectableMessageQueue messageQueue : messageQueueList) { +// Map 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> buildBrokerMap(List brokerDataList) { +// Map> brokerMap = new HashMap<>(); +// for (BrokerData brokerData : brokerDataList) { +// Map brokerIdMap = new HashMap<>(); +// String brokerName = brokerData.getBrokerName(); +// for (Map.Entry 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; +// } +//} diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/LocalGrpcServiceTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/LocalGrpcServiceTest.java similarity index 98% rename from proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/LocalGrpcServiceTest.java rename to proxy/src/test/resources/backup/test/proxy/grpc/v2/service/LocalGrpcServiceTest.java index 13a87b25d1..b1760e52f1 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/LocalGrpcServiceTest.java +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/LocalGrpcServiceTest.java @@ -79,17 +79,15 @@ 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.ChangeInvisibleTimeResponseHeader; -import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader; import org.apache.rocketmq.common.protocol.header.PopMessageResponseHeader; import org.apache.rocketmq.proxy.common.TelemetryCommandManager; import org.apache.rocketmq.proxy.common.TelemetryCommandRecord; import org.apache.rocketmq.proxy.config.ConfigurationManager; import org.apache.rocketmq.proxy.config.InitConfigAndLoggerTest; -import org.apache.rocketmq.proxy.connector.transaction.TransactionId; +import org.apache.rocketmq.proxy.service.transaction.TransactionId; 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.remoting.common.RemotingUtil; import org.apache.rocketmq.remoting.exception.RemotingCommandException; import org.apache.rocketmq.remoting.netty.NettyRemotingServer; import org.apache.rocketmq.remoting.protocol.RemotingCommand; @@ -106,7 +104,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; @RunWith(MockitoJUnitRunner.class) public class LocalGrpcServiceTest extends InitConfigAndLoggerTest { - private LocalGrpcService localGrpcService; + private LocalGrpcActivity localGrpcService; @Mock private SendMessageProcessor sendMessageProcessorMock; @Mock @@ -144,7 +142,7 @@ public class LocalGrpcServiceTest extends InitConfigAndLoggerTest { Mockito.doNothing().when(producerManagerMock).appendProducerChangeListener(Mockito.any(ProducerChangeListener.class)); Mockito.when(brokerControllerMock.getConsumerManager()).thenReturn(consumerManagerMock); Mockito.when(brokerControllerMock.getProducerManager()).thenReturn(producerManagerMock); - localGrpcService = new LocalGrpcService(brokerControllerMock, telemetryCommandManager); + localGrpcService = new LocalGrpcActivity(brokerControllerMock, telemetryCommandManager); metadata = new Metadata(); metadata.put(InterceptorConstants.REMOTE_ADDRESS, "1.1.1.1"); metadata.put(InterceptorConstants.LOCAL_ADDRESS, "0.0.0.0"); diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/BaseServiceTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/BaseServiceTest.java similarity index 79% rename from proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/BaseServiceTest.java rename to proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/BaseServiceTest.java index 0d35cfbe5f..3be07c9518 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/BaseServiceTest.java +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/BaseServiceTest.java @@ -27,13 +27,13 @@ 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.proxy.config.InitConfigAndLoggerTest; -import org.apache.rocketmq.proxy.connector.ConnectorManager; -import org.apache.rocketmq.proxy.connector.DefaultForwardClient; -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.TopicRouteCache; -import org.apache.rocketmq.proxy.connector.transaction.TransactionHeartbeatRegisterService; +import org.apache.rocketmq.proxy.service.ServiceManager; +import org.apache.rocketmq.proxy.service.DefaultForwardClient; +import org.apache.rocketmq.proxy.service.ForwardProducer; +import org.apache.rocketmq.proxy.service.ForwardReadConsumer; +import org.apache.rocketmq.proxy.service.ForwardWriteConsumer; +import org.apache.rocketmq.proxy.service.route.TopicRouteService; +import org.apache.rocketmq.proxy.service.transaction.ClusterTransactionService; import org.apache.rocketmq.proxy.grpc.interceptor.InterceptorConstants; import org.apache.rocketmq.proxy.grpc.v2.service.GrpcClientManager; import org.apache.rocketmq.remoting.common.RemotingUtil; @@ -50,7 +50,7 @@ import static org.mockito.Mockito.when; public abstract class BaseServiceTest extends InitConfigAndLoggerTest { @Mock - protected ConnectorManager connectorManager; + protected ServiceManager serviceManager; @Mock protected DefaultForwardClient defaultClient; @Mock @@ -60,9 +60,9 @@ public abstract class BaseServiceTest extends InitConfigAndLoggerTest { @Mock protected ForwardWriteConsumer writeConsumerClient; @Mock - protected TopicRouteCache topicRouteCache; + protected TopicRouteService topicRouteService; @Mock - protected TransactionHeartbeatRegisterService transactionHeartbeatRegisterService; + protected ClusterTransactionService clusterTransactionService; @Mock protected GrpcClientManager grpcClientManager; @@ -74,12 +74,12 @@ public abstract class BaseServiceTest extends InitConfigAndLoggerTest { @Before public void before() throws Throwable { super.before(); - when(connectorManager.getDefaultForwardClient()).thenReturn(defaultClient); - when(connectorManager.getForwardProducer()).thenReturn(producerClient); - when(connectorManager.getForwardReadConsumer()).thenReturn(readConsumerClient); - when(connectorManager.getForwardWriteConsumer()).thenReturn(writeConsumerClient); - when(connectorManager.getTopicRouteCache()).thenReturn(topicRouteCache); - when(connectorManager.getTransactionHeartbeatRegisterService()).thenReturn(transactionHeartbeatRegisterService); + when(serviceManager.getDefaultForwardClient()).thenReturn(defaultClient); + when(serviceManager.getForwardProducer()).thenReturn(producerClient); + when(serviceManager.getForwardReadConsumer()).thenReturn(readConsumerClient); + when(serviceManager.getForwardWriteConsumer()).thenReturn(writeConsumerClient); + when(serviceManager.getTopicRouteService()).thenReturn(topicRouteService); + when(serviceManager.getTransactionHeartbeatRegisterService()).thenReturn(clusterTransactionService); initContext(); beforeEach(); diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ConsumerServiceTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/ConsumerServiceTest.java similarity index 95% rename from proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ConsumerServiceTest.java rename to proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/ConsumerServiceTest.java index b8d045dc8c..65cad72e35 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ConsumerServiceTest.java +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/ConsumerServiceTest.java @@ -52,7 +52,7 @@ import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.common.protocol.ResponseCode; import org.apache.rocketmq.common.protocol.header.ChangeInvisibleTimeRequestHeader; import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader; -import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue; +import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; import org.apache.rocketmq.remoting.protocol.RemotingCommand; import org.assertj.core.util.Lists; import org.junit.Test; @@ -80,12 +80,12 @@ public class ConsumerServiceTest extends BaseServiceTest { @Override public void beforeEach() throws Throwable { - consumerService = new ConsumerService(this.connectorManager, this.grpcClientManager); + consumerService = new ConsumerService(this.serviceManager, this.grpcClientManager); consumerService.start(); - receiveMessageResultFilter = new DefaultReceiveMessageResultFilter(producerClient, writeConsumerClient, grpcClientManager, topicRouteCache); + receiveMessageResultFilter = new DefaultReceiveMessageResultFilter(producerClient, writeConsumerClient, grpcClientManager, topicRouteService); consumerService.setReceiveMessageWriterBuilder((observer, hook) -> - new DefaultReceiveMessageResponseStreamWriter(observer, hook, writeConsumerClient, topicRouteCache, receiveMessageResultFilter)); + new DefaultReceiveMessageResponseStreamWriter(observer, hook, writeConsumerClient, topicRouteService, receiveMessageResultFilter)); consumerService.setReadQueueSelector(readQueueSelector); } @@ -110,7 +110,7 @@ public class ConsumerServiceTest extends BaseServiceTest { PopResult popResult = new PopResult(PopStatus.FOUND, messageExtList); when(readConsumerClient.popMessage(any(), anyString(), anyString(), any(), anyLong())) .thenReturn(CompletableFuture.completedFuture(popResult)); - when(topicRouteCache.getBrokerAddr(anyString())).thenReturn("brokerAddr"); + when(topicRouteService.getBrokerAddr(anyString())).thenReturn("brokerAddr"); when(writeConsumerClient.ackMessage(any(), anyString(), anyString(), any())) .thenReturn(CompletableFuture.completedFuture(new AckResult())); @@ -168,7 +168,7 @@ public class ConsumerServiceTest extends BaseServiceTest { PopResult popResult = new PopResult(PopStatus.FOUND, messageExtList); when(readConsumerClient.popMessage(any(), anyString(), anyString(), any(), anyLong())) .thenReturn(CompletableFuture.completedFuture(popResult)); - when(topicRouteCache.getBrokerAddr(anyString())).thenReturn("brokerAddr"); + when(topicRouteService.getBrokerAddr(anyString())).thenReturn("brokerAddr"); ArgumentCaptor sendMsgBackRequestHeaderArgumentCaptor = ArgumentCaptor.forClass(ConsumerSendMsgBackRequestHeader.class); when(producerClient.sendMessageBackThenAckOrg(any(), anyString(), sendMsgBackRequestHeaderArgumentCaptor.capture(), any())) @@ -207,7 +207,7 @@ public class ConsumerServiceTest extends BaseServiceTest { @Test public void testAckMessage() throws Exception { - when(topicRouteCache.getBrokerAddr(anyString())).thenReturn("brokerAddr"); + when(topicRouteService.getBrokerAddr(anyString())).thenReturn("brokerAddr"); AckResult ackResult = new AckResult(); ackResult.setStatus(AckStatus.OK); when(writeConsumerClient.ackMessage(any(), anyString(), anyString(), any())).thenReturn(CompletableFuture.completedFuture(ackResult)); @@ -239,7 +239,7 @@ public class ConsumerServiceTest extends BaseServiceTest { ackResult.setExtraInfo(receiptHandle.encode()); when(writeConsumerClient.changeInvisibleTimeAsync(any(), anyString(), anyString(), anyString(), headerArgumentCaptor.capture())) .thenReturn(CompletableFuture.completedFuture(ackResult)); - when(topicRouteCache.getBrokerAddr(anyString())).thenReturn("brokerAddr"); + when(topicRouteService.getBrokerAddr(anyString())).thenReturn("brokerAddr"); Settings clientSettings = createClientSettings(3); when(grpcClientManager.getClientSettings(any(Context.class))).thenReturn(clientSettings); diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReadQueueSelectorTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/DefaultReadQueueSelectorTest.java similarity index 91% rename from proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReadQueueSelectorTest.java rename to proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/DefaultReadQueueSelectorTest.java index b7436d29bc..231cb821e4 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultReadQueueSelectorTest.java +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/DefaultReadQueueSelectorTest.java @@ -21,7 +21,7 @@ import apache.rocketmq.v2.ReceiveMessageRequest; import io.grpc.Context; import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.common.protocol.header.PopMessageRequestHeader; -import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue; +import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; import org.junit.Test; import static org.junit.Assert.assertNull; @@ -46,10 +46,10 @@ public class DefaultReadQueueSelectorTest extends BaseServiceTest { SelectableMessageQueue messageQueue2 = new SelectableMessageQueue( new MessageQueue("oneReadBroker", "brokerName", 0), "brokerAddr1"); - when(topicRouteCache.selectReadBrokerByName(eq("readBrokerTopicByName"), anyString())).thenReturn(messageQueue1); - when(topicRouteCache.selectOneReadBroker(eq("oneReadBroker"), isNull())).thenReturn(messageQueue2); + when(topicRouteService.selectReadBrokerByName(eq("readBrokerTopicByName"), anyString())).thenReturn(messageQueue1); + when(topicRouteService.selectOneReadBroker(eq("oneReadBroker"), isNull())).thenReturn(messageQueue2); - ReadQueueSelector readQueueSelector = new DefaultReadQueueSelector(topicRouteCache); + ReadQueueSelector readQueueSelector = new DefaultReadQueueSelector(topicRouteService); { PopMessageRequestHeader requestHeader = new PopMessageRequestHeader(); diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultWriteQueueSelectorTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/DefaultWriteQueueSelectorTest.java similarity index 92% rename from proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultWriteQueueSelectorTest.java rename to proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/DefaultWriteQueueSelectorTest.java index 013814cc45..d5922a039d 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/DefaultWriteQueueSelectorTest.java +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/DefaultWriteQueueSelectorTest.java @@ -24,7 +24,7 @@ import com.google.protobuf.ByteString; import io.grpc.Context; import java.nio.charset.StandardCharsets; import org.apache.rocketmq.common.message.MessageQueue; -import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue; +import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -39,13 +39,13 @@ public class DefaultWriteQueueSelectorTest extends BaseServiceTest { SelectableMessageQueue queue = new SelectableMessageQueue( new MessageQueue("topic", "selectOrderQueue", 0), "selectOrderQueueAddr"); - when(topicRouteCache.selectOneWriteQueueByKey(anyString(), anyString())) + when(topicRouteService.selectOneWriteQueueByKey(anyString(), anyString())) .thenReturn(queue); queue = new SelectableMessageQueue( new MessageQueue("topic", "selectNormalQueue", 0), "selectNormalQueueAddr"); - when(topicRouteCache.selectOneWriteQueue(anyString(), isNull())) + when(topicRouteService.selectOneWriteQueue(anyString(), isNull())) .thenReturn(queue); } @@ -64,7 +64,7 @@ public class DefaultWriteQueueSelectorTest extends BaseServiceTest { .setBody(ByteString.copyFrom("hello", StandardCharsets.UTF_8)) .build()) .build(); - WriteQueueSelector queueSelector = new DefaultWriteQueueSelector(this.topicRouteCache); + WriteQueueSelector queueSelector = new DefaultWriteQueueSelector(this.topicRouteService); SelectableMessageQueue queue = queueSelector.selectQueue(Context.current(), request); assertEquals("selectOrderQueue", queue.getBrokerName()); @@ -85,7 +85,7 @@ public class DefaultWriteQueueSelectorTest extends BaseServiceTest { .setBody(ByteString.copyFrom("hello", StandardCharsets.UTF_8)) .build()) .build(); - WriteQueueSelector queueSelector = new DefaultWriteQueueSelector(this.topicRouteCache); + WriteQueueSelector queueSelector = new DefaultWriteQueueSelector(this.topicRouteService); SelectableMessageQueue queue = queueSelector.selectQueue(Context.current(), request); assertEquals("selectNormalQueue", queue.getBrokerName()); diff --git a/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/ForwardClientServiceTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/ForwardClientServiceTest.java new file mode 100644 index 0000000000..870fb0b88a --- /dev/null +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/ForwardClientServiceTest.java @@ -0,0 +1,142 @@ +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package org.apache.rocketmq.proxy.grpc.v2.service.cluster; +// +//import apache.rocketmq.v2.ClientType; +//import apache.rocketmq.v2.FilterExpression; +//import apache.rocketmq.v2.FilterType; +//import apache.rocketmq.v2.HeartbeatRequest; +//import apache.rocketmq.v2.NotifyClientTerminationRequest; +//import apache.rocketmq.v2.Publishing; +//import apache.rocketmq.v2.Resource; +//import apache.rocketmq.v2.Settings; +//import apache.rocketmq.v2.Subscription; +//import apache.rocketmq.v2.SubscriptionEntry; +//import io.grpc.Context; +//import io.netty.channel.Channel; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.concurrent.Executors; +//import org.apache.rocketmq.broker.client.ClientChannelInfo; +//import org.apache.rocketmq.broker.client.ConsumerGroupInfo; +//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.proxy.grpc.v2.common.ChannelManager; +//import org.apache.rocketmq.proxy.common.TelemetryCommandManager; +//import org.apache.rocketmq.proxy.grpc.v2.adapter.channel.GrpcClientChannel; +//import org.apache.rocketmq.remoting.protocol.LanguageCode; +//import org.junit.Test; +// +//import static org.junit.Assert.assertEquals; +//import static org.junit.Assert.assertNotNull; +//import static org.junit.Assert.assertNull; +//import static org.junit.Assert.assertTrue; +//import static org.mockito.ArgumentMatchers.anyString; +//import static org.mockito.Mockito.when; +// +//public class ForwardClientServiceTest extends BaseServiceTest { +// +// private ChannelManager channelManager = new ChannelManager(); +// private TelemetryCommandManager telemetryCommandManager = new TelemetryCommandManager(); +// private ForwardClientService clientService; +// +// @Override +// public void beforeEach() throws Throwable { +// clientService = new ForwardClientService( +// this.serviceManager, +// Executors.newSingleThreadScheduledExecutor(), +// this.channelManager, +// this.grpcClientManager, +// this.telemetryCommandManager); +// clientService.start(); +// } +// +// @Test +// public void testProducerHeartbeat() { +// Settings clientSettings = Settings.newBuilder() +// .setClientType(ClientType.PRODUCER) +// .setPublishing(Publishing.newBuilder() +// .addTopics(Resource.newBuilder() +// .setName("topic1") +// .build()) +// .addTopics(Resource.newBuilder() +// .setName("topic2") +// .build()) +// .build()) +// .build(); +// when(grpcClientManager.getClientSettings(anyString())).thenReturn(clientSettings); +// +// clientService.heartbeat(Context.current(), HeartbeatRequest.newBuilder().build()); +// +// assertEquals(2, clientService.getProducerManager().getGroupChannelTable().size()); +// Channel channel = clientService.getProducerManager().findChannel(CLIENT_ID); +// assertNotNull(channel); +// assertTrue(channel instanceof GrpcClientChannel); +// +// clientService.notifyClientTermination(Context.current(), NotifyClientTerminationRequest.newBuilder().build()); +// assertTrue(clientService.getProducerManager().getGroupChannelTable().isEmpty()); +// } +// +// @Test +// public void testConsumerHeartbeat() { +// List subscriptionEntryList = new ArrayList<>(); +// subscriptionEntryList.add(SubscriptionEntry.newBuilder() +// .setTopic(Resource.newBuilder() +// .setName("topic") +// .build()) +// .setExpression(FilterExpression.newBuilder() +// .setExpression("*") +// .setType(FilterType.TAG) +// .build()) +// .build()); +// +// Settings clientSettings = Settings.newBuilder() +// .setClientType(ClientType.PUSH_CONSUMER) +// .setSubscription(Subscription.newBuilder() +// .addAllSubscriptions(subscriptionEntryList) +// .build()) +// .build(); +// when(grpcClientManager.getClientSettings(anyString())).thenReturn(clientSettings); +// +// clientService.heartbeat(Context.current(), HeartbeatRequest.newBuilder() +// .setGroup(Resource.newBuilder() +// .setName("consumerGroup") +// .build()) +// .build()); +// +// ClientChannelInfo clientChannelInfo = clientService.getConsumerManager().findChannel("consumerGroup", CLIENT_ID); +// assertNotNull(clientChannelInfo); +// assertEquals(LanguageCode.JAVA, clientChannelInfo.getLanguage()); +// assertEquals(CLIENT_ID, clientChannelInfo.getClientId()); +// assertTrue(clientChannelInfo.getChannel() instanceof GrpcClientChannel); +// ConsumerGroupInfo consumerGroupInfo = clientService.getConsumerManager().getConsumerGroupInfo("consumerGroup"); +// assertEquals(MessageModel.CLUSTERING, consumerGroupInfo.getMessageModel()); +// assertEquals(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET, consumerGroupInfo.getConsumeFromWhere()); +// assertEquals(ConsumeType.CONSUME_PASSIVELY, consumerGroupInfo.getConsumeType()); +// assertEquals("TAG", consumerGroupInfo.getSubscriptionTable().get("topic").getExpressionType()); +// assertEquals("*", consumerGroupInfo.getSubscriptionTable().get("topic").getSubString()); +// +// +// clientService.notifyClientTermination(Context.current(), NotifyClientTerminationRequest.newBuilder() +// .setGroup(Resource.newBuilder() +// .setName("consumerGroup") +// .build()) +// .build()); +// assertNull(clientService.getConsumerManager().getConsumerGroupInfo("consumerGroup")); +// } +//} \ No newline at end of file diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ProducerServiceTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/ProducerServiceTest.java similarity index 97% rename from proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ProducerServiceTest.java rename to proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/ProducerServiceTest.java index 2b205e2298..68344d6899 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/ProducerServiceTest.java +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/ProducerServiceTest.java @@ -30,7 +30,7 @@ import java.util.concurrent.ExecutionException; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.client.producer.SendStatus; import org.apache.rocketmq.common.message.MessageQueue; -import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue; +import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; import org.apache.rocketmq.proxy.grpc.v2.adapter.ProxyException; import org.junit.Test; @@ -62,7 +62,7 @@ public class ProducerServiceTest extends BaseServiceTest { @Override public void beforeEach() throws Throwable { - producerService = new ProducerService(this.connectorManager); + producerService = new ProducerService(this.serviceManager); producerService.start(); } diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/RouteServiceTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/RouteServiceTest.java similarity index 96% rename from proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/RouteServiceTest.java rename to proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/RouteServiceTest.java index 65c0889a67..7cea2646cf 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/cluster/RouteServiceTest.java +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/RouteServiceTest.java @@ -41,7 +41,7 @@ import org.apache.rocketmq.common.protocol.ResponseCode; 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.connector.route.MessageQueueWrapper; +import org.apache.rocketmq.proxy.service.route.MessageQueueView; import org.apache.rocketmq.proxy.grpc.v2.adapter.GrpcConverter; import org.junit.Test; @@ -105,12 +105,12 @@ public class RouteServiceTest extends BaseServiceTest { routeData.setBrokerDatas(brokerDataList); routeData.setQueueDatas(queueDataList); - MessageQueueWrapper messageQueueWrapper = new MessageQueueWrapper("topic", routeData); - when(this.topicRouteCache.getMessageQueue("topic")).thenReturn(messageQueueWrapper); + MessageQueueView messageQueueView = new MessageQueueView("topic", routeData); + when(this.topicRouteService.getAllMessageQueueView("topic")).thenReturn(messageQueueView); - when(this.topicRouteCache.getMessageQueue("notExistTopic")).thenThrow(new MQClientException(ResponseCode.TOPIC_NOT_EXIST, "")); + when(this.topicRouteService.getAllMessageQueueView("notExistTopic")).thenThrow(new MQClientException(ResponseCode.TOPIC_NOT_EXIST, "")); - routeService = new RouteService(this.connectorManager, this.grpcClientManager); + routeService = new RouteService(this.serviceManager, this.grpcClientManager); routeService.start(); } diff --git a/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/TransactionServiceTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/TransactionServiceTest.java new file mode 100644 index 0000000000..f8f647cb01 --- /dev/null +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/cluster/TransactionServiceTest.java @@ -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.grpc.v2.service.cluster; +// +//import apache.rocketmq.v2.Code; +//import apache.rocketmq.v2.EndTransactionRequest; +//import apache.rocketmq.v2.EndTransactionResponse; +//import apache.rocketmq.v2.RecoverOrphanedTransactionCommand; +//import apache.rocketmq.v2.TelemetryCommand; +//import io.grpc.Context; +//import org.apache.rocketmq.common.protocol.header.EndTransactionRequestHeader; +//import org.apache.rocketmq.proxy.grpc.v2.common.ChannelManager; +//import org.apache.rocketmq.proxy.service.transaction.TransactionId; +//import org.apache.rocketmq.proxy.service.transaction.TransactionStateCheckRequest; +//import org.apache.rocketmq.proxy.grpc.v2.adapter.channel.GrpcClientChannel; +//import org.apache.rocketmq.remoting.common.RemotingHelper; +//import org.assertj.core.util.Lists; +//import org.junit.Test; +//import org.mockito.ArgumentCaptor; +//import org.mockito.Mock; +// +//import static org.junit.Assert.assertEquals; +//import static org.junit.Assert.assertTrue; +//import static org.mockito.ArgumentMatchers.any; +//import static org.mockito.ArgumentMatchers.anyString; +//import static org.mockito.Mockito.doNothing; +//import static org.mockito.Mockito.mock; +//import static org.mockito.Mockito.when; +// +//public class TransactionServiceTest extends BaseServiceTest { +// +// private TransactionService transactionService; +// @Mock +// private ChannelManager channelManager; +// +// @Override +// public void beforeEach() throws Throwable { +// transactionService = new TransactionService(this.serviceManager, this.channelManager); +// } +// +// @Test +// public void testCheckTransactionState() { +// GrpcClientChannel channel = mock(GrpcClientChannel.class); +// +// when(channelManager.getClientIdList(anyString())).thenReturn(Lists.newArrayList("clientId")); +// when(channelManager.getChannel(anyString(), any())).thenReturn(channel); +// ArgumentCaptor flushDataCaptor = ArgumentCaptor.forClass(Object.class); +// when(channel.writeAndFlush(flushDataCaptor.capture())).thenReturn(null); +// +// TransactionId transactionId = TransactionId.genByBrokerTransactionId( +// RemotingHelper.string2SocketAddress("127.0.0.1:8080"), +// "71F99B78B6E261357FA259CCA6456118", 1234, 5678); +// transactionService.checkTransactionState(new TransactionStateCheckRequest( +// "group", +// 1L, +// 2L, +// "msgId", +// transactionId, +// "brokerName", +// createMessageExt("msgId", "msgId") +// )); +// +// Object flushData = flushDataCaptor.getValue(); +// assertTrue(flushData instanceof TelemetryCommand); +// TelemetryCommand response = (TelemetryCommand) flushData; +// RecoverOrphanedTransactionCommand command = response.getRecoverOrphanedTransactionCommand(); +// assertEquals(transactionId.getProxyTransactionId(), command.getTransactionId()); +// assertEquals("brokerName", command.getMessageQueue().getBroker().getName()); +// } +// +// @Test +// public void testEndTransaction() throws Exception { +// TransactionId transactionId = TransactionId.genByBrokerTransactionId( +// RemotingHelper.string2SocketAddress("127.0.0.1:8080"), +// "71F99B78B6E261357FA259CCA6456118", 1234, 5678); +// ArgumentCaptor brokerAddrCaptor = ArgumentCaptor.forClass(String.class); +// ArgumentCaptor headerCaptor = ArgumentCaptor.forClass(EndTransactionRequestHeader.class); +// doNothing().when(producerClient) +// .endTransaction(any(), brokerAddrCaptor.capture(), headerCaptor.capture()); +// +// EndTransactionResponse response = transactionService.endTransaction(Context.current(), EndTransactionRequest.newBuilder() +// .setTransactionId(transactionId.getProxyTransactionId()) +// .build() +// ).get(); +// +// assertEquals(Code.OK, response.getStatus().getCode()); +// assertEquals(transactionId.getBrokerTransactionId(), headerCaptor.getValue().getTransactionId()); +// assertEquals("127.0.0.1:8080", brokerAddrCaptor.getValue()); +// } +//} \ No newline at end of file diff --git a/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/LocalPopMessageResultFilterTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/LocalPopMessageResultFilterTest.java new file mode 100644 index 0000000000..4ef59dcdc1 --- /dev/null +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/LocalPopMessageResultFilterTest.java @@ -0,0 +1,135 @@ +///* +// * 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.FilterExpression; +//import apache.rocketmq.v2.ReceiveMessageRequest; +//import apache.rocketmq.v2.Settings; +//import io.grpc.Context; +//import java.net.InetSocketAddress; +//import java.nio.charset.StandardCharsets; +//import java.util.ArrayList; +//import java.util.List; +//import org.apache.rocketmq.broker.BrokerController; +//import org.apache.rocketmq.broker.processor.AckMessageProcessor; +//import org.apache.rocketmq.broker.processor.SendMessageProcessor; +//import org.apache.rocketmq.common.consumer.ReceiptHandle; +//import org.apache.rocketmq.common.message.MessageConst; +//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.proxy.grpc.v2.common.ChannelManager; +//import org.apache.rocketmq.proxy.channel.SimpleChannel; +//import org.apache.rocketmq.proxy.config.InitConfigAndLoggerTest; +//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.junit.Test; +//import org.junit.runner.RunWith; +//import org.mockito.Mock; +//import org.mockito.Mockito; +//import org.mockito.junit.MockitoJUnitRunner; +// +//@RunWith(MockitoJUnitRunner.class) +//public class LocalPopMessageResultFilterTest extends InitConfigAndLoggerTest { +// @Mock +// private ChannelManager channelManagerMock; +// @Mock +// private BrokerController brokerControllerMock; +// @Mock +// private GrpcClientManager grpcClientManagerMock; +// @Mock +// private AckMessageProcessor ackMessageProcessorMock; +// @Mock +// private SendMessageProcessor sendMessageProcessorMock; +// +// private String topic = "topic"; +// +// @Test +// public void testFilterMessageWhenNotMatch() throws RemotingCommandException { +// RemotingCommand remotingCommand = RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, ""); +// Mockito.when(channelManagerMock.createChannel(Mockito.any(Context.class))).thenReturn(new SimpleChannel(null, null, null, 0)); +// Mockito.when(brokerControllerMock.getAckMessageProcessor()).thenReturn(ackMessageProcessorMock); +// Mockito.when(ackMessageProcessorMock.processRequest(Mockito.any(), Mockito.any())).thenReturn(remotingCommand); +// Mockito.when(grpcClientManagerMock.getClientSettings(Mockito.any(Context.class))).thenReturn(Settings.newBuilder().build()); +// List messageExtList = new ArrayList<>(); +// MessageExt messageExt = new MessageExt(); +// messageExt.setTopic(topic); +// messageExt.setQueueOffset(0L); +// messageExt.setBornHost(new InetSocketAddress("127.0.0.1", 10911)); +// messageExt.setStoreHost(new InetSocketAddress("127.0.0.1", 10911)); +// messageExt.setBody("body".getBytes(StandardCharsets.UTF_8)); +// messageExt.putUserProperty("key", "value"); +// messageExt.setTags("b"); +// messageExt.getProperties().put(MessageConst.PROPERTY_POP_CK, ReceiptHandle.builder() +// .startOffset(0L) +// .retrieveTime(0L) +// .invisibleTime(1000L) +// .nextVisibleTime(1000L) +// .reviveQueueId(0) +// .topicType("0") +// .brokerName("brokerName") +// .queueId(0) +// .offset(0L) +// .build().encode()); +// messageExtList.add(messageExt); +// LocalReceiveMessageResultFilter localReceiveMessageResultFilter = new LocalReceiveMessageResultFilter(channelManagerMock, brokerControllerMock, grpcClientManagerMock); +// localReceiveMessageResultFilter.filterMessage(Context.current(), ReceiveMessageRequest.newBuilder() +// .setFilterExpression(FilterExpression.newBuilder() +// .setExpression("a").build()).build(), messageExtList); +// Mockito.verify(ackMessageProcessorMock, Mockito.times(1)).processRequest(Mockito.any(), Mockito.argThat(r -> r.getCode() == RequestCode.ACK_MESSAGE)); +// } +// +// @Test +// public void testFilterMessageWhenDLQ() throws RemotingCommandException { +// RemotingCommand remotingCommand = RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, ""); +// Mockito.when(channelManagerMock.createChannel(Mockito.any(Context.class))).thenReturn(new SimpleChannel(null, null, null, 0)); +// Mockito.when(brokerControllerMock.getSendMessageProcessor()).thenReturn(sendMessageProcessorMock); +// Mockito.when(sendMessageProcessorMock.processRequest(Mockito.any(), Mockito.any())).thenReturn(remotingCommand); +// Mockito.when(brokerControllerMock.getAckMessageProcessor()).thenReturn(ackMessageProcessorMock); +// Mockito.when(ackMessageProcessorMock.processRequest(Mockito.any(), Mockito.any())).thenReturn(remotingCommand); +// Mockito.when(grpcClientManagerMock.getClientSettings(Mockito.any(Context.class))).thenReturn(Settings.newBuilder().build()); +// List messageExtList = new ArrayList<>(); +// MessageExt messageExt = new MessageExt(); +// messageExt.setTopic(topic); +// messageExt.setQueueOffset(0L); +// messageExt.setBornHost(new InetSocketAddress("127.0.0.1", 10911)); +// messageExt.setStoreHost(new InetSocketAddress("127.0.0.1", 10911)); +// messageExt.setBody("body".getBytes(StandardCharsets.UTF_8)); +// messageExt.putUserProperty("key", "value"); +// messageExt.setTags("a"); +// messageExt.getProperties().put(MessageConst.PROPERTY_POP_CK, ReceiptHandle.builder() +// .startOffset(0L) +// .retrieveTime(0L) +// .invisibleTime(1000L) +// .nextVisibleTime(1000L) +// .reviveQueueId(0) +// .topicType("0") +// .brokerName("brokerName") +// .queueId(0) +// .offset(0L) +// .build().encode()); +// messageExtList.add(messageExt); +// LocalReceiveMessageResultFilter localReceiveMessageResultFilter = new LocalReceiveMessageResultFilter(channelManagerMock, brokerControllerMock, grpcClientManagerMock); +// localReceiveMessageResultFilter.filterMessage(Context.current(), ReceiveMessageRequest.newBuilder() +// .setFilterExpression(FilterExpression.newBuilder() +// .setExpression("a").build()).build(), messageExtList); +// Mockito.verify(sendMessageProcessorMock, Mockito.times(1)).processRequest(Mockito.any(), Mockito.argThat(r -> r.getCode() == RequestCode.CONSUMER_SEND_MSG_BACK)); +// Mockito.verify(ackMessageProcessorMock, Mockito.times(1)).processRequest(Mockito.any(), Mockito.argThat(r -> r.getCode() == RequestCode.ACK_MESSAGE)); +// } +//} \ No newline at end of file diff --git a/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriterTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriterTest.java new file mode 100644 index 0000000000..d9ec53eb43 --- /dev/null +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/LocalReceiveMessageResponseStreamWriterTest.java @@ -0,0 +1,170 @@ +///* +// * 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.ReceiveMessageRequest; +//import apache.rocketmq.v2.ReceiveMessageResponse; +//import io.grpc.Context; +//import io.grpc.Status; +//import io.grpc.StatusRuntimeException; +//import io.grpc.stub.ServerCallStreamObserver; +//import java.net.InetSocketAddress; +//import java.nio.charset.StandardCharsets; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.concurrent.atomic.AtomicInteger; +//import org.apache.rocketmq.broker.BrokerController; +//import org.apache.rocketmq.broker.processor.ChangeInvisibleTimeProcessor; +//import org.apache.rocketmq.client.consumer.PopStatus; +//import org.apache.rocketmq.common.consumer.ReceiptHandle; +//import org.apache.rocketmq.common.message.MessageConst; +//import org.apache.rocketmq.common.message.MessageExt; +//import org.apache.rocketmq.proxy.grpc.v2.common.ChannelManager; +//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.service.ReceiveMessageResultFilter; +//import org.apache.rocketmq.remoting.exception.RemotingCommandException; +//import org.junit.Before; +//import org.junit.Test; +//import org.junit.runner.RunWith; +//import org.mockito.ArgumentCaptor; +//import org.mockito.Mock; +//import org.mockito.Mockito; +//import org.mockito.invocation.InvocationOnMock; +//import org.mockito.junit.MockitoJUnitRunner; +//import org.mockito.stubbing.Answer; +// +//import static org.assertj.core.api.Assertions.assertThat; +// +//@RunWith(MockitoJUnitRunner.class) +//public class LocalReceiveMessageResponseStreamWriterTest { +// @Mock +// private ServerCallStreamObserver streamObserverMock; +// @Mock +// private ChannelManager channelManagerMock; +// @Mock +// private BrokerController brokerControllerMock; +// @Mock +// private ReceiveMessageResultFilter receiveMessageResultFilterMock; +// @Mock +// private ChangeInvisibleTimeProcessor changeInvisibleTimeProcessorMock; +// +// private LocalReceiveMessageResponseStreamWriter localReceiveMessageResponseStreamWriter; +// +// @Before +// public void setup() { +// Mockito.when(receiveMessageResultFilterMock.filterMessage(Mockito.any(), Mockito.any(), Mockito.anyList())).thenAnswer((Answer>) invocation -> { +// List messageList = new ArrayList<>(); +// List messageExtList = invocation.getArgument(2); +// for (MessageExt messageExt : messageExtList) { +// messageList.add(GrpcConverter.buildMessage(messageExt)); +// } +// return messageList; +// }); +// localReceiveMessageResponseStreamWriter +// = new LocalReceiveMessageResponseStreamWriter(streamObserverMock, null, channelManagerMock, brokerControllerMock, receiveMessageResultFilterMock); +// } +// +// @Test +// public void testWrite() { +// MessageExt messageExt = new MessageExt(); +// messageExt.setTopic("topic"); +// messageExt.setQueueOffset(0L); +// messageExt.setBornHost(new InetSocketAddress("127.0.0.1", 10911)); +// messageExt.setStoreHost(new InetSocketAddress("127.0.0.1", 10911)); +// messageExt.setBody("body".getBytes(StandardCharsets.UTF_8)); +// messageExt.putUserProperty("key", "value"); +// messageExt.getProperties().put(MessageConst.PROPERTY_POP_CK, ReceiptHandle.builder() +// .startOffset(0L) +// .retrieveTime(0L) +// .invisibleTime(1000L) +// .nextVisibleTime(1000L) +// .reviveQueueId(0) +// .topicType("0") +// .brokerName("brokerName") +// .queueId(0) +// .offset(0L) +// .build().encode()); +// messageExt.putUserProperty("key", "value"); +// List messageExtList = new ArrayList<>(); +// messageExtList.add(messageExt); +// localReceiveMessageResponseStreamWriter.write(Context.current(), ReceiveMessageRequest.newBuilder().build(), PopStatus.FOUND, messageExtList); +// ArgumentCaptor argument = ArgumentCaptor.forClass(ReceiveMessageResponse.class); +// Mockito.verify(streamObserverMock, Mockito.times(2)).onNext(argument.capture()); +// assertThat(argument.getAllValues().get(0)).isEqualTo(ReceiveMessageResponse.newBuilder() +// .setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name())).build()); +// assertThat(argument.getAllValues().get(1)).isEqualTo(ReceiveMessageResponse.newBuilder() +// .setMessage(GrpcConverter.buildMessage(messageExt)).build()); +// } +// +// @Test +// public void testWriteWhenNoMessage() { +// localReceiveMessageResponseStreamWriter.write(Context.current(), ReceiveMessageRequest.newBuilder().build(), PopStatus.FOUND, new ArrayList<>()); +// Mockito.verify(streamObserverMock, Mockito.times(1)).onNext(Mockito.eq(ReceiveMessageResponse.newBuilder() +// .setStatus(ResponseBuilder.buildStatus(Code.OK, "no new message")).build())); +// } +// +// @Test +// public void testWriteWhenCancel() throws RemotingCommandException { +// AtomicInteger onNextCallTimes = new AtomicInteger(0); +// Mockito.doAnswer(mock -> { +// if (onNextCallTimes.get() <=0) { +// onNextCallTimes.incrementAndGet(); +// return null; +// } +// throw new StatusRuntimeException(Status.CANCELLED); +// }).when(streamObserverMock).onNext(Mockito.any()); +// Mockito.when(brokerControllerMock.getChangeInvisibleTimeProcessor()).thenReturn(changeInvisibleTimeProcessorMock); +// MessageExt messageExt = new MessageExt(); +// messageExt.setTopic("topic"); +// messageExt.setQueueOffset(0L); +// messageExt.setBornHost(new InetSocketAddress("127.0.0.1", 10911)); +// messageExt.setStoreHost(new InetSocketAddress("127.0.0.1", 10911)); +// messageExt.setBody("body".getBytes(StandardCharsets.UTF_8)); +// messageExt.putUserProperty("key", "value"); +// messageExt.getProperties().put(MessageConst.PROPERTY_POP_CK, ReceiptHandle.builder() +// .startOffset(0L) +// .retrieveTime(0L) +// .invisibleTime(1000L) +// .nextVisibleTime(1000L) +// .reviveQueueId(0) +// .topicType("0") +// .brokerName("brokerName") +// .queueId(0) +// .offset(0L) +// .build().encode()); +// messageExt.putUserProperty("key", "value"); +// List messageExtList = new ArrayList<>(); +// messageExtList.add(messageExt); +// localReceiveMessageResponseStreamWriter.write(Context.current(), ReceiveMessageRequest.newBuilder().build(), PopStatus.FOUND, messageExtList); +// Mockito.verify(streamObserverMock, Mockito.times(1)).onNext(Mockito.eq(ReceiveMessageResponse.newBuilder() +// .setStatus(ResponseBuilder.buildStatus(Code.OK, Code.OK.name())).build())); +// Mockito.verify(changeInvisibleTimeProcessorMock, Mockito.times(1)).processRequest(Mockito.any(), Mockito.any()); +// } +// +// @Test +// public void testWriteError() { +// String info = "error"; +// localReceiveMessageResponseStreamWriter.write(Context.current(), ReceiveMessageRequest.newBuilder().build(), new ProxyException(Code.ILLEGAL_MESSAGE, info)); +// Mockito.verify(streamObserverMock, Mockito.times(1)).onNext(Mockito.eq(ReceiveMessageResponse.newBuilder() +// .setStatus(ResponseBuilder.buildStatus(Code.ILLEGAL_MESSAGE, info)).build())); +// } +//} \ No newline at end of file diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalWriteQueueSelectorTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/LocalWriteQueueSelectorTest.java similarity index 98% rename from proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalWriteQueueSelectorTest.java rename to proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/LocalWriteQueueSelectorTest.java index bacf434fd5..4e6a871928 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/LocalWriteQueueSelectorTest.java +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/LocalWriteQueueSelectorTest.java @@ -24,7 +24,7 @@ import apache.rocketmq.v2.SystemProperties; import io.grpc.Context; import org.apache.rocketmq.broker.topic.TopicConfigManager; import org.apache.rocketmq.common.TopicConfig; -import org.apache.rocketmq.proxy.connector.route.SelectableMessageQueue; +import org.apache.rocketmq.proxy.service.route.SelectableMessageQueue; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/RouteServiceTest.java b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/RouteServiceTest.java similarity index 89% rename from proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/RouteServiceTest.java rename to proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/RouteServiceTest.java index 25078d4a89..d7c8bee76f 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/v2/service/local/RouteServiceTest.java +++ b/proxy/src/test/resources/backup/test/proxy/grpc/v2/service/local/RouteServiceTest.java @@ -38,7 +38,7 @@ import org.apache.rocketmq.common.protocol.ResponseCode; 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.connector.route.MessageQueueWrapper; +import org.apache.rocketmq.proxy.service.route.MessageQueueView; import org.apache.rocketmq.proxy.grpc.v2.service.cluster.BaseServiceTest; import org.junit.Test; @@ -60,7 +60,7 @@ public class RouteServiceTest extends BaseServiceTest { @Test public void testLocalModeQueryRoute() throws Exception { - RouteService routeService = new RouteService(this.connectorManager, this.grpcClientManager); + RouteService routeService = new RouteService(this.serviceManager, this.grpcClientManager); routeService.start(); when(grpcClientManager.getClientSettings(any(Context.class))).thenReturn(WITH_HOST_SETTINGS); @@ -79,7 +79,7 @@ public class RouteServiceTest extends BaseServiceTest { @Test public void testLocalModeQueryAssignment() throws Exception { - RouteService routeService = new RouteService(this.connectorManager, this.grpcClientManager); + RouteService routeService = new RouteService(this.serviceManager, this.grpcClientManager); routeService.start(); when(grpcClientManager.getClientSettings(any(Context.class))).thenReturn(WITH_HOST_SETTINGS); @@ -124,9 +124,9 @@ public class RouteServiceTest extends BaseServiceTest { routeData.setBrokerDatas(brokerDataList); routeData.setQueueDatas(queueDataList); - MessageQueueWrapper messageQueueWrapper = new MessageQueueWrapper("topic", routeData); - when(this.topicRouteCache.getMessageQueue("topic")).thenReturn(messageQueueWrapper); + MessageQueueView messageQueueView = new MessageQueueView("topic", routeData); + when(this.topicRouteService.getAllMessageQueueView("topic")).thenReturn(messageQueueView); - when(this.topicRouteCache.getMessageQueue("notExistTopic")).thenThrow(new MQClientException(ResponseCode.TOPIC_NOT_EXIST, "")); + when(this.topicRouteService.getAllMessageQueueView("notExistTopic")).thenThrow(new MQClientException(ResponseCode.TOPIC_NOT_EXIST, "")); } } \ No newline at end of file diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/connector/ForwardClientManagerTest.java b/proxy/src/test/resources/backup/test/proxy/service/ForwardClientManagerTest.java similarity index 66% rename from proxy/src/test/java/org/apache/rocketmq/proxy/connector/ForwardClientManagerTest.java rename to proxy/src/test/resources/backup/test/proxy/service/ForwardClientManagerTest.java index 0cd627411a..b6b81eb2d8 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/connector/ForwardClientManagerTest.java +++ b/proxy/src/test/resources/backup/test/proxy/service/ForwardClientManagerTest.java @@ -15,9 +15,9 @@ * limitations under the License. */ -package org.apache.rocketmq.proxy.connector; +package org.apache.rocketmq.proxy.service; -import org.apache.rocketmq.proxy.connector.transaction.TransactionStateChecker; +import org.apache.rocketmq.proxy.service.transaction.TransactionStateChecker; import org.apache.rocketmq.proxy.config.ConfigurationManager; import org.apache.rocketmq.proxy.config.InitConfigAndLoggerTest; import org.junit.Test; @@ -32,23 +32,23 @@ public class ForwardClientManagerTest extends InitConfigAndLoggerTest { ConfigurationManager.getProxyConfig().setNameSrvAddr("127.0.0.1:9876"); TransactionStateChecker mockedTransactionStateChecker = Mockito.mock(TransactionStateChecker.class); - ConnectorManager connectorManager = new ConnectorManager(mockedTransactionStateChecker); - connectorManager.start(); + ServiceManager serviceManager = new ServiceManager(mockedTransactionStateChecker); + serviceManager.start(); - assertThat(connectorManager.getDefaultForwardClient()).isNotNull(); - assertThat(connectorManager.getDefaultForwardClient().getClientNum()) + assertThat(serviceManager.getDefaultForwardClient()).isNotNull(); + assertThat(serviceManager.getDefaultForwardClient().getClientNum()) .isEqualTo(ConfigurationManager.getProxyConfig().getDefaultForwardClientNum()); - assertThat(connectorManager.getForwardProducer()).isNotNull(); - assertThat(connectorManager.getForwardProducer().getClientNum()) + assertThat(serviceManager.getForwardProducer()).isNotNull(); + assertThat(serviceManager.getForwardProducer().getClientNum()) .isEqualTo(ConfigurationManager.getProxyConfig().getForwardProducerNum()); - assertThat(connectorManager.getForwardReadConsumer()).isNotNull(); - assertThat(connectorManager.getForwardReadConsumer().getClientNum()) + assertThat(serviceManager.getForwardReadConsumer()).isNotNull(); + assertThat(serviceManager.getForwardReadConsumer().getClientNum()) .isEqualTo(ConfigurationManager.getProxyConfig().getForwardConsumerNum()); - assertThat(connectorManager.getForwardWriteConsumer()).isNotNull(); - assertThat(connectorManager.getForwardWriteConsumer().getClientNum()) + assertThat(serviceManager.getForwardWriteConsumer()).isNotNull(); + assertThat(serviceManager.getForwardWriteConsumer().getClientNum()) .isEqualTo(ConfigurationManager.getProxyConfig().getForwardConsumerNum()); } diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingCommand.java b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingCommand.java index 7d614cffb5..c2f7b308d0 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingCommand.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingCommand.java @@ -106,7 +106,7 @@ public class RemotingCommand { return cmd; } - private static void setCmdVersion(RemotingCommand cmd) { + protected static void setCmdVersion(RemotingCommand cmd) { if (configVersion >= 0) { cmd.setVersion(configVersion); } else { diff --git a/test/src/test/java/org/apache/rocketmq/test/grpc/v2/ClusterGrpcIT.java b/test/src/test/java/org/apache/rocketmq/test/grpc/v2/ClusterGrpcIT.java index 3aa510ed0d..e9bbdc14f8 100644 --- a/test/src/test/java/org/apache/rocketmq/test/grpc/v2/ClusterGrpcIT.java +++ b/test/src/test/java/org/apache/rocketmq/test/grpc/v2/ClusterGrpcIT.java @@ -23,9 +23,8 @@ import java.time.Duration; import java.util.Map; import org.apache.rocketmq.common.protocol.route.BrokerData; import org.apache.rocketmq.proxy.config.ConfigurationManager; -import org.apache.rocketmq.proxy.grpc.v2.GrpcMessagingProcessor; -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.GrpcMessagingApplication; +import org.apache.rocketmq.proxy.service.ServiceManager; import org.apache.rocketmq.test.util.MQAdminTestUtils; import org.junit.After; import org.junit.Before; @@ -35,16 +34,18 @@ import static org.awaitility.Awaitility.await; public class ClusterGrpcIT extends GrpcBaseIT { - private GrpcForwardService grpcForwardService; + private ServiceManager serviceManager; + private GrpcMessagingApplication grpcMessagingApplication; @Before public void setUp() throws Exception { super.setUp(); ConfigurationManager.getProxyConfig().setTransactionHeartbeatPeriodSecond(3); - grpcForwardService = new ClusterGrpcService(); - grpcForwardService.start(); - GrpcMessagingProcessor processor = new GrpcMessagingProcessor(grpcForwardService); - setUpServer(processor, ConfigurationManager.getProxyConfig().getGrpcServerPort(), true); + serviceManager = ServiceManager.createForClusterMode(); + serviceManager.start(); + grpcMessagingApplication = GrpcMessagingApplication.create(serviceManager); + grpcMessagingApplication.start(); + setUpServer(grpcMessagingApplication, ConfigurationManager.getProxyConfig().getGrpcServerPort(), true); await().atMost(Duration.ofSeconds(40)).until(() -> { Map brokerDataMap = MQAdminTestUtils.getCluster(nsAddr).getBrokerAddrTable(); @@ -54,7 +55,8 @@ public class ClusterGrpcIT extends GrpcBaseIT { @After public void tearDown() throws Exception { - grpcForwardService.shutdown(); + serviceManager.shutdown(); + grpcMessagingApplication.shutdown(); shutdown(); } diff --git a/test/src/test/java/org/apache/rocketmq/test/grpc/v2/LocalGrpcIT.java b/test/src/test/java/org/apache/rocketmq/test/grpc/v2/LocalGrpcIT.java index df68948808..954167e79f 100644 --- a/test/src/test/java/org/apache/rocketmq/test/grpc/v2/LocalGrpcIT.java +++ b/test/src/test/java/org/apache/rocketmq/test/grpc/v2/LocalGrpcIT.java @@ -20,27 +20,31 @@ package org.apache.rocketmq.test.grpc.v2; import apache.rocketmq.v2.QueryAssignmentResponse; import apache.rocketmq.v2.QueryRouteResponse; import org.apache.rocketmq.proxy.config.ConfigurationManager; -import org.apache.rocketmq.proxy.grpc.v2.GrpcMessagingProcessor; -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.junit.After; import org.junit.Before; import org.junit.Test; public class LocalGrpcIT extends GrpcBaseIT { - private LocalGrpcService localGrpcService; + + private ServiceManager serviceManager; + private GrpcMessagingApplication grpcMessagingApplication; @Before public void setUp() throws Exception { super.setUp(); - localGrpcService = new LocalGrpcService(brokerController1); - localGrpcService.start(); - GrpcMessagingProcessor processor = new GrpcMessagingProcessor(localGrpcService); - setUpServer(processor, ConfigurationManager.getProxyConfig().getGrpcServerPort(), true); + serviceManager = ServiceManager.createForClusterMode(); + serviceManager.start(); + grpcMessagingApplication = GrpcMessagingApplication.create(serviceManager); + grpcMessagingApplication.start(); + setUpServer(grpcMessagingApplication, ConfigurationManager.getProxyConfig().getGrpcServerPort(), true); } @After public void clean() throws Exception { - localGrpcService.shutdown(); + serviceManager.shutdown(); + grpcMessagingApplication.shutdown(); shutdown(); }