日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

FastDFS 端口映射問題,---spring改變jar包中bean方法邏輯的另一種特殊方式

作者:阿亮_1024 更新時間: 2022-02-25 編程語言

前言概述

最近在做一個系統,是面向事業單位的,其中文件系統,用的是FastDFS,因為這個自己之前用過,所以就用了這個,沒有考慮與本項目契合的問題(這也是菜的表現,技術面廣度窄,用過哪個就用哪個,沒有進行技術選型),然后引出了一系列問題……。

因為系統是面向事業單位的,所以這些數據、文件都要放在內網里面 ,然后通過一個公網的ip和端口映射出來。比如:我在內網里面,MySQL在192.168.1.154這臺機器上,端口是3360,那我在內網直接連192.168.1.154:3360,就能連上這個MySQL,但是當出了這個內網就不行。所以把這個192.168.1.154:3360通過一個公網的ip和端口映射起來,如:172.138.182.199:5735,這個172.138.182.199:5735是一個暴露在公網中的端口,所以外部可以訪問這個地址,然后這個地址又被映射到了它局域網內的192.168.1.154:3360,所以我直接連172.138.182.199:5735,就能連上內網那臺機器的mysql。
網絡圖

大概就是這么個意思,因為事業單位的對數據這些看得比較嚴,不會放到公網上去,所以就這么弄。項目的mysql、redis都是這么弄的,但是在用FastDFS這樣搞的時候卻出了問題

問題來了

我們在最開發的時候,都是在內網里面弄,后面有同學說,有時候回家也想弄一下,因為在內網里面,只有在公司的WiFi下,才能連上mysql、redis、FastDFS這些,所以只有在辦公室里才能開發。既然有同學又這樣的想法,反正我們最后也是這個樣子部署的,所以把這些都映射到一個公網機器暴露出去。在弄mysql、redis的時候一切正常,但是在把FastDFS映射出去的時候,卻碰到了問題。

FastDFS是分布式文件系統,所以是用分布式思想設計的,其中有兩種類型的角色,一種tracker,調度中心,一種是storage,存儲節點。tracker就像古代青樓中的老鴇,storage就像老鴇手下的姑娘,顧客有需求時,就去找老鴇,對老鴇說:我要一個xxx的姑娘,然后老鴇就把她手下某個滿足條件的姑娘推給顧客,剩下的時候就是顧客和姑娘直接交流了。tracker和storage之間的關系也是這樣,當客戶端需要上傳或者下載文件時,是先和tracker進行通信的,tracker把它下面某個可用的storage的地址返給客戶端,然后客戶端再和這個strong通信,進行存儲或者下載文件。當然,在這個過程中,tracker也起到了負載均衡的作用。

FastDFS的原理圖

原理大概就是這樣子,想起來,這樣子的過程,和映射也不沖突,不就是一個tracker和一個storage嗎?我把他們兩個都映射到公網,不就可以通信了嗎?想想這樣是可以,但是實際上客戶端只能主動和tracker通信,不能主動去和storage通信,什么意思呢?就是在項目的配置中,只能配置tracker的地址,不能配置storage的地址。其實想想也是,你去找青樓找姑娘,不能直接去和姑娘聯系吧?因為你沒有姑娘的聯系方式,你也不知道這些姑娘的性格脾氣是否符合你的要求,所以你只能去找老鴇,老鴇再把姑娘的聯系方式給你。放到FastDFS中也是這樣,客戶端肯定不能直接就去和strong通信的,因為客戶端不知道有哪些storage,這些strong是否可用,而且如果繞過了tracker,那tracker的負載均衡不就成擺設了嗎?所以客戶端只有去和tracker通信, tracker把某個可用的storage地址返給客戶端,然后客戶端再和這個storage通信,進行文件的上傳下載。所以呢,我們把tracker的地址映射出來,可以配置到項目,然后去通信,但是將storage映射出來沒啥用,因為項目沒有配置storage地址的地方,不能主動去和storage通信,客戶端storage的地址都是和tracker通信,tracker返給客戶端的。

