Soul網關的Http/SpringMvc 資料注冊和同步資料流探究
Soul-Admin端資料的探究
首先啟動客戶端專案soul-examples-http的程序中看到控制臺會輸出和

可以很明顯的看到這段資訊就是我們注解了@SoulSpringMvcClient的介面資訊,那么這個操作是在哪里產生的了,
全域搜索了register success 字樣,發現在soul-examples-http依賴的子專案soul-client-springmvc中有對應的SpringMvcClientBeanPostProcessor來進行注冊的代碼,這個SpringMvcClientBeanPostProcessor實作了Spring的BeanPostProcessor介面,如果我們想在Spring容器中完成bean實體化、配置以及其他初始化方法前后要添加一些自己邏輯處理,我們需要定義一個或多個BeanPostProcessor介面實作類,然后注冊到Spring IoC容器中,因此這里就是soul的客戶端將資料注冊到soul-admin的入口,
于是我在注冊這里進行了斷點除錯,看看他到底是在那個介面注冊從而將資料傳遞到soul-admin專案和網關,

可以看到http的專案是在控制臺專案的http://localhost:9095/soul-client/springmvc-register 介面進行了注冊,
隨后我們轉到這個介面
/**
* Register spring cloud string.
*
* @param springCloudRegisterDTO the spring cloud register dto
* @return the string
*/
@PostMapping("/springcloud-register")
public String registerSpringCloud(@RequestBody final SpringCloudRegisterDTO springCloudRegisterDTO) {
return soulClientRegisterService.registerSpringCloud(springCloudRegisterDTO);
}
深入到service層可以看到是應用的資料利用到上一節的Spring的事件處理機制來實作了

隨后斷點進入到實作了Spring的事件監聽介面ApplicationListener的事件分發類DataChangedEventDispatcher

可以看到,我啟動的時候因為沒有增加插件,所以此時是進入了SELECTOR斷點中,由于此時我選擇的zookeepr作為資料同步的組件,我找到了ZookeeperDataChangedListener中進行斷點 ,發現這里資料同步是將資料放入到zookeeper

