1.客户端启动和注册
在引入sentinel组建后,启动时会通过spi机制加载spring.factories文件中的自动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration,\
com.alibaba.cloud.sentinel.SentinelWebFluxAutoConfiguration,\
com.alibaba.cloud.sentinel.endpoint.SentinelEndpointAutoConfiguration,\
com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration,\
com.alibaba.cloud.sentinel.feign.SentinelFeignAutoConfiguration
org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
com.alibaba.cloud.sentinel.custom.SentinelCircuitBreakerConfiguration
SentinelWebAutoConfiguration类中声明了一个FilterRegistrationBean,在这个方法中创建了一个核心类CommonFilter,拦截所有请求
@Bean
@ConditionalOnProperty(
name = {"spring.cloud.sentinel.filter.enabled"},
matchIfMissing = true
)
public FilterRegistrationBean sentinelFilter() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean();
com.alibaba.cloud.sentinel.SentinelProperties.Filter filterConfig = this.properties.getFilter();
if(filterConfig.getUrlPatterns() == null || filterConfig.getUrlPatterns().isEmpty()) {
List<String> defaultPatterns = new ArrayList();
//拦截所有请求
defaultPatterns.add("/*");
filterConfig.setUrlPatterns(defaultPatterns);
}
registration.addUrlPatterns((String[])filterConfig.getUrlPatterns().toArray(new String[0]));
Filter filter = new CommonFilter();
registration.setFilter(filter);
registration.setOrder(filterConfig.getOrder());
registration.addInitParameter("HTTP_METHOD_SPECIFY", String.valueOf(this.properties.getHttpMethodSpecify()));
log.info("[Sentinel Starter] register Sentinel CommonFilter with urlPatterns: {}.", filterConfig.getUrlPatterns());
return registration;
}
查看CommonFilter.doFilter方法
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//拦截过滤
HttpServletRequest sRequest = (HttpServletRequest) request;
Entry urlEntry = null;
Entry httpMethodUrlEntry = null;
try {
//返回资源路径
String target = FilterUtil.filterTarget(sRequest);
// Clean and unify the URL.
// For REST APIs, you have to clean the URL (e.g. `/foo/1` and `/foo/2` -> `/foo/:id`), or
// the amount of context and resources will exceed the threshold.
UrlCleaner urlCleaner = WebCallbackManager.getUrlCleaner();
if (urlCleaner != null) {
target = urlCleaner.clean(target);
}
// If you intend to exclude some URLs, you can convert the URLs to the empty string ""
// in the UrlCleaner implementation.
if (!StringUtil.isEmpty(target)) {
// Parse the request origin using registered origin parser.
String origin = parseOrigin(sRequest);
//初始化sentinel上下文
ContextUtil.enter(WebServletConfig.WEB_SERVLET_CONTEXT_NAME, origin);
//核心逻辑-资源检查
urlEntry = SphU.entry(target, EntryType.IN);
// Add method specification if necessary
if (httpMethodSpecify) {
httpMethodUrlEntry = SphU.entry(sRequest.getMethod().toUpperCase() + COLON + target,
EntryType.IN);
}
}
//继续往下执行其他过滤器
chain.doFilter(request, response);
} catch (BlockException e) {
HttpServletResponse sResponse = (HttpServletResponse) response;
// Return the block page, or redirect to another URL.
WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e);
} catch (IOException | ServletException | RuntimeException e2) {
Tracer.traceEntry(e2, urlEntry);
Tracer.traceEntry(e2, httpMethodUrlEntry);
throw e2;
} finally {
if (httpMethodUrlEntry != null) {
httpMethodUrlEntry.exit();
}
if (urlEntry != null) {
urlEntry.exit();
}
ContextUtil.exit();
}
}
SphU.entry(target, EntryType.IN)是资源检查的核心逻辑
public static Entry entry(String name, EntryType type) throws BlockException {
//Env有静态代码块
return Env.sph.entry(name, type, 1, OBJECTS0);
}
Env类
public class Env {
public static final Sph sph = new CtSph();
static {
// If init fails, the process will exit.
//初始化
InitExecutor.doInit();
}
}
执行初始化doInit方法
public static void doInit() {
//保证只会初始化一次
if (!initialized.compareAndSet(false, true)) {
return;
}
try {
//SPI
//加载"META-INF/services/"目录下所配置的所有实现了InitFunc接口的类
ServiceLoader<InitFunc> loader = ServiceLoader.load(InitFunc.class);
List<OrderWrapper> initList = new ArrayList<OrderWrapper>();
for (InitFunc initFunc : loader) {
RecordLog.info("[InitExecutor] Found init func: " + initFunc.getClass().getCanonicalName());
//根据InitOrder注解对初始化实现类进行排序,值越小优先级越高
insertSorted(initList, initFunc);
}
for (OrderWrapper w : initList) {
//对每个实现类执行初始化
w.func.init();
RecordLog.info(String.format("[InitExecutor] Executing %s with order %d",
w.func.getClass().getCanonicalName(), w.order));
}
} catch (Exception ex) {
RecordLog.warn("[InitExecutor] WARN: Initialization failed", ex);
ex.printStackTrace();
} catch (Error error) {
RecordLog.warn("[InitExecutor] ERROR: Initialization failed with fatal error", error);
error.printStackTrace();
}
}
这里通过SPI机制会去加载META-INF/services/下的com.alibaba.csp.sentinel.init.InitFunc文件获取InitFunc接口的实现类
com.alibaba.csp.sentinel.transport.init.CommandCenterInitFunc
com.alibaba.csp.sentinel.transport.init.HeartbeatSenderInitFunc
com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit
w.func.init()方法会执行每个InitFunc实现类的init()方法,根据InitOrder注解对初始化实现类进行排序,值越小优先级越高,会先执行CommandCenterInitFunc和HeartbeatSenderInitFunc的init方法。
我们先看HeartbeatSenderInitFunc,这个实现类主要完成客户端向服务端注册的心跳发送
public void init() {
HeartbeatSender sender = HeartbeatSenderProvider.getHeartbeatSender();
if (sender == null) {
RecordLog.warn("[HeartbeatSenderInitFunc] WARN: No HeartbeatSender loaded");
return;
}
initSchedulerIfNeeded();
//心跳发送间隔,默认为10s
long interval = retrieveInterval(sender);
setIntervalIfNotExists(interval);
//启动心跳
scheduleHeartbeatTask(sender, interval);
}
private void scheduleHeartbeatTask(/*@NonNull*/ final HeartbeatSender sender, /*@Valid*/ long interval) {
pool.scheduleAtFixedRate(new Runnable() {//定时发送心跳
@Override
public void run() {
try {
sender.sendHeartbeat();
} catch (Throwable e) {
RecordLog.warn("[HeartbeatSender] Send heartbeat error", e);
}
}
}, 5000, interval, TimeUnit.MILLISECONDS);
RecordLog.info("[HeartbeatSenderInit] HeartbeatSender started: "
+ sender.getClass().getCanonicalName());
}
这里启动定时任务默认每隔10s发送一次心跳,调用sendHeartbeat方法发送请求
private static final String HEARTBEAT_PATH = "/registry/machine";
public boolean sendHeartbeat() throws Exception {
if (TransportConfig.getRuntimePort() <= 0) {
RecordLog.info("[SimpleHttpHeartbeatSender] Runtime port not initialized, won't send heartbeat");
return false;
}
//循环获取连接地址
InetSocketAddress addr = getAvailableAddress();
if (addr == null) {
return false;
}
//构建request请求
SimpleHttpRequest request = new SimpleHttpRequest(addr, HEARTBEAT_PATH);
//设置请求参数
request.setParams(heartBeat.generateCurrentMessage());
try {
//使用socket发送http请求
SimpleHttpResponse response = httpClient.post(request);
if (response.getStatusCode() == OK_STATUS) {
return true;
}
} catch (Exception e) {
RecordLog.warn("[SimpleHttpHeartbeatSender] Failed to send heartbeat to " + addr + " : ", e);
}
return false;
}
这里发送请求路径为"/registry/machine"到服务端,最终会到达sentinel服务的MachineRegistryController的receiveHeartBeat方法
@ResponseBody
@RequestMapping("/machine")
public Result<?> receiveHeartBeat(String app, @RequestParam(value = "app_type", required = false, defaultValue = "0") Integer appType, Long version, String v, String hostname, String ip, Integer port) {
if (app == null) {
app = MachineDiscovery.UNKNOWN_APP_NAME;
}
if (ip == null) {
return Result.ofFail(-1, "ip can't be null");
}
if (port == null) {
return Result.ofFail(-1, "port can't be null");
}
if (port == -1) {
logger.info("Receive heartbeat from " + ip + " but port not set yet");
return Result.ofFail(-1, "your port not set yet");
}
String sentinelVersion = StringUtil.isEmpty(v) ? "unknown" : v;
version = version == null ? System.currentTimeMillis() : version;
try {
//创建实例,封装信息
MachineInfo machineInfo = new MachineInfo();
machineInfo.setApp(app);
machineInfo.setAppType(appType);
machineInfo.setHostname(hostname);
machineInfo.setIp(ip);
machineInfo.setPort(port);
machineInfo.setHeartbeatVersion(version);
//设置心跳时间
machineInfo.setLastHeartbeat(System.currentTimeMillis());
machineInfo.setVersion(sentinelVersion);
//交给appManagement进行管理
appManagement.addMachine(machineInfo);
return Result.ofSuccessMsg("success");
} catch (Exception e) {
logger.error("Receive heartbeat error", e);
return Result.ofFail(-1, e.getMessage());
}
}
public long addMachine(MachineInfo machineInfo) {
return machineDiscovery.addMachine(machineInfo);
}
private final ConcurrentMap<String, AppInfo> apps = new ConcurrentHashMap<>();
@Override
public long addMachine(MachineInfo machineInfo) {
AssertUtil.notNull(machineInfo, "machineInfo cannot be null");
AppInfo appInfo = apps.computeIfAbsent(machineInfo.getApp(), o -> new AppInfo(machineInfo.getApp(), machineInfo.getAppType()));
appInfo.addMachine(machineInfo);
return 1;
}
客户端完成注册
2349



被折叠的 条评论
为什么被折叠?



