主頁 > 後端開發 > 再見!公司的爛系統……

再見!公司的爛系統……

2021-02-21 06:10:07 後端開發

作者:zhanlijun
來源:www.cnblogs.com/LBSer/p/6195309.html

1 為什么要拆分?

先看一段對話,

從上面對話可以看出拆分的理由:

1) 應用間耦合嚴重,系統內各個應用之間不通,同樣一個功能在各個應用中都有實作,后果就是改一處功能,需要同時改系統中的所有應用,這種情況多存在于歷史較長的系統,因各種原因,系統內的各個應用都形成了自己的業務小倍訓;

2) 業務擴展性差,資料模型從設計之初就只支持某一類的業務,來了新型別的業務后又得重新寫代碼實作,結果就是專案延期,大大影響業務的接入速度;

3) 代碼老舊,難以維護,各種隨意的if else、寫死邏輯散落在應用的各個角落,處處是坑,開發維護起來戰戰兢兢;

4) 系統擴展性差,系統支撐現有業務已是顫顫巍巍,不論是應用還是DB都已經無法承受業務快速發展帶來的壓力;

5) 新坑越挖越多,惡性回圈,不改變的話,最終的結果就是把系統做死了,

2 拆前準備什么?

2.1 多維度把握業務復雜度

一個老生常談的問題,系統與業務的關系?

我們最期望的理想情況是第一種關系(車輛與人),業務覺得不合適,可以馬上換一輛新的,但現實的情況是更像心臟起搏器與人之間的關系,不是說換就能換,一個系統接的業務越多,耦合越緊密,如果在沒有真正把握住業務復雜度之前貿然行動,最終的結局就是把心臟帶飛,

如何把握住業務復雜度?需要多維度的思考、實踐,

一個是技術層面,通過與pd以及開發的討論,熟悉現有各個應用的領域模型,以及優缺點,這種討論只能讓人有個大概,更多的細節如代碼、架構等需要通過做需求、改造、優化這些實踐來掌握,

各個應用熟悉之后,需要從系統層面來構思,我們想打造平臺型的產品,那么最重要也是最難的一點就是功能集中管控,打破各個應用的業務小倍訓,統一收攏,這個決心更多的是開發、產品、業務方、各個團隊之間達成的共識,“按照業務或者客戶需求組織資源”,

此外也要與業務方保持功能溝通、計劃溝通,確保應用拆分出來后符合使用需求、擴展需求,獲取他們的支持,

2.2 定義邊界,原則:高內聚,低耦合,單一職責!

業務復雜度把握后,需要開始定義各個應用的服務邊界,怎么才算是好的邊界?像葫蘆娃兄弟一樣的應用就是好的!

舉個例子,葫蘆娃兄弟(應用)間的技能是相互獨立的,遵循單一職責原則,比如水娃只能噴水,火娃只會噴火,隱形娃不會噴水噴火但能隱身,更為關鍵的是,葫蘆娃兄弟最終可以合體為金剛葫蘆娃,即這些應用雖然功能彼此獨立,但又相互打通,最后合體在一起就成了我們的平臺,

這里很多人會有疑惑,拆分粒度怎么控制?很難有一個明確的結論,只能說是結合業務場景、目標、進度的一個折中,但總體的原則是先從一個大的服務邊界開始,不要太細,因為隨著架構、業務的演進,應用自然而然會再次拆分,讓正確的事情自然發生才最合理,

2.3 確定拆分后的應用目標

一旦系統的宏觀應用拆分圖出來后,就要落實到某一具體的應用拆分上了,

首先要確定的就是某一應用拆分后的目標,拆分優化是沒有底的,可能越做越深,越做越沒結果,繼而又影響自己和團隊的士氣,比如說可以定這期的目標就是將db、應用分拆出去,資料模型的重新設計可以在第二期,

2.4 確定當前要拆分應用的架構狀態、代碼情況、依賴狀況,并推演可能的各種例外,

動手前的思考成本遠遠低于動手后遇到問題的解決成本,應用拆分最怕的是中途說“他*的,這塊不能動,原來當時這樣設計是有原因的,得想別的路子!”這時的壓力可想而知,整個節奏不符合預期后,很可能會接二連三遇到同樣的問題,這時不僅同事們士氣下降,自己也會喪失信心,繼而可能導致拆分失敗,

