<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>

          RabbitMQ 的延時(shí)隊(duì)列和鏡像隊(duì)列原理與實(shí)戰(zhàn)

          共 6555字,需瀏覽 14分鐘

           ·

          2021-11-13 18:46

          你好呀,我是歪歪。

          前幾天的時(shí)候看了一個(gè)阿里云棲開(kāi)發(fā)者沙龍的視頻錄播。

          其中一個(gè)分享的主題就是掌閱資深后端工程師、掘金小測(cè)《Redis深度歷險(xiǎn)》作者錢文品老師給大家介紹的RabbitMQ的延時(shí)隊(duì)列和鏡像隊(duì)列的原理與實(shí)踐。

          重點(diǎn)比較了 RabbitMQ 提供的消息可靠與不可靠模式,同時(shí)介紹了生產(chǎn)環(huán)境下如何使用 RabbitMQ 實(shí)現(xiàn)集群間消息傳輸。

          如果大家有時(shí)間的話,可以看看這個(gè)直播回顧的視頻:

          https://yq.aliyun.com/live/965

          如果你需要錢老師的 PPT 可以在公眾號(hào)后臺(tái)回復(fù)關(guān)鍵字 01,即可領(lǐng)取。

          本文根據(jù)演講視頻以及PPT整理而成。

          將主要圍繞以下四個(gè)方面進(jìn)行分享:

          • RabbitMQ特性
          • RabbitMQ中的消息不可靠問(wèn)題及其解決方案
          • 死信隊(duì)列
          • 生產(chǎn)環(huán)境下使用RabbitMQ應(yīng)注意的事項(xiàng)

          RabbitMQ特性

          48c3fc58954c545a6a42102bbf743b5e.webp

          對(duì)于左邊的Client Publisher而言,RabbitMQ Server是消息的接收者,也就是消費(fèi)者。

          對(duì)于右邊的Client Consumer而言,RabbitMQ Server是消息的發(fā)送者,也就是生產(chǎn)者。

          RabbitMQ Server將消息從Client Publisher傳送給Client Consumer,扮演著消息中間商的角色。

          d57b833ae50f9fb399d3c28ea77ae031.webp

          RabbitMQ Server負(fù)責(zé)將Client Publisher傳遞來(lái)的消息持久化,延后地將消息傳遞給Client Consumer.這樣,即使消費(fèi)者掛掉,RabbitMQ Server也可以存儲(chǔ)消息,當(dāng)消費(fèi)者重新工作時(shí)再將存儲(chǔ)的消息傳遞過(guò)去,從而保證消息不丟失

          RabbitMQ Server提供了堆積消息的能力。

          a7e73537d07f307933a3c1a11d42b9e2.webp

          另外,RabbitMQ Server還具有復(fù)制和廣播消息的能力。

          具體來(lái)說(shuō),RabbitMQ Server可以將Client Publisher發(fā)布的消息分發(fā)給多個(gè)消費(fèi)者,比如它能夠?qū)⑻囟ǖ南凑仗囟ǖ年?duì)列分發(fā)給特定的消費(fèi)者。

          “特定”指不同消息具有不同的routing key屬性,由上圖實(shí)例,不同的消息生產(chǎn)者生產(chǎn)了具有不同routing key的消息,通過(guò)exchange路由器將不同的routing key消息投遞到不同隊(duì)列,從而分發(fā)給不同消費(fèi)者。

          RabbitMQ中的消息不可靠問(wèn)題及其解決方案

          消費(fèi)端消息不可靠問(wèn)題及其解決方案

          f43a57378e770982dd54923a464186a4.webp

          實(shí)際上,RabbitMQ Server將消息投遞給消費(fèi)者,具有消息不可靠的特點(diǎn)。

          具體來(lái)說(shuō),RabbitMQ Server將消息投遞給消費(fèi)者時(shí)會(huì)調(diào)用套接字的write操作,而write操作的過(guò)程是不可靠性的。

          在write操作的過(guò)程中,Server需要將消息發(fā)送到套接字的緩存中,通過(guò)網(wǎng)卡轉(zhuǎn)發(fā)到鏈路上,最終到達(dá)消費(fèi)者所在的機(jī)器內(nèi)核的套接字緩存中,由消費(fèi)者使用套接字的read操作將消息讀出來(lái)。

          68407bd7f3e98f08f4a0ee643052a559.webp

          即使套接字的write操作成功也無(wú)法保證消息可靠,潛在的網(wǎng)絡(luò)故障可能使消費(fèi)者接收不到消息。

          機(jī)器宕機(jī)也可能使消息不可靠,即使消息字節(jié)流已經(jīng)到達(dá)消費(fèi)者所在機(jī)器,消費(fèi)者所在機(jī)器的宕機(jī)也可能使消息無(wú)法被即時(shí)讀取并處理。

          另外,即使消費(fèi)者即時(shí)讀取消息,內(nèi)存消息隊(duì)列中的所有消息也可能因?yàn)閗ill-9操作發(fā)生丟失。

          這些可能性都直接導(dǎo)致了消息不可靠。

          822d9425b93b5afdb00c536b131d055a.webp

          因此,需要額外的措施為消息提供可靠保障。

          一種消息可靠性保障方式是,Server投遞消息后并不立即將消息從Server刪除,而是等到消費(fèi)者接收、處理消息并返回Ack包給Server后,Server才刪除該消息。

          如果消費(fèi)者沒(méi)有發(fā)送Ack包,那么Server將重新投遞該消息。

          這個(gè)過(guò)程確保消息被消費(fèi)者處理,保證了消息可靠。

          另外,假如消費(fèi)者已處理消息并發(fā)送Ack包給Server,但由于網(wǎng)絡(luò)故障等問(wèn)題導(dǎo)致Ack包丟失時(shí),那么Server同樣會(huì)重新投遞該消息,導(dǎo)致消息被重復(fù)處理。

          消息的重復(fù)處理通常由業(yè)務(wù)層面的技術(shù)手段來(lái)避免,比如在數(shù)據(jù)庫(kù)層面添加主鍵約束等。

          另一種重復(fù)消息處理的避免方式是客戶端對(duì)每條消息維護(hù)ID,將被處理消息的ID記錄在列表中,同時(shí)檢查新到消息是否在該列表中。

          d4f07df6a87771d11544742e9494b284.webp

          RabbitMQ中的Auto Ack和Manual Ack對(duì)應(yīng)著消息不可靠模式和消息可靠模式. Auto Ack即no ack,指消息投后即刪除,對(duì)應(yīng)消息不可靠傳輸。

          Manual Ack即手動(dòng)Ack,消費(fèi)者處理完消息后使用Ack包通知Server刪除消息,對(duì)應(yīng)消息可靠傳輸。

          Auto Ack是RabbitMQ中最常用的模式,性能較好,但具有以下問(wèn)題。

          當(dāng)消息通過(guò)套接字write操作投遞后,RabbitMQ Server立即刪除該消息,該模式在遇到網(wǎng)絡(luò)故障時(shí)容易發(fā)生消息丟失。

          另外,假如消費(fèi)者處理消息的速率過(guò)低,可能導(dǎo)致消息在消費(fèi)者recv buffer中大量堆積,從而導(dǎo)致Server端send buffer也堆積大量消息,Server端無(wú)法繼續(xù)調(diào)用套接字write操作。

          這樣,一段時(shí)間之后,Server可能強(qiáng)制關(guān)閉消息傳輸鏈接,導(dǎo)致消息不可傳輸。

          盡管Auto Ack存在一定風(fēng)險(xiǎn),目前許多公司仍在應(yīng)用Auto Ack模式。

          使用Auto Ack模式時(shí),開(kāi)發(fā)者需要注意消費(fèi)者和生產(chǎn)者的實(shí)例數(shù)量比例,使消息生產(chǎn)者產(chǎn)生消息的速率與消費(fèi)者消費(fèi)消息的速率大致持平。

          a9dc496736bde5622fb2e7ae8ad0cf65.webp

          Manual Ack是RabbitMQ 中更加智能的一種模式。

          Manual Ack在工作時(shí)會(huì)考慮消息消費(fèi)者的消息接收能力,根據(jù)消費(fèi)者的消息接受能力和當(dāng)前接收到的Ack包自動(dòng)調(diào)節(jié)分發(fā)消息的速率,保證消息分發(fā)可靠、不阻塞。具體來(lái)說(shuō),客戶端通過(guò)PrefetchCount告知Server自身堆積消息的能力。

          生產(chǎn)端消息不可靠問(wèn)題及其解決方案

          3422bb3a48064aa69246ef15089bc02c.webp

          消息生產(chǎn)端同樣存在消息的可靠性問(wèn)題。

          從Client Publisher將消息傳遞給Server和從Server將消息傳遞給Client Consumer的過(guò)程是完全對(duì)等的,Server和Client Consumer間傳遞消息的可靠性問(wèn)題在Client Publisher和Server間同樣存在。

          bc3cb4216137177966fc399e5126c211.webp

          Client Publisher首先將消息寫到套接字,再通過(guò)網(wǎng)絡(luò)傳遞給Server的套接字buffer,最終由Server讀取該消息。這一過(guò)程的潛在網(wǎng)絡(luò)問(wèn)題也可能使Server端接收不到消息。

          821d71b57a34fa395c39aa23e80baec1.webp

          另外,Server端本身也可能導(dǎo)致消息不可靠。

          Server端需要持久化消息,但出于性能開(kāi)銷的考慮,Server端并不在每次持久化消息時(shí)都刷盤。

          具體來(lái)說(shuō),Server端會(huì)對(duì)文件執(zhí)行write操作,將臟數(shù)據(jù)寫入操作系統(tǒng)的緩存中,而不是立即將數(shù)據(jù)寫入磁盤。

          一般情況下,Server可能每幾百毫秒執(zhí)行一次fsync操作,通過(guò)fsync操作將文件的臟數(shù)據(jù)寫入磁盤。

          由于Server具有宕機(jī)風(fēng)險(xiǎn),那么每次Server宕機(jī)時(shí),還未被fsync操作處理的數(shù)據(jù)就可能丟失,此過(guò)程類似于Redis AOF。

          6ecb839f06368687e51695d3ebfb3094.webp

          RabbitMQ通過(guò)生產(chǎn)者事務(wù)和生產(chǎn)者確認(rèn)兩個(gè)方法解決Server產(chǎn)生的數(shù)據(jù)不可靠問(wèn)題。

          生產(chǎn)者事務(wù)的基本原理是采用select和commit指令包裹publish,在消息生產(chǎn)者publish數(shù)據(jù)之前執(zhí)行select操作,相當(dāng)于begin transaction事務(wù)開(kāi)始,在執(zhí)行若干個(gè)publish操作后,再執(zhí)行commit操作,相當(dāng)于提交事務(wù)。根據(jù)tcp包的有序性,commit包成功接收意味著commit包之前的包也成功接收。

          因此,收到從Client Publisher傳遞過(guò)來(lái)的commit包意味著該commit包之前的所有publish包都已成功接收,即所有消息都成功接收。

          然而,commit包只有等到Server端的fsync操作執(zhí)行完畢時(shí)才返回,因此生產(chǎn)者事務(wù)的效率較低,通常只在有批量publish操作時(shí)才使用生產(chǎn)者事務(wù)模式。

          也就是說(shuō),客戶端將消息累計(jì)起來(lái)批量發(fā)送,以降低fsync操作帶來(lái)的性能損失。

          此外,在進(jìn)程中累計(jì)消息也存在風(fēng)險(xiǎn),累計(jì)的消息可能由于進(jìn)程掛掉而丟失。

          總的來(lái)說(shuō),生產(chǎn)者事務(wù)由于性能缺點(diǎn)不被RabbitMQ官方推薦

          a9c5e9594043d1d539a0bd8a9ffb36be.webp

          另一種Server帶來(lái)的數(shù)據(jù)不可靠問(wèn)題的解決方案是生產(chǎn)者確認(rèn)。

          生產(chǎn)者確認(rèn)類似于消費(fèi)端的Ack機(jī)制,生產(chǎn)者可能連續(xù)發(fā)送多條消息,Server將這些消息異步地通過(guò)fsync操作寫入磁盤再異步地給生產(chǎn)者發(fā)送Ack包,告知生產(chǎn)者消息的接收成功。

          由于Ack包異步傳輸,不影響生產(chǎn)者端消息的正常發(fā)送。生產(chǎn)者確認(rèn)模式下,Ack包批量發(fā)送,并且都攜帶有序號(hào),以告知生產(chǎn)者該序號(hào)以前的所有消息都已正常落盤。

          盡管RabbitMQ推薦用戶使用生產(chǎn)者確認(rèn)模式,目前的RabbitMQ版本還未實(shí)現(xiàn)消息的重發(fā)機(jī)制,只實(shí)現(xiàn)了Ack包的批量發(fā)送,以通知Client Publisher哪些消息接收成功。

          當(dāng)消息丟失時(shí),Client Publisher端已publish的消息在進(jìn)程掛掉時(shí)也可能丟失,而不是重新發(fā)送,因此生產(chǎn)者確認(rèn)的作用也不明顯。

          當(dāng)然,生產(chǎn)者確認(rèn)起到了降低消息發(fā)布速度的作用,減小了消息丟失的數(shù)量。

          c33fe9251a9e6a8cc06cc5021a8c567a.webp

          生產(chǎn)者確認(rèn)中的消息重發(fā)可以通過(guò)以下幾種方法實(shí)現(xiàn)。

          第一種方式在內(nèi)存中累積還未收到Ack包的消息,收到Ack包后刪除該消息,對(duì)于一段時(shí)間內(nèi)還停留在內(nèi)存中的消息,重發(fā)該消息。

          這種方式將未Ack消息存入內(nèi)存,一旦消息生產(chǎn)者宕機(jī),這些消息也會(huì)丟失。

          另一種方式將未收到Ack包消息存入磁盤,當(dāng)收到Ack包后刪除該消息,然而,磁盤存儲(chǔ)依賴于fsync操作,降低了系統(tǒng)處理消息的性能。

          同時(shí),這還會(huì)提高編程的復(fù)雜度,因?yàn)檫@要求發(fā)布消息時(shí)維護(hù)文件隊(duì)列,還要求一個(gè)異步線程將文件隊(duì)列中的消息發(fā)布到Server,帶來(lái)了多線程和鎖問(wèn)題。

          還有一種方式將未Ack消息存入Redis,但當(dāng)出現(xiàn)網(wǎng)絡(luò)故障時(shí),Redis也是不可靠的。

          目前提供的生產(chǎn)者確認(rèn)中的消息重發(fā)方案都還存在問(wèn)題,具體的方案選擇依賴于實(shí)際場(chǎng)景和個(gè)人取舍。

          生產(chǎn)者確認(rèn)中的消息重發(fā)可以通過(guò)以下幾種方法實(shí)現(xiàn)。

          第一種方式在內(nèi)存中累積還未收到Ack包的消息,收到Ack包后刪除該消息,對(duì)于一段時(shí)間內(nèi)還停留在內(nèi)存中的消息,重發(fā)該消息。這種方式將未Ack消息存入內(nèi)存,一旦消息生產(chǎn)者宕機(jī),這些消息也會(huì)丟失。

          另一種方式將未收到Ack包消息存入磁盤,當(dāng)收到Ack包后刪除該消息,然而,磁盤存儲(chǔ)依賴于fsync操作,降低了系統(tǒng)處理消息的性能。同時(shí),這還會(huì)提高編程的復(fù)雜度,因?yàn)檫@要求發(fā)布消息時(shí)維護(hù)文件隊(duì)列,還要求一個(gè)異步線程將文件隊(duì)列中的消息發(fā)布到Server,帶來(lái)了多線程和鎖問(wèn)題。

          還有一種方式將未Ack消息存入Redis,但當(dāng)出現(xiàn)網(wǎng)絡(luò)故障時(shí),Redis也是不可靠的。目前提供的生產(chǎn)者確認(rèn)中的消息重發(fā)方案都還存在問(wèn)題,具體的方案選擇依賴于實(shí)際場(chǎng)景和個(gè)人取舍。

          死信隊(duì)列

          4dcb147bb2d63bb917ab406eacfb32ae.webp

          死信隊(duì)列使用了RabbitMQ中的一種特殊隊(duì)列屬性,即x-message-ttl屬性,表示隊(duì)列中消息的構(gòu)建時(shí)間。

          假如用戶在聲明隊(duì)列時(shí)定義隊(duì)列的x-message-ttl屬性,此后所有進(jìn)入該隊(duì)列的消息都將持有構(gòu)建時(shí)間,到達(dá)構(gòu)建時(shí)間的消息將被刪除。

          如果還為隊(duì)列配置了回收站屬性,那么即使構(gòu)建時(shí)間到達(dá),RabbitMQ也不會(huì)立即刪除這些消息,而是將這些過(guò)期消息丟入回收站,即死信隊(duì)列。

          b10c0c227419e2ae4a04296a8a4f4623.webp

          死信隊(duì)列的工作方式如上圖。

          Client Publisher將消息投遞給路由器,也就是exchange,再由exchange將消息投遞給隊(duì)列,由隊(duì)列生成該消息的構(gòu)建時(shí)間,到達(dá)構(gòu)建時(shí)間的消息將過(guò)期,同時(shí)進(jìn)入死信隊(duì)列。

          過(guò)期消息進(jìn)入死信隊(duì)列的方式和進(jìn)入普通隊(duì)列的方式基本一致,即先投遞給exchange路由器,再由exchange投遞消息。

          消費(fèi)者消費(fèi)死信隊(duì)列,得到的消息是延后的消息,延遲的時(shí)間長(zhǎng)度即構(gòu)建時(shí)間。

          9f7203093eb20e6f7a0902717c732ee2.webp

          死信隊(duì)列的另一個(gè)使用場(chǎng)景是Retry Later,即在一段時(shí)間后才重新處理此前處理失敗的消息,這時(shí)可能用到雙重死信。

          具體來(lái)說(shuō),死信隊(duì)列不僅可以接收過(guò)期消息,還可以接收被reject的消息,即消費(fèi)端拒絕處理或處理過(guò)程發(fā)生異常的消息,Reject操作具有requeue參數(shù),當(dāng)requeue設(shè)為true時(shí)被reject消息會(huì)重新進(jìn)入消息隊(duì)列并被重新投遞,當(dāng)requeue設(shè)為false時(shí)被reject消息將進(jìn)入死信隊(duì)列。

          假如死信隊(duì)列持有構(gòu)建時(shí)間,那么到達(dá)構(gòu)建消息的消息將重新投遞給原有隊(duì)列,實(shí)現(xiàn)Retry Later。雙重死信在使用過(guò)程中需注意消息處理的死循環(huán)問(wèn)題,因?yàn)橄⒖赡軣o(wú)限循環(huán)地進(jìn)入死信隊(duì)列。

          生產(chǎn)環(huán)境下使用RabbitMQ應(yīng)注意的事項(xiàng)

          8e18efcee2eeeb13a0165a53b96a0bbd.webp

          生產(chǎn)環(huán)境下,RabbitMQ通過(guò)使用集群模式。

          集群模式下,只有元信息分布在所有節(jié)點(diǎn)中。

          元信息指隊(duì)列信息,路由器信息等,隊(duì)列中的信息只存儲(chǔ)在一個(gè)節(jié)點(diǎn)中,因此,單個(gè)節(jié)點(diǎn)宕機(jī)會(huì)導(dǎo)致所有節(jié)點(diǎn)都不可用。

          另外,RabbitMQ的所有節(jié)點(diǎn)間存在轉(zhuǎn)發(fā)機(jī)制,即允許節(jié)點(diǎn)轉(zhuǎn)發(fā)其他目標(biāo)節(jié)點(diǎn)的消息處理請(qǐng)求,這樣客戶端只需連接到任意一個(gè)節(jié)點(diǎn)就可以實(shí)現(xiàn)其消息轉(zhuǎn)發(fā)需求。

          ba4c1911e9ade6c3c363b7d630ae6b66.webp

          隊(duì)列的高可用依賴于RabbitMQ的鏡像隊(duì)列,即在其他節(jié)點(diǎn)上備份某節(jié)點(diǎn)的消息內(nèi)容。這樣,當(dāng)消息所在主節(jié)點(diǎn)宕機(jī)時(shí),其他鏡像節(jié)點(diǎn)可以替代主節(jié)點(diǎn)完成消息傳遞任務(wù)。

          236c88c9f5bf45e09562017dd4bc720e.webp

          通常情況下,鏡像節(jié)點(diǎn)是默默無(wú)聞的,客戶端無(wú)需感知鏡像節(jié)點(diǎn)的存在。只有當(dāng)主節(jié)點(diǎn)宕機(jī)時(shí),鏡像節(jié)點(diǎn)才發(fā)揮作用。鏡像隊(duì)列的配置如下:

          Ha-mode具有三個(gè)選項(xiàng),all指將所有隊(duì)列的信息存入所有節(jié)點(diǎn),這種模式最安全,但也最浪費(fèi)存儲(chǔ)空間;exactly指由用戶精確指定每個(gè)隊(duì)列的復(fù)制數(shù),當(dāng)ha-mode設(shè)置為exactly,ha-params設(shè)置為2時(shí)表示“一主一從”,這種模式是官方推薦的;nodes指由用戶指定副本所在的節(jié)點(diǎn),這種模式極少被使用。

          x-queue-master-locator用于設(shè)置存儲(chǔ)隊(duì)列主節(jié)點(diǎn)的RabbitMQ節(jié)點(diǎn)。min-master指將隊(duì)列主節(jié)點(diǎn)設(shè)置在隊(duì)列數(shù)量最少的RabbitMQ節(jié)點(diǎn),client-local指將隊(duì)列主節(jié)點(diǎn)設(shè)置在當(dāng)前客戶端所在的RabbitMQ節(jié)點(diǎn),random即隨機(jī)選擇節(jié)點(diǎn)。

          Ha-sync-mode用于鏡像節(jié)點(diǎn)代替宕機(jī)主節(jié)點(diǎn)并創(chuàng)建新節(jié)點(diǎn)以彌補(bǔ)缺失節(jié)點(diǎn)時(shí),設(shè)置新節(jié)點(diǎn)上數(shù)據(jù)的同步策略。automatic指自動(dòng)地將新主節(jié)點(diǎn)上數(shù)據(jù)全部同步給新節(jié)點(diǎn),manual指不同步新主節(jié)點(diǎn)上的老數(shù)據(jù),只同步新產(chǎn)生的數(shù)據(jù)。由于節(jié)點(diǎn)間數(shù)據(jù)同步需要耗費(fèi)時(shí)間,長(zhǎng)時(shí)間的數(shù)據(jù)同步可能會(huì)影響服務(wù)的穩(wěn)定性,但通常情況下RabbitMQ的節(jié)點(diǎn)堆積的數(shù)據(jù)量并不大,因此RabbitMQ官方推薦使用Automatic進(jìn)行數(shù)據(jù)同步。

          Ha-sync-batch-size指節(jié)點(diǎn)間批量同步的數(shù)據(jù)量。

          Ha-promote-on-shutdown表示主動(dòng)停止主節(jié)點(diǎn)的服務(wù)時(shí),其他節(jié)點(diǎn)如何替代主節(jié)點(diǎn)。Always指其他節(jié)點(diǎn)總是能順利地替代主節(jié)點(diǎn),when-synced要求與原主節(jié)點(diǎn)數(shù)據(jù)完全一致的節(jié)點(diǎn)才能替代主節(jié)點(diǎn)。

          Ha-promote-on-failure表示異常情況下其他節(jié)點(diǎn)如何替代主節(jié)點(diǎn),always和when-synced的含義與Ha-promote-on-shutdown中一致。

          5b47fa6eb5ff86f9e41ec71b84a6761a.webp

          許多公司為RabbitMQ集群設(shè)置了內(nèi)存模式,認(rèn)為內(nèi)存模式無(wú)需落盤,能夠提升系統(tǒng)性能。但實(shí)際上,RabbitMQ官方文檔指出,內(nèi)存模式無(wú)法提升系統(tǒng)性能,它只提升了產(chǎn)生元信息數(shù)據(jù)的速度,即Ram Node指將元信息存入內(nèi)存,可以提升元信息的創(chuàng)建速度,而不是消息數(shù)據(jù)的性能。這是使用RabbitMQ時(shí)的一個(gè)常見(jiàn)誤區(qū)。

          如果你需要錢老師的 PPT 可以在公眾號(hào)后臺(tái)回復(fù)關(guān)鍵字 01,即可領(lǐng)取。

          原文鏈接:https://developer.aliyun.com/article/699886


          ··································

          你好呀,我是歪歪。一個(gè)主要敲代碼,經(jīng)常懟文章,偶爾拍視頻的成都人。


          我沒(méi)進(jìn)過(guò)一線大廠,沒(méi)創(chuàng)過(guò)業(yè),也沒(méi)寫過(guò)書(shū),更不是技術(shù)專家,所以也沒(méi)有什么亮眼的title。


          當(dāng)年以超過(guò)二本線 13 分的優(yōu)異成績(jī)順利進(jìn)入某二本院校計(jì)算機(jī)專業(yè),誤打誤撞,進(jìn)入了程序員的行列,開(kāi)始了運(yùn)氣爆棚的程序員之路。


          說(shuō)起程序員之路還是有點(diǎn)意思,可以看看。點(diǎn)擊藍(lán)字,查看我的程序員之路

          瀏覽 81
          點(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>
                  黄色一级网址 | a人妻免费视频 | 成人性爱视频在线免费观看 | 囯产精品久久 | 亚洲黄色电影一级片 |