前置概念
無(wú)并發(fā)的解決方案
一些小型項(xiàng)目,或極少有并發(fā)的項(xiàng)目,這些策略在無(wú)并發(fā)情況下,不會(huì)有什么問題。
- 讀數(shù)據(jù)策略:有緩存則讀緩存,然后接口返回。沒有緩存,查詢出數(shù)據(jù),載入緩存,然后接口返回。
- 寫數(shù)據(jù)策略:數(shù)據(jù)發(fā)生了變動(dòng),先刪除緩存,再更新數(shù)據(jù),等下次讀取的時(shí)候載入緩存,或一步到位更新數(shù)據(jù)后直接更新緩存。
- 以上這種方案,有個(gè)高大上的名字,叫Cache Aside Pattern。
并發(fā)情況下的分布式緩存一致性問題
- 并發(fā):無(wú)論是Java的多線程,還是PHP的多進(jìn)程(默認(rèn)的單線程),用戶量或請(qǐng)求量一上來(lái),就可能有并發(fā)問題,正確的應(yīng)對(duì)并發(fā),保證數(shù)據(jù)不出錯(cuò),顯得尤為重要。
- 緩存:任何組件(不僅是Redis與MySQL),只要源數(shù)據(jù)并非一成不變,且有緩存機(jī)制,就會(huì)有一致性的問題。
- 單體架構(gòu):強(qiáng)一致性若在單機(jī)上,并不是一個(gè)問題,例如MySQL的非冗余字段的變動(dòng),關(guān)聯(lián)另一個(gè)冗余它的字段,這等強(qiáng)一致性的緩存問題,在一個(gè)事務(wù)里就能維護(hù)。
- 分布式:一致性的難點(diǎn)主要在分布式環(huán)境下,例如MySQL主從,就添加了bin log與redo log的兩階段提交策略來(lái)防止主從數(shù)據(jù)不一致。針對(duì)MySQL與Redis,可以理解成分布式系統(tǒng),如果沒有并發(fā),則按照文章開頭的方案正常處理,如果有并發(fā),就需要一些策略保證讀取的是最新的緩存數(shù)據(jù),因?yàn)槟壳皼]有一些機(jī)制,讓MySQL和Redis共同在一個(gè)事務(wù)內(nèi),能一起提交或者回滾,保持強(qiáng)一致。
- 注意:緩存一致性問題是個(gè)概率問題,不是一定出現(xiàn)或一定不出現(xiàn),并發(fā)情況下,如果不加鎖,MySQL與Redis讀寫時(shí)序是不可控的。
并發(fā)與并行,同步與異步
經(jīng)常聽到并發(fā),可真的理解這個(gè)概念嗎?
- 并發(fā):多過(guò)分任務(wù)同時(shí)進(jìn)行,但這些任務(wù)是交替執(zhí)行(分配不同的時(shí)間片,進(jìn)程或者線程的上下文切換),好比排n個(gè)隊(duì)去1個(gè)窗口辦事。
- 并行:多個(gè)任務(wù)在同一時(shí)刻同時(shí)執(zhí)行,通常需要多個(gè)或多核處理器,好比排n個(gè)隊(duì)去n個(gè)窗口辦事。
- 同步:上個(gè)任務(wù)執(zhí)行完畢后再執(zhí)行下一個(gè)任務(wù),所以同步?jīng)]有并發(fā)或并行的概念。
- 異步:下一個(gè)任務(wù)不用等待上個(gè)任務(wù)執(zhí)行完。
分布式必知的CAP定理
- 一致性(Consistency):每個(gè)節(jié)點(diǎn)的數(shù)據(jù)需要與源數(shù)據(jù)保持一致,這里往往指的是強(qiáng)一致性。
- 可用性(Availability):這指的是系統(tǒng)能夠在任何時(shí)刻都對(duì)外提供服務(wù),所謂的穩(wěn)定高可用。
- 分區(qū)容錯(cuò)性(Partition Tolerance):分布式系統(tǒng)下,某節(jié)點(diǎn)掛掉,還能夠?qū)ν馓峁┓?wù)的能力。
為什么CAP只能3選2
CP舍去A:意味著分布式環(huán)境保證強(qiáng)一致,一個(gè)數(shù)據(jù)的變動(dòng)必須及時(shí)通知所有的節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)為了保證強(qiáng)一致,不得不加鎖,此時(shí)一個(gè)并發(fā)過(guò)來(lái)請(qǐng)求上鎖的數(shù)據(jù),不是失敗就是被阻塞,高可用就達(dá)不到。常見于銀行,金融,支付系統(tǒng)。
AP舍去C:意味著分布式系統(tǒng)舍棄掉一致性,一個(gè)數(shù)據(jù)的變動(dòng)必須不一定會(huì)通知所有的節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)也不一定加鎖,所以就不會(huì)出現(xiàn)阻塞或者失敗的情況。常見的DNS、CDN系統(tǒng)就是這樣,修改源數(shù)據(jù)不會(huì)立即生效,盡管短時(shí)間讀取還是老數(shù)據(jù),但是它不會(huì)因?yàn)槟阈薷木图渔i或者阻塞,它還讓你用。
CA舍去P:意味著分布式系統(tǒng)下不具有分區(qū)容錯(cuò)性,但是是個(gè)分布式就有小概率會(huì)出問題,盡管很低,想杜絕分區(qū)容錯(cuò)性,只能是單體架構(gòu)。常見于單機(jī)系統(tǒng)。
架構(gòu)的設(shè)計(jì)是根據(jù)當(dāng)前業(yè)務(wù)特性權(quán)衡而來(lái)的結(jié)果。一個(gè)兜底的策略,要看當(dāng)前業(yè)務(wù)不能接受哪些缺點(diǎn),而不是看有哪些優(yōu)點(diǎn),然后去一步步演進(jìn),改善它。
ACID中的C與CAP中的C
不是一個(gè)概念。
- ACID中的C:是事務(wù)內(nèi)的數(shù)據(jù),從一個(gè)合法狀態(tài)轉(zhuǎn)換到另一個(gè)合法的狀態(tài)(這里的合法,指符合業(yè)務(wù),符合事務(wù)的變化規(guī)律,A轉(zhuǎn)賬B 50元,雙方余額加減的過(guò)程,數(shù)只能是50,不會(huì)是60)。
- CAP中的C,緩存的數(shù)據(jù)需要與源數(shù)據(jù)保持一致。
分布式必知的BASE理論
可以理解為CAP的寬松方案,通過(guò)犧牲數(shù)據(jù)的強(qiáng)一致性,來(lái)獲得高可用性。
BASE理論最經(jīng)典的場(chǎng)景,就是支付回調(diào),支付狀態(tài)允許存在幾秒鐘的延遲,而不是支付后實(shí)時(shí)獲取。
基本可用性(Basic Availability):允許系統(tǒng)中的某些部分出現(xiàn)故障,保證核心功能的正常運(yùn)行。常見的負(fù)載均衡、服務(wù)降級(jí)、限流等方式。
軟狀態(tài)(Soft state):允許數(shù)據(jù)存在中間狀態(tài),或者說(shuō)是游離態(tài),允許數(shù)據(jù)在某些時(shí)刻不一致,但是最終達(dá)到一致性的狀態(tài)。支付回調(diào)前支付狀態(tài)的場(chǎng)景。
最終一致性(Eventually Consistency):系統(tǒng)中的數(shù)據(jù)在經(jīng)過(guò)一段時(shí)間后,最終會(huì)達(dá)到一致的狀態(tài)。不需要強(qiáng)制性的實(shí)時(shí)保持一致,只需要最后保持一致性。支付回調(diào)后支付狀態(tài)的場(chǎng)景。
強(qiáng)一致性、弱一致性、順序一致性、線性一致性、因果一致性、最終一致性
- 強(qiáng)一致性:等價(jià)嚴(yán)格、原子、線性一致,所有節(jié)點(diǎn)操作順序都與全局時(shí)鐘下的幾乎一致,需加鎖,常見于金融、銀行場(chǎng)景。
- 弱一致性:能容忍數(shù)據(jù)短時(shí)間內(nèi)不一致,或能容忍部分?jǐn)?shù)據(jù)不一致。常見于CDN和DNS場(chǎng)景。
- 順序一致性:強(qiáng)一致,在不同的節(jié)點(diǎn)上保持一致的操作執(zhí)行順序,需要加鎖。常見于分布式隊(duì)列場(chǎng)景。
- 最終一致性:弱一致,最終一致性就屬于弱一致性,概念相似。常見于支付回調(diào),離線下載,異步同步大文件的場(chǎng)景。
- 線性一致性:強(qiáng)一致,強(qiáng)一致性、嚴(yán)格一致性、原子一致性一回事,同上。
- 因果一致性:弱一致,屬于事件觸發(fā)類型,因?yàn)橛|發(fā),果為執(zhí)行,常見于MySQL異步主從(弱一致)、分布式評(píng)論系統(tǒng)。
詳解順序一致性:
假設(shè)有兩個(gè)節(jié)點(diǎn),在一個(gè)分布式系統(tǒng)中執(zhí)行寫操作。如果 節(jié)點(diǎn)A 在時(shí)間點(diǎn) 1 執(zhí)行了寫操作 W1,然后 節(jié)點(diǎn)B 在時(shí)間點(diǎn) 2 執(zhí)行了寫操作 W2。那么順序一致性要求在分布式系統(tǒng)的其它節(jié)點(diǎn)上,讀取數(shù)據(jù)的時(shí)候:
應(yīng)該先看到 W1 的效果,然后才能看到 W2 的效果,而不是先看到W2再看到W1的結(jié)果。
并且保證,再同一時(shí)間,不能出現(xiàn)節(jié)點(diǎn)1看到 W1 的效果,節(jié)點(diǎn)2看到 W2 的效果。
全局時(shí)鐘
分布式下的全局時(shí)鐘,指的是分布式的每個(gè)節(jié)點(diǎn),都有著一致的時(shí)間基準(zhǔn),就像共用一個(gè)時(shí)鐘一樣,讓每個(gè)節(jié)點(diǎn)在一致的時(shí)間線上處理各自的數(shù)據(jù),這個(gè)非常重要。
實(shí)操
增數(shù)據(jù)后,保證Redis讀取的是最新的數(shù)據(jù)
不存在一致性問題。
請(qǐng)求A新增數(shù)據(jù)時(shí),有并發(fā)讀請(qǐng)求B,此時(shí)B是查不到緩存的的,就會(huì)查詢MySQL。
如果B查到數(shù)據(jù)就載入緩存。
如果B沒查到數(shù)據(jù),就等后面的請(qǐng)求查詢到數(shù)據(jù)后再載入緩存。
無(wú)論B能否查詢到緩存,都不影響A的插入,或C的讀取,因此不影響數(shù)據(jù)一致性。
刪數(shù)據(jù)后,保證Redis讀取的是最新的數(shù)據(jù)
- 情況1:不存在一致性問題,緩存中沒有這塊數(shù)據(jù),刪除MySQL數(shù)據(jù)后沒有其它副本。
- 情況2:存在一致性問題,緩存中有這塊數(shù)據(jù),此時(shí)就有2種策略:
先刪除緩存再刪除數(shù)據(jù):
不行。如果請(qǐng)求A緩存刪除成功,此時(shí)一個(gè)過(guò)來(lái)一個(gè)讀請(qǐng)求B,會(huì)查詢到即將要?jiǎng)h掉的MySQL數(shù)據(jù),并將其重新載入緩存,請(qǐng)求A執(zhí)行MySQL delete后,會(huì)造成MySQL無(wú)數(shù)據(jù),Redis有數(shù)據(jù)的情況,緩存不一致。先刪除數(shù)據(jù)再刪除緩存:
也有問題。請(qǐng)求A發(fā)現(xiàn)緩存數(shù)據(jù)不存在,讀取了MySQL數(shù)據(jù),此時(shí)請(qǐng)求B刪除MySQL數(shù)據(jù),接著請(qǐng)求B刪除緩存數(shù)據(jù),請(qǐng)求A將老數(shù)據(jù)寫入緩存。此時(shí)數(shù)據(jù)庫(kù)里沒數(shù)據(jù),Redis里有數(shù)據(jù),緩存不一致。
并發(fā)情況下,沒辦法控制執(zhí)行順序問題,所以這就是個(gè)概率問題。
改數(shù)據(jù)后,保證Redis讀取的是最新的數(shù)據(jù)
- 情況1:不存在一致性問題,緩存中沒有這塊數(shù)據(jù),更新MySQL數(shù)據(jù)后沒有其它副本。
- 情況2:存在一致性問題,緩存中有這塊數(shù)據(jù),此時(shí)就有5種策略:
先更新數(shù)據(jù)再更新緩存:
不行。v初始值為0,更新請(qǐng)求A,將值改為1,此時(shí)過(guò)來(lái)更新請(qǐng)求B,將值改為2,確定MySQL最終的值是2。但是受redis網(wǎng)絡(luò)連接卡頓等影響,更新請(qǐng)求B先將緩存中的v值改為2,更新請(qǐng)求A再將緩存中的值改為1。此時(shí)MySQL的值為2,緩存中的值為1,緩存不一致。先更新緩存再更新數(shù)據(jù):
不行。v初始值為0,更新請(qǐng)求A修改緩存數(shù)據(jù),v值改為1,然后更新MySQL,此時(shí)MySQL更新失敗,或事務(wù)回滾,雖然后續(xù)的讀緩存的是新數(shù)據(jù),數(shù)據(jù)庫(kù)的是老數(shù)據(jù),但這種臟數(shù)據(jù)再某些場(chǎng)景下是不允許發(fā)生的,緩存不一致。如果非要使用,可以再更新完緩存后,通過(guò)消息中間件異步更新數(shù)據(jù)庫(kù)。先更新數(shù)據(jù)再刪除緩存:
不行。v初始值為0,更新請(qǐng)求A將v值改為1,更新請(qǐng)求B將v值改為2,更新請(qǐng)求A刪除緩存,更新請(qǐng)求B刪除緩存,然后A、B都將值寫入MySQL。此時(shí)查詢請(qǐng)求C過(guò)來(lái),獲取的可以使最新的數(shù)據(jù)并載入緩存。
另一種情況:
v初始值為0,更新請(qǐng)求A將v值改為1,更新請(qǐng)求B將v值改為2,更新請(qǐng)求A刪除緩存,更新請(qǐng)求B刪除緩存,然后更新請(qǐng)求A將1寫入MySQL,此時(shí)查詢請(qǐng)求C過(guò)來(lái),沒有緩存,查MySQL發(fā)現(xiàn)是1,載入緩存。更新請(qǐng)求B將MySQL值改為2,此時(shí)緩存不一致。
另一種情況:
v初始值為0,讀請(qǐng)求X查詢不到緩存的數(shù)據(jù),于是讀MySQL,獲取值為0,此時(shí)過(guò)來(lái)一個(gè)更新請(qǐng)求Y,將MySQL中的0改為1,然后刪除緩存,請(qǐng)求X又將老數(shù)據(jù)0載入緩存。
此時(shí)MySQL值為1,Redis值為0,緩存不一致。先刪除緩存再更新數(shù)據(jù):
不行。v初始值為0,更新請(qǐng)求A先刪除緩存,此時(shí)過(guò)來(lái)一個(gè)讀請(qǐng)求B,發(fā)現(xiàn)沒有緩存,讀取MySQL,獲取值為0,此時(shí)更新請(qǐng)求A將MySQL的0改為1,讀請(qǐng)求B將Redis v的值改為0,緩存不一致。延時(shí)雙刪:
可以。是最終一致性的方案。
延遲:更新MySQL后,隔一段時(shí)間再刪除緩存,一般間隔0.3-~1.5秒左右,略大于一個(gè)讀請(qǐng)求周期的耗時(shí)即可。
雙刪:更新數(shù)據(jù)庫(kù)的前后都刪除一遍緩存。
v初始值為0,更新請(qǐng)求A先刪除緩存,此時(shí)讀請(qǐng)求B過(guò)來(lái),發(fā)現(xiàn)沒有緩存,去查MySQL后載入緩存,然后更新請(qǐng)求A更新MySQL更新v值為1,再等一段時(shí)間,再次刪除緩存。此時(shí)讀請(qǐng)求B查詢的是0(為了保證全局的最終一致性,只能犧牲查詢請(qǐng)求B),更新請(qǐng)求A中,MySQL和緩存數(shù)據(jù)一致,都是1。
延遲雙刪的延遲,是為了保證查詢請(qǐng)求B走完流程,如果刪除的早,更新請(qǐng)求A先走完流程,那還是會(huì)被讀請(qǐng)求B的將老數(shù)據(jù)載入緩存。
延時(shí)可通過(guò)用Laravel queue或者其它消息中間件去實(shí)現(xiàn)。
那如何保證,第二次刪除成功呢?
添加重試機(jī)制,如果刪除失敗,可再刪除3次。
查數(shù)據(jù)后,保證Redis讀取的是最新的數(shù)據(jù)
不存在一致性問題。
數(shù)據(jù)沒有寫操作。
小結(jié)
可見若數(shù)據(jù)發(fā)生了變動(dòng),無(wú)論以上方案怎么搞,都可能會(huì)有不一致的情況。
即使是延遲雙刪,也會(huì)增加運(yùn)維成本,多了一些工序,它們的高可用又是一類問題。
如果有MySQL+Redis的鎖機(jī)制,那么其它請(qǐng)求就會(huì)阻塞,性能就下降。反過(guò)來(lái)就影響一致性,這也是CAP三選二的體現(xiàn)。
換個(gè)角度講,對(duì)于緩存一致性問題,刪除緩存,比更新緩存相對(duì)可靠。
- 如果用更新緩存策略:兩個(gè)更新的并發(fā)請(qǐng)求,更新MySQL的順序是一種順序,受網(wǎng)絡(luò)波動(dòng)和卡機(jī)的影響,更新緩存可能又一種順序,這可能導(dǎo)致緩存與MySQL值不一致,緩存內(nèi)部的可能是個(gè)錯(cuò)值。
- 如果用刪除緩存策略:兩個(gè)更新的并發(fā)請(qǐng)求,更新MySQL的順序是一種順序,受網(wǎng)絡(luò)波動(dòng)和卡機(jī)的影響,緩存也是被刪除。最多其它讀請(qǐng)求把舊值又給緩存了進(jìn)去,但至少是個(gè)舊值,而不是個(gè)錯(cuò)值。
簡(jiǎn)單的兜底策略
加上緩存過(guò)期時(shí)間。避免MySQL與緩存長(zhǎng)期不一致,對(duì)實(shí)時(shí)性要求越高,則緩存過(guò)期時(shí)間越少。
一些粒度更細(xì)的自定義存儲(chǔ)方式,用不了Redis對(duì)key的自動(dòng)過(guò)期功能,可添加時(shí)間戳字段,用程序邏輯控制過(guò)期。
Canal組件的策略
- 官網(wǎng):https://github.com/alibaba/canal
- 簡(jiǎn)介:Canal是用于解決緩存一致性問題的組件。由阿里巴巴開源,Java編寫的C/S架構(gòu)的軟件。它的服務(wù)端可以偽裝成MySQL從機(jī),實(shí)時(shí)捕獲 MySQL 主機(jī)的bin log,并將變更事件推送到消息隊(duì)列或者其它存儲(chǔ)中,以實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)同步、對(duì)數(shù)據(jù)倉(cāng)庫(kù)的實(shí)時(shí)分析等應(yīng)用場(chǎng)景。
- 客戶端支持:支持Java、C#、Go、PHP、Python、Rust、NodeJs客戶端。
- 支持同步Kafka、ElasticSearch、HBase、RocketMQ、RabbitMQ、pulsarMQ、不支持直連Redis。
- 前置知識(shí):一文讀懂MySQL7大日志(slow、redo、undo、bin、relay、general、error)和簡(jiǎn)單搭建MySQL主從復(fù)制。
- Linux環(huán)境,MySQL主機(jī)配置
可參考https://github.com/alibaba/canal/wiki/QuickStart
vim /etc/my.cnf
在[mysqld]下寫入以下配置
server-id=180 //主機(jī)標(biāo)識(shí),得有一個(gè)唯一編號(hào)
log-bin=mysql-bin //bin log日志名
binlog_format=row //注意這里一定要用row,用statement或mixed,canal將無(wú)法解析
binlog-do-db=test //數(shù)據(jù)庫(kù)名
service mysql restart 保存后重啟
確認(rèn)bin log是否開啟
select @@sql_log_bin;
+---------------+
| @@sql_log_bin |
+---------------+
| 1 |
+---------------+
登錄mysql命令行
CREATE USER canal IDENTIFIED BY 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO '從機(jī)用戶名'@'%';
alter user 'canal'@'%' identified with mysql_native_password by '從機(jī)密碼,這里設(shè)置成canal';
FLUSH PRIVILEGES;
- Linux環(huán)境,Canal配置
不管是不是Java開發(fā)者,不需要安裝JDK或者JRE
mkdir /usr/local/cancl
cd /usr/local/cancl
wget https://github.com/alibaba/canal/releases/download/canal-1.1.7/canal.deployer-1.1.7.tar.gz
tar zxf canal.deployer-1.1.7.tar.gz
修改配置文件
vim /usr/local/canal/conf/example/instance.properties
canal.instance.mysql.slaveId=1 //去掉注釋,并修改為非主庫(kù)server-id的數(shù)據(jù)
canal.instance.master.address=127.0.0.1:3306 //主庫(kù)的IP:Port
canal.instance.dbUsername=從機(jī)用戶名
canal.instance.dbPassword=從機(jī)密碼
啟動(dòng),改配置后記得重啟。
/usr/local/canal/bin/startup.sh
查看
ps aux | grep canal
- Java或其它語(yǔ)言配置canal客戶端:https://github.com/alibaba/canal。
- PHP canal客戶端配置:
git clone https://github.com/xingwenge/canal-php.git
cd canal-php
composer install
php src/sample/client.php
只要沒提示Socket error: Connection refused (SOCKET_ECONNREFUSED),就說(shuō)明連接成功。
示例代碼如下:
需要注意兩個(gè)地方
$client->connect("127.0.0.1", 11111);
參數(shù)1的值是canal server的ip。
參數(shù)2的值是/usr/local/canal/conf/canal.properties文件中的canal.port項(xiàng),遠(yuǎn)程連接記得要開放端口。
$client->subscribe("1", "example", ".*\\..*");
參數(shù)1是/usr/local/canal/conf/example/instance.properties文件的canal.instance.mysql.slaveId項(xiàng)。
參數(shù)2是/usr/local/canal/conf/example,example的目錄名,一般不動(dòng)他,可以配置多個(gè)。
參數(shù)3有個(gè)默認(rèn)值,排除某個(gè)庫(kù)的某個(gè)表。
try {
$client = CanalConnectorFactory::createClient(CanalClient::TYPE_SOCKET_CLUE);
# $client = CanalConnectorFactory::createClient(CanalClient::TYPE_SWOOLE);
$client->connect("127.0.0.1", 11111);
$client->subscribe("1", "example", ".*\\..*");
# $client->subscribe("1001", "example", "db_name.tb_name"); # 設(shè)置過(guò)濾
while (true) {
$message = $client->get(100);
if ($entries = $message->getEntries()) {
foreach ($entries as $entry) {
Fmt::println($entry);
}
}
sleep(1);
}
$client->disConnect();
} catch (\Exception $e) {
echo $e->getMessage(), PHP_EOL;
}
//當(dāng)出現(xiàn)以下字樣時(shí),說(shuō)明聯(lián)調(diào)成功。
================> binlog[mysql-bin.000044 : 3130],name[test,cs], eventType: 2
-------> before
id : 1 update= false
num : 6 update= false
-------> after
id : 1 update= false
num : 7 update= true
性能:
官方給出的消費(fèi)速度:sql insert 10000 事件,32秒消耗完成。消費(fèi)速度 312.5 條/s。
實(shí)測(cè)消費(fèi)速度遠(yuǎn)高于官方的消費(fèi)速度,1C1G的本地搭建的服務(wù)器:
表中共10000條,不加where全部更新,全部同步耗時(shí)2.5秒。
實(shí)測(cè)平均4000/s的消費(fèi)速度,意味著每秒有略低于4000個(gè)redis key被修改,配置更高的服務(wù)器性能將會(huì)更好。
這速消費(fèi)度,大部分的后端接口qps都趕不上,所以足以應(yīng)對(duì)99%的業(yè)務(wù)場(chǎng)景。
- 對(duì)于PHP cancel二次修改,集成到Laravel框架的思路
這種不怎么參與業(yè)務(wù),可以集成到框架,也可以不集成,在服務(wù)器上單獨(dú)放一個(gè)目錄去,cli模式下直接跑也行。
方案1,粗略的整理:將這個(gè)包放進(jìn)laravel的app/Libs中,所有目錄結(jié)構(gòu)均不改動(dòng),跟框架邏輯無(wú)關(guān),僅僅變動(dòng)跟隨Git同步。
二開,redis的連接參數(shù)可以硬編碼,也可以讀取.env的配置,正則匹配獲取,要寫在while(true)的外面(指的是cancal-php/src/sample/client.php中的while(true))。
方案2,細(xì)致的整理:因?yàn)槟壳绊?xiàng)目用不上,所以暫時(shí)不準(zhǔn)備實(shí)操,但是思路得有,示例:
composer中的配置,需要集成到框架的composer中,
"require": {
"google/protobuf": "^3.8",
"php": ">=5.6",
"clue/socket-raw": "^1.4"
},
"autoload": {
"psr-4": {
"Com\\Alibaba\\Otter\\Canal\\Protocol\\": "src/protocol/Com/Alibaba/Otter/Canal/Protocol/",
"GPBMetadata\\": "src/protocol/GPBMetadata/",
"xingwenge\\canal_php\\": "src/"
}
}
cancal-php/src/sample/client.php的文件也就不到40行。
這塊邏輯可以放到框架的app/Libs下,也可以放到app\Console\Commands,用php artisan xxx命令去執(zhí)行。
不是依賴包內(nèi)的其它文件,放在app/Libs/cancal目錄下。
核心邏輯接口再src/Fmt.php文件的printLn方法中提供了的可用參數(shù),
有數(shù)據(jù)庫(kù)名、表名、操作主鍵、更改前的值、更改后的值、以及DML動(dòng)作類型,可以根據(jù)這個(gè)二開,根據(jù)自定義的命名規(guī)則,配置自定義redis的動(dòng)作。