@@ -144,6 +144,11 @@ | |||
<artifactId>sentinel-transport-netty-http</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.alibaba.csp</groupId> | |||
<artifactId>sentinel-transport-spring-mvc</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.alibaba.csp</groupId> | |||
<artifactId>sentinel-transport-common</artifactId> | |||
@@ -40,6 +40,7 @@ | |||
<module>sentinel-demo-quarkus</module> | |||
<module>sentinel-demo-annotation-cdi-interceptor</module> | |||
<module>sentinel-demo-motan</module> | |||
<module>sentinel-demo-transport-spring-mvc</module> | |||
</modules> | |||
<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-netty-http</module> | |||
<module>sentinel-transport-spring-mvc</module> | |||
</modules> | |||
</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 |