Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -1,3 +1,18 @@ | |||||
/* | |||||
* 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.adapter.grpc; | package com.alibaba.csp.sentinel.adapter.grpc; | ||||
import io.grpc.Server; | import io.grpc.Server; | ||||
@@ -6,18 +21,18 @@ import io.grpc.ServerBuilder; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
class GrpcTestServer { | class GrpcTestServer { | ||||
private Server server; | private Server server; | ||||
GrpcTestServer() { | |||||
} | |||||
GrpcTestServer() {} | |||||
void start(int port, boolean shouldintercept) throws IOException { | |||||
void start(int port, boolean shouldIntercept) throws IOException { | |||||
if (server != null) { | if (server != null) { | ||||
throw new IllegalStateException("Server already running!"); | throw new IllegalStateException("Server already running!"); | ||||
} | } | ||||
ServerBuilder<?> serverBuild = ServerBuilder.forPort(port) | ServerBuilder<?> serverBuild = ServerBuilder.forPort(port) | ||||
.addService(new FooServiceImpl()); | |||||
if (shouldintercept) { | |||||
.addService(new FooServiceImpl()); | |||||
if (shouldIntercept) { | |||||
serverBuild.intercept(new SentinelGrpcServerInterceptor()); | serverBuild.intercept(new SentinelGrpcServerInterceptor()); | ||||
} | } | ||||
server = serverBuild.build(); | server = serverBuild.build(); | ||||
@@ -27,6 +27,8 @@ import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; | |||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | ||||
import io.grpc.StatusRuntimeException; | import io.grpc.StatusRuntimeException; | ||||
import org.junit.After; | |||||
import org.junit.Test; | |||||
import static org.junit.Assert.*; | import static org.junit.Assert.*; | ||||
@@ -41,9 +43,9 @@ public class SentinelGrpcClientInterceptorTest { | |||||
private final int threshold = 2; | private final int threshold = 2; | ||||
private final GrpcTestServer server = new GrpcTestServer(); | private final GrpcTestServer server = new GrpcTestServer(); | ||||
private void configureFlowRule() { | |||||
private void configureFlowRule(int count) { | |||||
FlowRule rule = new FlowRule() | FlowRule rule = new FlowRule() | ||||
.setCount(threshold) | |||||
.setCount(count) | |||||
.setGrade(RuleConstant.FLOW_GRADE_QPS) | .setGrade(RuleConstant.FLOW_GRADE_QPS) | ||||
.setResource(resourceName) | .setResource(resourceName) | ||||
.setLimitApp("default") | .setLimitApp("default") | ||||
@@ -51,42 +53,44 @@ public class SentinelGrpcClientInterceptorTest { | |||||
FlowRuleManager.loadRules(Collections.singletonList(rule)); | FlowRuleManager.loadRules(Collections.singletonList(rule)); | ||||
} | } | ||||
//@Test | |||||
@Test | |||||
public void testGrpcClientInterceptor() throws Exception { | public void testGrpcClientInterceptor() throws Exception { | ||||
final int port = 19328; | final int port = 19328; | ||||
configureFlowRule(); | |||||
configureFlowRule(threshold); | |||||
server.start(port, false); | server.start(port, false); | ||||
FooServiceClient client = new FooServiceClient("localhost", port, new SentinelGrpcClientInterceptor()); | FooServiceClient client = new FooServiceClient("localhost", port, new SentinelGrpcClientInterceptor()); | ||||
final int total = 8; | |||||
for (int i = 0; i < total; i++) { | |||||
sendRequest(client); | |||||
} | |||||
assertTrue(sendRequest(client)); | |||||
ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceName, EntryType.OUT); | ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceName, EntryType.OUT); | ||||
assertNotNull(clusterNode); | assertNotNull(clusterNode); | ||||
assertEquals(1, clusterNode.passQps()); | |||||
assertEquals((total - threshold) / 2, clusterNode.blockRequest()); | |||||
assertEquals(total / 2, clusterNode.totalRequest()); | |||||
// Not allowed to pass. | |||||
configureFlowRule(0); | |||||
long totalQps = clusterNode.totalQps(); | |||||
long passQps = clusterNode.passQps(); | |||||
long blockQps = clusterNode.blockQps(); | |||||
assertEquals(total, totalQps); | |||||
assertEquals(total - threshold, blockQps); | |||||
assertEquals(threshold, passQps); | |||||
// The second request will be blocked. | |||||
assertFalse(sendRequest(client)); | |||||
assertEquals(1, clusterNode.blockQps()); | |||||
server.stop(); | server.stop(); | ||||
} | } | ||||
private void sendRequest(FooServiceClient client) { | |||||
private boolean sendRequest(FooServiceClient client) { | |||||
try { | try { | ||||
FooResponse response = client.sayHello(FooRequest.newBuilder().setName("Sentinel").setId(666).build()); | FooResponse response = client.sayHello(FooRequest.newBuilder().setName("Sentinel").setId(666).build()); | ||||
System.out.println(ClusterBuilderSlot.getClusterNode(resourceName, EntryType.OUT).avgRt()); | |||||
System.out.println("Response: " + response); | System.out.println("Response: " + response); | ||||
return true; | |||||
} catch (StatusRuntimeException ex) { | } catch (StatusRuntimeException ex) { | ||||
System.out.println("Blocked, cause: " + ex.getMessage()); | System.out.println("Blocked, cause: " + ex.getMessage()); | ||||
return false; | |||||
} | } | ||||
} | } | ||||
@After | |||||
public void cleanUp() { | |||||
FlowRuleManager.loadRules(null); | |||||
ClusterBuilderSlot.getClusterNodeMap().clear(); | |||||
} | |||||
} | } |
@@ -27,6 +27,8 @@ import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; | |||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | ||||
import io.grpc.StatusRuntimeException; | import io.grpc.StatusRuntimeException; | ||||
import org.junit.After; | |||||
import org.junit.Test; | |||||
import static org.junit.Assert.*; | import static org.junit.Assert.*; | ||||
@@ -43,9 +45,9 @@ public class SentinelGrpcServerInterceptorTest { | |||||
private FooServiceClient client; | private FooServiceClient client; | ||||
private void configureFlowRule() { | |||||
private void configureFlowRule(int count) { | |||||
FlowRule rule = new FlowRule() | FlowRule rule = new FlowRule() | ||||
.setCount(threshold) | |||||
.setCount(count) | |||||
.setGrade(RuleConstant.FLOW_GRADE_QPS) | .setGrade(RuleConstant.FLOW_GRADE_QPS) | ||||
.setResource(resourceName) | .setResource(resourceName) | ||||
.setLimitApp("default") | .setLimitApp("default") | ||||
@@ -53,41 +55,44 @@ public class SentinelGrpcServerInterceptorTest { | |||||
FlowRuleManager.loadRules(Collections.singletonList(rule)); | FlowRuleManager.loadRules(Collections.singletonList(rule)); | ||||
} | } | ||||
//@Test | |||||
@Test | |||||
public void testGrpcServerInterceptor() throws Exception { | public void testGrpcServerInterceptor() throws Exception { | ||||
final int port = 19329; | final int port = 19329; | ||||
client = new FooServiceClient("localhost", port); | client = new FooServiceClient("localhost", port); | ||||
configureFlowRule(); | |||||
configureFlowRule(threshold); | |||||
server.start(port, true); | server.start(port, true); | ||||
final int total = 8; | |||||
for (int i = 0; i < total; i++) { | |||||
sendRequest(); | |||||
} | |||||
assertTrue(sendRequest()); | |||||
ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceName, EntryType.IN); | ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceName, EntryType.IN); | ||||
assertNotNull(clusterNode); | assertNotNull(clusterNode); | ||||
assertEquals(1, clusterNode.passQps()); | |||||
assertEquals((total - threshold) / 2, clusterNode.blockRequest()); | |||||
assertEquals(total / 2, clusterNode.totalRequest()); | |||||
// Not allowed to pass. | |||||
configureFlowRule(0); | |||||
long totalQps = clusterNode.totalQps(); | |||||
long passQps = clusterNode.passQps(); | |||||
long blockQps = clusterNode.blockQps(); | |||||
assertEquals(total, totalQps); | |||||
assertEquals(total - threshold, blockQps); | |||||
assertEquals(threshold, passQps); | |||||
// The second request will be blocked. | |||||
assertFalse(sendRequest()); | |||||
assertEquals(1, clusterNode.blockQps()); | |||||
server.stop(); | server.stop(); | ||||
} | } | ||||
private void sendRequest() { | |||||
private boolean sendRequest() { | |||||
try { | try { | ||||
FooResponse response = client.anotherHello(FooRequest.newBuilder().setName("Sentinel").setId(666).build()); | FooResponse response = client.anotherHello(FooRequest.newBuilder().setName("Sentinel").setId(666).build()); | ||||
System.out.println("Response: " + response); | System.out.println("Response: " + response); | ||||
return true; | |||||
} catch (StatusRuntimeException ex) { | } catch (StatusRuntimeException ex) { | ||||
System.out.println("Blocked, cause: " + ex.getMessage()); | System.out.println("Blocked, cause: " + ex.getMessage()); | ||||
return false; | |||||
} | } | ||||
} | } | ||||
@After | |||||
public void cleanUp() { | |||||
FlowRuleManager.loadRules(null); | |||||
ClusterBuilderSlot.getClusterNodeMap().clear(); | |||||
} | |||||
} | } |
@@ -28,5 +28,23 @@ | |||||
<version>${servlet.api.version}</version> | <version>${servlet.api.version}</version> | ||||
<scope>provided</scope> | <scope>provided</scope> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-web</artifactId> | |||||
<version>1.5.17.RELEASE</version> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-test</artifactId> | |||||
<version>1.5.17.RELEASE</version> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
</dependencies> | </dependencies> | ||||
</project> | </project> |
@@ -57,8 +57,7 @@ public class WebCallbackManager { | |||||
return requestOriginParser; | return requestOriginParser; | ||||
} | } | ||||
public static void setRequestOriginParser( | |||||
RequestOriginParser requestOriginParser) { | |||||
public static void setRequestOriginParser(RequestOriginParser requestOriginParser) { | |||||
WebCallbackManager.requestOriginParser = requestOriginParser; | WebCallbackManager.requestOriginParser = requestOriginParser; | ||||
} | } | ||||
} | } |
@@ -64,7 +64,7 @@ public final class FilterUtil { | |||||
url.append("?").append(request.getQueryString()); | url.append("?").append(request.getQueryString()); | ||||
} | } | ||||
if (StringUtil.isEmpty(WebServletConfig.getBlockPage())) { | |||||
if (StringUtil.isBlank(WebServletConfig.getBlockPage())) { | |||||
writeDefaultBlockedPage(response); | writeDefaultBlockedPage(response); | ||||
} else { | } else { | ||||
String redirectUrl = WebServletConfig.getBlockPage() + "?http_referer=" + url.toString(); | String redirectUrl = WebServletConfig.getBlockPage() + "?http_referer=" + url.toString(); | ||||
@@ -75,7 +75,7 @@ public final class FilterUtil { | |||||
private static void writeDefaultBlockedPage(HttpServletResponse response) throws IOException { | private static void writeDefaultBlockedPage(HttpServletResponse response) throws IOException { | ||||
PrintWriter out = response.getWriter(); | PrintWriter out = response.getWriter(); | ||||
out.println("Blocked by Sentinel (flow limiting)"); | |||||
out.print(DEFAULT_BLOCK_MSG); | |||||
out.flush(); | out.flush(); | ||||
out.close(); | out.close(); | ||||
} | } | ||||
@@ -183,5 +183,7 @@ public final class FilterUtil { | |||||
return i; | return i; | ||||
} | } | ||||
public static final String DEFAULT_BLOCK_MSG = "Blocked by Sentinel (flow limiting)"; | |||||
private FilterUtil() {} | private FilterUtil() {} | ||||
} | } |
@@ -0,0 +1,176 @@ | |||||
/* | |||||
* 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.adapter.servlet; | |||||
import java.util.Collections; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import com.alibaba.csp.sentinel.adapter.servlet.callback.DefaultUrlCleaner; | |||||
import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser; | |||||
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner; | |||||
import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager; | |||||
import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig; | |||||
import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil; | |||||
import com.alibaba.csp.sentinel.node.ClusterNode; | |||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | |||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; | |||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; | |||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | |||||
import com.alibaba.csp.sentinel.util.StringUtil; | |||||
import org.junit.After; | |||||
import org.junit.Test; | |||||
import org.junit.runner.RunWith; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; | |||||
import org.springframework.boot.test.context.SpringBootTest; | |||||
import org.springframework.http.MediaType; | |||||
import org.springframework.test.context.junit4.SpringRunner; | |||||
import org.springframework.test.web.servlet.MockMvc; | |||||
import static org.junit.Assert.*; | |||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; | |||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
@RunWith(SpringRunner.class) | |||||
@SpringBootTest(classes = TestApplication.class) | |||||
@AutoConfigureMockMvc | |||||
public class CommonFilterTest { | |||||
private static final String HELLO_STR = "Hello!"; | |||||
@Autowired | |||||
private MockMvc mvc; | |||||
private void configureRulesFor(String resource, int count) { | |||||
configureRulesFor(resource, count, "default"); | |||||
} | |||||
private void configureRulesFor(String resource, int count, String limitApp) { | |||||
FlowRule rule = new FlowRule() | |||||
.setCount(count) | |||||
.setGrade(RuleConstant.FLOW_GRADE_QPS); | |||||
rule.setResource(resource); | |||||
if (StringUtil.isNotBlank(limitApp)) { | |||||
rule.setLimitApp(limitApp); | |||||
} | |||||
FlowRuleManager.loadRules(Collections.singletonList(rule)); | |||||
} | |||||
@Test | |||||
public void testCommonFilterMiscellaneous() throws Exception { | |||||
String url = "/hello"; | |||||
this.mvc.perform(get(url)) | |||||
.andExpect(status().isOk()) | |||||
.andExpect(content().string(HELLO_STR)); | |||||
ClusterNode cn = ClusterBuilderSlot.getClusterNode(url); | |||||
assertNotNull(cn); | |||||
assertEquals(1, cn.passQps()); | |||||
testCommonBlockAndRedirectBlockPage(url, cn); | |||||
// Test for url cleaner. | |||||
testUrlCleaner(); | |||||
testCustomOriginParser(); | |||||
} | |||||
private void testCommonBlockAndRedirectBlockPage(String url, ClusterNode cn) throws Exception { | |||||
configureRulesFor(url, 0); | |||||
// The request will be blocked and response is default block message. | |||||
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN)) | |||||
.andExpect(status().isOk()) | |||||
.andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG)); | |||||
assertEquals(1, cn.blockQps()); | |||||
// Test for redirect. | |||||
String redirectUrl = "http://some-location.com"; | |||||
WebServletConfig.setBlockPage(redirectUrl); | |||||
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN)) | |||||
.andExpect(status().is3xxRedirection()) | |||||
.andExpect(header().string("Location", redirectUrl + "?http_referer=http://localhost/hello")); | |||||
FlowRuleManager.loadRules(null); | |||||
WebServletConfig.setBlockPage(""); | |||||
} | |||||
private void testUrlCleaner() throws Exception { | |||||
final String fooPrefix = "/foo/"; | |||||
String url1 = fooPrefix + 1; | |||||
String url2 = fooPrefix + 2; | |||||
WebCallbackManager.setUrlCleaner(new UrlCleaner() { | |||||
@Override | |||||
public String clean(String originUrl) { | |||||
if (originUrl.startsWith(fooPrefix)) { | |||||
return "/foo/*"; | |||||
} | |||||
return originUrl; | |||||
} | |||||
}); | |||||
this.mvc.perform(get(url1).accept(MediaType.TEXT_PLAIN)) | |||||
.andExpect(status().isOk()) | |||||
.andExpect(content().string("Hello 1")); | |||||
this.mvc.perform(get(url2).accept(MediaType.TEXT_PLAIN)) | |||||
.andExpect(status().isOk()) | |||||
.andExpect(content().string("Hello 2")); | |||||
ClusterNode cn = ClusterBuilderSlot.getClusterNode(fooPrefix + "*"); | |||||
assertEquals(2, cn.passQps()); | |||||
assertNull(ClusterBuilderSlot.getClusterNode(url1)); | |||||
assertNull(ClusterBuilderSlot.getClusterNode(url2)); | |||||
WebCallbackManager.setUrlCleaner(new DefaultUrlCleaner()); | |||||
} | |||||
private void testCustomOriginParser() throws Exception { | |||||
String url = "/hello"; | |||||
String limitOrigin = "userA"; | |||||
final String headerName = "S-User"; | |||||
configureRulesFor(url, 0, limitOrigin); | |||||
WebCallbackManager.setRequestOriginParser(new RequestOriginParser() { | |||||
@Override | |||||
public String parseOrigin(HttpServletRequest request) { | |||||
String origin = request.getHeader(headerName); | |||||
return origin != null ? origin : ""; | |||||
} | |||||
}); | |||||
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN).header(headerName, "userB")) | |||||
.andExpect(status().isOk()) | |||||
.andExpect(content().string(HELLO_STR)); | |||||
// This will be blocked. | |||||
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN).header(headerName, limitOrigin)) | |||||
.andExpect(status().isOk()) | |||||
.andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG)); | |||||
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN)) | |||||
.andExpect(status().isOk()) | |||||
.andExpect(content().string(HELLO_STR)); | |||||
WebCallbackManager.setRequestOriginParser(null); | |||||
FlowRuleManager.loadRules(null); | |||||
} | |||||
@After | |||||
public void cleanUp() { | |||||
FlowRuleManager.loadRules(null); | |||||
ClusterBuilderSlot.getClusterNodeMap().clear(); | |||||
} | |||||
} |
@@ -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.adapter.servlet; | |||||
import org.springframework.boot.web.servlet.FilterRegistrationBean; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.Configuration; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
@Configuration | |||||
public class FilterConfig { | |||||
@Bean | |||||
public FilterRegistrationBean sentinelFilterRegistration() { | |||||
FilterRegistrationBean registration = new FilterRegistrationBean(); | |||||
registration.setFilter(new CommonFilter()); | |||||
registration.addUrlPatterns("/*"); | |||||
registration.setName("sentinelFilter"); | |||||
registration.setOrder(1); | |||||
return registration; | |||||
} | |||||
} |
@@ -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.adapter.servlet; | |||||
import org.springframework.boot.SpringApplication; | |||||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
@SpringBootApplication | |||||
public class TestApplication { | |||||
public static void main(String[] args) { | |||||
SpringApplication.run(TestApplication.class, args); | |||||
} | |||||
} |
@@ -0,0 +1,42 @@ | |||||
/* | |||||
* 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.adapter.servlet; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.PathVariable; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
/** | |||||
* @author Eric Zhao | |||||
*/ | |||||
@RestController | |||||
public class TestController { | |||||
@GetMapping("/hello") | |||||
public String apiHello() { | |||||
return "Hello!"; | |||||
} | |||||
@GetMapping("/err") | |||||
public String apiError() { | |||||
return "Oops..."; | |||||
} | |||||
@GetMapping("/foo/{id}") | |||||
public String apiFoo(@PathVariable("id") Long id) { | |||||
return "Hello " + id; | |||||
} | |||||
} |