This commit is contained in:
shuhongfan
2023-09-04 16:40:17 +08:00
commit cf5ac25c14
8267 changed files with 1305066 additions and 0 deletions

View File

@@ -0,0 +1 @@
# Sentinel Cluster Client (Default)

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sentinel-cluster</artifactId>
<groupId>com.alibaba.csp</groupId>
<version>1.8.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sentinel-cluster-client-default</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-cluster-common-default</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,33 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public final class ClientConstants {
public static final int TYPE_PING = 0;
public static final int TYPE_FLOW = 1;
public static final int TYPE_PARAM_FLOW = 2;
public static final int CLIENT_STATUS_OFF = 0;
public static final int CLIENT_STATUS_PENDING = 1;
public static final int CLIENT_STATUS_STARTED = 2;
private ClientConstants() {}
}

View File

@@ -0,0 +1,233 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import com.alibaba.csp.sentinel.cluster.ClusterConstants;
import com.alibaba.csp.sentinel.cluster.ClusterErrorMessages;
import com.alibaba.csp.sentinel.cluster.ClusterTransportClient;
import com.alibaba.csp.sentinel.cluster.TokenResult;
import com.alibaba.csp.sentinel.cluster.TokenResultStatus;
import com.alibaba.csp.sentinel.cluster.TokenServerDescriptor;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientAssignConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfigManager;
import com.alibaba.csp.sentinel.cluster.client.config.ServerChangeObserver;
import com.alibaba.csp.sentinel.cluster.log.ClusterClientStatLogUtil;
import com.alibaba.csp.sentinel.cluster.request.ClusterRequest;
import com.alibaba.csp.sentinel.cluster.request.data.FlowRequestData;
import com.alibaba.csp.sentinel.cluster.request.data.ParamFlowRequestData;
import com.alibaba.csp.sentinel.cluster.response.ClusterResponse;
import com.alibaba.csp.sentinel.cluster.response.data.FlowTokenResponseData;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.StringUtil;
/**
* Default implementation of {@link ClusterTokenClient}.
*
* @author Eric Zhao
* @since 1.4.0
*/
public class DefaultClusterTokenClient implements ClusterTokenClient {
private ClusterTransportClient transportClient;
private TokenServerDescriptor serverDescriptor;
private final AtomicBoolean shouldStart = new AtomicBoolean(false);
public DefaultClusterTokenClient() {
ClusterClientConfigManager.addServerChangeObserver(new ServerChangeObserver() {
@Override
public void onRemoteServerChange(ClusterClientAssignConfig assignConfig) {
changeServer(assignConfig);
}
});
initNewConnection();
}
private boolean serverEqual(TokenServerDescriptor descriptor, ClusterClientAssignConfig config) {
if (descriptor == null || config == null) {
return false;
}
return descriptor.getHost().equals(config.getServerHost()) && descriptor.getPort() == config.getServerPort();
}
private void initNewConnection() {
if (transportClient != null) {
return;
}
String host = ClusterClientConfigManager.getServerHost();
int port = ClusterClientConfigManager.getServerPort();
if (StringUtil.isBlank(host) || port <= 0) {
return;
}
try {
this.transportClient = new NettyTransportClient(host, port);
this.serverDescriptor = new TokenServerDescriptor(host, port);
RecordLog.info("[DefaultClusterTokenClient] New client created: {}", serverDescriptor);
} catch (Exception ex) {
RecordLog.warn("[DefaultClusterTokenClient] Failed to initialize new token client", ex);
}
}
private void changeServer(/*@Valid*/ ClusterClientAssignConfig config) {
if (serverEqual(serverDescriptor, config)) {
return;
}
try {
if (transportClient != null) {
transportClient.stop();
}
// Replace with new, even if the new client is not ready.
this.transportClient = new NettyTransportClient(config.getServerHost(), config.getServerPort());
this.serverDescriptor = new TokenServerDescriptor(config.getServerHost(), config.getServerPort());
startClientIfScheduled();
RecordLog.info("[DefaultClusterTokenClient] New client created: {}", serverDescriptor);
} catch (Exception ex) {
RecordLog.warn("[DefaultClusterTokenClient] Failed to change remote token server", ex);
}
}
private void startClientIfScheduled() throws Exception {
if (shouldStart.get()) {
if (transportClient != null) {
transportClient.start();
} else {
RecordLog.warn("[DefaultClusterTokenClient] Cannot start transport client: client not created");
}
}
}
private void stopClientIfStarted() throws Exception {
if (shouldStart.compareAndSet(true, false)) {
if (transportClient != null) {
transportClient.stop();
}
}
}
@Override
public void start() throws Exception {
if (shouldStart.compareAndSet(false, true)) {
startClientIfScheduled();
}
}
@Override
public void stop() throws Exception {
stopClientIfStarted();
}
@Override
public int getState() {
if (transportClient == null) {
return ClientConstants.CLIENT_STATUS_OFF;
}
return transportClient.isReady() ? ClientConstants.CLIENT_STATUS_STARTED : ClientConstants.CLIENT_STATUS_OFF;
}
@Override
public TokenServerDescriptor currentServer() {
return serverDescriptor;
}
@Override
public TokenResult requestToken(Long flowId, int acquireCount, boolean prioritized) {
if (notValidRequest(flowId, acquireCount)) {
return badRequest();
}
FlowRequestData data = new FlowRequestData().setCount(acquireCount)
.setFlowId(flowId).setPriority(prioritized);
ClusterRequest<FlowRequestData> request = new ClusterRequest<>(ClusterConstants.MSG_TYPE_FLOW, data);
try {
TokenResult result = sendTokenRequest(request);
logForResult(result);
return result;
} catch (Exception ex) {
ClusterClientStatLogUtil.log(ex.getMessage());
return new TokenResult(TokenResultStatus.FAIL);
}
}
@Override
public TokenResult requestParamToken(Long flowId, int acquireCount, Collection<Object> params) {
if (notValidRequest(flowId, acquireCount) || params == null || params.isEmpty()) {
return badRequest();
}
ParamFlowRequestData data = new ParamFlowRequestData().setCount(acquireCount)
.setFlowId(flowId).setParams(params);
ClusterRequest<ParamFlowRequestData> request = new ClusterRequest<>(ClusterConstants.MSG_TYPE_PARAM_FLOW, data);
try {
TokenResult result = sendTokenRequest(request);
logForResult(result);
return result;
} catch (Exception ex) {
ClusterClientStatLogUtil.log(ex.getMessage());
return new TokenResult(TokenResultStatus.FAIL);
}
}
@Override
public TokenResult requestConcurrentToken(String clientAddress, Long ruleId, int acquireCount) {
return null;
}
@Override
public void releaseConcurrentToken(Long tokenId) {
}
private void logForResult(TokenResult result) {
switch (result.getStatus()) {
case TokenResultStatus.NO_RULE_EXISTS:
ClusterClientStatLogUtil.log(ClusterErrorMessages.NO_RULES_IN_SERVER);
break;
case TokenResultStatus.TOO_MANY_REQUEST:
ClusterClientStatLogUtil.log(ClusterErrorMessages.TOO_MANY_REQUESTS);
break;
default:
}
}
private TokenResult sendTokenRequest(ClusterRequest request) throws Exception {
if (transportClient == null) {
RecordLog.warn(
"[DefaultClusterTokenClient] Client not created, please check your config for cluster client");
return clientFail();
}
ClusterResponse response = transportClient.sendRequest(request);
TokenResult result = new TokenResult(response.getStatus());
if (response.getData() != null) {
FlowTokenResponseData responseData = (FlowTokenResponseData)response.getData();
result.setRemaining(responseData.getRemainingCount())
.setWaitInMs(responseData.getWaitInMs());
}
return result;
}
private boolean notValidRequest(Long id, int count) {
return id == null || id <= 0 || count <= 0;
}
private TokenResult badRequest() {
return new TokenResult(TokenResultStatus.BAD_REQUEST);
}
private TokenResult clientFail() {
return new TokenResult(TokenResultStatus.FAIL);
}
}

