<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          還重構(gòu)?就這代碼只能鏟了重寫!

          共 9740字,需瀏覽 20分鐘

           ·

          2022-01-21 07:25

          往期熱門文章:

          1、是怎么樣的SQL優(yōu)化能做到 900W+數(shù)據(jù),從17s到300ms?
          2、再見(jiàn) BeanUtils!對(duì)比 12 種 Bean 自動(dòng)映射工具,就它性能最拉跨!
          3、暴力拒絕白嫖,著名開(kāi)源項(xiàng)目作者刪庫(kù)跑路,數(shù)千個(gè)應(yīng)用程序無(wú)限輸出亂碼
          4、兩天兩夜,1M圖片優(yōu)化到100kb!
          5、12 個(gè)頂級(jí) Bug 跟蹤工具

          一、前言

          我們不一樣,就你沒(méi)對(duì)象!?對(duì),你是面向過(guò)程編程的!
          我說(shuō)的,絕大多數(shù)碼農(nóng)沒(méi)日沒(méi)夜被需求憋著肝出來(lái)的代碼,無(wú)論有多么的吭哧癟肚,都不可能有重構(gòu),只有重新寫。為什么?因?yàn)橹匦聦懰ǖ臅r(shí)間成本,遠(yuǎn)比重構(gòu)一份已經(jīng)爛成團(tuán)的代碼,要節(jié)省時(shí)間。但誰(shuí)又不敢保證重寫完的代碼,就比之前能好多少,況且還要承擔(dān)著重寫后的代碼事故風(fēng)險(xiǎn)和幾乎體現(xiàn)不出來(lái)的業(yè)務(wù)價(jià)值!
          雖然代碼是給機(jī)器運(yùn)行的,但同樣也是給人看的,并且隨著每次需求的迭代、變更、升級(jí),都需要研發(fā)人員對(duì)同一份代碼進(jìn)行多次開(kāi)發(fā)和上線,那么這里就會(huì)涉及到可維護(hù)、易擴(kuò)展、好交接的特點(diǎn)。
          而那些不合理分層實(shí)現(xiàn)代碼邏輯、不寫代碼注釋、不按規(guī)范提交、不做格式化、命名隨意甚至把 queryBatch 寫成 queryBitch 的,都會(huì)造成后續(xù)代碼沒(méi)法重構(gòu)的問(wèn)題。那么接下來(lái)我們就分別介紹下,開(kāi)發(fā)好能重構(gòu)的代碼,都要怎么干!

          二、代碼優(yōu)化

          1. 約定規(guī)范

          #?提交:主要 type
          feat:?????增加新功能
          fix:??????修復(fù)bug

          #?提交:特殊 type
          docs:?????只改動(dòng)了文檔相關(guān)的內(nèi)容
          style:????不影響代碼含義的改動(dòng),例如去掉空格、改變縮進(jìn)、增刪分號(hào)
          build:????構(gòu)造工具的或者外部依賴的改動(dòng),例如webpack,npm
          refactor:?代碼重構(gòu)時(shí)使用
          revert:???執(zhí)行g(shù)it?revert打印的message

          #?提交:暫不使用type
          test:?????添加測(cè)試或者修改現(xiàn)有測(cè)試
          perf:?????提高性能的改動(dòng)
          ci:???????與CI(持續(xù)集成服務(wù))有關(guān)的改動(dòng)
          chore:????不修改src或者test的其余修改,例如構(gòu)建過(guò)程或輔助工具的變動(dòng)

          #?注釋:類注釋配置
          /**
          *?@description:?
          *?@author:?${USER}
          *?@date:?${DATE}
          */

          • 分支:開(kāi)發(fā)前提前約定好拉分支的規(guī)范,比如日期_用戶_用途,210905_xfg_updateRuleLogic
          • 提交作者,type: desc?如:小傅哥,fix:更新規(guī)則邏輯問(wèn)題?參考Commit message 規(guī)范
          • 注釋:包括類注釋、方法注釋、屬性注釋,在 IDEA 中可以設(shè)置類注釋的頭信息?Editor -> File and Code Templates -> File Header?推薦下載安裝 IDEA P3C 插件?Alibaba Java Coding Guidelines,統(tǒng)一標(biāo)準(zhǔn)化編碼方式。

          2. 接口標(biāo)準(zhǔn)

          在編寫 RPC 接口的時(shí)候,返回的結(jié)果中一定要包含明確的Code碼Info描述,否則使用方很難知道這個(gè)接口是否調(diào)用成功還是異常,以及是什么情況的異常。
          定義 Result
          public?class?Result?implements?java.io.Serializable?{

          ????private?static?final?long?serialVersionUID?=?752386055478765987L;

          ????/**?返回結(jié)果碼?*/
          ????private?String?code;

          ????/**?返回結(jié)果信息?*/
          ????private?String?info;

          ????public?Result()?{
          ????}

          ????public?Result(String?code,?String?info)?{
          ????????this.code?=?code;
          ????????this.info?=?info;
          ????}

          ????public?static?Result?buildSuccessResult()?{
          ????????Result?result?=?new?Result();
          ????????result.setCode(Constants.ResponseCode.SUCCESS.getCode());
          ????????result.setInfo(Constants.ResponseCode.SUCCESS.getInfo());
          ????????return?result;
          ????}
          ????
          ????//?...get/set
          }
          返回結(jié)果包裝:繼承
          public?class?RuleResult?extends?Result?{

          ????private?String?ruleId;
          ????private?String?ruleDesc;

          ????public?RuleResult(String?code,?String?info)?{
          ????????super(code,?info);
          ????}
          ?
          ?//?...get/set
          }

          //?使用
          public?RuleResult?execRule(DecisionMatter?request)?{
          ????return?new?RuleResult(Constants.ResponseCode.SUCCESS.getCode(),?Constants.ResponseCode.SUCCESS.getInfo());
          }
          返回結(jié)果包裝:泛型
          public?class?ResultData<T>?implements?Serializable?{

          ????private?Result?result;
          ????private?T?data;

          ????public?ResultData(Result?result,?T?data)?{
          ????????this.result?=?result;
          ????????this.data?=?data;
          ????}???
          ?
          ?//?...get/set
          }??

          //?使用
          public?ResultData?execRule(DecisionMatter?request)?{
          ????return?new?ResultData(Result.buildSuccessResult(),?new?Rule());
          }
          • 兩種接口返回結(jié)果的包裝定義,都可以規(guī)范返回結(jié)果。在這樣的方式包裝后,使用方就可以用統(tǒng)一的方式來(lái)判斷Code碼并做出相應(yīng)的處理。

          3. 庫(kù)表設(shè)計(jì)

          三范式:是數(shù)據(jù)庫(kù)的規(guī)范化的內(nèi)容,所謂的數(shù)據(jù)庫(kù)三范式通俗的講就是設(shè)計(jì)數(shù)據(jù)庫(kù)表所應(yīng)該遵守的一套規(guī)范,如果不遵守就會(huì)造成設(shè)計(jì)的數(shù)據(jù)庫(kù)不規(guī)范,出現(xiàn)數(shù)據(jù)庫(kù)字段冗余,數(shù)據(jù)的查詢,插入等操作等問(wèn)題。
          數(shù)據(jù)庫(kù)不僅僅只有三范式(1NF/2NF/3NF),還有BCNF、4NF、5NF…,不過(guò)在實(shí)際的數(shù)據(jù)庫(kù)設(shè)計(jì)時(shí),遵守前三個(gè)范式就足夠了。再向下就會(huì)造成設(shè)計(jì)的數(shù)據(jù)庫(kù)產(chǎn)生過(guò)多不必要的約束。

          0NF

          • 第零范式是指沒(méi)有使用任何范式,數(shù)據(jù)存放冗余大量表字段,而且這樣的表結(jié)構(gòu)非常難以維護(hù)。

          1NF

          • 第一范式是在第零范式冗余字段上的改進(jìn),把重復(fù)字段抽離出來(lái),設(shè)計(jì)成一個(gè)冗余數(shù)據(jù)較少便于存儲(chǔ)和讀取的表結(jié)構(gòu)。
          • 同時(shí)在第一范式中也指出,表中的所有字段都應(yīng)該是原子的、不可再分割的,例如:你不能把公司雇員表的部門名稱和職責(zé)存放到一個(gè)字段。需要確保每列保持原子性

          2NF

          • 滿足1NF后,要求表中的列,都必須依賴主鍵,確保每個(gè)列都和主鍵列之間聯(lián)系,而不能間接聯(lián)系,也就是一個(gè)表只能描述一件事情。需要確保表中的每列都和主鍵相關(guān)。

          3NF

          • 不能存在依賴關(guān)系,學(xué)號(hào)、姓名,到院系,院系到宿舍,需要確保每列都和主鍵列直接相關(guān),而不是間接相關(guān)。

          反三范式

          三大范式是設(shè)計(jì)數(shù)據(jù)庫(kù)表結(jié)構(gòu)的規(guī)則約束,但是在實(shí)際開(kāi)發(fā)中允許局部變通:
          1. 有時(shí)候?yàn)榱吮阌诓樵儯瑫?huì)在如訂單表冗余上當(dāng)時(shí)用戶的快照信息,比如用戶下單時(shí)候的一些設(shè)置信息。
          2. 單列列表數(shù)據(jù)匯總到總表中一個(gè)數(shù)量值,便于查詢的時(shí)候可以避免列表匯總操作。
          3. 可以在設(shè)計(jì)表的時(shí)候冗余一些字段,避免因業(yè)務(wù)發(fā)展情況多變,考慮不周導(dǎo)致該表繁瑣的問(wèn)題。

          4. 算法邏輯

          通常在我們實(shí)際的業(yè)務(wù)功能邏輯開(kāi)發(fā)中,為了能滿足一些高并發(fā)的場(chǎng)景,是不可能對(duì)數(shù)據(jù)庫(kù)表上鎖扣減庫(kù)存、也不能直接for循環(huán)大量輪訓(xùn)操作的,通常需要考慮??在這樣場(chǎng)景怎么去中心化以及降低時(shí)間復(fù)雜度。
          秒殺:去中心化
          • 背景:這個(gè)一個(gè)商品活動(dòng)秒殺的實(shí)現(xiàn)方案,最開(kāi)始的設(shè)計(jì)是基于一個(gè)活動(dòng)號(hào)ID進(jìn)行鎖定,秒殺時(shí)鎖定這個(gè)ID,用戶購(gòu)買完后就進(jìn)行釋放。但在大量用戶搶購(gòu)時(shí),出現(xiàn)了秒殺分布式獨(dú)占鎖后的業(yè)務(wù)邏輯處理中發(fā)生異常,釋放鎖失敗。導(dǎo)致所有的用戶都不能再拿到鎖,也就造成了有商品但不能下單的問(wèn)題。
          • 優(yōu)化:優(yōu)化獨(dú)占競(jìng)態(tài)為分段靜態(tài),將活動(dòng)ID+庫(kù)存編號(hào)作為動(dòng)態(tài)鎖標(biāo)識(shí)。當(dāng)前秒殺的用戶如果發(fā)生鎖失敗那么后面的用戶可以繼續(xù)秒殺不受影響。而失敗的鎖會(huì)有worker進(jìn)行補(bǔ)償恢復(fù),那么最終會(huì)避免超賣以及不能售賣。
          算法:反面教材
          @Test
          public?void?test_idx_hashMap()?{
          ????Map?map?=?new?HashMap<>(64);
          ????map.put("alderney",?"未實(shí)現(xiàn)服務(wù)");
          ????map.put("luminance",?"未實(shí)現(xiàn)服務(wù)");
          ????map.put("chorology",?"未實(shí)現(xiàn)服務(wù)");
          ????map.put("carline",?"未實(shí)現(xiàn)服務(wù)");
          ????map.put("fluorosis",?"未實(shí)現(xiàn)服務(wù)");
          ????map.put("angora",?"未實(shí)現(xiàn)服務(wù)");
          ????map.put("insititious",?"未實(shí)現(xiàn)服務(wù)");
          ????map.put("insincere",?"已實(shí)現(xiàn)服務(wù)");
          ????
          ????long?startTime?=?System.currentTimeMillis();
          ????for?(int?i?=?0;?i?100000000;?i++)?{
          ????????map.get("insincere");
          ????}
          ????System.out.println("耗時(shí)(initialCapacity):"?+?(System.currentTimeMillis()?-?startTime));
          }
          • 背景:HashMap 數(shù)據(jù)獲取時(shí)間復(fù)雜度在 O(1) -> O(logn) -> O(n),但經(jīng)過(guò)特殊操作,可以把這個(gè)時(shí)間復(fù)雜度,拉到O(n)
          • 操作:這是一個(gè)定義HashMap存放業(yè)務(wù)實(shí)現(xiàn)key,通過(guò)key調(diào)用服務(wù)的功能。但這里的key,只有insincere有用,其他的都是未實(shí)現(xiàn)服務(wù)。那你看到有啥問(wèn)題了嗎?
            • 這點(diǎn)代碼乍一看沒(méi)什么問(wèn)題,看明白了就是代碼里下砒霜!它的目的就一個(gè),要讓所有的key成一個(gè)鏈表放到HashMap中,而且把有用的key放到鏈表的最后,增加get時(shí)的耗時(shí)!
            • 首先,new HashMap<>(64);為啥默認(rèn)初始化64個(gè)長(zhǎng)度?因?yàn)槟J(rèn)長(zhǎng)度是8,插入元素時(shí),當(dāng)鏈表長(zhǎng)度為8時(shí)候會(huì)進(jìn)行擴(kuò)容和鏈表樹(shù)化判斷,此時(shí)就會(huì)把原有的key散列了,不能讓所有key構(gòu)成一個(gè)時(shí)間復(fù)雜度較高的鏈表。
            • 其次,所有的?key?都是刻意選出來(lái)的,因?yàn)樗麄冊(cè)?HashMap?計(jì)算下標(biāo)時(shí),下標(biāo)值都為0,idx =?(size - 1) & (key.hashCode() ^ (key.hashCode() >>> 16)),這樣就能讓所有?key?都散列到同一個(gè)位置進(jìn)行碰撞。而且單詞?insincere?的意思是;不誠(chéng)懇的、不真誠(chéng)的!
            • 最后,前7個(gè)key其實(shí)都是廢?key,不起任何作用,只有最后一個(gè) key 有服務(wù)。那么這樣就可以在HashMap中建出來(lái)很多這樣耗時(shí)的碰撞鏈表,當(dāng)然要滿足0.75的負(fù)載因子,不要讓HashMap擴(kuò)容。

          其實(shí)很多算法包括:散列、倒排、負(fù)載等,都是可以用到很多實(shí)際的業(yè)務(wù)場(chǎng)景中的,包括:人群過(guò)濾、抽獎(jiǎng)邏輯、數(shù)據(jù)路由等等方面,這些功能的使用可以降低時(shí)間復(fù)雜度,提升系統(tǒng)的性能,降低接口響應(yīng)時(shí)常。

          5. 職責(zé)分離

          為了可以讓程序的邏輯實(shí)現(xiàn)更具有擴(kuò)展性,通常我們都需要使用設(shè)計(jì)模式來(lái)處理各個(gè)場(chǎng)景的代碼實(shí)現(xiàn)結(jié)構(gòu)。而設(shè)計(jì)模式的使用在代碼開(kāi)發(fā)中的體現(xiàn)也主要為接口的定義、抽象類的包裝和繼承類的實(shí)現(xiàn)。通過(guò)這樣的方式來(lái)隔離各個(gè)功能領(lǐng)域的開(kāi)發(fā),以此保障每次需求擴(kuò)展時(shí)可以更加靈活的添加,而不至于讓代碼因需求迭代而變得更加混亂。
          案例
          public?interface?IRuleExec?{

          ????void?doRuleExec(String?req);

          }

          public?class?RuleConfig?{

          ????protected?Map?configGroup?=?new?ConcurrentHashMap<>();

          ????static?{
          ????????//?...
          ????}

          }

          public?class?RuleDataSupport?extends?RuleConfig{

          ????protected?String?queryRuleConfig(String?ruleId){
          ????????return?"xxx";
          ????}

          }

          public?abstract?class?AbstractRuleBase?extends?RuleDataSupport?implements?IRuleExec{

          ????@Override
          ????public?void?doRuleExec(String?req)?{
          ????????//?1.?查詢配置
          ????????String?ruleConfig?=?super.queryRuleConfig("10001");

          ????????//?2.?校驗(yàn)信息
          ????????checkRuleConfig(ruleConfig);

          ????????//?3.?執(zhí)行規(guī)則{含業(yè)務(wù)邏輯,交給業(yè)務(wù)自己處理}
          ????????this.doLogic(configGroup.get(ruleConfig));
          ????}

          ????/**
          ?????*?執(zhí)行規(guī)則{含業(yè)務(wù)邏輯,交給業(yè)務(wù)自己處理}
          ?????*/

          ????protected?abstract?void?doLogic(String?req);

          ????private?void?checkRuleConfig(String?ruleConfig)?{
          ????????//?...?校驗(yàn)配置
          ????}

          }

          public?class?RuleExec?extends?AbstractRuleBase?{

          ????@Override
          ????protected?void?doLogic(String?req)?{
          ????????//?封裝自身業(yè)務(wù)邏輯
          ????}

          }
          類圖
          • 這是一種模版模式結(jié)構(gòu)的定義,使用到了接口實(shí)現(xiàn)、抽象類繼承,同時(shí)可以看到在?AbstractRuleBase?抽象類中,是負(fù)責(zé)完成整個(gè)邏輯調(diào)用的定義,并且這個(gè)抽象類把一些通用的配置和數(shù)據(jù)使用單獨(dú)隔離出去,而公用的簡(jiǎn)單方法放到自身實(shí)現(xiàn),最后是關(guān)于抽象方法的定義和調(diào)用,而業(yè)務(wù)類?RuleExec?就可以按需實(shí)現(xiàn)自己的邏輯功能了。

          6. 邏輯縝密

          你的代碼出過(guò)線上事故嗎?為什么出的事故,是樹(shù)上有十只鳥(niǎo)開(kāi)一槍還剩幾只的問(wèn)題嗎?比如:槍是無(wú)聲的嗎、鳥(niǎo)聾嗎、有懷孕的嗎、有綁在樹(shù)上的鳥(niǎo)嗎、邊上的樹(shù)還有鳥(niǎo)嗎、鳥(niǎo)害怕槍聲嗎、有殘疾的鳥(niǎo)嗎、打鳥(niǎo)的人眼睛花不花,... ...
          實(shí)際上你的線上事故基本回圍繞在:數(shù)據(jù)庫(kù)連接和慢查詢、服務(wù)器負(fù)載和宕機(jī)、異常邏輯兜底、接口冪等性、數(shù)據(jù)防重性、MQ消費(fèi)速度、RPC響應(yīng)時(shí)常、工具類使用錯(cuò)誤等等。
          下面舉個(gè)例子:用戶積分多支付,造成批量客訴。
          • 背景:這個(gè)產(chǎn)品功能的背景可能很大一部分研發(fā)都參與開(kāi)發(fā)過(guò),簡(jiǎn)單說(shuō)就是滿足用戶使用積分抽獎(jiǎng)的一個(gè)需求。上圖左側(cè)就是研發(fā)最開(kāi)始設(shè)計(jì)的流程,通過(guò)RPC接口扣減用戶積分,扣減成功后進(jìn)行抽獎(jiǎng)。但由于當(dāng)天RPC服務(wù)不穩(wěn)定,造成RPC實(shí)際調(diào)用成功,但返回超時(shí)失敗。而調(diào)用RPC接口的uuid是每次自動(dòng)生成的,不具備調(diào)用冪等性。所以造成了用戶積分多支付現(xiàn)象。
          • 處理:事故后修改抽獎(jiǎng)流程,先生成待抽獎(jiǎng)的抽獎(jiǎng)單,由抽獎(jiǎng)單ID調(diào)用RPC接口,保證接口冪等性。在RPC接口失敗時(shí)由定時(shí)任務(wù)補(bǔ)償?shù)姆绞綀?zhí)行抽獎(jiǎng)。流程整改后發(fā)現(xiàn),補(bǔ)償任務(wù)每周發(fā)生1~3次,那么也就是證明了RPC接口確實(shí)有可用率問(wèn)題,同時(shí)也說(shuō)明很久之前就有流程問(wèn)題,但由于用戶客訴較少,所以沒(méi)有反饋。

          7. 領(lǐng)域聚合

          不夠抽象、不能寫死、不好擴(kuò)展,是不是總是你的代碼,每次都像一錘子買賣,完全是寫死的、綁定的,根本沒(méi)有一點(diǎn)縫隙讓新的需求擴(kuò)展進(jìn)去。
          為什么呢,因?yàn)楹芏嘌邪l(fā)寫出來(lái)的代碼都不具有領(lǐng)域聚合的特點(diǎn),當(dāng)然這并不一定非得是在DDD的結(jié)構(gòu)下,哪怕是在MVC的分層里,也一樣可以寫出很多好的聚合邏輯,把功能實(shí)現(xiàn)和業(yè)務(wù)的調(diào)用分離開(kāi)。
          • 依靠領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的設(shè)計(jì)思想,通過(guò)事件風(fēng)暴建立領(lǐng)域模型,合理劃分領(lǐng)域邏輯和物理邊界,建立領(lǐng)域?qū)ο蠹胺?wù)矩陣和服務(wù)架構(gòu)圖,定義符合DDD分層架構(gòu)思想的代碼結(jié)構(gòu)模型,保證業(yè)務(wù)模型與代碼模型的一致性。通過(guò)上述設(shè)計(jì)思想、方法和過(guò)程,指導(dǎo)團(tuán)隊(duì)按照DDD設(shè)計(jì)思想完成微服務(wù)設(shè)計(jì)和開(kāi)發(fā)。
            • 拒絕泥球小單體、拒絕污染功能與服務(wù)、拒絕一加功能排期一個(gè)月
            • 架構(gòu)出高可用極易符合互聯(lián)網(wǎng)高速迭代的應(yīng)用服務(wù)
            • 物料化、組裝化、可編排的服務(wù),提高人效

          8. 服務(wù)分層

          如果你想讓你的系統(tǒng)工程代碼可以支撐絕對(duì)多數(shù)的業(yè)務(wù)需求,并且能沉淀下來(lái)可以服用的功能,那么基本你就需要在做代碼開(kāi)發(fā)實(shí)現(xiàn)的時(shí)候,抽離出技術(shù)組件、功能領(lǐng)域和業(yè)務(wù)邏輯這樣幾個(gè)分層,不要把頻繁變化的業(yè)務(wù)邏輯寫入到各個(gè)功能領(lǐng)域中,應(yīng)該讓功能領(lǐng)域更具有獨(dú)立性,可以被業(yè)務(wù)層串聯(lián)、編排、組合實(shí)現(xiàn)不同業(yè)務(wù)需求。這樣你的功能領(lǐng)域才能被逐步沉淀下來(lái),也更易于每次需求都 擴(kuò)展。
          • 這是一個(gè)簡(jiǎn)化的分層邏輯結(jié)構(gòu),有聚合的領(lǐng)域、SDK組件、中間件和代碼編排,并提供一些通用共性凝練出的服務(wù)治理功能。通過(guò)這樣的分層和各個(gè)層級(jí)的實(shí)現(xiàn)方式,就可以更加靈活的承接需求了。

          9. 并發(fā)優(yōu)化

          在分布式場(chǎng)景開(kāi)發(fā)系統(tǒng),要盡可能運(yùn)用上分布式的能力,從程序設(shè)計(jì)上盡可能的去避免一些集中的、分布式事物的、數(shù)據(jù)庫(kù)加鎖的,因?yàn)檫@些方式的使用都可能在某些極端情況下,造成系統(tǒng)的負(fù)載的超標(biāo),從而引發(fā)事故。
          • 所以通常情況下更需要做去集中化處理,使用MQ消除峰,降低耦合,讓數(shù)據(jù)可以最終一致性,也更要考慮在 Redis 下的使用,減少對(duì)數(shù)據(jù)庫(kù)的大量鎖處理。
          • 合理的運(yùn)用MQ、RPC、分布式任務(wù)、Redis、分庫(kù)分表以及分布式事務(wù)只有這樣的操作你才可能讓自己的程序代碼可以支撐起更大的業(yè)務(wù)體量。

          10. 源碼能力

          你有了解過(guò) HashMap 的拉鏈尋址數(shù)據(jù)結(jié)構(gòu)嗎、知道哈希散列和擾動(dòng)函數(shù)嗎、懂得怎么結(jié)合Spring動(dòng)態(tài)切換數(shù)據(jù)源嗎、AOP 是怎么實(shí)現(xiàn)以及使用的、MyBatis 是怎么和 Spring 結(jié)合交管Bean對(duì)象的,等等??此贫际切┟嬖嚨陌斯晌?,但在實(shí)際的開(kāi)發(fā)中其實(shí)是可以解決很多問(wèn)題的。
          @Around("aopPoint()?&&?@annotation(dbRouter)")
          public?Object?doRouter(ProceedingJoinPoint?jp,?DBRouter?dbRouter)?throws?Throwable?{
          ????String?dbKey?=?dbRouter.key();
          ????if?(StringUtils.isBlank(dbKey))?throw?new?RuntimeException("annotation DBRouter key is null!");

          ????//?計(jì)算路由
          ????String?dbKeyAttr?=?getAttrValue(dbKey,?jp.getArgs());
          ????int?size?=?dbRouterConfig.getDbCount()?*?dbRouterConfig.getTbCount();

          ????//?擾動(dòng)函數(shù)
          ????int?idx?=?(size?-?1)?&?(dbKeyAttr.hashCode()?^?(dbKeyAttr.hashCode()?>>>?16));

          ????//?庫(kù)表索引
          ????int?dbIdx?=?idx?/?dbRouterConfig.getTbCount()?+?1;
          ????int?tbIdx?=?idx?-?dbRouterConfig.getTbCount()?*?(dbIdx?-?1);???

          ????//?設(shè)置到?ThreadLocal
          ????DBContextHolder.setDBKey(String.format("%02d",?dbIdx));
          ????DBContextHolder.setTBKey(String.format("%02d",?tbIdx));
          ????logger.info("數(shù)據(jù)庫(kù)路由 method:{} dbIdx:{} tbIdx:{}",?getMethod(jp).getName(),?dbIdx,?tbIdx);
          ???
          ????//?返回結(jié)果
          ????try?{
          ????????return?jp.proceed();
          ????}?finally?{
          ????????DBContextHolder.clearDBKey();
          ????????DBContextHolder.clearTBKey();
          ????}
          }
          • 這是 HashMap 哈希桶數(shù)組 + 鏈表 + 紅黑樹(shù)的數(shù)據(jù)結(jié)構(gòu),通過(guò)擾動(dòng)函數(shù)?(size - 1) & (key.hashCode() ^ (key.hashCode() >>> 16));?解決數(shù)據(jù)碰撞嚴(yán)重的問(wèn)題。
          • 但其實(shí)這樣的散列算法、尋址方式都可以運(yùn)用到數(shù)據(jù)庫(kù)路由的設(shè)計(jì)實(shí)現(xiàn)中,還有整個(gè)數(shù)組+鏈表的方式其實(shí)庫(kù)+表的方式也有類似之處。
          • 數(shù)據(jù)庫(kù)路由簡(jiǎn)化的核心邏輯實(shí)現(xiàn)代碼如上,首先我們提取了庫(kù)表乘積的數(shù)量,把它當(dāng)成 HashMap 一樣的長(zhǎng)度進(jìn)行使用。
          • 當(dāng) idx 計(jì)算完總長(zhǎng)度上的一個(gè)索引位置后,還需要把這個(gè)位置折算到庫(kù)表中,看看總體長(zhǎng)度的索引因?yàn)槁涞侥膫€(gè)庫(kù)哪個(gè)表。
          • 最后是把這個(gè)計(jì)算的索引信息存放到 ThreadLocal 中,用于傳遞在方法調(diào)用過(guò)程中可以提取到索引信息。

          三、總結(jié)

          • 講道理,你幾乎不太可能把一堆已經(jīng)爛的不行的代碼,通過(guò)重構(gòu)的方式把他處理干凈。細(xì)了說(shuō),你要改變代碼結(jié)構(gòu)分層、屬性對(duì)象整合、調(diào)用邏輯封裝,但任何一步的操作都可能會(huì)對(duì)原有的接口定義和調(diào)用造成風(fēng)險(xiǎn)影響,而且外部現(xiàn)有調(diào)用你的接口還需要隨著你的改動(dòng)而升級(jí),可能你會(huì)想著在包裝一層,但這一層包裝仍需要較大的時(shí)間成本和幾乎沒(méi)有價(jià)值的適配。
          • 所以我們?cè)趯?shí)際開(kāi)發(fā)中,如果能讓這些代碼具有重構(gòu)的可能,幾乎就是要實(shí)時(shí)重構(gòu),每當(dāng)你在添加新的功能、新的邏輯、修復(fù)異常時(shí),就要考慮是否可以通過(guò)代碼結(jié)構(gòu)、實(shí)現(xiàn)方式、設(shè)計(jì)模式等手段的使用,改變不合理的功能實(shí)現(xiàn)。每一次,一點(diǎn)的優(yōu)化和改變,也不會(huì)有那么難。
          • 當(dāng)你在接需求的時(shí)候,認(rèn)真思考承接這樣的業(yè)務(wù)訴求,都需要建設(shè)怎樣的數(shù)據(jù)結(jié)構(gòu)、算法邏輯、設(shè)計(jì)模式、領(lǐng)域聚合、服務(wù)編排、系統(tǒng)架構(gòu)等,才能更合理的搭建出良好的具有易維護(hù)、可擴(kuò)展的系統(tǒng)服務(wù)。如果你對(duì)這些還沒(méi)有什么感覺(jué),可以閱讀?設(shè)計(jì)模式?和?手寫Spring,這些內(nèi)容可以幫助你提升不少的編程邏輯設(shè)計(jì)。

          往期熱門文章:

          1、歷史文章分類導(dǎo)讀列表!精選優(yōu)秀博文都在這里了!》
          2、程序員裸辭全職接單一個(gè)月的感觸
          3Java8 Stream:2萬(wàn)字20個(gè)實(shí)例,玩轉(zhuǎn)集合的篩選、歸約、分組、聚合
          4、字節(jié)終面:兩個(gè)文件的公共URL怎么找?
          5、留在一線,逃離一線?我從上海舉家回成都的生活經(jīng)歷告訴你
          6、公司規(guī)定所有接口都用 POST請(qǐng)求,這是為什么?
          7、我被這個(gè)瀏覽了 746000 次的問(wèn)題驚住了!
          8、騰訊三面:40億個(gè)QQ號(hào)碼如何去重?
          9、自從用完Gradle后,有點(diǎn)嫌棄Maven了!速度賊快!
          10、一個(gè)員工的離職成本有多恐怖!

          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久久久无码精品人妻一区蜜桃影院 | 成人综合一区二区 | www国产夜插内射视频网站 | 97超碰碰 | 豆花一区二区 |