2.5 給自己留個錦囊,“有備無患”,

錦囊就四個字“有備無患”,可以貼在桌面或者手機上,在以后具體實施程序中,多思考下“方案是否有多種可以選擇?復雜問題能否拆解?實際操作時是否有預案?”,應用拆分在具體實踐程序中比拼得就是細致二字,多一份方案,多一份預案,不僅能提升成功概率,更給自己信心,

2.6 放松心情,緩解壓力

收拾下心情,開干!

3 實踐

3.1 db拆分實踐

DB拆分在整個應用拆分環節里最復雜,分為垂直拆分和水平拆分兩種場景,我們都遇到了,垂直拆分是將庫里的各個表拆分到合適的資料庫中,比如一個庫中既有訊息表,又有人員組織結構表,那么將這兩個表拆分到獨立的資料庫中更合適,

水平拆分:以訊息表為例好了,單表突破了千萬行記錄,查詢效率較低,這時候就要將其分庫分表,

3.1.1 主鍵id接入全域id發生器

DB拆分的第一件事情就是使用全域id發生器來生成各個表的主鍵id,為什么?

舉個例子,假如我們有一張表,兩個欄位id和token,id是自增主鍵生成,要以token維度來分庫分表,這時繼續使用自增主鍵會出現問題,

正向遷移擴容中,通過自增的主鍵,到了新的分庫分表里一定是唯一的,但是,我們要考慮遷移失敗的場景,如下圖所示,新的表里假設已經插入了一條新的記錄,主鍵id也是2,這個時候假設開始回滾,需要將兩張表的資料合并成一張表(逆向回流),就會產生主鍵沖突!

因此在遷移之前,先要用全域唯一id發生器生成的id來替代主鍵自增id,這里有幾種全域唯一id生成方法可以選擇,

1)snowflake(非全域遞增)

https://github.com/twitter/snowflake

2) mysql新建一張表用來專門生成全域唯一id(利用auto_increment功能)(全域遞增);

3)有人說只有一張表怎么保證高可用?那兩張表好了(在兩個不同db),一張表產生奇數,一張表產生偶數,或者是n張表,每張表的負責的步長區間不同(非全域遞增)

4)……

我們使用的是阿里巴巴內部的tddl-sequence(mysql+記憶體),保證全域唯一但非遞增,在使用上遇到一些坑:

1)對按主鍵id排序的sql要提前改造,因為id已經不保證遞增,可能會出現亂序場景,這時候可以改造為按gmt_create排序;

2)報主鍵沖突問題,這里往往是代碼改造不徹底或者改錯造成的,比如忘記給某一insert sql的id添加#{},導致繼續使用自增,從而造成沖突;

3.1.2 建新表&遷移資料&binlog同步

1) 新表字符集建議是utf8mb4,支持表情符,新表建好后索引不要漏掉,否則可能會導致慢sql!從經驗來看索引被漏掉時有發生,建議事先列計劃的時候將這些要點記下,后面逐條檢查;

2) 使用全量同步工具或者自己寫job來進行全量遷移;全量資料遷移務必要在業務低峰期時操作,并根據系統情況調整并發數;

3) 增量同步,全量遷移完成后可使用binlog增量同步工具來追資料,比如阿里內部使用精衛,其它企業可能有自己的增量系統,或者使用阿里開源的cannal/otter:

https://github.com/alibaba/canal?spm=5176.100239.blogcont11356.10.5eNr98

https://github.com/alibaba/otter/wiki/QuickStart?spm=5176.100239.blogcont11356.21.UYMQ17

增量同步起始獲取的binlog位點必須在全量遷移之前,否則會丟資料,比如我中午12點整開始全量同步,13點整全量遷移完畢,那么增量同步的binlog的位點一定要選在12點之前,

位點在前會不會導致重復記錄?不會!線上的MySQL binlog是row 模式,如一個delete陳述句洗掉了100條記錄,binlog記錄的不是一條delete的邏輯sql,而是會有100條binlog記錄,insert陳述句插入一條記錄,如果主鍵沖突,插入不進去,

3.1.3 聯表查詢sql改造

現在主鍵已經接入全域唯一id,新的庫表、索引已經建立,且資料也在實時追平,現在可以開始切庫了嗎?no!

考慮以下非常簡單的聯表查詢sql,如果將B表拆分到另一個庫里的話,這個sql怎么辦?畢竟跨庫聯表查詢是不支持的!

