Skip to content

Commit 3cd8970

Browse files
icodeningsczyh30
authored andcommitted
Add interceptor SPI for transport command handler (#2587)
* support intercept specified command handler * add unit test and demo
1 parent 2cb9747 commit 3cd8970

File tree

10 files changed

+362
-2
lines changed

10 files changed

+362
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 1999-2022 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.alibaba.csp.sentinel.demo.commandhandler.interceptor;
17+
18+
import com.alibaba.csp.sentinel.command.CommandHandlerInterceptor;
19+
import com.alibaba.csp.sentinel.command.CommandRequest;
20+
import com.alibaba.csp.sentinel.command.CommandRequestExecution;
21+
import com.alibaba.csp.sentinel.command.CommandResponse;
22+
import com.alibaba.csp.sentinel.spi.Spi;
23+
24+
/**
25+
* @author icodening
26+
* @date 2022.03.23
27+
*/
28+
@Spi(order = Spi.ORDER_HIGHEST)
29+
public class AllCommandHandlerInterceptor implements CommandHandlerInterceptor {
30+
31+
@Override
32+
public boolean shouldIntercept(String commandName) {
33+
return true;
34+
}
35+
36+
@Override
37+
public CommandResponse intercept(CommandRequest request, CommandRequestExecution execution) {
38+
System.out.println("[AllCommandHandlerInterceptor] start");
39+
long begin = System.currentTimeMillis();
40+
try {
41+
return execution.execute(request);
42+
} catch (Throwable throwable) {
43+
System.out.println("[AllCommandHandlerInterceptor] catch exception: " + throwable.getMessage());
44+
throw throwable;
45+
} finally {
46+
long cost = System.currentTimeMillis() - begin;
47+
System.out.println("[AllCommandHandlerInterceptor] complete, cost " + cost + "ms");
48+
}
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 1999-2022 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.alibaba.csp.sentinel.demo.commandhandler.interceptor;
17+
18+
import com.alibaba.csp.sentinel.command.CommandHandlerInterceptor;
19+
import com.alibaba.csp.sentinel.command.CommandRequest;
20+
import com.alibaba.csp.sentinel.command.CommandRequestExecution;
21+
import com.alibaba.csp.sentinel.command.CommandResponse;
22+
23+
/**
24+
* @author icodening
25+
* @date 2022.03.23
26+
*/
27+
public class EchoCommandHandlerInterceptor implements CommandHandlerInterceptor<String> {
28+
29+
@Override
30+
public boolean shouldIntercept(String commandName) {
31+
return "echo".equals(commandName);
32+
}
33+
34+
@Override
35+
public CommandResponse<String> intercept(CommandRequest request, CommandRequestExecution<String> execution) {
36+
System.out.println("[EchoCommandHandlerInterceptor] intercept 'echo' command,all params is " + request.getParameters() + "");
37+
CommandResponse<String> response = execution.execute(request);
38+
return CommandResponse.ofSuccess("intercept result : [" + response.getResult() + "]");
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
com.alibaba.csp.sentinel.demo.commandhandler.interceptor.EchoCommandHandlerInterceptor
2+
com.alibaba.csp.sentinel.demo.commandhandler.interceptor.AllCommandHandlerInterceptor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 1999-2022 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.alibaba.csp.sentinel.command;
17+
18+
/**
19+
* Intercepts specified command, and can be extended using SPI.
20+
*
21+
* @author icodening
22+
* @since 1.8.4
23+
* @see com.alibaba.csp.sentinel.spi.SpiLoader
24+
* @see com.alibaba.csp.sentinel.spi.Spi
25+
*/
26+
public interface CommandHandlerInterceptor<R> {
27+
28+
/**
29+
* whether to intercept the specified command
30+
*
31+
* @param commandName command name, eg. getRules
32+
* @return "true" means intercept, "false" means skip
33+
*/
34+
boolean shouldIntercept(String commandName);
35+
36+
/**
37+
* intercept the given command request, and return a command response
38+
*
39+
* @param request commandRequest
40+
* @param execution interceptor chain execution
41+
* @return command response
42+
*/
43+
CommandResponse<R> intercept(CommandRequest request, CommandRequestExecution<R> execution);
44+
45+
}

sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandHandlerProvider.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.*;
1919

2020
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
21+
import com.alibaba.csp.sentinel.command.handler.InterceptingCommandHandler;
2122
import com.alibaba.csp.sentinel.spi.SpiLoader;
2223
import com.alibaba.csp.sentinel.util.StringUtil;
2324

@@ -38,11 +39,24 @@ public class CommandHandlerProvider implements Iterable<CommandHandler> {
3839
public Map<String, CommandHandler> namedHandlers() {
3940
Map<String, CommandHandler> map = new HashMap<String, CommandHandler>();
4041
List<CommandHandler> handlers = spiLoader.loadInstanceList();
42+
List<CommandHandlerInterceptor> commandHandlerInterceptors = SpiLoader.of(CommandHandlerInterceptor.class).loadInstanceListSorted();
4143
for (CommandHandler handler : handlers) {
4244
String name = parseCommandName(handler);
43-
if (!StringUtil.isEmpty(name)) {
44-
map.put(name, handler);
45+
if (StringUtil.isEmpty(name)) {
46+
continue;
4547
}
48+
if (!commandHandlerInterceptors.isEmpty()) {
49+
List<CommandHandlerInterceptor> interceptors = new ArrayList<>();
50+
for (CommandHandlerInterceptor commandHandlerInterceptor : commandHandlerInterceptors) {
51+
if (commandHandlerInterceptor.shouldIntercept(name)) {
52+
interceptors.add(commandHandlerInterceptor);
53+
}
54+
}
55+
if (!interceptors.isEmpty()) {
56+
handler = new InterceptingCommandHandler(handler, interceptors);
57+
}
58+
}
59+
map.put(name, handler);
4660
}
4761
return map;
4862
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 1999-2022 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.alibaba.csp.sentinel.command;
17+
18+
/**
19+
* @author icodening
20+
* @since 1.8.4
21+
*/
22+
public interface CommandRequestExecution<R> {
23+
24+
/**
25+
* execute the command request and return the command response.
26+
*
27+
* @param request command request
28+
* @return command response
29+
*/
30+
CommandResponse<R> execute(CommandRequest request);
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 1999-2022 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.alibaba.csp.sentinel.command.handler;
17+
18+
import com.alibaba.csp.sentinel.command.*;
19+
import com.alibaba.csp.sentinel.util.AssertUtil;
20+
21+
import java.util.Iterator;
22+
import java.util.List;
23+
import java.util.function.Function;
24+
25+
/**
26+
* intercept specified command handler
27+
*
28+
* @author icodening
29+
* @date 2022.03.03
30+
*/
31+
public class InterceptingCommandHandler<R> implements CommandHandler<R> {
32+
33+
private final CommandHandler<R> delegate;
34+
35+
private final List<CommandHandlerInterceptor<R>> commandHandlerInterceptors;
36+
37+
public InterceptingCommandHandler(CommandHandler<R> delegate, List<CommandHandlerInterceptor<R>> commandHandlerInterceptors) {
38+
AssertUtil.notNull(delegate, "delegate cannot be null");
39+
AssertUtil.notNull(commandHandlerInterceptors, "commandHandlerInterceptors cannot be null");
40+
this.delegate = delegate;
41+
this.commandHandlerInterceptors = commandHandlerInterceptors;
42+
}
43+
44+
@Override
45+
public CommandResponse<R> handle(CommandRequest request) {
46+
return new InterceptingRequestExecution<>(commandHandlerInterceptors.iterator(), delegate::handle).execute(request);
47+
}
48+
49+
private static class InterceptingRequestExecution<R> implements CommandRequestExecution<R> {
50+
51+
private final Iterator<CommandHandlerInterceptor<R>> iterator;
52+
53+
private final Function<CommandRequest, CommandResponse<R>> commandResponseFunction;
54+
55+
public InterceptingRequestExecution(Iterator<CommandHandlerInterceptor<R>> iterator,
56+
Function<CommandRequest, CommandResponse<R>> commandResponseFunction) {
57+
this.iterator = iterator;
58+
this.commandResponseFunction = commandResponseFunction;
59+
}
60+
61+
@Override
62+
public CommandResponse<R> execute(CommandRequest request) {
63+
if (this.iterator.hasNext()) {
64+
CommandHandlerInterceptor<R> nextInterceptor = this.iterator.next();
65+
return nextInterceptor.intercept(request, this);
66+
}
67+
return commandResponseFunction.apply(request);
68+
}
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 1999-2022 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.alibaba.csp.sentinel.transport.command;
17+
18+
import com.alibaba.csp.sentinel.command.CommandHandlerInterceptor;
19+
import com.alibaba.csp.sentinel.command.CommandRequest;
20+
import com.alibaba.csp.sentinel.command.CommandRequestExecution;
21+
import com.alibaba.csp.sentinel.command.CommandResponse;
22+
23+
/**
24+
* @author icodening
25+
* @date 2022.03.23
26+
*/
27+
public class GetRulesCommandHandlerInterceptor implements CommandHandlerInterceptor {
28+
29+
@Override
30+
public boolean shouldIntercept(String commandName) {
31+
return "getRules".equals(commandName);
32+
}
33+
34+
@Override
35+
public CommandResponse intercept(CommandRequest request, CommandRequestExecution execution) {
36+
String type = request.getParam("type");
37+
System.out.println("[GetRulesCommandHandlerInterceptor] get rules for [" + type + "]");
38+
return execution.execute(request);
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 1999-2022 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.alibaba.csp.sentinel.transport.command;
17+
18+
import com.alibaba.csp.sentinel.command.CommandHandler;
19+
import com.alibaba.csp.sentinel.command.CommandHandlerProvider;
20+
import com.alibaba.csp.sentinel.command.CommandRequest;
21+
import com.alibaba.csp.sentinel.command.CommandResponse;
22+
import com.alibaba.csp.sentinel.command.handler.InterceptingCommandHandler;
23+
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
24+
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
25+
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
26+
import com.alibaba.csp.sentinel.transport.util.HttpCommandUtils;
27+
import com.alibaba.csp.sentinel.util.AssertUtil;
28+
import org.junit.Before;
29+
import org.junit.Test;
30+
31+
import java.util.Collections;
32+
import java.util.Map;
33+
34+
/**
35+
* @author icodening
36+
* @date 2022.03.23
37+
*/
38+
@SuppressWarnings("all")
39+
public class InterceptingCommandHandlerTest {
40+
41+
private Map<String, CommandHandler> commandHandlerMap = CommandHandlerProvider.getInstance().namedHandlers();
42+
43+
@Before
44+
public void setUp() throws Exception {
45+
FlowRule flowRule = new FlowRule();
46+
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS)
47+
.setClusterMode(false)
48+
.setCount(1)
49+
.setResource("/test");
50+
FlowRuleManager.loadRules(Collections.singletonList(flowRule));
51+
}
52+
53+
@Test
54+
public void testInterceptCommand() {
55+
CommandHandler getRulesHandler = commandHandlerMap.get("getRules");
56+
AssertUtil.assertState(getRulesHandler instanceof InterceptingCommandHandler, "getRulesHandler should be an InterceptingCommandHandler");
57+
58+
CommandHandler basicInfoHandler = commandHandlerMap.get("basicInfo");
59+
AssertUtil.assertState(!(basicInfoHandler instanceof InterceptingCommandHandler), "basicInfoHandler should not be an InterceptingCommandHandler");
60+
61+
CommandRequest getRulesCommandRequest = new CommandRequest();
62+
getRulesCommandRequest.addMetadata(HttpCommandUtils.REQUEST_TARGET, "getRules");
63+
getRulesCommandRequest.addParam("type", "flow");
64+
CommandResponse getRulesResponse = getRulesHandler.handle(getRulesCommandRequest);
65+
System.out.println(getRulesResponse.getResult());
66+
}
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
com.alibaba.csp.sentinel.transport.command.GetRulesCommandHandlerInterceptor

0 commit comments

Comments
 (0)