View File

@@ -0,0 +1,272 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client;
import java.util.AbstractMap.SimpleEntry;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import com.alibaba.csp.sentinel.cluster.ClusterErrorMessages;
import com.alibaba.csp.sentinel.cluster.ClusterTransportClient;
import com.alibaba.csp.sentinel.cluster.client.codec.netty.NettyRequestEncoder;
import com.alibaba.csp.sentinel.cluster.client.codec.netty.NettyResponseDecoder;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfigManager;
import com.alibaba.csp.sentinel.cluster.client.handler.TokenClientHandler;
import com.alibaba.csp.sentinel.cluster.client.handler.TokenClientPromiseHolder;
import com.alibaba.csp.sentinel.cluster.exception.SentinelClusterException;
import com.alibaba.csp.sentinel.cluster.request.ClusterRequest;
import com.alibaba.csp.sentinel.cluster.request.Request;
import com.alibaba.csp.sentinel.cluster.response.ClusterResponse;
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.AssertUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.util.concurrent.GenericFutureListener;
/**
* Netty transport client implementation for Sentinel cluster transport.
*
* @author Eric Zhao
* @since 1.4.0
*/
public class NettyTransportClient implements ClusterTransportClient {
@SuppressWarnings("PMD.ThreadPoolCreationRule")
private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1,
new NamedThreadFactory("sentinel-cluster-transport-client-scheduler", true));
public static final int RECONNECT_DELAY_MS = 2000;
private final String host;
private final int port;
private Channel channel;
private NioEventLoopGroup eventLoopGroup;
private TokenClientHandler clientHandler;
private final AtomicInteger idGenerator = new AtomicInteger(0);
private final AtomicInteger currentState = new AtomicInteger(ClientConstants.CLIENT_STATUS_OFF);
private final AtomicInteger failConnectedTime = new AtomicInteger(0);
private final AtomicBoolean shouldRetry = new AtomicBoolean(true);
public NettyTransportClient(String host, int port) {
AssertUtil.assertNotBlank(host, "remote host cannot be blank");
AssertUtil.isTrue(port > 0, "port should be positive");
this.host = host;
this.port = port;
}
private Bootstrap initClientBootstrap() {
Bootstrap b = new Bootstrap();
eventLoopGroup = new NioEventLoopGroup();
b.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, ClusterClientConfigManager.getConnectTimeout())
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
clientHandler = new TokenClientHandler(currentState, disconnectCallback);
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 2, 0, 2));
pipeline.addLast(new NettyResponseDecoder());
pipeline.addLast(new LengthFieldPrepender(2));
pipeline.addLast(new NettyRequestEncoder());
pipeline.addLast(clientHandler);
}
});
return b;
}
private void connect(Bootstrap b) {
if (currentState.compareAndSet(ClientConstants.CLIENT_STATUS_OFF, ClientConstants.CLIENT_STATUS_PENDING)) {
b.connect(host, port)
.addListener(new GenericFutureListener<ChannelFuture>() {
@Override
public void operationComplete(ChannelFuture future) {
if (future.cause() != null) {
RecordLog.warn(
String.format("[NettyTransportClient] Could not connect to <%s:%d> after %d times",
host, port, failConnectedTime.get()), future.cause());
failConnectedTime.incrementAndGet();
channel = null;
} else {
failConnectedTime.set(0);
channel = future.channel();
RecordLog.info("[NettyTransportClient] Successfully connect to server <{}:{}>", host, port);
}
}
});
}
}
private Runnable disconnectCallback = new Runnable() {
@Override
public void run() {
if (!shouldRetry.get()) {
return;
}
SCHEDULER.schedule(new Runnable() {
@Override
public void run() {
if (shouldRetry.get()) {
RecordLog.info("[NettyTransportClient] Reconnecting to server <{}:{}>", host, port);
try {
startInternal();
} catch (Exception e) {
RecordLog.warn("[NettyTransportClient] Failed to reconnect to server", e);
}
}
}
}, RECONNECT_DELAY_MS * (failConnectedTime.get() + 1), TimeUnit.MILLISECONDS);
cleanUp();
}
};
@Override
public void start() throws Exception {
shouldRetry.set(true);
startInternal();
}
private void startInternal() {
connect(initClientBootstrap());
}
private void cleanUp() {
if (channel != null) {
channel.close();
channel = null;
}
if (eventLoopGroup != null) {
eventLoopGroup.shutdownGracefully();
}
}
@Override
public void stop() throws Exception {
// Stop retrying for connection.
shouldRetry.set(false);
while (currentState.get() == ClientConstants.CLIENT_STATUS_PENDING) {
try {
Thread.sleep(200);
} catch (Exception ex) {
// Ignore.
}
}
cleanUp();
failConnectedTime.set(0);
RecordLog.info("[NettyTransportClient] Cluster transport client stopped");
}
private boolean validRequest(Request request) {
return request != null && request.getType() >= 0;
}
@Override
public boolean isReady() {
return channel != null && clientHandler != null && clientHandler.hasStarted();
}
@Override
public ClusterResponse sendRequest(ClusterRequest request) throws Exception {
if (!isReady()) {
throw new SentinelClusterException(ClusterErrorMessages.CLIENT_NOT_READY);
}
if (!validRequest(request)) {
throw new SentinelClusterException(ClusterErrorMessages.BAD_REQUEST);
}
int xid = getCurrentId();
try {
request.setId(xid);
channel.writeAndFlush(request);
ChannelPromise promise = channel.newPromise();
TokenClientPromiseHolder.putPromise(xid, promise);
if (!promise.await(ClusterClientConfigManager.getRequestTimeout())) {
throw new SentinelClusterException(ClusterErrorMessages.REQUEST_TIME_OUT);
}
SimpleEntry<ChannelPromise, ClusterResponse> entry = TokenClientPromiseHolder.getEntry(xid);
if (entry == null || entry.getValue() == null) {
// Should not go through here.
throw new SentinelClusterException(ClusterErrorMessages.UNEXPECTED_STATUS);
}
return entry.getValue();
} finally {
TokenClientPromiseHolder.remove(xid);
}
}
private int getCurrentId() {
int pre, next;
do {
pre = idGenerator.get();
next = pre >= MAX_ID ? MIN_ID : pre + 1;
} while (!idGenerator.compareAndSet(pre, next));
return next;
}
/*public CompletableFuture<ClusterResponse> sendRequestAsync(ClusterRequest request) {
// Uncomment this when min target JDK is 1.8.
if (!validRequest(request)) {
return CompletableFuture.failedFuture(new IllegalArgumentException("Bad request"));
}
int xid = getCurrentId();
request.setId(xid);
CompletableFuture<ClusterResponse> future = new CompletableFuture<>();
channel.writeAndFlush(request)
.addListener(f -> {
if (f.isSuccess()) {
future.complete(someResult);
} else if (f.cause() != null) {
future.completeExceptionally(f.cause());
} else {
future.cancel(false);
}
});
return future;
}*/
private static final int MIN_ID = 1;
private static final int MAX_ID = 999_999_999;
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec;
import com.alibaba.csp.sentinel.spi.SpiLoader;
import com.alibaba.csp.sentinel.cluster.codec.request.RequestEntityWriter;
import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityDecoder;
import com.alibaba.csp.sentinel.log.RecordLog;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public final class ClientEntityCodecProvider {
private static RequestEntityWriter requestEntityWriter = null;
private static ResponseEntityDecoder responseEntityDecoder = null;
static {
resolveInstance();
}
private static void resolveInstance() {
RequestEntityWriter writer = SpiLoader.of(RequestEntityWriter.class).loadFirstInstance();
if (writer == null) {
RecordLog.warn("[ClientEntityCodecProvider] No existing request entity writer, resolve failed");
} else {
requestEntityWriter = writer;
RecordLog.info("[ClientEntityCodecProvider] Request entity writer resolved: {}",
requestEntityWriter.getClass().getCanonicalName());
}
ResponseEntityDecoder decoder = SpiLoader.of(ResponseEntityDecoder.class).loadFirstInstance();
if (decoder == null) {
RecordLog.warn("[ClientEntityCodecProvider] No existing response entity decoder, resolve failed");
} else {
responseEntityDecoder = decoder;
RecordLog.info("[ClientEntityCodecProvider] Response entity decoder resolved: {}",
responseEntityDecoder.getClass().getCanonicalName());
}
}
public static RequestEntityWriter getRequestEntityWriter() {
return requestEntityWriter;
}
public static ResponseEntityDecoder getResponseEntityDecoder() {
return responseEntityDecoder;
}
private ClientEntityCodecProvider() {}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec;
import com.alibaba.csp.sentinel.cluster.client.codec.registry.RequestDataWriterRegistry;
import com.alibaba.csp.sentinel.cluster.codec.EntityWriter;
import com.alibaba.csp.sentinel.cluster.codec.request.RequestEntityWriter;
import com.alibaba.csp.sentinel.cluster.request.ClusterRequest;
import com.alibaba.csp.sentinel.cluster.request.Request;
import com.alibaba.csp.sentinel.log.RecordLog;
import io.netty.buffer.ByteBuf;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public class DefaultRequestEntityWriter implements RequestEntityWriter<ClusterRequest, ByteBuf> {
@Override
public void writeTo(ClusterRequest request, ByteBuf target) {
int type = request.getType();
EntityWriter<Object, ByteBuf> requestDataWriter = RequestDataWriterRegistry.getWriter(type);
if (requestDataWriter == null) {
RecordLog.warn("[DefaultRequestEntityWriter] Cannot find matching request writer for type <{}>,"
+ " dropping the request", type);
return;
}
// Write head part of request.
writeHead(request, target);
// Write data part.
requestDataWriter.writeTo(request.getData(), target);
}
private void writeHead(Request request, ByteBuf out) {
out.writeInt(request.getId());
out.writeByte(request.getType());
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec;
import com.alibaba.csp.sentinel.cluster.client.codec.registry.ResponseDataDecodeRegistry;
import com.alibaba.csp.sentinel.cluster.codec.EntityDecoder;
import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityDecoder;
import com.alibaba.csp.sentinel.cluster.response.ClusterResponse;
import com.alibaba.csp.sentinel.log.RecordLog;
import io.netty.buffer.ByteBuf;
/**
* <p>Default entity decoder for any {@link ClusterResponse} entity.</p>
*
* <p>Decode format:</p>
* <pre>
* +--------+---------+-----------+---------+
* | xid(4) | type(1) | status(1) | data... |
* +--------+---------+-----------+---------+
* </pre>
*
* @author Eric Zhao
* @since 1.4.0
*/
public class DefaultResponseEntityDecoder implements ResponseEntityDecoder<ByteBuf, ClusterResponse> {
@Override
public ClusterResponse decode(ByteBuf source) {
if (source.readableBytes() >= 6) {
int xid = source.readInt();
int type = source.readByte();
int status = source.readByte();
EntityDecoder<ByteBuf, ?> decoder = ResponseDataDecodeRegistry.getDecoder(type);
if (decoder == null) {
RecordLog.warn("Unknown type of response data decoder: {}", type);
return null;
}
Object data;
if (source.readableBytes() == 0) {
data = null;
} else {
data = decoder.decode(source);
}
return new ClusterResponse<>(xid, type, status, data);
}
return null;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec.data;
import com.alibaba.csp.sentinel.cluster.codec.EntityWriter;
import com.alibaba.csp.sentinel.cluster.request.data.FlowRequestData;
import io.netty.buffer.ByteBuf;
/**
* +-------------------+--------------+----------------+---------------+------------------+
* | RequestID(8 byte) | Type(1 byte) | FlowID(8 byte) | Count(4 byte) | PriorityFlag (1) |
* +-------------------+--------------+----------------+---------------+------------------+
*
* @author Eric Zhao
* @since 1.4.0
*/
public class FlowRequestDataWriter implements EntityWriter<FlowRequestData, ByteBuf> {
@Override
public void writeTo(FlowRequestData entity, ByteBuf target) {
target.writeLong(entity.getFlowId());
target.writeInt(entity.getCount());
target.writeBoolean(entity.isPriority());
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec.data;
import com.alibaba.csp.sentinel.cluster.codec.EntityDecoder;
import com.alibaba.csp.sentinel.cluster.response.data.FlowTokenResponseData;
import io.netty.buffer.ByteBuf;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public class FlowResponseDataDecoder implements EntityDecoder<ByteBuf, FlowTokenResponseData> {
@Override
public FlowTokenResponseData decode(ByteBuf source) {
FlowTokenResponseData data = new FlowTokenResponseData();
if (source.readableBytes() == 8) {
data.setRemainingCount(source.readInt());
data.setWaitInMs(source.readInt());
}
return data;
}
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec.data;
import com.alibaba.csp.sentinel.cluster.ClusterConstants;
import com.alibaba.csp.sentinel.cluster.codec.EntityWriter;
import com.alibaba.csp.sentinel.cluster.request.data.ParamFlowRequestData;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.AssertUtil;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author jialiang.linjl
* @author Eric Zhao
* @since 1.4.0
*/
public class ParamFlowRequestDataWriter implements EntityWriter<ParamFlowRequestData, ByteBuf> {
private final int maxParamByteSize;
public ParamFlowRequestDataWriter() {
this(DEFAULT_PARAM_MAX_SIZE);
}
public ParamFlowRequestDataWriter(int maxParamByteSize) {
AssertUtil.isTrue(maxParamByteSize > 0, "maxParamByteSize should be positive");
this.maxParamByteSize = maxParamByteSize;
}
@Override
public void writeTo(ParamFlowRequestData entity, ByteBuf target) {
target.writeLong(entity.getFlowId());
target.writeInt(entity.getCount());
Collection<Object> params = entity.getParams();
params = resolveValidParams(params);
target.writeInt(params.size());
// Serialize parameters with type flag.
for (Object param : params) {
encodeValue(param, target);
}
}
/**
* Get valid parameters in provided parameter list
*
* @param params
* @return
*/
public List<Object> resolveValidParams(Collection<Object> params) {
List<Object> validParams = new ArrayList<>();
int size = 0;
for (Object param : params) {
int s = calculateParamTransportSize(param);
if (s <= 0) {
RecordLog.warn("[ParamFlowRequestDataWriter] WARN: Non-primitive type detected in params of "
+ "cluster parameter flow control, which is not supported: " + param);
continue;
}
if (size + s > maxParamByteSize) {
RecordLog.warn("[ParamFlowRequestDataWriter] WARN: params size is too big." +
" the configure value is : " + maxParamByteSize + ", the params size is: " + params.size());
break;
}
size += s;
validParams.add(param);
}
return validParams;
}
private void encodeValue(Object param, ByteBuf target) {
// Handle primitive type.
if (param instanceof Integer || int.class.isInstance(param)) {
target.writeByte(ClusterConstants.PARAM_TYPE_INTEGER);
target.writeInt((Integer) param);
} else if (param instanceof String) {
encodeString((String) param, target);
} else if (boolean.class.isInstance(param) || param instanceof Boolean) {
target.writeByte(ClusterConstants.PARAM_TYPE_BOOLEAN);
target.writeBoolean((Boolean) param);
} else if (long.class.isInstance(param) || param instanceof Long) {
target.writeByte(ClusterConstants.PARAM_TYPE_LONG);
target.writeLong((Long) param);
} else if (double.class.isInstance(param) || param instanceof Double) {
target.writeByte(ClusterConstants.PARAM_TYPE_DOUBLE);
target.writeDouble((Double) param);
} else if (float.class.isInstance(param) || param instanceof Float) {
target.writeByte(ClusterConstants.PARAM_TYPE_FLOAT);
target.writeFloat((Float) param);
} else if (byte.class.isInstance(param) || param instanceof Byte) {
target.writeByte(ClusterConstants.PARAM_TYPE_BYTE);
target.writeByte((Byte) param);
} else if (short.class.isInstance(param) || param instanceof Short) {
target.writeByte(ClusterConstants.PARAM_TYPE_SHORT);
target.writeShort((Short) param);
} else {
// Unexpected type, drop.
}
}
private void encodeString(String param, ByteBuf target) {
target.writeByte(ClusterConstants.PARAM_TYPE_STRING);
byte[] tmpChars = param.getBytes();
target.writeInt(tmpChars.length);
target.writeBytes(tmpChars);
}
int calculateParamTransportSize(Object value) {
if (value == null) {
return 0;
}
// Layout for primitives: |type flag(1)|value|
// size = original size + type flag (1)
if (value instanceof Integer || int.class.isInstance(value)) {
return 5;
} else if (value instanceof String) {
// Layout for string: |type flag(1)|length(4)|string content|
String tmpValue = (String) value;
byte[] tmpChars = tmpValue.getBytes();
return 1 + 4 + tmpChars.length;
} else if (boolean.class.isInstance(value) || value instanceof Boolean) {
return 2;
} else if (long.class.isInstance(value) || value instanceof Long) {
return 9;
} else if (double.class.isInstance(value) || value instanceof Double) {
return 9;
} else if (float.class.isInstance(value) || value instanceof Float) {
return 5;
} else if (byte.class.isInstance(value) || value instanceof Byte) {
return 2;
} else if (short.class.isInstance(value) || value instanceof Short) {
return 3;
} else {
// Ignore unexpected type.
return 0;
}
}
private static final int DEFAULT_PARAM_MAX_SIZE = 1024;
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec.data;
import com.alibaba.csp.sentinel.cluster.codec.EntityWriter;
import com.alibaba.csp.sentinel.util.StringUtil;
import io.netty.buffer.ByteBuf;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public class PingRequestDataWriter implements EntityWriter<String, ByteBuf> {
@Override
public void writeTo(String entity, ByteBuf target) {
if (StringUtil.isBlank(entity) || target == null) {
return;
}
byte[] bytes = entity.getBytes();
target.writeInt(bytes.length);
target.writeBytes(bytes);
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec.data;
import com.alibaba.csp.sentinel.cluster.codec.EntityDecoder;
import io.netty.buffer.ByteBuf;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public class PingResponseDataDecoder implements EntityDecoder<ByteBuf, Integer> {
@Override
public Integer decode(ByteBuf source) {
int size = source.readableBytes();
if (size == 1) {
// Compatible with old version (< 1.7.0).
return (int) source.readByte();
}
if (size >= 4) {
return source.readInt();
}
return -1;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec.netty;
import com.alibaba.csp.sentinel.cluster.client.codec.ClientEntityCodecProvider;
import com.alibaba.csp.sentinel.cluster.codec.request.RequestEntityWriter;
import com.alibaba.csp.sentinel.cluster.request.ClusterRequest;
import com.alibaba.csp.sentinel.cluster.request.Request;
import com.alibaba.csp.sentinel.log.RecordLog;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public class NettyRequestEncoder extends MessageToByteEncoder<ClusterRequest> {
@Override
protected void encode(ChannelHandlerContext ctx, ClusterRequest request, ByteBuf out) throws Exception {
RequestEntityWriter<Request, ByteBuf> requestEntityWriter = ClientEntityCodecProvider.getRequestEntityWriter();
if (requestEntityWriter == null) {
RecordLog.warn("[NettyRequestEncoder] Cannot resolve the global request entity writer, dropping the request");
return;
}
requestEntityWriter.writeTo(request, out);
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec.netty;
import java.util.List;
import com.alibaba.csp.sentinel.cluster.client.codec.ClientEntityCodecProvider;
import com.alibaba.csp.sentinel.cluster.client.codec.registry.ResponseDataDecodeRegistry;
import com.alibaba.csp.sentinel.cluster.codec.EntityDecoder;
import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityDecoder;
import com.alibaba.csp.sentinel.cluster.response.ClusterResponse;
import com.alibaba.csp.sentinel.cluster.response.Response;
import com.alibaba.csp.sentinel.log.RecordLog;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public class NettyResponseDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
ResponseEntityDecoder<ByteBuf, Response> responseDecoder = ClientEntityCodecProvider.getResponseEntityDecoder();
if (responseDecoder == null) {
RecordLog.warn("[NettyResponseDecoder] Cannot resolve the global response entity decoder, "
+ "dropping the response");
return;
}
// TODO: handle decode error here.
Response response = responseDecoder.decode(in);
if (response != null) {
out.add(response);
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec.registry;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.csp.sentinel.cluster.codec.EntityWriter;
import io.netty.buffer.ByteBuf;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public final class RequestDataWriterRegistry {
private static final Map<Integer, EntityWriter<Object, ByteBuf>> WRITER_MAP = new HashMap<>();
public static <T> boolean addWriter(int type, EntityWriter<T, ByteBuf> writer) {
if (WRITER_MAP.containsKey(type)) {
return false;
}
WRITER_MAP.put(type, (EntityWriter<Object, ByteBuf>)writer);
return true;
}
public static EntityWriter<Object, ByteBuf> getWriter(int type) {
return WRITER_MAP.get(type);
}
public static boolean remove(int type) {
return WRITER_MAP.remove(type) != null;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec.registry;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.csp.sentinel.cluster.codec.EntityDecoder;
import io.netty.buffer.ByteBuf;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public final class ResponseDataDecodeRegistry {
private static final Map<Integer, EntityDecoder<ByteBuf, ?>> DECODER_MAP = new HashMap<>();
public static boolean addDecoder(int type, EntityDecoder<ByteBuf, ?> decoder) {
if (DECODER_MAP.containsKey(type)) {
return false;
}
DECODER_MAP.put(type, decoder);
return true;
}
public static EntityDecoder<ByteBuf, Object> getDecoder(int type) {
return (EntityDecoder<ByteBuf, Object>)DECODER_MAP.get(type);
}
public static boolean removeDecoder(int type) {
return DECODER_MAP.remove(type) != null;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.config;
/**
* @author Eric Zhao
* @since 1.4.1
*/
public class ClusterClientAssignConfig {
private String serverHost;
private Integer serverPort;
public ClusterClientAssignConfig() {}
public ClusterClientAssignConfig(String serverHost, Integer serverPort) {
this.serverHost = serverHost;
this.serverPort = serverPort;
}
public String getServerHost() {
return serverHost;
}
public ClusterClientAssignConfig setServerHost(String serverHost) {
this.serverHost = serverHost;
return this;
}
public Integer getServerPort() {
return serverPort;
}
public ClusterClientAssignConfig setServerPort(Integer serverPort) {
this.serverPort = serverPort;
return this;
}
@Override
public String toString() {
return "ClusterClientAssignConfig{" +
"serverHost='" + serverHost + '\'' +
", serverPort=" + serverPort +
'}';
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.config;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public class ClusterClientConfig {
private Integer requestTimeout;
public Integer getRequestTimeout() {
return requestTimeout;
}
public ClusterClientConfig setRequestTimeout(Integer requestTimeout) {
this.requestTimeout = requestTimeout;
return this;
}
@Override
public String toString() {
return "ClusterClientConfig{" +
"requestTimeout=" + requestTimeout +
'}';
}
}

View File

@@ -0,0 +1,214 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.config;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.csp.sentinel.cluster.ClusterConstants;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
import com.alibaba.csp.sentinel.property.PropertyListener;
import com.alibaba.csp.sentinel.property.SentinelProperty;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public final class ClusterClientConfigManager {
/**
* Client config properties.
*/
private static volatile String serverHost = null;
private static volatile int serverPort = ClusterConstants.DEFAULT_CLUSTER_SERVER_PORT;
private static volatile int requestTimeout = ClusterConstants.DEFAULT_REQUEST_TIMEOUT;
private static volatile int connectTimeout = ClusterConstants.DEFAULT_CONNECT_TIMEOUT_MILLIS;
private static final PropertyListener<ClusterClientConfig> CONFIG_PROPERTY_LISTENER
= new ClientConfigPropertyListener();
private static final PropertyListener<ClusterClientAssignConfig> ASSIGN_PROPERTY_LISTENER
= new ClientAssignPropertyListener();
private static SentinelProperty<ClusterClientConfig> clientConfigProperty = new DynamicSentinelProperty<>();
private static SentinelProperty<ClusterClientAssignConfig> clientAssignProperty = new DynamicSentinelProperty<>();
private static final List<ServerChangeObserver> SERVER_CHANGE_OBSERVERS = new ArrayList<>();
static {
bindPropertyListener();
}
private static void bindPropertyListener() {
removePropertyListener();
clientAssignProperty.addListener(ASSIGN_PROPERTY_LISTENER);
clientConfigProperty.addListener(CONFIG_PROPERTY_LISTENER);
}
private static void removePropertyListener() {
clientAssignProperty.removeListener(ASSIGN_PROPERTY_LISTENER);
clientConfigProperty.removeListener(CONFIG_PROPERTY_LISTENER);
}
public static void registerServerAssignProperty(SentinelProperty<ClusterClientAssignConfig> property) {
AssertUtil.notNull(property, "property cannot be null");
synchronized (ASSIGN_PROPERTY_LISTENER) {
RecordLog.info("[ClusterClientConfigManager] Registering new server assignment property to cluster "
+ "client config manager");
clientAssignProperty.removeListener(ASSIGN_PROPERTY_LISTENER);
property.addListener(ASSIGN_PROPERTY_LISTENER);
clientAssignProperty = property;
}
}
public static void registerClientConfigProperty(SentinelProperty<ClusterClientConfig> property) {
AssertUtil.notNull(property, "property cannot be null");
synchronized (CONFIG_PROPERTY_LISTENER) {
RecordLog.info("[ClusterClientConfigManager] Registering new global client config property to "
+ "cluster client config manager");
clientConfigProperty.removeListener(CONFIG_PROPERTY_LISTENER);
property.addListener(CONFIG_PROPERTY_LISTENER);
clientConfigProperty = property;
}
}
public static void addServerChangeObserver(ServerChangeObserver observer) {
AssertUtil.notNull(observer, "observer cannot be null");
SERVER_CHANGE_OBSERVERS.add(observer);
}
/**
* Apply new {@link ClusterClientConfig}, while the former config will be replaced.
*
* @param config new config to apply
*/
public static void applyNewConfig(ClusterClientConfig config) {
clientConfigProperty.updateValue(config);
}
public static void applyNewAssignConfig(ClusterClientAssignConfig clusterClientAssignConfig) {
clientAssignProperty.updateValue(clusterClientAssignConfig);
}
private static class ClientAssignPropertyListener implements PropertyListener<ClusterClientAssignConfig> {
@Override
public void configLoad(ClusterClientAssignConfig config) {
if (config == null) {
RecordLog.warn("[ClusterClientConfigManager] Empty initial client assignment config");
return;
}
applyConfig(config);
}
@Override
public void configUpdate(ClusterClientAssignConfig config) {
applyConfig(config);
}
private synchronized void applyConfig(ClusterClientAssignConfig config) {
if (!isValidAssignConfig(config)) {
RecordLog.warn(
"[ClusterClientConfigManager] Invalid cluster client assign config, ignoring: " + config);
return;
}
if (serverPort == config.getServerPort() && config.getServerHost().equals(serverHost)) {
return;
}
RecordLog.info("[ClusterClientConfigManager] Assign to new target token server: {}", config);
updateServerAssignment(config);
}
}
private static class ClientConfigPropertyListener implements PropertyListener<ClusterClientConfig> {
@Override
public void configLoad(ClusterClientConfig config) {
if (config == null) {
RecordLog.warn("[ClusterClientConfigManager] Empty initial client config");
return;
}
applyConfig(config);
}
@Override
public void configUpdate(ClusterClientConfig config) {
applyConfig(config);
}
private synchronized void applyConfig(ClusterClientConfig config) {
if (!isValidClientConfig(config)) {
RecordLog.warn(
"[ClusterClientConfigManager] Invalid cluster client config, ignoring: {}", config);
return;
}
RecordLog.info("[ClusterClientConfigManager] Updating to new client config: {}", config);
updateClientConfigChange(config);
}
}
private static void updateClientConfigChange(ClusterClientConfig config) {
if (config.getRequestTimeout() != requestTimeout) {
requestTimeout = config.getRequestTimeout();
}
}
private static void updateServerAssignment(/*@Valid*/ ClusterClientAssignConfig config) {
String host = config.getServerHost();
int port = config.getServerPort();
for (ServerChangeObserver observer : SERVER_CHANGE_OBSERVERS) {
observer.onRemoteServerChange(config);
}
serverHost = host;
serverPort = port;
}
public static boolean isValidAssignConfig(ClusterClientAssignConfig config) {
return config != null && StringUtil.isNotBlank(config.getServerHost())
&& config.getServerPort() > 0
&& config.getServerPort() <= 65535;
}
public static boolean isValidClientConfig(ClusterClientConfig config) {
return config != null && config.getRequestTimeout() > 0;
}
public static String getServerHost() {
return serverHost;
}
public static int getServerPort() {
return serverPort;
}
public static int getRequestTimeout() {
return requestTimeout;
}
public static int getConnectTimeout() {
return connectTimeout;
}
private ClusterClientConfigManager() {}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.config;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.log.RecordLog;
/**
* <p>
* this class dedicated to reading startup configurations of cluster client
* </p>
*
* @author lianglin
* @since 1.7.0
*/
public class ClusterClientStartUpConfig {
private static final String MAX_PARAM_BYTE_SIZE = "csp.sentinel.cluster.max.param.byte.size";
/**
* Get the max bytes params can be serialized
*
* @return the max bytes, may be null
*/
public static Integer getMaxParamByteSize() {
String maxParamByteSize = SentinelConfig.getConfig(MAX_PARAM_BYTE_SIZE);
try {
return maxParamByteSize == null ? null : Integer.valueOf(maxParamByteSize);
} catch (Exception ex) {
RecordLog.warn("[ClusterClientStartUpConfig] Failed to parse maxParamByteSize: " + maxParamByteSize);
return null;
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.config;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public interface ServerChangeObserver {
/**
* Callback on remote server address change.
*
* @param assignConfig new cluster assignment config
*/
void onRemoteServerChange(ClusterClientAssignConfig assignConfig);
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.handler;
import java.net.InetSocketAddress;
import java.util.concurrent.atomic.AtomicInteger;
import com.alibaba.csp.sentinel.cluster.ClusterConstants;
import com.alibaba.csp.sentinel.cluster.client.ClientConstants;
import com.alibaba.csp.sentinel.cluster.registry.ConfigSupplierRegistry;
import com.alibaba.csp.sentinel.cluster.request.ClusterRequest;
import com.alibaba.csp.sentinel.cluster.response.ClusterResponse;
import com.alibaba.csp.sentinel.log.RecordLog;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* Netty client handler for Sentinel token client.
*
* @author Eric Zhao
* @since 1.4.0
*/
public class TokenClientHandler extends ChannelInboundHandlerAdapter {
private final AtomicInteger currentState;
private final Runnable disconnectCallback;
public TokenClientHandler(AtomicInteger currentState, Runnable disconnectCallback) {
this.currentState = currentState;
this.disconnectCallback = disconnectCallback;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
currentState.set(ClientConstants.CLIENT_STATUS_STARTED);
fireClientPing(ctx);
RecordLog.info("[TokenClientHandler] Client handler active, remote address: {}", getRemoteAddress(ctx));
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ClusterResponse) {
ClusterResponse<?> response = (ClusterResponse) msg;
if (response.getType() == ClusterConstants.MSG_TYPE_PING) {
handlePingResponse(ctx, response);
return;
}
TokenClientPromiseHolder.completePromise(response.getId(), response);
}
}
private void fireClientPing(ChannelHandlerContext ctx) {
// Data body: namespace of the client.
ClusterRequest<String> ping = new ClusterRequest<String>().setId(0)
.setType(ClusterConstants.MSG_TYPE_PING)
.setData(ConfigSupplierRegistry.getNamespaceSupplier().get());
ctx.writeAndFlush(ping);
}
private void handlePingResponse(ChannelHandlerContext ctx, ClusterResponse response) {
if (response.getStatus() == ClusterConstants.RESPONSE_STATUS_OK) {
int count = (int) response.getData();
RecordLog.info("[TokenClientHandler] Client ping OK (target server: {}, connected count: {})",
getRemoteAddress(ctx), count);
} else {
RecordLog.warn("[TokenClientHandler] Client ping failed (target server: {})", getRemoteAddress(ctx));
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
RecordLog.warn("[TokenClientHandler] Client exception caught", cause);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
RecordLog.info("[TokenClientHandler] Client handler inactive, remote address: {}", getRemoteAddress(ctx));
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
RecordLog.info("[TokenClientHandler] Client channel unregistered, remote address: {}", getRemoteAddress(ctx));
currentState.set(ClientConstants.CLIENT_STATUS_OFF);
disconnectCallback.run();
}
private String getRemoteAddress(ChannelHandlerContext ctx) {
if (ctx.channel().remoteAddress() == null) {
return null;
}
InetSocketAddress inetAddress = (InetSocketAddress) ctx.channel().remoteAddress();
return inetAddress.getAddress().getHostAddress() + ":" + inetAddress.getPort();
}
public int getCurrentState() {
return currentState.get();
}
public boolean hasStarted() {
return getCurrentState() == ClientConstants.CLIENT_STATUS_STARTED;
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.handler;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.csp.sentinel.cluster.response.ClusterResponse;
import io.netty.channel.ChannelPromise;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public final class TokenClientPromiseHolder {
private static final Map<Integer, SimpleEntry<ChannelPromise, ClusterResponse>> PROMISE_MAP = new ConcurrentHashMap<>();
public static void putPromise(int xid, ChannelPromise promise) {
PROMISE_MAP.put(xid, new SimpleEntry<ChannelPromise, ClusterResponse>(promise, null));
}
public static SimpleEntry<ChannelPromise, ClusterResponse> getEntry(int xid) {
return PROMISE_MAP.get(xid);
}
public static void remove(int xid) {
PROMISE_MAP.remove(xid);
}
public static <T> boolean completePromise(int xid, ClusterResponse<T> response) {
if (!PROMISE_MAP.containsKey(xid)) {
return false;
}
SimpleEntry<ChannelPromise, ClusterResponse> entry = PROMISE_MAP.get(xid);
if (entry != null) {
ChannelPromise promise = entry.getKey();
if (promise.isDone() || promise.isCancelled()) {
return false;
}
entry.setValue(response);
promise.setSuccess();
return true;
}
return false;
}
private TokenClientPromiseHolder() {}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.init;
import com.alibaba.csp.sentinel.cluster.client.ClientConstants;
import com.alibaba.csp.sentinel.cluster.client.codec.data.FlowRequestDataWriter;
import com.alibaba.csp.sentinel.cluster.client.codec.data.FlowResponseDataDecoder;
import com.alibaba.csp.sentinel.cluster.client.codec.data.ParamFlowRequestDataWriter;
import com.alibaba.csp.sentinel.cluster.client.codec.data.PingRequestDataWriter;
import com.alibaba.csp.sentinel.cluster.client.codec.data.PingResponseDataDecoder;
import com.alibaba.csp.sentinel.cluster.client.codec.registry.RequestDataWriterRegistry;
import com.alibaba.csp.sentinel.cluster.client.codec.registry.ResponseDataDecodeRegistry;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientStartUpConfig;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.init.InitOrder;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@InitOrder(0)
public class DefaultClusterClientInitFunc implements InitFunc {
@Override
public void init() throws Exception {
initDefaultEntityWriters();
initDefaultEntityDecoders();
}
private void initDefaultEntityWriters() {
RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_PING, new PingRequestDataWriter());
RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_FLOW, new FlowRequestDataWriter());
Integer maxParamByteSize = ClusterClientStartUpConfig.getMaxParamByteSize();
if (maxParamByteSize == null) {
RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_PARAM_FLOW, new ParamFlowRequestDataWriter());
} else {
RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_PARAM_FLOW, new ParamFlowRequestDataWriter(maxParamByteSize));
}
}
private void initDefaultEntityDecoders() {
ResponseDataDecodeRegistry.addDecoder(ClientConstants.TYPE_PING, new PingResponseDataDecoder());
ResponseDataDecodeRegistry.addDecoder(ClientConstants.TYPE_FLOW, new FlowResponseDataDecoder());
ResponseDataDecodeRegistry.addDecoder(ClientConstants.TYPE_PARAM_FLOW, new FlowResponseDataDecoder());
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.command.entity;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientAssignConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfig;
/**
* @author Eric Zhao
* @since 1.4.1
*/
public class ClusterClientStateEntity {
private String serverHost;
private Integer serverPort;
private Integer clientState;
private Integer requestTimeout;
public String getServerHost() {
return serverHost;
}
public ClusterClientStateEntity setServerHost(String serverHost) {
this.serverHost = serverHost;
return this;
}
public Integer getServerPort() {
return serverPort;
}
public ClusterClientStateEntity setServerPort(Integer serverPort) {
this.serverPort = serverPort;
return this;
}
public Integer getRequestTimeout() {
return requestTimeout;
}
public ClusterClientStateEntity setRequestTimeout(Integer requestTimeout) {
this.requestTimeout = requestTimeout;
return this;
}
public Integer getClientState() {
return clientState;
}
public ClusterClientStateEntity setClientState(Integer clientState) {
this.clientState = clientState;
return this;
}
public ClusterClientConfig toClientConfig() {
return new ClusterClientConfig().setRequestTimeout(requestTimeout);
}
public ClusterClientAssignConfig toAssignConfig() {
return new ClusterClientAssignConfig()
.setServerHost(serverHost)
.setServerPort(serverPort);
}
@Override
public String toString() {
return "ClusterClientStateEntity{" +
"serverHost='" + serverHost + '\'' +
", serverPort=" + serverPort +
", clientState=" + clientState +
", requestTimeout=" + requestTimeout +
'}';
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.command.handler;
import com.alibaba.csp.sentinel.cluster.client.ClientConstants;
import com.alibaba.csp.sentinel.cluster.client.TokenClientProvider;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfigManager;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.command.entity.ClusterClientStateEntity;
import com.alibaba.fastjson.JSON;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@CommandMapping(name = "cluster/client/fetchConfig", desc = "get cluster client config")
public class FetchClusterClientConfigHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
ClusterClientStateEntity stateVO = new ClusterClientStateEntity()
.setServerHost(ClusterClientConfigManager.getServerHost())
.setServerPort(ClusterClientConfigManager.getServerPort())
.setRequestTimeout(ClusterClientConfigManager.getRequestTimeout());
if (TokenClientProvider.isClientSpiAvailable()) {
stateVO.setClientState(TokenClientProvider.getClient().getState());
} else {
stateVO.setClientState(ClientConstants.CLIENT_STATUS_OFF);
}
return CommandResponse.ofSuccess(JSON.toJSONString(stateVO));
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.command.handler;
import java.net.URLDecoder;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfigManager;
import com.alibaba.csp.sentinel.command.CommandConstants;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.command.entity.ClusterClientStateEntity;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fastjson.JSON;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@CommandMapping(name = "cluster/client/modifyConfig", desc = "modify cluster client config")
public class ModifyClusterClientConfigHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
String data = request.getParam("data");
if (StringUtil.isBlank(data)) {
return CommandResponse.ofFailure(new IllegalArgumentException("empty data"));
}
try {
data = URLDecoder.decode(data, "utf-8");
RecordLog.info("[ModifyClusterClientConfigHandler] Receiving cluster client config: {}", data);
ClusterClientStateEntity entity = JSON.parseObject(data, ClusterClientStateEntity.class);
ClusterClientConfigManager.applyNewConfig(entity.toClientConfig());
ClusterClientConfigManager.applyNewAssignConfig(entity.toAssignConfig());
return CommandResponse.ofSuccess(CommandConstants.MSG_SUCCESS);
} catch (Exception e) {
RecordLog.warn("[ModifyClusterClientConfigHandler] Decode client cluster config error", e);
return CommandResponse.ofFailure(e, "decode client cluster config error");
}
}
}

View File

@@ -0,0 +1 @@
com.alibaba.csp.sentinel.cluster.client.DefaultClusterTokenClient

View File

@@ -0,0 +1 @@
com.alibaba.csp.sentinel.cluster.client.codec.DefaultRequestEntityWriter

View File

@@ -0,0 +1 @@
com.alibaba.csp.sentinel.cluster.client.codec.DefaultResponseEntityDecoder

View File

@@ -0,0 +1,2 @@
com.alibaba.csp.sentinel.command.handler.ModifyClusterClientConfigHandler
com.alibaba.csp.sentinel.command.handler.FetchClusterClientConfigHandler

View File

@@ -0,0 +1 @@
com.alibaba.csp.sentinel.cluster.client.init.DefaultClusterClientInitFunc

View File

@@ -0,0 +1,36 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec.data;
import com.alibaba.csp.sentinel.cluster.response.data.FlowTokenResponseData;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.junit.Assert;
import org.junit.Test;
public class FlowResponseDataDecoderTest {
@Test
public void testDecode() {
ByteBuf buf = Unpooled.buffer();
FlowResponseDataDecoder decoder = new FlowResponseDataDecoder();
FlowTokenResponseData data = new FlowTokenResponseData();
data.setRemainingCount(12);
data.setWaitInMs(13);
buf.writeInt(12);
buf.writeInt(13);
Assert.assertEquals(decoder.decode(buf), data);
}
}

View File

@@ -0,0 +1,77 @@
package com.alibaba.csp.sentinel.cluster.client.codec.data;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.*;
/**
* @author Eric Zhao
*/
public class ParamFlowRequestDataWriterTest {
@Test
public void testCalculateParamTransportSize() {
ParamFlowRequestDataWriter writer = new ParamFlowRequestDataWriter();
// POJO (non-primitive type) should not be regarded as a valid parameter.
assertEquals(0, writer.calculateParamTransportSize(new SomePojo().setParam1("abc")));
assertEquals(4 + 1, writer.calculateParamTransportSize(1));
assertEquals(1 + 1, writer.calculateParamTransportSize((byte) 1));
assertEquals(1 + 1, writer.calculateParamTransportSize(false));
assertEquals(8 + 1, writer.calculateParamTransportSize(2L));
assertEquals(8 + 1, writer.calculateParamTransportSize(4.0d));
final String paramStr = "Sentinel";
assertEquals(1 + 4 + paramStr.getBytes().length, writer.calculateParamTransportSize(paramStr));
}
@Test
public void testResolveValidParams() {
final int maxSize = 15;
ParamFlowRequestDataWriter writer = new ParamFlowRequestDataWriter(maxSize);
ArrayList<Object> params = new ArrayList<Object>() {{
add(1);
add(64);
add(3);
}};
List<Object> validParams = writer.resolveValidParams(params);
assertTrue(validParams.contains(1) && validParams.contains(64) && validParams.contains(3));
//when over maxSize, the exceed number should not be contained
params.add(5);
assertFalse(writer.resolveValidParams(params).contains(5));
//POJO (non-primitive type) should not be regarded as a valid parameter
assertTrue(writer.resolveValidParams(new ArrayList<Object>() {{
add(new SomePojo());
}}).size() == 0);
}
private static class SomePojo {
private String param1;
public String getParam1() {
return param1;
}
public SomePojo setParam1(String param1) {
this.param1 = param1;
return this;
}
@Override
public String toString() {
return "SomePojo{" +
"param1='" + param1 + '\'' +
'}';
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.csp.sentinel.cluster.client.codec.data;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eric Zhao
*/
public class PingResponseDataDecoderTest {
@Test
public void testDecodePingResponseData() {
ByteBuf buf = Unpooled.buffer();
PingResponseDataDecoder decoder = new PingResponseDataDecoder();
int big = Integer.MAX_VALUE;
buf.writeInt(big);
assertThat(decoder.decode(buf)).isEqualTo(big);
byte small = 12;
buf.writeByte(small);
assertThat(decoder.decode(buf)).isEqualTo(small);
buf.release();
}
}