因此,在切庫之前,需要將系統中上百個聯表查詢的sql改造完畢,

如何改造呢?

1) 業務避免

業務上松耦合后技術才能松耦合,繼而避免聯表sql,但短期內不現實,需要時間沉淀;

2) 全域表

每個應用的庫里都冗余一份表,缺點:等于沒有拆分,而且很多場景不現實,表結構變更麻煩;

3) 冗余欄位

就像訂單表一樣,冗余商品id欄位,但是我們需要冗余的欄位太多,而且要考慮欄位變更后資料更新問題;

4) 記憶體拼接

4.1)通過RPC呼叫來獲取另一張表的資料,然后再記憶體拼接,1)適合job類的sql,或改造后RPC查詢量較少的sql;2)不適合大資料量的實時查詢sql,假設10000個ID,分頁RPC查詢,每次查100個,需要5ms,共需要500ms,rt太高,

4.2)本地快取另一張表的資料

適合資料變化不大、資料量查詢大、介面性能穩定性要求高的sql,

3.1.4切庫方案設計與實作(兩種方案)

以上步驟準備完成后,就開始進入真正的切庫環節,這里提供兩種方案,我們在不同的場景下都有使用,

a)DB停寫方案

優點:快,成本低;

缺點:

1)如果要回滾得聯系DBA執行線上停寫操作,風險高,因為有可能在業務高峰期回滾;

2)只有一處地方校驗,出問題的概率高,回滾的概率高

舉個例子,如果面對的是比較復雜的業務遷移,那么很可能發生如下情況導致回滾:

sql聯表查詢改造不完全;

sql聯表查詢改錯&性能問題;

索引漏加導致性能問題;

字符集問題

此外,binlog逆向回流很可能發生字符集問題(utf8mb4到gbk),導致回流失敗,這些binlog同步工具為了保證強最終一致性,一旦某條記錄回流失敗,就卡住不同步,繼而導致新老表的資料不同步,繼而無法回滾!

b)雙寫方案

第2步“打開雙寫開關,先寫老表A再寫新表B”,這時候確保寫B表時try catch住,例外要用很明確的標識打出來,方便排查問題,第2步雙寫持續短暫時間后(比如半分鐘后),可以關閉binlog同步任務,

優點:

1)將復雜任務分解為一系列可測小任務,步步為贏;

2)線上不停服,回滾容易;

3)字符集問題影響小

缺點:

1)流程步驟多,周期長;

2)雙寫造成RT增加

3.1.5 開關要寫好

不管什么切庫方案,開關少不了,這里開關的初始值一定要設定為null!

如果隨便設定一個默認值,比如”讀老表A“,假設我們已經進行到讀新表B的環節了,這時重啟了應用,在應用啟動的一瞬間,最新的“讀新表B”的開關推送等可能沒有推送過來,這個時候就可能使用默認值,繼而造成臟資料!

3.2 拆分后一致性怎么保證?

以前很多表都在一個資料庫內,使用事務非常方便,現在拆分出去了,如何保證一致性?

1)分布式事務

性能較差,幾乎不考慮,

2)訊息機制補償

3)定時任務補償

用得較多,實作最終一致,分為加資料補償,刪資料補償兩種,

3.3 應用拆分后穩定性怎么保證?

一句話:懷疑第三方防備使用方做好自己!

1)懷疑第三方

a)防御式編程,制定好各種降級策略;

  • 比如快取主備、推拉結合、本地快取……

b)遵循快速失敗原則,一定要設定超時時間,并例外捕獲;

c)強依賴轉弱依賴,旁支邏輯異步化

  • 我們對某一個核心應用的旁支邏輯異步化后,回應時間幾乎縮短了1/3,且后面中間件、其它應用等都出現過抖動情況,而核心鏈路一切正常;

d)適當保護第三方,慎重選擇重試機制

2)防備使用方

a)設計一個好的介面,避免誤用

  • 遵循介面最少暴露原則;很多同學搭建完新應用后會隨手暴露很多介面,而這些介面由于沒人使用而缺乏維護,很容易給以后挖坑,聽到過不只一次對話,”你怎么用我這個介面啊,當時隨便寫的,性能很差的“;
  • 不要讓使用方做介面可以做的事情;比如你只暴露一個getMsgById介面,別人如果想批量呼叫的話,可能就直接for回圈rpc呼叫,如果提供getMsgListByIdList介面就不會出現這種情況了,
  • 避免長時間執行的介面;特別是一些老系統,一個介面背后對應的可能是for回圈select DB的場景,

