@@ -76,7 +76,7 @@ try { | |||||
} | } | ||||
``` | ``` | ||||
So far the code modification is done. | |||||
So far the code modification is done. We also provide [annotation support module](https://github.com/alibaba/Sentinel/blob/master/sentinel-extension/sentinel-annotation-aspectj/README.md) to define resource easier. | |||||
### 3. Define Rules | ### 3. Define Rules | ||||
@@ -93,6 +93,8 @@ rules.add(rule); | |||||
FlowRuleManager.loadRules(rules); | FlowRuleManager.loadRules(rules); | ||||
``` | ``` | ||||
For more information, please refer to [How To Use](https://github.com/alibaba/Sentinel/wiki/How-to-Use). | |||||
### 4. Check the Result | ### 4. Check the Result | ||||
After running the demo for a while, you can see the following records in `~/logs/csp/${appName}-metrics.log`. | After running the demo for a while, you can see the following records in `~/logs/csp/${appName}-metrics.log`. | ||||
@@ -144,3 +146,12 @@ These are only part of the companies using Sentinel, for reference only. If you | |||||
![Taiping Renshou](http://www.cntaiping.com/tplresource/cms/www/taiping/img/home_new/tp_logo_img.png) | ![Taiping Renshou](http://www.cntaiping.com/tplresource/cms/www/taiping/img/home_new/tp_logo_img.png) | ||||
![Shunfeng Technology](https://user-images.githubusercontent.com/9434884/48463502-2f48eb80-e817-11e8-984f-2f9b1b789e2d.png) | ![Shunfeng Technology](https://user-images.githubusercontent.com/9434884/48463502-2f48eb80-e817-11e8-984f-2f9b1b789e2d.png) | ||||
![Mandao](https://user-images.githubusercontent.com/9434884/48463559-6cad7900-e817-11e8-87e4-42952b074837.png) | ![Mandao](https://user-images.githubusercontent.com/9434884/48463559-6cad7900-e817-11e8-87e4-42952b074837.png) | ||||
![每日优鲜](https://home.missfresh.cn/statics/img/logo.png) | |||||
![二维火](https://user-images.githubusercontent.com/9434884/49358468-bc43de00-f70d-11e8-97fe-0bf05865f29f.png) | |||||
![文轩在线](http://static.winxuancdn.com/css/v2/images/logo.png) | |||||
![客如云](https://www.keruyun.com/static/krynew/images/logo.png) | |||||
![亲宝宝](https://stlib.qbb6.com/wclt/img/home_hd/version1/title_logo.png) | |||||
![杭州光云科技](https://www.raycloud.com/images/logo.png) | |||||
![金汇金融](https://res.jinhui365.com/r/images/logo2.png?v=1.527) | |||||
![Vivo](https://user-images.githubusercontent.com/9434884/49355264-c6f87600-f701-11e8-8109-054cf91df868.png) | |||||
![闪电购](http://cdn.52shangou.com/shandianbang/official-source/3.1.1/build/images/logo.png) |
@@ -46,17 +46,23 @@ import com.alibaba.csp.sentinel.util.StringUtil; | |||||
*/ | */ | ||||
public class CommonFilter implements Filter { | public class CommonFilter implements Filter { | ||||
private final static String HTTP_METHOD_SPECIFY = "HTTP_METHOD_SPECIFY"; | |||||
private final static String COLON = ":"; | |||||
private boolean httpMethodSpecify = false; | |||||
@Override | @Override | ||||
public void init(FilterConfig filterConfig) { | public void init(FilterConfig filterConfig) { | ||||
httpMethodSpecify = Boolean.parseBoolean(filterConfig.getInitParameter(HTTP_METHOD_SPECIFY)); | |||||
} | } | ||||
@Override | @Override | ||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | ||||
throws IOException, ServletException { | |||||
HttpServletRequest sRequest = (HttpServletRequest)request; | |||||
throws IOException, ServletException { | |||||
HttpServletRequest sRequest = (HttpServletRequest) request; | |||||
Entry entry = null; | Entry entry = null; | ||||
Entry methodEntry = null; | |||||
try { | try { | ||||
String target = FilterUtil.filterTarget(sRequest); | String target = FilterUtil.filterTarget(sRequest); | ||||
// Clean and unify the URL. | // Clean and unify the URL. | ||||
@@ -73,9 +79,16 @@ public class CommonFilter implements Filter { | |||||
ContextUtil.enter(target, origin); | ContextUtil.enter(target, origin); | ||||
entry = SphU.entry(target, EntryType.IN); | entry = SphU.entry(target, EntryType.IN); | ||||
// Add method specification if necessary | |||||
if (httpMethodSpecify) { | |||||
methodEntry = SphU.entry(sRequest.getMethod().toUpperCase() + COLON + target, | |||||
EntryType.IN); | |||||
} | |||||
chain.doFilter(request, response); | chain.doFilter(request, response); | ||||
} catch (BlockException e) { | } catch (BlockException e) { | ||||
HttpServletResponse sResponse = (HttpServletResponse)response; | |||||
HttpServletResponse sResponse = (HttpServletResponse) response; | |||||
// Return the block page, or redirect to another URL. | // Return the block page, or redirect to another URL. | ||||
WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e); | WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e); | ||||
} catch (IOException e2) { | } catch (IOException e2) { | ||||
@@ -88,6 +101,9 @@ public class CommonFilter implements Filter { | |||||
Tracer.trace(e4); | Tracer.trace(e4); | ||||
throw e4; | throw e4; | ||||
} finally { | } finally { | ||||
if (methodEntry != null) { | |||||
methodEntry.exit(); | |||||
} | |||||
if (entry != null) { | if (entry != null) { | ||||
entry.exit(); | entry.exit(); | ||||
} | } | ||||
@@ -171,6 +171,6 @@ public class CommonFilterTest { | |||||
@After | @After | ||||
public void cleanUp() { | public void cleanUp() { | ||||
FlowRuleManager.loadRules(null); | FlowRuleManager.loadRules(null); | ||||
ClusterBuilderSlot.getClusterNodeMap().clear(); | |||||
ClusterBuilderSlot.resetClusterNodes(); | |||||
} | } | ||||
} | } |
@@ -0,0 +1,133 @@ | |||||
/* | |||||
* 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.servletmethod; | |||||
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 java.util.Collections; | |||||
import static org.junit.Assert.*; | |||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; | |||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; | |||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; | |||||
/** | |||||
* @author Roger Law | |||||
*/ | |||||
@RunWith(SpringRunner.class) | |||||
@SpringBootTest(classes = TestApplication.class, | |||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) | |||||
@AutoConfigureMockMvc | |||||
public class CommonFilterMethodTest { | |||||
private static final String HELLO_STR = "Hello!"; | |||||
private static final String HELLO_POST_STR = "Hello Post!"; | |||||
private static final String GET = "GET"; | |||||
private static final String POST = "POST"; | |||||
private static final String COLON = ":"; | |||||
@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 cnGet = ClusterBuilderSlot.getClusterNode(GET + COLON + url); | |||||
assertNotNull(cnGet); | |||||
assertEquals(1, cnGet.passQps()); | |||||
ClusterNode cnPost = ClusterBuilderSlot.getClusterNode(POST + COLON + url); | |||||
assertNull(cnPost); | |||||
this.mvc.perform(post(url)) | |||||
.andExpect(status().isOk()) | |||||
.andExpect(content().string(HELLO_POST_STR)); | |||||
cnPost = ClusterBuilderSlot.getClusterNode(POST + COLON + url); | |||||
assertNotNull(cnPost); | |||||
assertEquals(1, cnPost.passQps()); | |||||
testCommonBlockAndRedirectBlockPage(url, cnGet, cnPost); | |||||
} | |||||
private void testCommonBlockAndRedirectBlockPage(String url, ClusterNode cnGet, ClusterNode cnPost) throws Exception { | |||||
configureRulesFor(GET + ":" + 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, cnGet.blockQps()); | |||||
// Test for post pass | |||||
this.mvc.perform(post(url)) | |||||
.andExpect(status().isOk()) | |||||
.andExpect(content().string(HELLO_POST_STR)); | |||||
assertEquals(2, cnPost.passQps()); | |||||
FlowRuleManager.loadRules(null); | |||||
WebServletConfig.setBlockPage(""); | |||||
} | |||||
@After | |||||
public void cleanUp() { | |||||
FlowRuleManager.loadRules(null); | |||||
ClusterBuilderSlot.resetClusterNodes(); | |||||
} | |||||
} |
@@ -0,0 +1,25 @@ | |||||
package com.alibaba.csp.sentinel.adapter.servletmethod; | |||||
import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter; | |||||
import org.springframework.boot.web.servlet.FilterRegistrationBean; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.Configuration; | |||||
/** | |||||
* @author: Roger Law | |||||
**/ | |||||
@Configuration | |||||
public class FilterMethodConfig { | |||||
@Bean | |||||
public FilterRegistrationBean sentinelFilterRegistration() { | |||||
FilterRegistrationBean registration = new FilterRegistrationBean(); | |||||
registration.setFilter(new CommonFilter()); | |||||
registration.addUrlPatterns("/*"); | |||||
registration.addInitParameter("HTTP_METHOD_SPECIFY", "true"); | |||||
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.servletmethod; | |||||
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,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.servletmethod; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.PostMapping; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
/** | |||||
* @author Roger Law | |||||
*/ | |||||
@RestController | |||||
public class TestMethodController { | |||||
@GetMapping("/hello") | |||||
public String apiHello() { | |||||
return "Hello!"; | |||||
} | |||||
@PostMapping("/hello") | |||||
public String apiHelloPost() { | |||||
return "Hello Post!"; | |||||
} | |||||
} |
@@ -0,0 +1,32 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.cluster; | |||||
/** | |||||
* Token client interface for distributed flow control. | |||||
* | |||||
* @author Eric Zhao | |||||
* @since 1.4.0 | |||||
*/ | |||||
public interface ClusterTokenClient extends TokenService { | |||||
/** | |||||
* Get descriptor of current token server. | |||||
* | |||||
* @return current token server | |||||
*/ | |||||
TokenServerDescriptor currentServer(); | |||||
} |
@@ -0,0 +1,61 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.cluster; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.ServiceLoader; | |||||
import com.alibaba.csp.sentinel.log.RecordLog; | |||||
/** | |||||
* Provider for a universal {@link ClusterTokenClient} instance. | |||||
* | |||||
* @author Eric Zhao | |||||
* @since 1.4.0 | |||||
*/ | |||||
public final class TokenClientProvider { | |||||
private static ClusterTokenClient client = null; | |||||
private static final ServiceLoader<ClusterTokenClient> LOADER = ServiceLoader.load(ClusterTokenClient.class); | |||||
static { | |||||
// Not strictly thread-safe, but it's OK since it will be resolved only once. | |||||
resolveTokenClientInstance(); | |||||
} | |||||
public static ClusterTokenClient getClient() { | |||||
return client; | |||||
} | |||||
private static void resolveTokenClientInstance() { | |||||
List<ClusterTokenClient> clients = new ArrayList<ClusterTokenClient>(); | |||||
for (ClusterTokenClient client : LOADER) { | |||||
clients.add(client); | |||||
} | |||||
if (!clients.isEmpty()) { | |||||
// Get first. | |||||
client = clients.get(0); | |||||
RecordLog.info("[TokenClientProvider] Token client resolved: " + client.getClass().getCanonicalName()); | |||||
} else { | |||||
RecordLog.warn("[TokenClientProvider] No existing token client, resolve failed"); | |||||
} | |||||
} | |||||
private TokenClientProvider() {} | |||||
} |
@@ -0,0 +1,59 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.cluster.log; | |||||
import java.io.File; | |||||
import com.alibaba.csp.sentinel.eagleeye.EagleEye; | |||||
import com.alibaba.csp.sentinel.eagleeye.StatLogger; | |||||
import com.alibaba.csp.sentinel.log.LogBase; | |||||
/** | |||||
* @author jialiang.linjl | |||||
* @author Eric Zhao | |||||
* @since 1.4.0 | |||||
*/ | |||||
public final class ClusterStatLogUtil { | |||||
private static final String FILE_NAME = "sentinel-cluster.log"; | |||||
private static StatLogger statLogger; | |||||
static { | |||||
String path = LogBase.getLogBaseDir() + FILE_NAME; | |||||
statLogger = EagleEye.statLoggerBuilder("sentinel-cluster-record") | |||||
.intervalSeconds(1) | |||||
.entryDelimiter('|') | |||||
.keyDelimiter(',') | |||||
.valueDelimiter(',') | |||||
.maxEntryCount(5000) | |||||
.configLogFilePath(path) | |||||
.maxFileSizeMB(300) | |||||
.maxBackupIndex(3) | |||||
.buildSingleton(); | |||||
} | |||||
public static void log(String msg) { | |||||
statLogger.stat(msg).count(); | |||||
} | |||||
public static void log(String msg, int count) { | |||||
statLogger.stat(msg).count(count); | |||||
} | |||||
private ClusterStatLogUtil() {} | |||||
} |
@@ -26,6 +26,7 @@ import com.alibaba.csp.sentinel.node.metric.MetricNode; | |||||
* @author qinan.qn | * @author qinan.qn | ||||
* @author leyou | * @author leyou | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
* @author leitao | |||||
*/ | */ | ||||
public interface Node { | public interface Node { | ||||
@@ -147,7 +148,7 @@ public interface Node { | |||||
void increaseThreadNum(); | void increaseThreadNum(); | ||||
/** | /** | ||||
* Increase current thread count. | |||||
* Decrease current thread count. | |||||
*/ | */ | ||||
void decreaseThreadNum(); | void decreaseThreadNum(); | ||||
@@ -68,7 +68,7 @@ app.controller('MetricCtl', ['$scope', '$stateParams', 'MetricService', '$interv | |||||
forceFit: true, | forceFit: true, | ||||
width: 100, | width: 100, | ||||
height: 250, | height: 250, | ||||
padding: [10, 30, 70, 30] | |||||
padding: [10, 30, 70, 50] | |||||
}); | }); | ||||
var maxQps = 0; | var maxQps = 0; | ||||
for (var i in metric.data) { | for (var i in metric.data) { | ||||
@@ -33,16 +33,14 @@ angular.module('sentinelDashboardApp') | |||||
// toggle side bar | // toggle side bar | ||||
$scope.click = function ($event) { | $scope.click = function ($event) { | ||||
let element = angular.element($event.target); | |||||
let entry = angular.element($event.target).scope().entry; | let entry = angular.element($event.target).scope().entry; | ||||
entry.active = !entry.active; | |||||
entry.active = !entry.active;// toggle this clicked app bar | |||||
if (entry.active === false) { | |||||
element.parent().children('ul').hide(); | |||||
} else { | |||||
element.parent().parent().children('li').children('ul').hide(); | |||||
element.parent().children('ul').show(); | |||||
} | |||||
$scope.apps.forEach(function (item) {// collapse other app bars | |||||
if (item != entry) { | |||||
item.active = false; | |||||
} | |||||
}); | |||||
}; | }; | ||||
/** | /** | ||||
@@ -16,6 +16,8 @@ | |||||
package com.alibaba.csp.sentinel.transport.config; | package com.alibaba.csp.sentinel.transport.config; | ||||
import com.alibaba.csp.sentinel.config.SentinelConfig; | import com.alibaba.csp.sentinel.config.SentinelConfig; | ||||
import com.alibaba.csp.sentinel.util.HostNameUtil; | |||||
import com.alibaba.csp.sentinel.util.StringUtil; | |||||
/** | /** | ||||
* @author leyou | * @author leyou | ||||
@@ -25,6 +27,7 @@ public class TransportConfig { | |||||
public static final String CONSOLE_SERVER = "csp.sentinel.dashboard.server"; | public static final String CONSOLE_SERVER = "csp.sentinel.dashboard.server"; | ||||
public static final String SERVER_PORT = "csp.sentinel.api.port"; | public static final String SERVER_PORT = "csp.sentinel.api.port"; | ||||
public static final String HEARTBEAT_INTERVAL_MS = "csp.sentinel.heartbeat.interval.ms"; | public static final String HEARTBEAT_INTERVAL_MS = "csp.sentinel.heartbeat.interval.ms"; | ||||
public static final String HEARTBEAT_CLIENT_IP = "csp.sentinel.heartbeat.client.ip"; | |||||
private static int runtimePort = -1; | private static int runtimePort = -1; | ||||
@@ -66,4 +69,18 @@ public class TransportConfig { | |||||
public static void setRuntimePort(int port) { | public static void setRuntimePort(int port) { | ||||
runtimePort = port; | runtimePort = port; | ||||
} | } | ||||
/** | |||||
* Get heartbeat client local ip. | |||||
* If the client ip not configured,it will be the address of local host | |||||
* | |||||
* @return the local ip. | |||||
*/ | |||||
public static String getHeartbeatClientIp() { | |||||
String ip = SentinelConfig.getConfig(HEARTBEAT_CLIENT_IP); | |||||
if (StringUtil.isBlank(ip)) { | |||||
ip = HostNameUtil.getIp(); | |||||
} | |||||
return ip; | |||||
} | |||||
} | } |
@@ -0,0 +1,25 @@ | |||||
package com.alibaba.csp.sentinel.transport.config; | |||||
import com.alibaba.csp.sentinel.config.SentinelConfig; | |||||
import org.junit.Test; | |||||
import static org.junit.Assert.*; | |||||
public class TransportConfigTest { | |||||
@Test | |||||
public void getClientIp() { | |||||
//config heartbeat client ip | |||||
System.setProperty(TransportConfig.HEARTBEAT_CLIENT_IP, "10.10.10.10"); | |||||
String ip = TransportConfig.getHeartbeatClientIp(); | |||||
assertNotNull(ip); | |||||
assertEquals(ip, "10.10.10.10"); | |||||
//no heartbeat client ip | |||||
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_CLIENT_IP, ""); | |||||
ip = TransportConfig.getHeartbeatClientIp(); | |||||
assertNotNull(ip); | |||||
} | |||||
} |
@@ -85,7 +85,7 @@ public class HttpHeartbeatSender implements HeartbeatSender { | |||||
.setParameter("v", Constants.SENTINEL_VERSION) | .setParameter("v", Constants.SENTINEL_VERSION) | ||||
.setParameter("version", String.valueOf(System.currentTimeMillis())) | .setParameter("version", String.valueOf(System.currentTimeMillis())) | ||||
.setParameter("hostname", HostNameUtil.getHostName()) | .setParameter("hostname", HostNameUtil.getHostName()) | ||||
.setParameter("ip", HostNameUtil.getIp()) | |||||
.setParameter("ip", TransportConfig.getHeartbeatClientIp()) | |||||
.setParameter("port", TransportConfig.getPort()) | .setParameter("port", TransportConfig.getPort()) | ||||
.setParameter("pid", String.valueOf(PidUtil.getPid())); | .setParameter("pid", String.valueOf(PidUtil.getPid())); | ||||
@@ -19,11 +19,11 @@ import java.io.IOException; | |||||
import java.net.ServerSocket; | import java.net.ServerSocket; | ||||
import java.net.Socket; | import java.net.Socket; | ||||
import java.net.SocketException; | import java.net.SocketException; | ||||
import java.util.HashMap; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Map.Entry; | import java.util.Map.Entry; | ||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.concurrent.ArrayBlockingQueue; | import java.util.concurrent.ArrayBlockingQueue; | ||||
import java.util.concurrent.ConcurrentHashMap; | |||||
import java.util.concurrent.ExecutorService; | import java.util.concurrent.ExecutorService; | ||||
import java.util.concurrent.Executors; | import java.util.concurrent.Executors; | ||||
import java.util.concurrent.RejectedExecutionException; | import java.util.concurrent.RejectedExecutionException; | ||||
@@ -53,7 +53,7 @@ public class SimpleHttpCommandCenter implements CommandCenter { | |||||
private static final int DEFAULT_PORT = 8719; | private static final int DEFAULT_PORT = 8719; | ||||
@SuppressWarnings("rawtypes") | @SuppressWarnings("rawtypes") | ||||
private static final Map<String, CommandHandler> handlerMap = new HashMap<String, CommandHandler>(); | |||||
private static final Map<String, CommandHandler> handlerMap = new ConcurrentHashMap<String, CommandHandler>(); | |||||
private ExecutorService executor = Executors.newSingleThreadExecutor( | private ExecutorService executor = Executors.newSingleThreadExecutor( | ||||
new NamedThreadFactory("sentinel-command-center-executor")); | new NamedThreadFactory("sentinel-command-center-executor")); | ||||
@@ -105,12 +105,14 @@ public class SimpleHttpCommandCenter implements CommandCenter { | |||||
executor.submit(new ServerThread(serverSocket)); | executor.submit(new ServerThread(serverSocket)); | ||||
success = true; | success = true; | ||||
port = serverSocket.getLocalPort(); | port = serverSocket.getLocalPort(); | ||||
} else { | |||||
CommandCenterLog.info("[CommandCenter] chooses port fail, http command center will not work"); | |||||
} | } | ||||
if (!success) { | if (!success) { | ||||
port = PORT_UNINITIALIZED; | port = PORT_UNINITIALIZED; | ||||
} | } | ||||
TransportConfig.setRuntimePort(port); | TransportConfig.setRuntimePort(port); | ||||
executor.shutdown(); | executor.shutdown(); | ||||
} | } | ||||
@@ -119,11 +121,11 @@ public class SimpleHttpCommandCenter implements CommandCenter { | |||||
new Thread(serverInitTask).start(); | new Thread(serverInitTask).start(); | ||||
} | } | ||||
/** | /** | ||||
* Get a server socket from an available port from a base port.<br> | * Get a server socket from an available port from a base port.<br> | ||||
* Increasing on port number will occur when the port has already been used. | * Increasing on port number will occur when the port has already been used. | ||||
* | |||||
* | |||||
* @param basePort base port to start | * @param basePort base port to start | ||||
* @return new socket with available port | * @return new socket with available port | ||||
*/ | */ | ||||
@@ -135,7 +137,7 @@ public class SimpleHttpCommandCenter implements CommandCenter { | |||||
server.setReuseAddress(true); | server.setReuseAddress(true); | ||||
return server; | return server; | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
tryCount ++; | |||||
tryCount++; | |||||
try { | try { | ||||
TimeUnit.MILLISECONDS.sleep(30); | TimeUnit.MILLISECONDS.sleep(30); | ||||
} catch (InterruptedException e1) { | } catch (InterruptedException e1) { | ||||
@@ -36,7 +36,7 @@ public class HeartbeatMessage { | |||||
public HeartbeatMessage() { | public HeartbeatMessage() { | ||||
message.put("hostname", HostNameUtil.getHostName()); | message.put("hostname", HostNameUtil.getHostName()); | ||||
message.put("ip", HostNameUtil.getIp()); | |||||
message.put("ip", TransportConfig.getHeartbeatClientIp()); | |||||
message.put("app", AppNameUtil.getAppName()); | message.put("app", AppNameUtil.getAppName()); | ||||
message.put("port", String.valueOf(TransportConfig.getPort())); | message.put("port", String.valueOf(TransportConfig.getPort())); | ||||
} | } | ||||