private void createZkNode(final String path) {
if (!zkClient.exists(path)) {
zkClient.createPersistent(path, true);
}
}
/**
* create or update zookeeper node.
* @param path node path
* @param data node data
*/
private void upsertZkNode(final String path, final Object data) {
if (!zkClient.exists(path)) {
zkClient.createPersistent(path, true);
}
zkClient.writeData(path, data);
}
private void deleteZkPath(final String path) {
if (zkClient.exists(path)) {
zkClient.delete(path);
}
}
private void deleteZkPathRecursive(final String path) {
if (zkClient.exists(path)) {
zkClient.deleteRecursive(path);
}
}
zookeeper內寫入節點和更新資料的流程如上
Soul-Boostrap端資料的探究
打開soul-boostrap可以看到,類很少,只有兩個,一個SoulNettyWebServerFactory和HealthFilter,HealthFilter是用來做服務健康檢查的,而SoulNettyWebServerFactory就是spring webflux應用的一個回應式Server工廠類,具體的可以去看https://www.jianshu.com/p/ada196969995 這篇文章
但是此時我們還是沒有達到我們想要的請求轉發的東西,去pom檔案中找到了核心的網關的專案soul-spring-boot-starter-gateway但是發現其中一個類都沒有,但是其中依賴了soul-web模塊,因此對soul-web模塊進行了解
首先可以看到配置類SoulConfiguration,
/**
* Init SoulWebHandler.
*
* @param plugins this plugins is All impl SoulPlugin.
* @return {@linkplain SoulWebHandler}
*/
@Bean("webHandler")
public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) {
List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
final List<SoulPlugin> soulPlugins = pluginList.stream()
.sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());
soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName()));
return new SoulWebHandler(soulPlugins);
}
可以看到這里,加載了一個webhandler的處理器,處理器的主要內容
public SoulWebHandler(final List<SoulPlugin> plugins) {
this.plugins = plugins;
String schedulerType = System.getProperty("soul.scheduler.type", "fixed");
if (Objects.equals(schedulerType, "fixed")) {
int threads = Integer.parseInt(System.getProperty(
"soul.work.threads", "" + Math.max((Runtime.getRuntime().availableProcessors() << 1) + 1, 16)));
scheduler = Schedulers.newParallel("soul-work-threads", threads);
} else {
scheduler = Schedulers.elastic();
}
}
/**
* Handle the web server exchange.
*
* @param exchange the current server exchange
* @return {@code Mono<Void>} to indicate when request handling is complete
*/
@Override
public Mono<Void> handle(@NonNull final ServerWebExchange exchange) {
MetricsTrackerFacade.getInstance().counterInc(MetricsLabelEnum.REQUEST_TOTAL.getName());
Optional<HistogramMetricsTrackerDelegate> startTimer = MetricsTrackerFacade.getInstance().histogramStartTimer(MetricsLabelEnum.REQUEST_LATENCY.getName());
return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler)
.doOnSuccess(t -> startTimer.ifPresent(time -> MetricsTrackerFacade.getInstance().histogramObserveDuration(time)));
}
主要內容是利用責任鏈對請求的執行緒數進行處理,handle方法用來處理請求
根據soul-example-springmvc的注解進行請求,可以在抽象的基礎AbstractSoulPlugin接受到這個請求
@Override
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
String pluginName = named();
final PluginData pluginData = https://www.cnblogs.com/zhendiao/p/BaseDataCache.getInstance().obtainPluginData(pluginName);
if (pluginData != null && pluginData.getEnabled()) {
final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);
if (CollectionUtils.isEmpty(selectors)) {
return handleSelectorIsNull(pluginName, exchange, chain);
}
final SelectorData selectorData = matchSelector(exchange, selectors);
if (Objects.isNull(selectorData)) {
return handleSelectorIsNull(pluginName, exchange, chain);
}
selectorLog(selectorData, pluginName);
final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
if (CollectionUtils.isEmpty(rules)) {
return handleRuleIsNull(pluginName, exchange, chain);
}
RuleData rule;
if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {
//get last
rule = rules.get(rules.size() - 1);
} else {
rule = matchRule(exchange, rules);
}
if (Objects.isNull(rule)) {
return handleRuleIsNull(pluginName, exchange, chain);
}
ruleLog(rule, pluginName);
return doExecute(exchange, chain, selectorData, rule);
}
return chain.execute(exchange);
}
這里有很明顯的規則和選擇器比較的相關邏輯,用來判斷當前的請求是否位于網關代理的請求中,但是在這里與上面對應的是,我如何取到上文已經設定的選擇器資料,這個留到后面繼續去探究
緊接著斷點來到了具體的業務的請求插件WebClientPlugin執行后面的業務請求
@Override
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
assert soulContext != null;
String urlPath = exchange.getAttribute(Constants.HTTP_URL);
if (StringUtils.isEmpty(urlPath)) {
Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null);
return WebFluxResultUtils.result(exchange, error);
}
long timeout = (long) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_TIME_OUT)).orElse(3000L);
int retryTimes = (int) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_RETRY)).orElse(0);
log.info("The request urlPath is {}, retryTimes is {}", urlPath, retryTimes);
HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue());
WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath);
return handleRequestBody(requestBodySpec, exchange, timeout, retryTimes, chain);
}
從如上可以看到,網關在代理請求這一塊的邏輯
問題
本文還剩下未解決的問題主要是
- 如何從abstractSoulPlugin執行完之后到WebClientPlugin的相同方法,是責任鏈模式還是其他的加載程序
- abstractSoulPlugin是如何加載注冊或修改后的選擇器等資料
- plugin 中的執行方法是如何獲取到ServerWebExchange的相關請求資料
參考文章 https://blog.csdn.net/u010084384/article/details/113010594
歡迎搜索關注本人與朋友共同開發的微信面經小程式【大廠面試助手】和公眾號【微瞰技術】,以及總結的分類面試題https://github.com/zhendiao/JavaInterview


轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/251382.html
標籤:Java
上一篇:Javadoc命令與API