b)容量限制

  • 按應用優先級進行流控;不僅有總流量限流,還要區分應用,比如核心應用的配額肯定比非核心應用配額高;
  • 業務容量控制,有些時候不僅僅是系統層面的限制,業務層面也需要限制,舉個例子,對saas化的一些系統來說,”你這個租戶最多1w人使用“,

3)做好自己

a)單一職責

b)及時清理歷史坑

  • 例:例如我們改造時候發現一年前留下的坑,去掉后整個集群cpu使用率下降1/3

c) 運維SOP化

  • 說實話,線上出現問題,如果沒有預案,再怎么處理都會超時,曾經遇到過一次DB故障導致臟資料問題,最終只能硬著頭皮寫代碼來清理臟資料,但是時間很長,只能眼睜睜看著故障不斷升級,經歷過這個事情后,我們馬上設想出現臟資料的各種場景,然后上線了三個清理臟資料的job,以防其它不可預知的產生臟資料的故障場景,以后只要遇到出現臟資料的故障,直接觸發這三個清理job,先恢復再排查,

d)資源使用可預測

  • 應用的cpu、記憶體、網路、磁盤心中有數
    • 正則匹配耗cpu
    • 耗性能的job優化、降級、下線(回圈呼叫rpc或sql)
    • 慢sql優化、降級、限流
    • tair/redis、db呼叫量要可預測
    • 例:tair、db

舉個例子: 某一個介面類似于秒殺功能,qps非常高(如下圖所示),請求先到tair,如果找不到會回源到DB,當請求突增時候,甚至會觸發tair/redis這層快取的限流,此外由于快取在一開始是沒資料的,請求會穿透到db,從而擊垮db,

這里的核心問題就是tair/redis這層資源的使用不可預測,因為依賴于介面的qps,怎么讓請求變得可預測呢?

如果我們再增加一層本地快取(guava,比如超時時間設定為1秒),保證單機對一個key只有一個請求回源,那樣對tair/redis這層資源的使用就可以預知了,假設有500臺client,對一個key來說,一瞬間最多500個請求穿透到Tair/redis,以此類推到db,

再舉個例子:

比如client有500臺,對某key一瞬間最多有500個請求穿透到db,如果key有10個,那么請求最多可能有5000個到db,恰好這些sql的RT有些高,怎么保護DB的資源?

可以通過一個定時程式不斷將資料從db刷到快取,這里就將不可控的5000個qps的db訪問變為可控的個位數qps的db訪問,

4 總結

1)做好準備面對壓力!

2)復雜問題要拆解為多步驟,每一步可測驗可回滾!

這是應用拆分程序中的最有價值的實踐經驗!

**3)墨菲定律:你所擔心的事情一定會發生,而且會很快發生,所以準備好你的SOP****(標準化解決方案)!

某個周五和組里同事吃飯時討論到某一個功能存在風險,約定在下周解決,結果周一剛上班該功能就出現故障了,以前講小概率不可能發生,但是概率再小也是有值的,比如p=0.00001%,互聯網環境下,請求量足夠大,小概率事件就真發生了,

4)借假修真

這個詞看上去有點玄乎,顧名思義,就是在借者一些事情,來提升另外一種能力,前者稱為假,后者稱為真,在任何一個單位,對核心系統進行大規模拆分改造的機會很少,因此一旦你承擔起責任,就毫不猶豫地全力以赴吧!不要被程序的曲折所嚇倒,心智的磨礪,才是本真,

另外,關注公眾號Java技術堆疊,在后臺回復:面試,可以獲取我整理的 Java 系列面試題和答案,非常齊全,
近期熱文推薦:

1.Java 15 正式發布, 14 個新特性,重繪你的認知!!

2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!

3.我用 Java 8 寫了一段邏輯,同事直呼看不懂,你試試看,,

4.吊打 Tomcat ,Undertow 性能很炸!!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/261600.html

標籤:Java

上一篇:C語言中字符指標陣列與字符陣列的區別(實體決議)

下一篇:JUnit5學習之二:Assumptions類

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more