問題分析

把問題分析得這么清楚,其實想想可能還是有解決辦法,storage的地址不是tracker返給客戶端的嗎,那我就去改tracker,把tracker下所有的storage地址都改成映射之后的地址,那tracker返回給客戶端的地址,就是映射過后的地址,客戶端一樣可以連上。然后我就去看他tracker的配置文件,找有沒有配置storage地址的配置項,結果是令人失望的,沒有這個配置項。

我再翻了一下storage的配置文件,發現了原因,FastDFS的設計中,tracker是不主動去配置storage的地址的,只是strong配置了tracker的地址。就像微服務一樣,注冊中心沒有去配置所有服務的地址,是每個服務配置了注冊中心的地址,然后服務定時向注冊中心發送自己的心跳,如果某個服務不發送或者什么了,注冊中心就認為這個服務掛了,就不會向外推送這個服務的地址,而是推送其他注冊中心認為可用的地址。這一條路也被堵死了,不能去tracker里面配置storage的地址。

那又繼續分析,FastDFS服務器那邊改不了,那我客戶端呢?無論怎樣,最終客戶端肯定是和一個strong通信的,就算tracker返回來的strong地址,那我對返回來的這個地址做手腳不是也可以嗎?

不分析不知道,一分析問題又來了,在項目里面客戶端和storage通信,可是在源碼里面的,通過spring容器注入FastDFS客戶端,然后來進行文件上傳下載的。那我想修改源碼里面的邏輯,那可就麻煩了。要對jar包進行反編譯,修改之后再打成jar包或者其它什么的,然后再用。我覺得這個方式太麻煩了,沒有去測試是否可行。還有別的方式,通過類的加載順序,在項目寫一個相同路徑的類,然后就會優先加載本項目中的類,我也沒有試。因為我想到,FastDFS中的那些邏輯,都是注入在spring容器中,那我能不能修改spring容器中某個bean的某個方法邏輯呢?這樣能看起來更具有可行性。

就這樣,從一個問題轉變成了另外一個問題,現在的問題是:怎么修改容器中bean的某個方法,或者直接替換掉容器中的bean,這時我第一時間想到的方法

解決問題

把上面的問題分析過后,開始解決新的問題,如何修改或替換 通過掃描jar包進spring容器中的bean,我翻了很多博客文章,有什么BeanPostProcessor、ClassPathBeanDefinitionScanner子類的,搞得很麻煩,也不能直接用到項目中,要慢慢的測試可行性。弄著弄著,我突然想到,我能不能把我需要改的那個bean的全部源碼復制出來,然后再掉我需要改的那部分,再注入ioc容器中,我使用時就用注入我自己寫的這個bean其實我最開始閃過這個念頭的,但是馬上就被我否決了,因為我想到了兩個問題:

  1. 如果需要修改的bean,不是直接被我注入使用的,而是被beanA注入,然后我再注入beanA來使用,那么完全行不通。
  2. 如果再往容器中注入一個我寫的寫的bean,那么這兒bean只能被我自己使用,可能不能被源碼中的其他bean使用,什么意思呢?我畫個圖說明一下
    spring中bean替換

簡單的說明一下:

  • jarbean1、jarbean2、jarbean3是jar包中注入到ioc的bean,這種bean只要在配置文件中配置好需要配置就能直接注入使用,比如org.springframework.jdbc.core.JdbcTemplate只需要配置好數據源,就能直接在service中注入使用。mybean1和myservice1是我自己寫的bean并且注入到容器中的

  • mybean1、mybean4是我們自己寫的bean并且注入到容器中,比如,service實現類,

  • 單向的箭頭表示某個bean使用另一個bean,上圖中jarbean2指向了jarbean1,表示jarbean1使用jarbean2。雙箭頭表示等效替換。

先忽略問題1,我們先分析問題2

