作者:京東科技 李君
公司最近一年在推進降本增效,在用盡各種手段之后,發(fā)現(xiàn)應用太多,每個應用都做跨機房容災部署,則最少需要 4 臺機器(稱為容器更合適)。那么,將相近應用做一個合并,減少維護項目,提高機器利用率就是一個可選方案。
?
經(jīng)過前后三次不同的折騰,最后探索出來一個可行方案。記錄一下,分享出來,希望對有相關(guān)需求的研發(fā)童鞋有所幫助。下面按照四種可能的方案,分別做介紹。另外,為了方便做演示,專門整了兩個演示項目:
?
??diguage/merge-demo-boot — 合并項目,下面簡稱為 boot。
??diguage/merge-demo-web — 被合并項目,下面簡稱為 web。
?
一、Jar 包引用
?
這個方式,可能是給人印象最容易的方式。仔細思考一下,從維護性的角度來看,這個方式反而是最麻煩的方式,理由如下:
?
1.web 項目每次更新,都需要重新打包發(fā)布新版; boot 項目也需要跟著更新發(fā)布。拉一次屎,脫兩次褲子。屬實麻煩。
2.還需要考慮 web 項目的加載問題,類似下面要描述的,是否共用容器:共用容器 — 這是最容器想到的方式。但是這種方式,需要解決 Bean 沖突的問題。不共用容器 — 這種方式需要處理 web 容器如何加載的問題。默認應該是無法識別。
3.?
基于這些考慮,這種方式直接被拋棄了。
?
二、倉庫合并,公用一套容器
?
這是第一次嘗試使用的方案。也是遇到問題最多的方案。
?
1.將兩個倉庫做合并。
1.將 web 倉庫的地址配置到 boot 項目里: git remote add web git@github.com:diguage/merge-demo-web.git;
2.在 boot 項目里,切出來一個分支: git switch -c web;
3.將 web 分支的提交清空: git update-ref -d HEAD,然后做一次提交;
4.將 web 項目的代碼克隆到 web 分支上: git pull --rebase --allow-unrelated-histories web master;注意,這里需要加 --allow-unrelated-histories 參數(shù),以允許不相干的倉庫進行合并。
5.從 boot 項目的 master 分支上,切出來一個合并分支: git switch -c merge;
6.將 web 項目向 boot 項目合并: git merge --allow-unrelated-histories web;注意,這里需要加 --allow-unrelated-histories 參數(shù),以允許不相干的倉庫進行合并。
7.處理代碼沖突,完成合并即可。
2.配置文件的合并于歸整。為了防止同名配置文件沖突,需要把 web 項目的配置文件調(diào)整到一個文件夾下,這里設定為 web 目錄。然后,需要把 web 項目的配置文件,讓 boot 可以加載到。這個調(diào)整相對簡單,只需要一個注解即可 @ImportResource({"classpath:web/spring-cfg.xml"})。
3.調(diào)整完配置文件,接著遇到的問題就是上面提到的 Bean 沖突的問題。由于兩個項目都訪問相同的數(shù)據(jù)庫, Dao 及 Service 層很多很多類都是同名的。另外,在 web 項目里,Dao 是基于 iBATIS 開發(fā)的,而在 boot 項目里,DAO 是基于 MyBATIS 開發(fā)的。所以,只能給 web 項目的相關(guān)代碼做重命名(嚴謹一點是給 Spring Bean 的 beanName 做重命名操作)。這又帶來了新問題:原來的項目里,注入方式是根據(jù)名稱注入的,就需要改動大量的代碼,給相關(guān)的 Bean 變量做重命名操作。這無形中增加了很多的復雜度和不確定性。
?
經(jīng)過不斷折騰,這種方式被迫放棄。
?
三、倉庫合并,Spring Boot 父子容器
?
在經(jīng)過上述方式折騰后,就想到了另外一個方案:可以考慮使用父子容器的方式來搞。接著就查到了這篇文章: Context Hierarchy with the Spring Boot Fluent Builder API。感覺這種方式挺不錯,就嘗試了一下。
?
1.代碼合并及文件調(diào)整,跟上述步驟類似,這個后面就不再贅述。
2.按照文章中的介紹,使用父子容器的方式來加載兩個項目。代碼如下:
3.原以為,這種方式屬于父子兩個容器,即使有同名的 Bean 應該也沒有影響。但是,經(jīng)過實踐才發(fā)現(xiàn),上面這個猜測是錯誤的。Spring Boot 在啟動的時候,它背后做了檢查,如果兩個容器有同名的 Bean,它也會報錯。也會帶來像上述方式那樣的大量重命名。折騰一兩天,最后還是放棄了這種寄予厚望的方式。
package com.diguage.demo.boot; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; /** * @author D瓜哥 · https://www.diguage.com */ public class DemoBootApplication { public static void main(String[] args) { new SpringApplicationBuilder() .parent(BootConfig.class).web(WebApplicationType.NONE) .child(WebConfig.class) // 如果有第三個項目,可以作為子容器的兄弟容器加載。 // .sibling(SiblingConfig.class) .run(args); } @Configuration @ImportResource({"classpath:spring-cfg.xml"}) @ComponentScan(basePackages = "com.diguage.demo.boot") public static class BootConfig { } @Configuration @ImportResource({"classpath:web/spring-cfg.xml"}) public static class WebConfig { } }
? |
Spring Boot 背后是否做了檢查,這個是根據(jù)報錯信息的猜測,沒有翻看代碼,所以這個猜測有一定的不確定性。有機會翻一下代碼,查看一下具體原因。 |
革命尚未成功,且聽下回分解……
下文:Spring 應用合并之路(二):峰回路轉(zhuǎn),柳暗花明?
審核編輯 黃宇
-
spring
+關(guān)注
關(guān)注
0文章
336瀏覽量
14280
發(fā)布評論請先 登錄
相關(guān)推薦
評論