@@ -144,6 +144,11 @@ | |||||
<artifactId>sentinel-transport-netty-http</artifactId> | <artifactId>sentinel-transport-netty-http</artifactId> | ||||
<version>${project.version}</version> | <version>${project.version}</version> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<artifactId>sentinel-transport-spring-mvc</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>com.alibaba.csp</groupId> | <groupId>com.alibaba.csp</groupId> | ||||
<artifactId>sentinel-transport-common</artifactId> | <artifactId>sentinel-transport-common</artifactId> | ||||
@@ -40,6 +40,7 @@ | |||||
<module>sentinel-demo-quarkus</module> | <module>sentinel-demo-quarkus</module> | ||||
<module>sentinel-demo-annotation-cdi-interceptor</module> | <module>sentinel-demo-annotation-cdi-interceptor</module> | ||||
<module>sentinel-demo-motan</module> | <module>sentinel-demo-motan</module> | ||||
<module>sentinel-demo-transport-spring-mvc</module> | |||||
</modules> | </modules> | ||||
<dependencies> | <dependencies> | ||||
@@ -0,0 +1,34 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
<parent> | |||||
<artifactId>sentinel-demo</artifactId> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<version>1.8.2-SNAPSHOT</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>sentinel-demo-transport-spring-mvc</artifactId> | |||||
<properties> | |||||
<spring.boot.version>2.1.3.RELEASE</spring.boot.version> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<artifactId>sentinel-core</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<artifactId>sentinel-transport-spring-mvc</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-web</artifactId> | |||||
<version>${spring.boot.version}</version> | |||||
</dependency> | |||||
</dependencies> | |||||
</project> |
@@ -0,0 +1,96 @@ | |||||
/* | |||||
* Copyright 1999-2019 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.demo.transport.springmvc; | |||||
import com.alibaba.csp.sentinel.Entry; | |||||
import com.alibaba.csp.sentinel.SphU; | |||||
import com.alibaba.csp.sentinel.init.InitExecutor; | |||||
import com.alibaba.csp.sentinel.slots.block.BlockException; | |||||
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.transport.command.SentinelApiHandlerAdapter; | |||||
import com.alibaba.csp.sentinel.transport.command.SentinelApiHandlerMapping; | |||||
import org.springframework.boot.SpringApplication; | |||||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.stereotype.Controller; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.ResponseBody; | |||||
import java.time.LocalDateTime; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
/** | |||||
* <p>Add the JVM parameter to connect to the dashboard:</p> | |||||
* {@code -Dcsp.sentinel.dashboard.server=127.0.0.1:8080 -Dproject.name=sentinel-demo-transport-spring-mvc} | |||||
* | |||||
* <p>Add the JVM parameter to tell dashboard your application port:</p> | |||||
* {@code -Dcsp.sentinel.api.port=10000} | |||||
* | |||||
* @author shenbaoyong | |||||
*/ | |||||
@SpringBootApplication | |||||
@Controller | |||||
public class TransportSpringMvcDemoApplication { | |||||
public static void main(String[] args) { | |||||
triggerSentinelInit(); | |||||
initFlowRules(); | |||||
SpringApplication.run(TransportSpringMvcDemoApplication.class); | |||||
} | |||||
public static void initFlowRules() { | |||||
List<FlowRule> rules = new ArrayList<>(); | |||||
FlowRule rule = new FlowRule(); | |||||
rule.setResource("demo-hello-api"); | |||||
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); | |||||
rule.setCount(1); | |||||
rules.add(rule); | |||||
FlowRuleManager.loadRules(rules); | |||||
} | |||||
@GetMapping("/hello") | |||||
@ResponseBody | |||||
public String hello() { | |||||
Entry entry = null; | |||||
try { | |||||
entry = SphU.entry("demo-hello-api"); | |||||
return "ok: " + LocalDateTime.now(); | |||||
} catch (BlockException e1) { | |||||
return "helloBlockHandler: " + LocalDateTime.now(); | |||||
} finally { | |||||
if (entry != null) { | |||||
entry.exit(); | |||||
} | |||||
} | |||||
} | |||||
private static void triggerSentinelInit() { | |||||
new Thread(() -> InitExecutor.doInit()).start(); | |||||
} | |||||
@Bean | |||||
public SentinelApiHandlerMapping sentinelApiHandlerMapping() { | |||||
return new SentinelApiHandlerMapping(); | |||||
} | |||||
@Bean | |||||
public SentinelApiHandlerAdapter sentinelApiHandlerAdapter() { | |||||
return new SentinelApiHandlerAdapter(); | |||||
} | |||||
} |
@@ -0,0 +1 @@ | |||||
server.port=10000 |
@@ -16,5 +16,6 @@ | |||||
<module>sentinel-transport-simple-http</module> | <module>sentinel-transport-simple-http</module> | ||||
<module>sentinel-transport-netty-http</module> | <module>sentinel-transport-netty-http</module> | ||||
<module>sentinel-transport-spring-mvc</module> | |||||
</modules> | </modules> | ||||
</project> | </project> |
@@ -0,0 +1,50 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
<parent> | |||||
<artifactId>sentinel-transport</artifactId> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<version>1.8.2-SNAPSHOT</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>sentinel-transport-spring-mvc</artifactId> | |||||
<properties> | |||||
<apache.httpclient.version>4.5.3</apache.httpclient.version> | |||||
<servlet.api.version>3.1.0</servlet.api.version> | |||||
<spring.version>5.1.8.RELEASE</spring.version> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<artifactId>sentinel-transport-common</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework</groupId> | |||||
<artifactId>spring-webmvc</artifactId> | |||||
<version>${spring.version}</version> | |||||
<scope>provided</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>javax.servlet</groupId> | |||||
<artifactId>javax.servlet-api</artifactId> | |||||
<version>${servlet.api.version}</version> | |||||
<scope>provided</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.apache.httpcomponents</groupId> | |||||
<artifactId>httpclient</artifactId> | |||||
<version>${apache.httpclient.version}</version> | |||||
</dependency> | |||||
</dependencies> | |||||
</project> |
@@ -0,0 +1,100 @@ | |||||
/* | |||||
* 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.transport.command; | |||||
import com.alibaba.csp.sentinel.command.CommandHandler; | |||||
import com.alibaba.csp.sentinel.command.CommandRequest; | |||||
import com.alibaba.csp.sentinel.command.CommandResponse; | |||||
import com.alibaba.csp.sentinel.config.SentinelConfig; | |||||
import com.alibaba.csp.sentinel.transport.command.http.StatusCode; | |||||
import com.alibaba.csp.sentinel.transport.log.CommandCenterLog; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import javax.servlet.http.HttpServletResponse; | |||||
import java.io.PrintWriter; | |||||
import java.util.Map; | |||||
/** | |||||
* @author shenbaoyong | |||||
*/ | |||||
public class SentinelApiHandler { | |||||
public static final String SERVER_ERROR_MESSAGE = "Command server error"; | |||||
private CommandHandler commandHandler; | |||||
public SentinelApiHandler(CommandHandler commandHandler) { | |||||
this.commandHandler = commandHandler; | |||||
} | |||||
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { | |||||
PrintWriter printWriter = null; | |||||
try { | |||||
long start = System.currentTimeMillis(); | |||||
printWriter = httpServletResponse.getWriter(); | |||||
CommandCenterLog.debug("[SentinelApiHandler] request income: {}", httpServletRequest.getRequestURL()); | |||||
CommandRequest request = new CommandRequest(); | |||||
Map<String, String[]> parameterMap = httpServletRequest.getParameterMap(); | |||||
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) { | |||||
String[] value = entry.getValue(); | |||||
if (value != null && value.length >= 1) { | |||||
request.addParam(entry.getKey(), value[0]); | |||||
} | |||||
} | |||||
CommandResponse<?> response = commandHandler.handle(request); | |||||
handleResponse(response, httpServletResponse, printWriter); | |||||
long cost = System.currentTimeMillis() - start; | |||||
CommandCenterLog.debug("[SentinelApiHandler] Deal request: {}, time cost: {} ms", httpServletRequest.getRequestURL(), cost); | |||||
} catch (Throwable e) { | |||||
CommandCenterLog.warn("[SentinelApiHandler] error", e); | |||||
try { | |||||
if (printWriter != null) { | |||||
writeResponse(httpServletResponse, printWriter, StatusCode.INTERNAL_SERVER_ERROR, SERVER_ERROR_MESSAGE); | |||||
} | |||||
} catch (Exception e1) { | |||||
CommandCenterLog.warn("Failed to write error response", e1); | |||||
} | |||||
} | |||||
} | |||||
private void writeResponse(HttpServletResponse httpServletResponse, PrintWriter out, StatusCode statusCode, String message) { | |||||
httpServletResponse.setStatus(statusCode.getCode()); | |||||
if (message != null) { | |||||
out.print(message); | |||||
} | |||||
out.flush(); | |||||
} | |||||
private <T> void handleResponse(CommandResponse<T> response, HttpServletResponse httpServletResponse, final PrintWriter printWriter) throws Exception { | |||||
if (response.isSuccess()) { | |||||
if (response.getResult() == null) { | |||||
writeResponse(httpServletResponse, printWriter, StatusCode.OK, null); | |||||
return; | |||||
} | |||||
// Here we directly use `toString` to encode the result to plain text. | |||||
byte[] buffer = response.getResult().toString().getBytes(SentinelConfig.charset()); | |||||
writeResponse(httpServletResponse, printWriter, StatusCode.OK, new String(buffer)); | |||||
} else { | |||||
String msg = SERVER_ERROR_MESSAGE; | |||||
if (response.getException() != null) { | |||||
msg = response.getException().getMessage(); | |||||
} | |||||
writeResponse(httpServletResponse, printWriter, StatusCode.BAD_REQUEST, msg); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,57 @@ | |||||
/* | |||||
* 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.transport.command; | |||||
import org.springframework.core.Ordered; | |||||
import org.springframework.web.servlet.HandlerAdapter; | |||||
import org.springframework.web.servlet.ModelAndView; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import javax.servlet.http.HttpServletResponse; | |||||
/** | |||||
* @author shenbaoyong | |||||
*/ | |||||
public class SentinelApiHandlerAdapter implements HandlerAdapter, Ordered { | |||||
private int order = Ordered.LOWEST_PRECEDENCE; | |||||
public void setOrder(int order) { | |||||
this.order = order; | |||||
} | |||||
@Override | |||||
public int getOrder() { | |||||
return order; | |||||
} | |||||
@Override | |||||
public boolean supports(Object handler) { | |||||
return handler instanceof SentinelApiHandler; | |||||
} | |||||
@Override | |||||
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { | |||||
SentinelApiHandler sentinelApiHandler = (SentinelApiHandler) handler; | |||||
sentinelApiHandler.handle(request, response); | |||||
return null; | |||||
} | |||||
@Override | |||||
public long getLastModified(HttpServletRequest request, Object handler) { | |||||
return -1; | |||||
} | |||||
} |
@@ -0,0 +1,117 @@ | |||||
/* | |||||
* 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.transport.command; | |||||
import com.alibaba.csp.sentinel.command.CommandHandler; | |||||
import com.alibaba.csp.sentinel.log.RecordLog; | |||||
import com.alibaba.csp.sentinel.transport.config.TransportConfig; | |||||
import com.alibaba.csp.sentinel.transport.log.CommandCenterLog; | |||||
import com.alibaba.csp.sentinel.util.StringUtil; | |||||
import org.springframework.beans.BeanWrapper; | |||||
import org.springframework.beans.BeanWrapperImpl; | |||||
import org.springframework.context.ApplicationEvent; | |||||
import org.springframework.context.ApplicationListener; | |||||
import org.springframework.core.Ordered; | |||||
import org.springframework.util.ClassUtils; | |||||
import org.springframework.web.servlet.HandlerExecutionChain; | |||||
import org.springframework.web.servlet.handler.AbstractHandlerMapping; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import java.util.Map; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
/** | |||||
* @author shenbaoyong | |||||
*/ | |||||
public class SentinelApiHandlerMapping extends AbstractHandlerMapping implements ApplicationListener { | |||||
private static final String SPRING_BOOT_WEB_SERVER_INITIALIZED_EVENT_CLASS = "org.springframework.boot.web.context.WebServerInitializedEvent"; | |||||
private static Class webServerInitializedEventClass; | |||||
static { | |||||
try { | |||||
webServerInitializedEventClass = ClassUtils.forName(SPRING_BOOT_WEB_SERVER_INITIALIZED_EVENT_CLASS, null); | |||||
RecordLog.info("[SentinelApiHandlerMapping] class {} is present, this is a spring-boot app, we can auto detect port", SPRING_BOOT_WEB_SERVER_INITIALIZED_EVENT_CLASS); | |||||
} catch (ClassNotFoundException e) { | |||||
RecordLog.info("[SentinelApiHandlerMapping] class {} is not present, this is not a spring-boot app, we can not auto detect port", SPRING_BOOT_WEB_SERVER_INITIALIZED_EVENT_CLASS); | |||||
} | |||||
} | |||||
final static Map<String, CommandHandler> handlerMap = new ConcurrentHashMap<>(); | |||||
private boolean ignoreInterceptor = true; | |||||
public SentinelApiHandlerMapping() { | |||||
setOrder(Ordered.LOWEST_PRECEDENCE - 10); | |||||
} | |||||
@Override | |||||
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { | |||||
String commandName = request.getRequestURI(); | |||||
if (commandName.startsWith("/")) { | |||||
commandName = commandName.substring(1); | |||||
} | |||||
CommandHandler commandHandler = handlerMap.get(commandName); | |||||
return commandHandler != null ? new SentinelApiHandler(commandHandler) : null; | |||||
} | |||||
@Override | |||||
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { | |||||
return ignoreInterceptor ? new HandlerExecutionChain(handler) : super.getHandlerExecutionChain(handler, request); | |||||
} | |||||
public void setIgnoreInterceptor(boolean ignoreInterceptor) { | |||||
this.ignoreInterceptor = ignoreInterceptor; | |||||
} | |||||
public static void registerCommand(String commandName, CommandHandler handler) { | |||||
if (StringUtil.isEmpty(commandName) || handler == null) { | |||||
return; | |||||
} | |||||
if (handlerMap.containsKey(commandName)) { | |||||
CommandCenterLog.warn("[SentinelApiHandlerMapping] Register failed (duplicate command): " + commandName); | |||||
return; | |||||
} | |||||
handlerMap.put(commandName, handler); | |||||
} | |||||
public static void registerCommands(Map<String, CommandHandler> handlerMap) { | |||||
if (handlerMap != null) { | |||||
for (Map.Entry<String, CommandHandler> e : handlerMap.entrySet()) { | |||||
registerCommand(e.getKey(), e.getValue()); | |||||
} | |||||
} | |||||
} | |||||
@Override | |||||
public void onApplicationEvent(ApplicationEvent applicationEvent) { | |||||
if (webServerInitializedEventClass != null && webServerInitializedEventClass.isAssignableFrom(applicationEvent.getClass())) { | |||||
Integer port = null; | |||||
try { | |||||
BeanWrapper beanWrapper = new BeanWrapperImpl(applicationEvent); | |||||
port = (Integer) beanWrapper.getPropertyValue("webServer.port"); | |||||
} catch (Exception e) { | |||||
RecordLog.warn("[SentinelApiHandlerMapping] resolve port from event " + applicationEvent + " fail", e); | |||||
} | |||||
if (port != null && TransportConfig.getPort() == null) { | |||||
RecordLog.info("[SentinelApiHandlerMapping] resolve port {} from event {}", port, applicationEvent); | |||||
TransportConfig.setRuntimePort(port); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -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.transport.command; | |||||
import com.alibaba.csp.sentinel.command.CommandHandler; | |||||
import com.alibaba.csp.sentinel.command.CommandHandlerProvider; | |||||
import com.alibaba.csp.sentinel.spi.Spi; | |||||
import com.alibaba.csp.sentinel.transport.CommandCenter; | |||||
import java.util.Map; | |||||
/** | |||||
* @author shenbaoyong | |||||
*/ | |||||
@Spi(order = Spi.ORDER_LOWEST - 100) | |||||
public class SpringMvcHttpCommandCenter implements CommandCenter { | |||||
@Override | |||||
public void start() throws Exception { | |||||
} | |||||
@Override | |||||
public void stop() throws Exception { | |||||
} | |||||
@Override | |||||
public void beforeStart() throws Exception { | |||||
// Register handlers | |||||
Map<String, CommandHandler> handlers = CommandHandlerProvider.getInstance().namedHandlers(); | |||||
SentinelApiHandlerMapping.registerCommands(handlers); | |||||
} | |||||
} |
@@ -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.transport.command.http; | |||||
/** | |||||
* @author Jason Joo | |||||
*/ | |||||
public enum StatusCode { | |||||
/** | |||||
* 200 OK. | |||||
*/ | |||||
OK(200, "OK"), | |||||
BAD_REQUEST(400, "Bad Request"), | |||||
REQUEST_TIMEOUT(408, "Request Timeout"), | |||||
LENGTH_REQUIRED(411, "Length Required"), | |||||
UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), | |||||
INTERNAL_SERVER_ERROR(500, "Internal Server Error"); | |||||
private int code; | |||||
private String desc; | |||||
private String representation; | |||||
StatusCode(int code, String desc) { | |||||
this.code = code; | |||||
this.desc = desc; | |||||
this.representation = code + " " + desc; | |||||
} | |||||
public int getCode() { | |||||
return code; | |||||
} | |||||
public String getDesc() { | |||||
return desc; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return representation; | |||||
} | |||||
} |
@@ -0,0 +1,123 @@ | |||||
/* | |||||
* 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.transport.heartbeat; | |||||
import com.alibaba.csp.sentinel.Constants; | |||||
import com.alibaba.csp.sentinel.config.SentinelConfig; | |||||
import com.alibaba.csp.sentinel.log.RecordLog; | |||||
import com.alibaba.csp.sentinel.spi.Spi; | |||||
import com.alibaba.csp.sentinel.transport.HeartbeatSender; | |||||
import com.alibaba.csp.sentinel.transport.config.TransportConfig; | |||||
import com.alibaba.csp.sentinel.transport.endpoint.Endpoint; | |||||
import com.alibaba.csp.sentinel.transport.endpoint.Protocol; | |||||
import com.alibaba.csp.sentinel.transport.heartbeat.client.HttpClientsFactory; | |||||
import com.alibaba.csp.sentinel.util.AppNameUtil; | |||||
import com.alibaba.csp.sentinel.util.HostNameUtil; | |||||
import com.alibaba.csp.sentinel.util.PidUtil; | |||||
import com.alibaba.csp.sentinel.util.StringUtil; | |||||
import org.apache.http.client.config.RequestConfig; | |||||
import org.apache.http.client.methods.CloseableHttpResponse; | |||||
import org.apache.http.client.methods.HttpGet; | |||||
import org.apache.http.client.utils.URIBuilder; | |||||
import org.apache.http.impl.client.CloseableHttpClient; | |||||
import java.util.List; | |||||
/** | |||||
* @author Eric Zhao | |||||
* @author Carpenter Lee | |||||
* @author Leo Li | |||||
*/ | |||||
@Spi(order = Spi.ORDER_LOWEST - 100) | |||||
public class SpringMvcHttpHeartbeatSender implements HeartbeatSender { | |||||
private final CloseableHttpClient client; | |||||
private static final int OK_STATUS = 200; | |||||
private final int timeoutMs = 3000; | |||||
private final RequestConfig requestConfig = RequestConfig.custom() | |||||
.setConnectionRequestTimeout(timeoutMs) | |||||
.setConnectTimeout(timeoutMs) | |||||
.setSocketTimeout(timeoutMs) | |||||
.build(); | |||||
private final Protocol consoleProtocol; | |||||
private final String consoleHost; | |||||
private final int consolePort; | |||||
public SpringMvcHttpHeartbeatSender() { | |||||
List<Endpoint> dashboardList = TransportConfig.getConsoleServerList(); | |||||
if (dashboardList == null || dashboardList.isEmpty()) { | |||||
RecordLog.info("[HttpHeartbeatSender] No dashboard server available"); | |||||
consoleProtocol = Protocol.HTTP; | |||||
consoleHost = null; | |||||
consolePort = -1; | |||||
} else { | |||||
consoleProtocol = dashboardList.get(0).getProtocol(); | |||||
consoleHost = dashboardList.get(0).getHost(); | |||||
consolePort = dashboardList.get(0).getPort(); | |||||
RecordLog.info("[HttpHeartbeatSender] Dashboard address parsed: <{}:{}>", consoleHost, consolePort); | |||||
} | |||||
this.client = HttpClientsFactory.getHttpClientsByProtocol(consoleProtocol); | |||||
} | |||||
@Override | |||||
public boolean sendHeartbeat() throws Exception { | |||||
if (StringUtil.isEmpty(consoleHost)) { | |||||
return false; | |||||
} | |||||
URIBuilder uriBuilder = new URIBuilder(); | |||||
uriBuilder.setScheme(consoleProtocol.getProtocol()).setHost(consoleHost).setPort(consolePort) | |||||
.setPath(TransportConfig.getHeartbeatApiPath()) | |||||
.setParameter("app", AppNameUtil.getAppName()) | |||||
.setParameter("app_type", String.valueOf(SentinelConfig.getAppType())) | |||||
.setParameter("v", Constants.SENTINEL_VERSION) | |||||
.setParameter("version", String.valueOf(System.currentTimeMillis())) | |||||
.setParameter("hostname", HostNameUtil.getHostName()) | |||||
.setParameter("ip", TransportConfig.getHeartbeatClientIp()) | |||||
.setParameter("port", TransportConfig.getPort()) | |||||
.setParameter("pid", String.valueOf(PidUtil.getPid())); | |||||
HttpGet request = new HttpGet(uriBuilder.build()); | |||||
request.setConfig(requestConfig); | |||||
// Send heartbeat request. | |||||
CloseableHttpResponse response = client.execute(request); | |||||
response.close(); | |||||
int statusCode = response.getStatusLine().getStatusCode(); | |||||
if (statusCode == OK_STATUS) { | |||||
return true; | |||||
} else if (clientErrorCode(statusCode) || serverErrorCode(statusCode)) { | |||||
RecordLog.warn("[HttpHeartbeatSender] Failed to send heartbeat to " | |||||
+ consoleHost + ":" + consolePort + ", http status code: " + statusCode); | |||||
} | |||||
return false; | |||||
} | |||||
@Override | |||||
public long intervalMs() { | |||||
return 5000; | |||||
} | |||||
private boolean clientErrorCode(int code) { | |||||
return code > 399 && code < 500; | |||||
} | |||||
private boolean serverErrorCode(int code) { | |||||
return code > 499 && code < 600; | |||||
} | |||||
} |
@@ -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.transport.heartbeat.client; | |||||
import com.alibaba.csp.sentinel.transport.endpoint.Protocol; | |||||
import com.alibaba.csp.sentinel.transport.ssl.SslFactory; | |||||
import org.apache.http.conn.ssl.NoopHostnameVerifier; | |||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||||
import org.apache.http.impl.client.CloseableHttpClient; | |||||
import org.apache.http.impl.client.HttpClients; | |||||
/** | |||||
* @author Leo Li | |||||
*/ | |||||
public class HttpClientsFactory { | |||||
private static class SslConnectionSocketFactoryInstance { | |||||
private static final SSLConnectionSocketFactory SSL_CONNECTION_SOCKET_FACTORY = new SSLConnectionSocketFactory(SslFactory.getSslConnectionSocketFactory(), NoopHostnameVerifier.INSTANCE); | |||||
} | |||||
public static CloseableHttpClient getHttpClientsByProtocol(Protocol protocol) { | |||||
return protocol == Protocol.HTTP ? HttpClients.createDefault() : HttpClients.custom(). | |||||
setSSLSocketFactory(SslConnectionSocketFactoryInstance.SSL_CONNECTION_SOCKET_FACTORY).build(); | |||||
} | |||||
} |
@@ -0,0 +1 @@ | |||||
com.alibaba.csp.sentinel.transport.command.SpringMvcHttpCommandCenter |
@@ -0,0 +1 @@ | |||||
com.alibaba.csp.sentinel.transport.heartbeat.SpringMvcHttpHeartbeatSender |