在上圖中,我引入某個依賴時,它就自動向容器中注入了三個bean,jarbean1、jarbean2和jarbean3,并且存在一些關系,jarbean1使用了jarbean2,jarbean3使用jarbean1。原本我在myservice1中使用了jarbean1,但是現在我發現jarbean1中某個方法的邏輯我需要改一下,于是乎,我就新建了一個類mybean1,將jarbean1中所有的代碼都復制到mybean1,包括繼承、實現關系,然后我將mybea1中我想要修改的地方改成了我自己想要的邏輯,再將mybean1注入到spring容器中。jarbean1中用到了jarbean2,mybean1中同樣也能使用jarbean2,因為mybean1已經被注入到容器中了,mybean1使用jjarbean2就像controller中使用service那樣簡單,直接注入就行了。然后我再在我的myservice1中,把原本注入的jarbean1替換成了mybean1,因為mybean1是復制的jarbean1,所以該有的方法一個不少,以前怎么用jarbean1的,現在就直接怎么用mybean1,mybean1中的某些方法還被按照我的需求改了,簡直美滋滋。

但是這樣可能會存在問題,可能會存在什么問題呢?jarbean3中是使用了jarbean1的,但是現在容器中存在的jarbean1和mybean1這兩個bean,jarbean3很可能注入不進去,為什么呢?因為注入bean,一般都是通過接口來類型來的,就像我們在controller中注入service,聲明的service變量都是接口類型。jarbean1和mybean1肯定也是實現了相同類型的接口的,如果在jarbean3中使用jarbean1時,是通過接口類型注入的,那么就出問題了,因為現在容器中有兩個相同類型的bean,jarbean1和mybean1。如果是通過名稱注入的,就不會出現這樣的問題。如果jarbean3通過名字成功注入jarbean1了,那jarbean3中使用的jarbean1中的和myservice1中使用的mybean1有些地方的邏輯就不一樣了,這樣會不會造成其他問題,我也不知道了。而且,有的jar包中的bean,不允許同時出現兩個類型的bean。所以,如果我們想按照上面那種方法來替換,最好的是,保證被替換的那個bean在ioc容器除了我們自己的bean之外沒有別的jar包中的bean在使用。

那么問題1說的是什么呢?
就是假如我需要修改的邏輯在jarbean2中,而我的myservice1不是直接注入jarbean2使用,而是通過注入jarbean1使用,jarbean1再使用jarbean2。那這時,我再自己復制一個jarbean2,修改想要修改的邏輯之后,注入到容器中,怎么能使jarbean1中注入的jarbean2是我復制修改過后的的bean呢?如果不能保證,那么我復制修改之后注入容器中的bean將毫無意義,甚至可能引起bean之間的沖突。

我最開始否決這個想法就是因為第一時間想到了這兩個問題,但是我沒有仔細分析,后來回過頭來仔細分析,我現在的情況,這個兩個問題都是完全不存在的。FastDFS中用來上傳上傳下載文件的bean是com.github.tobato.fastdfs.service.DefaultFastFileStorageClient,這個bean,除了我自己之外,沒有源碼在使用,而且我需要修改的邏輯就在這一層bean里面。

需要修改的邏輯是:這個bean里面有個方法是獲取可用的storage,這個storage是通過tracker獲取的,我在這里做了點改動,把獲取到的strong,到映射配置文件里面拿到對應的映射ip和端口,然后用映射ip和端口構造成一個新的storage,返回給調用者,OK萬事大吉。上傳文件修改也是類似的修改邏輯,把從tracker獲取到的storage用對應的映射ip和端口重新構造一下。

目前項目在開發階段,沒有發現問題。

反思

  1. 這樣做雖然能達到我的目標,但其實出現這個問題的根本原因,是技術棧太窄、對FastDFS的理解太淺,用過FastDFS就直接用FastDFS,沒有想過FastDFS的分布式設計理念是否符合項目的要求。
  2. 對spring容器的理解還不夠深入,這樣子的操作我始終覺得不是優雅,看看有沒有更優雅的替換方式。

原文鏈接:https://blog.csdn.net/ql_7256/article/details/120482888

欄目分類
最近更新