程序員應(yīng)該如何自我驅(qū)動,迅速獲得成長?
我發(fā)了一篇《工作沒有挑戰(zhàn)性,怎么辦?》, echo_陳同學(xué)的留言講述了自己的故事,我覺得他的經(jīng)歷比我那“說教”有意義得多,邀請他拓展了一下,寫了一篇文章出來。
大家可以看一下,他是怎么發(fā)現(xiàn)痛點問題,解決痛點問題的。 在一年多的時間內(nèi),他做了多少事情!取得了多么大的成長!人和人的差距就是這么產(chǎn)生的。
長文預(yù)警,碎片化時代,專注和深度思考是稀缺品質(zhì),建議耐心閱讀,不要錯過!
初入公司,從CRUD到運維支持
一年之前,我還是一個只會CRUD的普通程序員,常年與業(yè)務(wù)打交道,一套花式SSM框架三板斧從頭玩到底。
我入職了一個初創(chuàng)型的互聯(lián)網(wǎng)項目團隊,在迅速融入工作環(huán)境以后,我就開始上手寫起了CRUD代碼。雖然不知道底層原理, 但是SSM模版代碼已經(jīng)爛熟于心,再加上有一些在以前工作時學(xué)習(xí)到的基礎(chǔ)和避坑的經(jīng)驗,比如空值驗證,防止重復(fù)提交等, 讓我能夠比較快地完成業(yè)務(wù)代碼。
領(lǐng)導(dǎo)看到了這一點,把我安排到了運維支持部,開始讓我干一些運維的活(公司在初創(chuàng)期用人是有這個特點)。 那個時候,開始逐個上線一個一個的服務(wù)。這是我從零開始了解公司技術(shù)現(xiàn)狀的開端,也是一個加班到死的開端。其實加班的原因我是非常理解的,這個和公司的技術(shù)現(xiàn)狀是離不開的,且聽我慢慢道來。
艱難的系統(tǒng)部署:了解技術(shù)全貌
在一個晚上,領(lǐng)導(dǎo)拿了一份文檔,列了一系列要上線的服務(wù)和服務(wù)要部署在的服務(wù)器,然后我們就逐漸開干了。
新服務(wù)器都是極為純凈的,連一些基礎(chǔ)命令都沒有,只好慢慢學(xué),慢慢裝。yum install xxxx,run xxx , ps aux|grep,telnet ....,一天過去了,什么jdk,tomcat,全都裝好了。
(ps. 做運維這段時間***的收獲就是linux命令玩得很熟練。)
把服務(wù)部署后,啟動時就遇上了難題:以前的服務(wù)是打war包放到tomcat的,但是現(xiàn)在的服務(wù),需要java -jar 來啟動。 嘗試了可以后臺啟動的nohup java -jar , 直接翻車。 無奈去詢問之前的開發(fā),人家說得用screen 命令后臺啟動程序。我是一個刨根問底的人,后來發(fā)現(xiàn),開發(fā)在為了保持進程不退出,錯誤的使用了監(jiān)聽控制臺事件的方式,導(dǎo)致了nohup啟動異常。
那個時候,正在搞核心業(yè)務(wù)開發(fā)的人因為一直忙于開發(fā)和調(diào)試,和我們的溝通少之又少;而負(fù)責(zé)部署的我們,又不了解業(yè)務(wù),有些問題當(dāng)時的開發(fā)也懶得詳細(xì)解釋,于是我們只能靠猜,來部署程序。
混亂的程度可見一斑, 但這不是幾個程序員所能左右的。
我在那個時候?qū)W習(xí)到的***件事就是:在信息不足和溝通受限的時候,你要嘗試學(xué)會必要的自行推理,根據(jù)已有信息的上下文來補全缺少的信息。
上線要緊,暫時就screen啟動程序吧。但是程序啟動以后,又遇到了更加尷尬的事情。
當(dāng)時服務(wù)之間的調(diào)用方式,全部都是通過HttpClient直接調(diào)用目標(biāo)服務(wù)。假如,我先啟動A服務(wù),A服務(wù)依賴B服務(wù),B服務(wù)沒啟動,A服務(wù)初始化時會報錯。那么,到底先啟動誰后啟動誰成了一個棘手的問題。
我查看了每一個服務(wù)的工程配置示例,發(fā)現(xiàn)每一個服務(wù)的config.properties,都有一個配置為root的選項,標(biāo)識該服務(wù)的發(fā)布路徑,例如,用戶服務(wù),他的config.properties 會配置 root=/userservice/ ,我就知道了,調(diào)用該服務(wù)肯定是這樣的http路徑:http://ip:port/userservice/xxxx。config.properties中,也會有一些帶有如下特征的配置 reference_user=http://ip:port/userservice/xxxx。我很自然的明白了,這肯定是配置了該服務(wù)依賴的其他服務(wù)的調(diào)用地址。因此,我就想到,我可以根據(jù)每一個服務(wù)的配置文件理順服務(wù)之間的調(diào)用關(guān)系。
為了確保我的猜想是正確的,我用網(wǎng)上的工具反編譯了一個工程,發(fā)現(xiàn),果然原來服務(wù)之間都是通過HttpClient調(diào)用的。
然后我就畫了工程依賴圖,仔細(xì)抽絲剝繭的梳理出來了程序的具體的依賴關(guān)系。***,我終于對服務(wù)的部署順序和方式有了思路,而有些程序員,私下已經(jīng)有人開始說干不下去了要離職。
怎么部署終于明白了,可是緊接著的一個尷尬的問題就是,程序都啟動了,也調(diào)通了,但是領(lǐng)導(dǎo)要求負(fù)載均衡,集群化,一個服務(wù)里直接調(diào)用另外一個服務(wù)使用了HttpClient直連的方式,怎么搞負(fù)載均衡?怎么搞?怎么集群?
當(dāng)時離DeadLine不遠了,上線重要,于是忍痛購買了阿里云的SLB實現(xiàn)每一個服務(wù)的負(fù)載均衡,當(dāng)時最痛苦的事情就是要理順這種蜘蛛網(wǎng)式的服務(wù)調(diào)用關(guān)系,好在我之前已經(jīng)畫了工程依賴圖,這個也就好辦了。
天天加班折騰到凌晨兩點,一個半月以后,終于把全部的Service部署成功,難以想象的是,我一個完全不懂業(yè)務(wù)的人,完全根據(jù)日志信息和配置關(guān)系,搭建成功的服務(wù),居然有80%的功能正常可用,這給了我這個苦哈哈加班的程序員不小的成就感,雖然搞不定的20%的功能后來由負(fù)責(zé)這塊的開發(fā)親自去搞了(領(lǐng)導(dǎo)出面,不能敷衍了)。雖然,后面還有一些小的插曲,雖然這個應(yīng)用剛開始可能全是窟窿,但好在也終于如期上線。
這一個半月,我掌握了公司的技術(shù)架構(gòu),在一些程序員以不會為理由拒絕部署kafka,nginx,zookeeper,activemq等基礎(chǔ)設(shè)施的時候,我出手去部署,其實我也不會,但是從學(xué)怎么用,到部署完,時間也是夠的,我還學(xué)習(xí)了這些技術(shù)是干什么的,不虧。
最終,我整理了公司當(dāng)時的技術(shù)全貌,一些網(wǎng)絡(luò)轉(zhuǎn)發(fā)和機房架構(gòu)由于保密原因不予描述(雖然我也已經(jīng)掌握了),我只單純說一下代碼層面的:
一,服務(wù)與服務(wù)之間,通過RESTful風(fēng)格的HTTP調(diào)用,使用了HttpClient,需要自己維護蜘蛛網(wǎng)一樣的服務(wù)調(diào)用關(guān)系。
二,配置文件有在本地的,有在數(shù)據(jù)庫的,代碼里每次獲取配置每次都要load本地,select數(shù)據(jù)庫。(影響性能)。
三,無用的配置文件和無用代碼散落在各處,給運維造成了干擾,代碼有歷史遺留的味道。
四,技術(shù)棧不規(guī)范,有的人外置tomcat,有的人內(nèi)置tomcat,有的人用了Netty,簡直就是群魔亂舞。
五,命名不規(guī)范不統(tǒng)一,詞不達意,公司用了elastic-job作為分布式任務(wù)調(diào)度平臺,elastic-job要求每一個job啟動時需要指定job-name,但是,運維經(jīng)常在控制臺上無法根據(jù)job-name來定位到底是哪個job,比如,工程job為payServiceAllJob,在控制臺上卻注冊為AllJob,讓人摸不著頭腦。
痛點一:缺乏服務(wù)的自動發(fā)現(xiàn)
服務(wù)上線以后,由于本身項目就是在沒有規(guī)范,沒有制度的情況下放肆生長出來的,因此服務(wù)上線以后還是會瘋狂的加班,來彌補一個又一個補不完的窟窿。那個時候我還沒有接觸業(yè)務(wù),但是也要和開發(fā)一起加班,人工值班監(jiān)控。
已經(jīng)一個月沒有寫增刪改查了,我就有機會開始搞一些事情了。那個時候,我請我的領(lǐng)導(dǎo)去說動技術(shù)部使用Dubbo,因為它能做服務(wù)自動發(fā)現(xiàn),免配置擴容服務(wù),是一個挺流行的RPC框架。但是由于當(dāng)時我們沒有技術(shù)權(quán)限,這個事情是很難推動的,一切以穩(wěn)定為主。
我使用過Dubbo,但是我一直不理解的是,為什么Dubbo使用了ZooKeeper就能自動擴容?就能服務(wù)自動發(fā)現(xiàn)?這個肯定和ZooKeeper有關(guān)系。由于我是刨根問底型的開發(fā),因此我開始在周末時間和晚上和上下班路上自己學(xué)習(xí)ZooKeeper,什么順序節(jié)點,***節(jié)點,臨時節(jié)點,什么樹形存儲,動態(tài)監(jiān)聽和通知。
終于,我就在沒有看Dubbo源碼的情況下突然恍然大悟:Dubbo的服務(wù)提供者和服務(wù)消費者都配置了服務(wù)name,如果我在ZooKeeper的一個name節(jié)點下存儲服務(wù)路徑集合,那么每次新增服務(wù)或者下線服務(wù)都會通知到任何監(jiān)聽這個name的客戶端?(后來知道這叫做namespace命名服務(wù)),Dubbo肯定是使用了Zookeeper的命名服務(wù)來實現(xiàn)服務(wù)的動態(tài)發(fā)現(xiàn)的!
大部分程序員使用HttpClient都是使用的一個叫HttpHelper的工具類,我的改造就開始從HttpHelper這個工具類開始吧,讓程序員傳遞自己的服務(wù)的ip,port,se rviceName,destinctName,zookeeperUrl,然后我在工具類里封裝了獲取調(diào)用路徑列表,并封裝了兩個負(fù)載均衡算法:ip_hash,隨機數(shù)。(其實我只會寫這兩種)。
在公司的四次迭代里,他們在一邊飛速業(yè)務(wù)開發(fā),一邊逐漸把自己的HttpHelper工具類替換給我寫的這個,終于可以卸下slb,實現(xiàn)簡單的HTTP服務(wù)動態(tài)發(fā)現(xiàn)了。
當(dāng)時的反對聲音其實還是挺大的,不過運維的需求呼聲更高,配置URL太麻煩了!
那個時候,公司架構(gòu)重組,得領(lǐng)導(dǎo)賞識,我被重新劃分到了技術(shù)部,做了基礎(chǔ)平臺研發(fā)部門的Team Leader,管理幾個程序員。
痛點二:缺乏配置中心
解決了***個痛點以后,我一邊寫增刪改查,一邊又想解決第二個痛點:配置問題。
之前每次部署程序到線上時,每次都要一個服務(wù)器一個服務(wù)器的改配置,我就想,我把我的時間浪費在這種一個服務(wù)器一個服務(wù)器改配置的事情上,真的是不甘心。
而且這么改配置,還容易出錯。雖然公司沒有主動要求寫什么。(公司的主要眼睛還是放在了實現(xiàn)功能和業(yè)務(wù)上)那個時候加班沒有那么狠了,程序員寫完增刪改查就回家了,我還在想配置中心怎么實現(xiàn)。那個時候微服務(wù)的概念火熱,我了解到了Spring-cloud-config。知道有叫做配置中心的這種東東,對,我們也需要配置中心,把配置集中抽出來管理。由于我已經(jīng)掌握了ZooKeeper,自然知道了做配置中心的思路。
半個月時間我就寫完了,時間主要浪費在了寫頁面上。(汗,作為一個后臺程序員我承認(rèn)我的頁面能力比較差)當(dāng)然,這期間也經(jīng)過了一些改版。我竟然不知道ZooKeeper有Curator這么好用的客戶端,之前一直使用的原生的org.apache.zookeeper這個原生客戶端操作,監(jiān)聽消費以后還要重新監(jiān)聽。
配置中心寫完以后,我也在一個周末發(fā)到了群里,并推廣了出去。這次,由于公司的人員團結(jié)力已經(jīng)大有改觀,而且我也已經(jīng)是組內(nèi)Team Leader,先在組內(nèi)普及了。
而且我這次組外的游說和推廣也沒有那么困難了,最終也都一個服務(wù)一個服務(wù)的落地了配置中心,并實現(xiàn)了一處改動,處處生效,也實現(xiàn)了傳說中的配置熱更新。這些,并沒有什么高深的技術(shù),僅僅就是依賴了一個ZooKeeper。
弄完這些,我已經(jīng)感受到了做技術(shù)的樂趣,同時,我的領(lǐng)導(dǎo)也覺得好像讓我寫增刪改查有點浪費,要求我把我手頭的任務(wù)全部分給組員,自己則是去解決別人的問題......我贏得了更多的時間去自我驅(qū)動,改善公司的基礎(chǔ)設(shè)置。
痛點三:缺乏緩存框架
接下來我本以為自己要沒事做了,但是,得益于左耳聽風(fēng)的一個專欄,他說,描述一個業(yè)務(wù)(DSL),要比編寫一個業(yè)務(wù)更具有維護性,公司很多的人都在使用Redis緩存,但是,用的庫各不一樣,還經(jīng)常出各種奇怪的問題,調(diào)試起來及其繁瑣。
當(dāng)然,最終讓我受不了開始決定寫緩存框架的是因為我看到了我的組員寫的代碼與業(yè)務(wù)嚴(yán)重耦合,一會操作Redis,一會操作數(shù)據(jù)庫。我發(fā)現(xiàn)了這個痛點,研究了一下Spring的AOP和他的@Transtional 注解如何實現(xiàn)以后,終于決定手寫一個微型的緩存框架。基礎(chǔ)思路就是通過可插拔的@CacheEnable(key=xxxx,timeout=xxx......)實現(xiàn)Redis緩存,完全不侵入業(yè)務(wù)代碼。如果不用了,把注解在方法上移除了就好了。
手寫這個Redis緩存框架使用了半個月,畢竟自己的技術(shù)能力還是有限,如何實現(xiàn)AOP和Spring集成,怎么抽象等等,都會讓我每天都思考半天才下筆,甚至有時候一天寫不了一行代碼。
期間看了一本叫做《面向?qū)ο缶幊獭返臅锩嬲f,面向接口編程,依賴抽象,而不是依賴具體的實現(xiàn)......我突然就像打通了任督二脈。我在CacheHandler里面依賴了CacheStorage接口,而把RedisCacheStorage作為一個實現(xiàn)類注入了進去,因此,后來我這個框架可以同時支持redis,memcache,local。
寫完這個框架以后,我總結(jié)了三個可抽象的點:
1. 序列化方式(jdk,protobuf,thift,json)
2. 腳本解析器方式(就是解析key的方式:比如,可以使用spel或者ognl:"userId"+#user.id )
3. 緩存實現(xiàn)方式(redis,memcache)。
后來我從劉大的碼農(nóng)翻身公眾號的一篇將日志系統(tǒng)的設(shè)計里知道了正交一詞,大概這就是正交吧。最尷尬的是,原來Spring早就實現(xiàn)了,叫做SpringCache。汗。
(碼農(nóng)翻身注:那篇文章叫做《一個著名的日志系統(tǒng)是怎么設(shè)計的》)
后來的事情,我也不詳細(xì)的講了,大概就是從私有框架,換成了公共成熟的庫,我們也終于用上了Dubbo框架。
而對我自己本身,我只是更加理解了一些之前無法理解的事情:Dubbo為什么能改一個組件的配置那個組件就換了一種實現(xiàn)。(面向接口編程)看Mybatis的源碼也沒那么困難了,兩年前完全下不去手。當(dāng)然,我也變得更加熱愛技術(shù)了。
我的心得體會
那就給大家分享一下我近兩年來的心得體會。
1. 作為一個程序員,一定要懂得自我挖掘,而不是僅僅實現(xiàn)業(yè)務(wù)功能就好了,也不能干等著領(lǐng)導(dǎo)分配任務(wù)。
功能耦合了,寫代碼慢了,運維麻煩了,這些,都是潛在的需求,我們現(xiàn)在的加班,是為了以后不加班,是為了提高自己的效率,是為了不能原地踏步。
2. 一定要真正地學(xué)會一個技術(shù),我在實際工作中,發(fā)現(xiàn)有的同學(xué)使用了一年的Git,竟然不知道如何把GitHub的項目拉取到本地,但是他會把GitLib的項目拉取到本地。
程序啟動異常,有同學(xué)從網(wǎng)上找了一個jar包導(dǎo)入到了工程里,但是問他為什么把jar包導(dǎo)入到工程里代碼就啟動正常了,竟然不知道。
有的同學(xué)使用了一年的Maven,不知道m(xù)vn compile 是什么含義,只知道m(xù)vn package是打包。
這些,就是沒有真正的學(xué)會了一個技術(shù),僅僅是工作時機械式的使用了,僅僅是復(fù)制粘貼了,下一個項目再復(fù)制粘貼過來就行了,模仿著別人的代碼寫邏輯,這是學(xué)不到東西的。
我覺得,你一定會有時間去學(xué)習(xí)這些東西,網(wǎng)上大把大包的資料和教程。一定要知其然而知其所以然,一定不要一模一樣的配置換了一種配置方式你就看不懂了。
3. 要掌握公司的技術(shù)棧,要刨根問底,公司用的什么rpc框架?怎么使用?原理是什么?公司用的nginx,nginx怎么配置的。公司用了配置中心,配置中心是什么?公司的負(fù)載均衡框架用什么做的,存在什么問題?
我寫了一個緩存框架,你有沒有沖動研究一下到底是怎么實現(xiàn)的?保持技術(shù)好奇心是十分重要的。我認(rèn)識的一個同學(xué),來我們公司以后,簡歷上寫的,會SSM。后來經(jīng)歷了我的配置中心演進之路,還學(xué)會用了。后來他離職了,簡歷上寫的還是會SSM,沒別的了。本來,如果他把配置中心講一講會是一個很大的亮點。工作了一年,最怕的就是揮一揮衣袖,不帶走一片云彩。
4. 要善于發(fā)掘,總有一些東西是亮點,是金子,只是你發(fā)現(xiàn)不了。
我在我們公司學(xué)到的一個安全方面的設(shè)計就是token機制,這改變了我的觀念,以前一直以為做單點登陸只能session共享,現(xiàn)在才知道,還能使用時間換空間的方式,使用token簽名代替sessionId,使登錄這個過程變得成為無狀態(tài)的計算......這是我發(fā)現(xiàn)的,很多人程序員根本不能意識到這些技術(shù)亮點,并融化到自己腦海里成為自己的東西。
5. 使用碎片化時間去學(xué)習(xí),不要總抱怨沒有時間。
學(xué)習(xí)是枯燥的,但是,當(dāng)你收到了學(xué)習(xí)帶來的巨大紅利,你會越來越想學(xué)習(xí)。我親身的體驗就是,當(dāng)我寫的配置中心和緩存框架投放到了生產(chǎn)中,并得到了我想要的效果后,我現(xiàn)在更加喜歡學(xué)習(xí),更加癡迷于技術(shù)了,我的技術(shù)能力并不強,很多的東西,都是因為我的興趣逐漸被激發(fā),因為我的熱愛,才發(fā)生了質(zhì)變。
6. 多思考,正確地實現(xiàn)業(yè)務(wù)。
有沒有觀察過,同樣的業(yè)務(wù),有的人寫出來的實現(xiàn)就非常的豐滿和穩(wěn)定,而有的人寫出來的接口可能直接調(diào)用一下就拋出了錯誤。很多程序員寫代碼只關(guān)心正常分支的邏輯,從來不考慮異常邏輯的處理。在寫業(yè)務(wù)代碼的時候多去分析用戶場景,也會規(guī)避很多問題。
舉個例子,接口的參數(shù)的驗證,你可以根據(jù)使用場景來編寫額外的適配和容錯。比如,接口中有一個字段A,這個字段大小寫敏感嗎?這個字段萬一用戶多輸入了一個空格怎么辦?如果你能根據(jù)業(yè)務(wù)分析到,字段大小寫不敏感,我要全部轉(zhuǎn)成小寫來比較,空格不是正常參數(shù)的一部分,我要進行去空格來簡單的容錯。那么,你可能會避免有一天某個業(yè)務(wù)人員跑過來跟你說,我明明寫的正確為什么你告訴我參數(shù)不存在。
不要小看業(yè)務(wù)代碼,不必掌握多么高深的技術(shù)原理,只要能把業(yè)務(wù)邏輯實現(xiàn)的健壯,也已經(jīng)很考驗?zāi)芰α耍f明了你是一個喜歡思考和注重細(xì)節(jié)的人。
結(jié)束語
現(xiàn)在的我,越來越喜歡研究源碼,研究底層,并伴隨著的是,我以前的一些疑惑都迎刃而解,這當(dāng)然是因為我得到了正向的反饋,付出會有收獲。現(xiàn)在被公司內(nèi)部的部分程序員誤認(rèn)為是大神,當(dāng)然,我還差得很遠。
現(xiàn)在公司技術(shù),代碼逐漸在變好,工程規(guī)范逐漸在行程和標(biāo)準(zhǔn)化,統(tǒng)一化。這是值得欣慰的,我最近在研究API網(wǎng)關(guān),公司的業(yè)務(wù)并發(fā)量在上升,我覺得需要一個入口統(tǒng)一管理API,負(fù)責(zé)鑒權(quán),認(rèn)證,限流,熔斷等一系列的功能。(這些詞都是在微服務(wù)課堂上學(xué)習(xí)的)。我已經(jīng)開發(fā)了一部分了。使用Netty接收請求,使用HttpClientPool轉(zhuǎn)發(fā)請求,中間使用責(zé)任鏈模式做Handler中轉(zhuǎn)攔截處理,不知道何時能竣工。
【本文為51CTO專欄作者“劉欣”的原創(chuàng)稿件,轉(zhuǎn)載請通過作者微信公眾號coderising獲取授權(quán)】