Add default Sentinel cluster transport implementation with Netty
- Add Netty transport client implementation and default cluster token client - Add config manager for cluster client - Add codec SPI mechanism for client entity decoder / writer Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
b3ae7f58bf
commit
6caa19ea95
|
|
@ -0,0 +1 @@
|
|||
# Sentinel Cluster Client (Default)
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
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() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 com.alibaba.csp.sentinel.cluster.ClusterConstants;
|
||||
import com.alibaba.csp.sentinel.cluster.ClusterTokenClient;
|
||||
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.ClusterClientConfig;
|
||||
import com.alibaba.csp.sentinel.cluster.log.ClusterStatLogUtil;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class DefaultClusterTokenClient implements ClusterTokenClient {
|
||||
|
||||
private ClusterTransportClient transportClient;
|
||||
|
||||
public DefaultClusterTokenClient() {
|
||||
// TODO: load and create transport client here.
|
||||
}
|
||||
|
||||
public DefaultClusterTokenClient(ClusterTransportClient transportClient) {
|
||||
this.transportClient = transportClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenServerDescriptor currentServer() {
|
||||
return new TokenServerDescriptor();
|
||||
}
|
||||
|
||||
@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 {
|
||||
return sendTokenRequest(request);
|
||||
} catch (Exception ex) {
|
||||
ClusterStatLogUtil.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 {
|
||||
return sendTokenRequest(request);
|
||||
} catch (Exception ex) {
|
||||
ClusterStatLogUtil.log(ex.getMessage());
|
||||
return new TokenResult(TokenResultStatus.FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
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 sendTokenRequest(ClusterRequest request) throws Exception {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* 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.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.ClusterClientConfig;
|
||||
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.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 {
|
||||
|
||||
private ClusterClientConfig clientConfig;
|
||||
private String host;
|
||||
private int port;
|
||||
|
||||
private Channel channel;
|
||||
private NioEventLoopGroup eventLoopGroup;
|
||||
private TokenClientHandler clientHandler;
|
||||
|
||||
private AtomicInteger idGenerator = new AtomicInteger(0);
|
||||
|
||||
private AtomicInteger failConnectedTime = new AtomicInteger(0);
|
||||
|
||||
public NettyTransportClient(ClusterClientConfig clientConfig, String host, int port) {
|
||||
this.clientConfig = clientConfig;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
private Bootstrap initClientBootstrap() {
|
||||
Bootstrap b = new Bootstrap();
|
||||
eventLoopGroup = new NioEventLoopGroup();
|
||||
b.group(eventLoopGroup)
|
||||
.channel(NioSocketChannel.class)
|
||||
.option(ChannelOption.SO_TIMEOUT, 20)
|
||||
.option(ChannelOption.TCP_NODELAY, true)
|
||||
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
clientHandler = new TokenClientHandler();
|
||||
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) {
|
||||
b.connect(host, port).addListener(new GenericFutureListener<ChannelFuture>() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) {
|
||||
if (future.cause() != null) {
|
||||
RecordLog.warn(
|
||||
"[NettyTransportClient] Could not connect after " + failConnectedTime.get() + " times",
|
||||
future.cause());
|
||||
failConnectedTime.incrementAndGet();
|
||||
channel = null;
|
||||
} else {
|
||||
failConnectedTime.set(0);
|
||||
channel = future.channel();
|
||||
RecordLog.info("[NettyTransportClient] Successfully connect to server " + host + ":" + port);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void start() {
|
||||
connect(initClientBootstrap());
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (channel != null) {
|
||||
channel.close();
|
||||
channel = null;
|
||||
}
|
||||
if (eventLoopGroup != null) {
|
||||
eventLoopGroup.shutdownGracefully();
|
||||
}
|
||||
failConnectedTime.set(0);
|
||||
|
||||
RecordLog.info("[NettyTransportClient] Token client stopped");
|
||||
}
|
||||
|
||||
private boolean validRequest(Request request) {
|
||||
return request != null && request.getType() >= 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// TODO: timeout
|
||||
if (!promise.await(20)) {
|
||||
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() {
|
||||
if (idGenerator.get() > MAX_ID) {
|
||||
idGenerator.set(0);
|
||||
}
|
||||
return idGenerator.incrementAndGet();
|
||||
}
|
||||
|
||||
/*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 MAX_ID = 999_999_999;
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.util.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.loadFirstInstance(RequestEntityWriter.class);
|
||||
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.loadFirstInstance(ResponseEntityDecoder.class);
|
||||
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() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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) {
|
||||
// TODO: may need to throw exception?
|
||||
RecordLog.warn(
|
||||
"[NettyRequestEncoder] Cannot find matching request writer for type <{0}>, 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());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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: {0}", type);
|
||||
return null;
|
||||
}
|
||||
|
||||
Object data;
|
||||
if (source.readableBytes() == 0) {
|
||||
data = null;
|
||||
} else {
|
||||
// TODO: handle decode error here.
|
||||
data = decoder.decode(source);
|
||||
}
|
||||
|
||||
return new ClusterResponse<>(xid, type, status, data);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -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(4 byte) | Type(1 byte) | FlowID(4 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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* 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 java.util.Collection;
|
||||
|
||||
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 io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class ParamFlowRequestDataWriter implements EntityWriter<ParamFlowRequestData, ByteBuf> {
|
||||
|
||||
@Override
|
||||
public void writeTo(ParamFlowRequestData entity, ByteBuf target) {
|
||||
target.writeLong(entity.getFlowId());
|
||||
target.writeInt(entity.getCount());
|
||||
|
||||
Collection<Object> params = entity.getParams();
|
||||
|
||||
// Write parameter amount.
|
||||
int amount = calculateParamAmount(params);
|
||||
target.writeInt(amount);
|
||||
|
||||
// Serialize parameters with type flag.
|
||||
for (Object param : entity.getParams()) {
|
||||
encodeValue(param, target);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private int calculateParamAmount(/*@NonEmpty*/ Collection<Object> params) {
|
||||
int size = 0;
|
||||
int length = 0;
|
||||
for (Object param : params) {
|
||||
int s = calculateParamTransportSize(param);
|
||||
if (size + s > PARAM_MAX_SIZE) {
|
||||
break;
|
||||
}
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
private int calculateParamTransportSize(Object value) {
|
||||
// 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 PARAM_MAX_SIZE = 1000;
|
||||
}
|
||||
|
|
@ -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
|
||||
*/
|
||||
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) {
|
||||
// TODO: may need to throw exception?
|
||||
RecordLog.warn("[NettyRequestEncoder] Cannot resolve the global request entity writer, dropping the request");
|
||||
return;
|
||||
}
|
||||
|
||||
requestEntityWriter.writeTo(request, out);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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) {
|
||||
// TODO: may need to throw exception?
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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.config;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class ClusterClientConfig {
|
||||
|
||||
private String serverHost;
|
||||
private int serverPort;
|
||||
|
||||
private int requestTimeout;
|
||||
private int connectTimeout;
|
||||
|
||||
public String getServerHost() {
|
||||
return serverHost;
|
||||
}
|
||||
|
||||
public ClusterClientConfig setServerHost(String serverHost) {
|
||||
this.serverHost = serverHost;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
return serverPort;
|
||||
}
|
||||
|
||||
public ClusterClientConfig setServerPort(int serverPort) {
|
||||
this.serverPort = serverPort;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRequestTimeout() {
|
||||
return requestTimeout;
|
||||
}
|
||||
|
||||
public ClusterClientConfig setRequestTimeout(int requestTimeout) {
|
||||
this.requestTimeout = requestTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getConnectTimeout() {
|
||||
return connectTimeout;
|
||||
}
|
||||
|
||||
public ClusterClientConfig setConnectTimeout(int connectTimeout) {
|
||||
this.connectTimeout = connectTimeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
import com.alibaba.csp.sentinel.cluster.ClusterConstants;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public final class ClusterClientConfigManager {
|
||||
|
||||
private static volatile String serverIp;
|
||||
private static volatile int serverPort = ClusterConstants.DEFAULT_CLUSTER_SERVER_PORT;
|
||||
private static volatile int requestTimeout = ClusterConstants.DEFAULT_REQUEST_TIMEOUT;
|
||||
|
||||
private ClusterClientConfigManager() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.alibaba.csp.sentinel.cluster.client.ClientConstants;
|
||||
import com.alibaba.csp.sentinel.cluster.response.ClusterResponse;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class TokenClientHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final AtomicInteger currentState = new AtomicInteger(ClientConstants.CLIENT_STATUS_OFF);
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
currentState.set(ClientConstants.CLIENT_STATUS_STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
System.out.println(String.format("[%s] Client message recv: %s", System.currentTimeMillis(), msg));
|
||||
if (msg instanceof ClusterResponse) {
|
||||
ClusterResponse<?> response = (ClusterResponse) msg;
|
||||
|
||||
TokenClientPromiseHolder.completePromise(response.getId(), response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
super.exceptionCaught(ctx, cause);
|
||||
cause.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
|
||||
currentState.set(ClientConstants.CLIENT_STATUS_OFF);
|
||||
}
|
||||
|
||||
public int getCurrentState() {
|
||||
return currentState.get();
|
||||
}
|
||||
|
||||
public boolean hasStarted() {
|
||||
return getCurrentState() == ClientConstants.CLIENT_STATUS_STARTED;
|
||||
}
|
||||
}
|
||||
|
|
@ -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.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;
|
||||
}
|
||||
ChannelPromise promise = PROMISE_MAP.get(xid).getKey();
|
||||
if (promise.isDone() || promise.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
PROMISE_MAP.get(xid).setValue(response);
|
||||
promise.setSuccess();
|
||||
return true;
|
||||
}
|
||||
|
||||
private TokenClientPromiseHolder() {}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
com.alibaba.csp.sentinel.cluster.client.codec.DefaultRequestEntityWriter
|
||||
|
|
@ -0,0 +1 @@
|
|||
com.alibaba.csp.sentinel.cluster.client.codec.DefaultResponseEntityDecoder
|
||||
Loading…
Reference in New Issue