diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/ClusterTokenClient.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/ClusterTokenClient.java new file mode 100644 index 00000000..6a230c07 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/ClusterTokenClient.java @@ -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; + +/** + * Token client interface for distributed flow control. + * + * @author Eric Zhao + * @since 1.4.0 + */ +public interface ClusterTokenClient extends TokenService { + + /** + * Get descriptor of current token server. + * + * @return current token server + */ + TokenServerDescriptor currentServer(); +} \ No newline at end of file diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenClientProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenClientProvider.java new file mode 100644 index 00000000..592046c9 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenClientProvider.java @@ -0,0 +1,61 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +import com.alibaba.csp.sentinel.log.RecordLog; + +/** + * Provider for a universal {@link ClusterTokenClient} instance. + * + * @author Eric Zhao + * @since 1.4.0 + */ +public final class TokenClientProvider { + + private static ClusterTokenClient client = null; + + private static final ServiceLoader LOADER = ServiceLoader.load(ClusterTokenClient.class); + + static { + // Not strictly thread-safe, but it's OK since it will be resolved only once. + resolveTokenClientInstance(); + } + + public static ClusterTokenClient getClient() { + return client; + } + + private static void resolveTokenClientInstance() { + List clients = new ArrayList(); + for (ClusterTokenClient client : LOADER) { + clients.add(client); + } + + if (!clients.isEmpty()) { + // Get first. + client = clients.get(0); + RecordLog.info("[TokenClientProvider] Token client resolved: " + client.getClass().getCanonicalName()); + } else { + RecordLog.warn("[TokenClientProvider] No existing token client, resolve failed"); + } + } + + private TokenClientProvider() {} +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResult.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResult.java new file mode 100644 index 00000000..29fa064a --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResult.java @@ -0,0 +1,86 @@ +/* + * 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; + +import java.util.Map; + +/** + * Result entity of acquiring cluster flow token. + * + * @author Eric Zhao + * @since 1.4.0 + */ +public class TokenResult { + + private Integer status; + + private int remaining; + private int waitInMs; + + private Map attachments; + + public TokenResult() {} + + public TokenResult(Integer status) { + this.status = status; + } + + public Integer getStatus() { + return status; + } + + public TokenResult setStatus(Integer status) { + this.status = status; + return this; + } + + public int getRemaining() { + return remaining; + } + + public TokenResult setRemaining(int remaining) { + this.remaining = remaining; + return this; + } + + public int getWaitInMs() { + return waitInMs; + } + + public TokenResult setWaitInMs(int waitInMs) { + this.waitInMs = waitInMs; + return this; + } + + public Map getAttachments() { + return attachments; + } + + public TokenResult setAttachments(Map attachments) { + this.attachments = attachments; + return this; + } + + @Override + public String toString() { + return "TokenResult{" + + "status=" + status + + ", remaining=" + remaining + + ", waitInMs=" + waitInMs + + ", attachments=" + attachments + + '}'; + } +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResultStatus.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResultStatus.java new file mode 100644 index 00000000..275d761e --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResultStatus.java @@ -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; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public final class TokenResultStatus { + + /** + * Bad client request. + */ + public static final int BAD_REQUEST = -4; + /** + * Server or client unexpected failure (due to transport or serialization failure). + */ + public static final int FAIL = -1; + + /** + * Token acquired. + */ + public static final int OK = 0; + + /** + * Token acquire failed (blocked). + */ + public static final int BLOCKED = 1; + /** + * Should wait for next buckets. + */ + public static final int SHOULD_WAIT = 2; + /** + * Token acquire failed (no rule exists). + */ + public static final int NO_RULE_EXISTS = 3; + /** + * Token acquire failed (reference resource is not available). + */ + public static final int NO_REF_RULE_EXISTS = 4; + /** + * Token acquire failed (strategy not available). + */ + public static final int NOT_AVAILABLE = 5; + + private TokenResultStatus() {} +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenServerDescriptor.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenServerDescriptor.java new file mode 100644 index 00000000..98739179 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenServerDescriptor.java @@ -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; + +/** + * A simple descriptor for Sentinel token server. + * + * @author Eric Zhao + * @since 1.4.0 + */ +public class TokenServerDescriptor { + + private String host; + private int port; + private String type; + + public TokenServerDescriptor() {} + + public TokenServerDescriptor(String host, int port) { + this.host = host; + this.port = port; + } + + public String getHost() { + return host; + } + + public TokenServerDescriptor setHost(String host) { + this.host = host; + return this; + } + + public int getPort() { + return port; + } + + public TokenServerDescriptor setPort(int port) { + this.port = port; + return this; + } + + public String getType() { + return type; + } + + public TokenServerDescriptor setType(String type) { + this.type = type; + return this; + } + + @Override + public String toString() { + return "TokenServerDescriptor{" + + "host='" + host + '\'' + + ", port=" + port + + ", type='" + type + '\'' + + '}'; + } +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java new file mode 100644 index 00000000..d02c562f --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java @@ -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; + +import java.util.Collection; + +/** + * Service interface of flow control. + * + * @author Eric Zhao + * @since 1.4.0 + */ +public interface TokenService { + + /** + * Request tokens from remote token server. + * + * @param ruleId the unique rule ID + * @param acquireCount token count to acquire + * @param prioritized whether the request is prioritized + * @return result of the token request + */ + TokenResult requestToken(Integer ruleId, int acquireCount, boolean prioritized); + + /** + * Request tokens for a specific parameter from remote token server. + * + * @param ruleId the unique rule ID + * @param acquireCount token count to acquire + * @param params parameter list + * @return result of the token request + */ + TokenResult requestParamToken(Integer ruleId, int acquireCount, Collection params); +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/log/ClusterStatLogUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/log/ClusterStatLogUtil.java new file mode 100644 index 00000000..16f914c6 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/log/ClusterStatLogUtil.java @@ -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.log; + +import java.io.File; + +import com.alibaba.csp.sentinel.eagleeye.EagleEye; +import com.alibaba.csp.sentinel.eagleeye.StatLogger; +import com.alibaba.csp.sentinel.log.LogBase; + +/** + * @author jialiang.linjl + * @author Eric Zhao + * @since 1.4.0 + */ +public final class ClusterStatLogUtil { + + private static final String FILE_NAME = "sentinel-cluster.log"; + + private static StatLogger statLogger; + + static { + String path = LogBase.getLogBaseDir() + FILE_NAME; + + statLogger = EagleEye.statLoggerBuilder("sentinel-cluster-record") + .intervalSeconds(1) + .entryDelimiter('|') + .keyDelimiter(',') + .valueDelimiter(',') + .maxEntryCount(5000) + .configLogFilePath(path) + .maxFileSizeMB(300) + .maxBackupIndex(3) + .buildSingleton(); + } + + public static void log(String msg) { + statLogger.stat(msg).count(); + } + + public static void log(String msg, int count) { + statLogger.stat(msg).count(count); + } + + private ClusterStatLogUtil() {} +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlotTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlotTest.java index 552f8407..ee5de0a8 100644 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlotTest.java +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlotTest.java @@ -54,7 +54,7 @@ public class FlowSlotTest { Context context = mock(Context.class); DefaultNode node = mock(DefaultNode.class); doCallRealMethod().when(flowSlot).checkFlow(any(ResourceWrapper.class), any(Context.class), - any(DefaultNode.class), anyInt()); + any(DefaultNode.class), anyInt(), anyBoolean()); String resA = "resAK"; String resB = "resBK"; @@ -63,13 +63,13 @@ public class FlowSlotTest { // Here we only load rules for resA. FlowRuleManager.loadRules(Collections.singletonList(rule1)); - when(flowSlot.canPassCheck(eq(rule1), any(Context.class), any(DefaultNode.class), anyInt())) + when(flowSlot.canPassCheck(eq(rule1), any(Context.class), any(DefaultNode.class), anyInt(), anyBoolean())) .thenReturn(true); - when(flowSlot.canPassCheck(eq(rule2), any(Context.class), any(DefaultNode.class), anyInt())) + when(flowSlot.canPassCheck(eq(rule2), any(Context.class), any(DefaultNode.class), anyInt(), anyBoolean())) .thenReturn(false); - flowSlot.checkFlow(new StringResourceWrapper(resA, EntryType.IN), context, node, 1); - flowSlot.checkFlow(new StringResourceWrapper(resB, EntryType.IN), context, node, 1); + flowSlot.checkFlow(new StringResourceWrapper(resA, EntryType.IN), context, node, 1, false); + flowSlot.checkFlow(new StringResourceWrapper(resB, EntryType.IN), context, node, 1, false); } @Test(expected = FlowException.class) @@ -78,15 +78,15 @@ public class FlowSlotTest { Context context = mock(Context.class); DefaultNode node = mock(DefaultNode.class); doCallRealMethod().when(flowSlot).checkFlow(any(ResourceWrapper.class), any(Context.class), - any(DefaultNode.class), anyInt()); + any(DefaultNode.class), anyInt(), anyBoolean()); String resA = "resAK"; FlowRule rule = new FlowRule(resA).setCount(10); FlowRuleManager.loadRules(Collections.singletonList(rule)); - when(flowSlot.canPassCheck(any(FlowRule.class), any(Context.class), any(DefaultNode.class), anyInt())) + when(flowSlot.canPassCheck(any(FlowRule.class), any(Context.class), any(DefaultNode.class), anyInt(), anyBoolean())) .thenReturn(false); - flowSlot.checkFlow(new StringResourceWrapper(resA, EntryType.IN), context, node, 1); + flowSlot.checkFlow(new StringResourceWrapper(resA, EntryType.IN), context, node, 1, false); } } \ No newline at end of file