文章目錄
- 1 子項目如何不繼承主項目,而繼承最新的 Spring Boot 依賴?
- 2 子項目繼承主項目,然後選擇不同的 SpringBoot 版本,是否可行?
- 3 Zookeeper 的 Maven 版本選擇
- 4 SpringBoot Zookeeper 項目正在運行時刪除 Zookeeper 的節點會怎麼樣?
- 5 Feign 調用 Spring Cloud Zookeeper 服務只能設置一種請求方式
- 6 使用 Feign 調用遠程 Zookeeper 服務提示 FeignException$BadRequest: status 400
- 7 使用 Feign 調用遠程 Zookeeper 服務提示 FeignException$MethodNotAllowed: status 405
- 8 Spring Cloud Zookeeper 的 @LoadBalanced 註解是什麼?有什麼用?不用可不可以?
- 9 Spring Cloud Config 配置中心的配置文件有哪些注意事項?
- X Github 源碼
1 子項目如何不繼承主項目,而繼承最新的 Spring Boot 依賴?
對於一個多模塊的項目而言,新建的項目通常是繼承主項目,但是新建的子項目也可以選擇繼承最新的 SpringBoot 項目,具體操作如下:
子項目 pom.xml
文件中指定最新的 SpringBoot parent 模塊進行繼承:
./cloud-zookeeper-provider/pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath/>
</parent>
父項目 pom.xml
文件添加子模塊的 module
:
./pom.xml
<modules>
<module>cloud-zookeeper-provider</module>
</modules>
在父項目 pom.xml
的 module
中添加之後,使用 maven
命令可以將子模塊一起打包
2 子項目繼承主項目,然後選擇不同的 SpringBoot 版本,是否可行?
理論上是可行的,但是實際操作不行。具體看下圖:
操作的 pom.xml
文件爲: ./cloud-zookeeper-provider/pom.xml
將主項目作爲父項目進行繼承,更改 spring-boot-starter-web
的版本爲當前最新版 2.2.4.RELEASE
,但是實際的 Maven
依賴卻依舊是主項目的版本,即 2.0.6.RELEASE
3 Zookeeper 的 Maven 版本選擇
官方文檔推薦的 Zookeeper
的 Maven
依賴使用方式爲:
<!-- Zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
具體可參考:
作者在示例中使用的版本是 3.4.12
, 2018 年 4 月推出的,這不是最新版,3.4
系列的最新版本爲 3.4.14
,2019 年 4 月推出的,當前(2020年3月) 的最新版爲 3.5.7
,2020 年 2 月推出的。
在實際操作中,使用 3.4.12
版本,連接 zookeeper
耗時不到 3 秒,基本都是秒連;使用 3.4.14
版本,連接 zookeeper
需要耗時 60 秒;使用 3.5.7
版本根本連接不上 zookeeper
使用 3.4.14
版本的 SpringBoot 項目啓動日誌:
2020-03-01 16:33:20.630 INFO 50249 --- [ restartedMain] org.apache.zookeeper.ZooKeeper : Initiating client connection, connectString=172.16.140.10:2181 sessionTimeout=60000 watcher=org.apache.curator.ConnectionState@29fd1024
2020-03-01 16:33:20.650 INFO 50249 --- [ restartedMain] o.a.c.f.imps.CuratorFrameworkImpl : Default schema
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.4.RELEASE)
2020-03-01 16:34:20.651 INFO 50249 --- [16.140.10:2181)] org.apache.zookeeper.ClientCnxn : Opening socket connection to server 172.16.140.10/172.16.140.10:2181. Will not attempt to authenticate using SASL (unknown error)
2020-03-01 16:34:20.674 INFO 50249 --- [16.140.10:2181)] org.apache.zookeeper.ClientCnxn : Socket connection established to 172.16.140.10/172.16.140.10:2181, initiating session
2020-03-01 16:34:20.683 INFO 50249 --- [16.140.10:2181)] org.apache.zookeeper.ClientCnxn : Session establishment complete on server 172.16.140.10/172.16.140.10:2181, sessionid = 0x1000575d4b10047, negotiated timeout = 40000
2020-03-01 16:34:20.689 INFO 50249 --- [ain-EventThread] o.a.c.f.state.ConnectionStateManager : State change: CONNECTED
根據日誌可以看出從 2020-03-01 16:33:20
開始初始化 zookeeper
連接到 2020-03-01 16:34:20
成功連接,中間消耗了 60 秒,雖然項目可以運行,但是連接耗時太多,每次啓動項目需要太長時間,因此不建議使用該版本
使用 3.5.7
版本 SpringBoot 項目啓動日誌:
2020-03-01 16:43:53.931 ERROR 50516 --- [tor-Framework-0] o.a.c.f.imps.CuratorFrameworkImpl : Background operation retry gave up
org.apache.zookeeper.KeeperException$ConnectionLossException: KeeperErrorCode = ConnectionLoss
at org.apache.zookeeper.KeeperException.create(KeeperException.java:102) ~[zookeeper-3.5.7.jar:3.5.7]
at org.apache.curator.framework.imps.CuratorFrameworkImpl.checkBackgroundRetry(CuratorFrameworkImpl.java:844) [curator-framework-4.0.1.jar:4.0.1]
at org.apache.curator.framework.imps.CuratorFrameworkImpl.performBackgroundOperation(CuratorFrameworkImpl.java:972) [curator-framework-4.0.1.jar:4.0.1]
at org.apache.curator.framework.imps.CuratorFrameworkImpl.backgroundOperationsLoop(CuratorFrameworkImpl.java:925) [curator-framework-4.0.1.jar:4.0.1]
at org.apache.curator.framework.imps.CuratorFrameworkImpl.access$300(CuratorFrameworkImpl.java:73) [curator-framework-4.0.1.jar:4.0.1]
at org.apache.curator.framework.imps.CuratorFrameworkImpl$4.call(CuratorFrameworkImpl.java:322) [curator-framework-4.0.1.jar:4.0.1]
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) [na:1.8.0_191]
at java.util.concurrent.FutureTask.run(FutureTask.java) [na:1.8.0_191]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_191]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_191]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191]
2020-03-01 16:43:53.931 ERROR 50516 --- [tor-Framework-0] o.a.c.f.imps.CuratorFrameworkImpl : Background retry gave up
org.apache.curator.CuratorConnectionLossException: KeeperErrorCode = ConnectionLoss
at org.apache.curator.framework.imps.CuratorFrameworkImpl.performBackgroundOperation(CuratorFrameworkImpl.java:954) [curator-framework-4.0.1.jar:4.0.1]
at org.apache.curator.framework.imps.CuratorFrameworkImpl.backgroundOperationsLoop(CuratorFrameworkImpl.java:925) [curator-framework-4.0.1.jar:4.0.1]
at org.apache.curator.framework.imps.CuratorFrameworkImpl.access$300(CuratorFrameworkImpl.java:73) [curator-framework-4.0.1.jar:4.0.1]
at org.apache.curator.framework.imps.CuratorFrameworkImpl$4.call(CuratorFrameworkImpl.java:322) [curator-framework-4.0.1.jar:4.0.1]
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) [na:1.8.0_191]
at java.util.concurrent.FutureTask.run(FutureTask.java) [na:1.8.0_191]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_191]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_191]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191]
根據日誌可以看出 3.5
系列的 zookeeper
連接方式已經發生了改變,與當前SpringBoot 的版本中包含的相關依賴是不兼容的,因此切不可貿然使用最新版本
4 SpringBoot Zookeeper 項目正在運行時刪除 Zookeeper 的節點會怎麼樣?
SpringBoot Zookeeper 項目在啓動的時候會向 Zookeeper
中註冊節點,節點的名稱即爲 Spring Boot application.yml
配置文件中的 spring.application.name
屬性
本着不作死就不會死的心態,作者將正在運行的 cloud-zookeeper-provider
項目註冊的 zookeeper
節點進行刪除,然後看看項目是否會自動重新註冊節點,然而實際情況是不會,而且項目還會報錯。
具體操作流程如下:
- 1 啓動
cloud-zookeeper-provider
項目 - 2 查詢
cloud-zookeeper-provider
註冊的zookeeper
節點,校驗節點是否成功註冊,結果是節點註冊成功 - 3 請求
cloud-zookeeper-provider
項目的接口http://127.0.0.1:8100/api/cloud/zookeeper/hello?name=dakhfak
,驗證服務是否可用,結果是可用 - 4 刪除
cloud-zookeeper-provider
註冊的zookeeper
節點(刪除命令:rmr /services/cloud-zookeeper-provider
) - 5 再次請求
cloud-zookeeper-provider
項目的接口http://127.0.0.1:8100/api/cloud/zookeeper/hello?name=dakhfak
,驗證服務是否可用,結果是可用 - 6 啓動
cloud-zookeeper-feign
項目,用於調用cloud-zookeeper-provider
服務,項目可成功啓動 - 7 請求
cloud-zookeeper-feign
項目的接口http://127.0.0.1:8102/api/cloud/zookeeper/feign/sayHello?name=dada666
,校驗cloud-zookeeper-provider
的zookeeper
服務是否可調用,結果是拋出異常,拋出的異常如下:
com.netflix.client.ClientException: Load balancer does not have available server for client: cloud-zookeeper-provider
at com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:483) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:184) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar:1.3.8]
根據日誌可以看出 cloud-zookeeper-provider
服務已經不存在,從而導致系統異常。cloud-zookeeper-provider
項目在沒有重啓的情況下並沒有在 zookeeper
節點刪除之後重新創建新節點
5 Feign 調用 Spring Cloud Zookeeper 服務只能設置一種請求方式
在 Cloud Zookeeper 服務註冊中心項目中 ,服務允許兩種請求方式,即 GET 和 POST,但是在使用 Feign 進行調用時,只能設置成一種,具體代碼如下:
服務註冊中心的控制層:
./cloud-zookeeper-provider/src/main/java/com/ljq/demo/springboot/cloud/zookeeper/provider/controller/CloudZookeeperProviderController.java
@RequestMapping(value = "/hello", method = {RequestMethod.GET, RequestMethod.POST},
produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<String> hello(@RequestParam("name") String name) {
hello
方法對應的接口是允許使用 GET 和 POST 方法進行請求的
服務調用者-Feign 客戶端:
./cloud-zookeeper-feign/src/main/java/com/ljq/demo/springboot/cloud/zookeeper/feign/service/CloudZookeeperFeignService.java
@GetMapping(value = "/api/cloud/zookeeper/hello", produces = {MediaType.APPLICATION_JSON_VALUE})
String sayHello(@RequestParam("name") String name);
這裏調用遠程 cloud-zookeeper-provider
的服務,只能指定一種請求方式,如果按照上邊的方式 method = {RequestMethod.GET, RequestMethod.POST
允許了兩種方式請求的話,會拋出異常
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cloudZookeeperFeignController': Unsatisfied dependency expressed through field 'cloudZookeeperFeignService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.ljq.demo.springboot.cloud.zookeeper.feign.service.CloudZookeeperFeignService': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: Method sayHello can only contain 1 method field. Found: [GET, POST]
6 使用 Feign 調用遠程 Zookeeper 服務提示 FeignException$BadRequest: status 400
很多人會納悶,方法,參數,請求地址都沒有問題,爲什麼還會拋出 BadRequest 異常呢?這裏有一個很難讓人想到的問題,通常在寫 Controller 中方法時,很多人不會專門寫 @requestParam
註解來指定參數的名稱,請求依然可以成功,但是在使用 Feign 進行遠程調用的時候就必須使用 @requestParam
註解指定請求參數名稱
./cloud-zookeeper-feign/src/main/java/com/ljq/demo/springboot/cloud/zookeeper/feign/service/CloudZookeeperFeignService.java
@GetMapping(value = "/api/cloud/zookeeper/hello", produces = {MediaType.APPLICATION_JSON_VALUE})
String sayHello(@RequestParam("name") String name);
7 使用 Feign 調用遠程 Zookeeper 服務提示 FeignException$MethodNotAllowed: status 405
方法不被允許,這個問題就比較好排查了,通常出現在服務註冊中心的接口允許的請求方式與 Feign 調用的請求方式不一致導致的,如註冊中心的接口只允許 POST 方式請求,而 Feign 調用時爲 GET 方式。
Feign 調用遠程 Zookeeper 服務時與服務端接口請求方式保持一致即可。
參考文檔: 【feign】解決–feign.FeignException$MethodNotAllowed: status 405 reading
8 Spring Cloud Zookeeper 的 @LoadBalanced 註解是什麼?有什麼用?不用可不可以?
@LoadBalanced
是用於客戶端負載均衡的註解,可以根據 URL 解析來確定使用哪一個服務實例。
具體說明可參考: 由springcloud ribbon的 @LoadBalanced註解的使用理解
不用可不可以?答案是不行,否則默認使用的是 http 請求
異常日誌如下:
2020-03-01 20:42:18.411 ERROR 55042 --- [nio-8101-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://cloud-zookeeper-provider/api/cloud/zookeeper/hello": cloud-zookeeper-provider; nested exception is java.net.UnknownHostException: cloud-zookeeper-provider] with root cause
java.net.UnknownHostException: cloud-zookeeper-provider
9 Spring Cloud Config 配置中心的配置文件有哪些注意事項?
- 1 中文需要進行 Unicode 轉碼
- 2 不要對配置信息添加引號,否則引號也被算作配置的一部分
- 3 若同時存在同名的
properties
和yml
配置文件,則只能讀取到同名的properties
文件中的配置信息 - 4 如果配置中心的配置與項目中的本地配置名衝突,則會在項目啓動時覆蓋本地的配置
- 5 如果使用 Git 倉庫存儲配置信息,那麼 Spring Cloud Config Server 職能讀取到已經提交的配置信息
- 6 配置中心的配置更新不能及時更新到客戶端。直接調用 Cloud Config Server 端配置可獲取到最新配置,但是 Client 端不能讀取到最新配置(關於這個問題,可通過 webHook 或 Spring Cloud Bus 解決,參考: Spring Cloud(七):配置中心(Git 版與動態刷新)【Finchley 版】 )
X Github 源碼
Gtihub 源碼地址 : https://github.com/Flying9001/springBootDemo
個人公衆號:404Code,分享半個互聯網人的技術與思考,感興趣的可以關注.