可落地的DDD(5)-戰術設計

摘要

本篇是DDD的戰術篇,也就是關於領域事件、領域對象、聚合根、實體、值對象的討論。也是DDD系列的完結篇。
這一部分在我們團隊爭論最多的,也有很多月經貼,比如對資源庫的操作應該放在領域服務還是領域對象中。
聚合根應不應該暴露給外部,還是要轉成DTO。這些問題我們討論了大半年,最後大家基本達成了共識,在當前的業務規模下,
這些問題沒那麼重要,可東可西。不會對代碼的質量有啥大的影響。關於DDD的實踐,與團隊的水平、業務複雜度息息相關。我們的經驗並不一定就適用你們團隊。我將戰術篇的這麼多的內容放在了一篇文章中,並且大部分都是引用之前的討論、總結。
原因還是在於我內心深處並沒有覺得戰術篇的實踐給我們團隊帶來多麼大的改變。戰略篇的是我認為更重要的。

DDD系列文章斷斷續續也有十來篇了,主要是總結我們團隊落地過程遇到的問題和解決方案,算是DDD從學習到落地實踐的一個完整的閉環鏈路,希望對你有所啟發。當然這個過程受益最大的肯定是我本人。系統性的思考問題、總結問題、闡述問題是非常有助於提升個人思維能力,朋友們你們也可以嘗試一下。

建模

DDD的出現,是大家對於事務性編程,面向數據庫表編程的一個反思,明明軟件設計是一個面向對象的設計,需要考慮對象之間的繼承、多態、組合。
為什麼到實際編碼過程中成了過程性的編程,為什麼對象只有屬性沒有方法了,也就是失血模型。

關於這幾種編程的詳細介紹可以參考Martin的《Patterns of Enterprise Application Architecture》Page110

所以我個人覺得,DDD的作用有兩個,一個是面向業務的,幫助分析業務模型,進行業務建模。另外一個是面向解決域,即代碼落地。
即使用一個規範能夠反映對象之間的關係,即OO編程。

目前對DDD研究主要有以下類別

  1. 關於業務分析層面,如何進行概念層面的抽象和設計的方法論
  2. 關於服務劃分、代碼分層、職責定義的方法論
  3. DDD框架的討論,比如jdon

第3點基本上沒怎麼廣泛的討論。我認為未來也不會出現什麼牛逼的DDD框架能夠流行起來。DDD是一種建模方法,是針對不同的業務領域的,
在不同的團隊有不同的落地方案,是沒辦法靠一種框架來約束,來把一件不統一的事情來統一起來。就好比我們面向對象的設計針對問題域,抽象出來了
20多種設計模式。這些設計模式都是指導思想,你不能搞出一種框架,來約束大家使用某種設計模式就基於這種框架擴展,以此來達到代碼統一或者降低
編程難度的目的。

前面的文章主要是比較大的方面,比較適合做整體業務分析。也就是第一個點。今天主要討論第二點。

OO 編程

DDD的代碼分層、職責定義本質上就是OO編程。OO的三大基本要素就是繼承、多態、組合。這三個是深度抽象的結果。沒法指導具體的編程。
於是我們有了設計模式,前輩們針對問題域,總結除了24種設計模式,這樣遇到類似的問題時,我們可以使用對應的設計模式去解決問題。
而這些設計模式底層使用到還是繼承、多態、組合。

那有了設計模式了,為什麼還要DDD呢?為什麼很少看到開源軟件用DDD呢?
個人的理解DDD還是面向企業應用架構的,是在眾多不確定的業務,系統中提煉出來的一套規範,這樣必然是高度抽象的。而開源軟件大多是領域比較確定的,比如數據庫領域,中間件領域。解決這類問題的系統架構通常會更加複雜以及具有擴展性。

DDD的工程架構網上有很多,我在之前的文章也提到過,這裏不再贅述,看下老馬的這個,我覺得非常清晰的展現出來了職責分離
https://martinfowler.com/articles/microservice-testing/#conclusion-summary

我們重點看領域一層。
領域包含3點

領域服務

領域對象與領域服務

領域對象

敢於聚合根的激烈討論

領域事件

CQRS能解什麼問題

基礎設施層

為各層提供資源服務(如數據庫、緩存等),實現各層的解耦,降低外部資源變化對業務邏輯的影響。

總結

  • DDD中國
    http://ddd-china.com/look-back-2017.html 視頻去網站看

  • 美團的實踐
    https://tech.meituan.com/2017/12/22/ddd-in-practice.html

  • 盒馬的實踐
    https://zhuanlan.zhihu.com/p/42565478

  • cnblog博客園有很多.net DDD實踐的文章

    https://www.cnblogs.com/zhili/category/603514.html
    http://www.cnblogs.com/daxnet/archive/2012/12/27/2836372.html

  • jdon上面的系列
    https://www.jdon.com/ddd.html

  • 框架方面(個人覺得沒啥用,參考看看)

    rafy框架:http://zgynhqf.github.io/Rafy/articles/%E9%A2%86%E5%9F%9F%E5%AE%9E%E4%BD%93%E6%A1%86%E6%9E%B6.html

    jdon https://github.com/banq/jivejdon

  • 一些相關的書籍pdf上傳到百度網盤,需要的自取。

    鏈接: https://pan.baidu.com/s/1kGYk1dHbTAjBS4kd9s1VWw
    提取碼: 36w4

關注公眾號【方丈的寺院】,第一時間收到文章的更新,與方丈一起開始技術修行之路

【精選推薦文章】

智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

帶您來看台北網站建置,台北網頁設計,各種案例分享

廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

kubernetes高級之創建只讀文件系統以及只讀asp.net core容器

系列目錄

使用docker創建只讀文件系統

容器化部署對應用的運維帶來了極大的方便,同時也帶來一些新的安全問題需要考慮.比如黑客入侵到容器內,對容器內的系統級別或者應用級別文件進行修改,會造成難以估量的損失.(比如修改hosts文件導致dns解析異常,修改web資源導致網站被嵌入廣告,後端邏輯被更改導致權限驗證失效等,由於是分佈式部署,哪些容器內的資源被修改也很難以發現).解決這個問題的辦法就是創建創建一個具有隻讀文件系統的容器.下面介紹使用docker run命令和docker compose來創建具有隻讀文件系統的容器.

使用docker run命令創建只讀文件系統

比如說要創建一個只讀文件系統的redis容器,可以執行以下命令

docker run --read-only redis

docker compose/swarm創建只讀文件系統

yaml編排文件示例如下

version: '3.3'
 
services:
  redis:
    image: redis:4.0.1-alpine
    networks:
      - myoverlay
    read_only: true

networks:
  myoverlay:

問題:創建只讀文件系統看起來很不錯,但是實際上往往會有各種各樣的問題,比如很多應用要寫temp文件或者寫日誌文件,如果對這樣的應用創建只讀容器則很可能導致應用無法正常啟動.對於需要往固定位置寫入日誌或者臨時文件的應用,可以掛載宿主機的存儲卷,雖然容器是只讀的,但是掛載的盤仍然是可讀寫的.

創建只讀的asp.net core容器

上面一節我們講到了創建容器只讀文件系統以增加安全性,以及如何解決需要寫入日誌文件或者臨時文件這樣常見的問題.我們嘗試創建一個只讀的asp.net應用時,即便不使用任何log組件(即不寫入日誌),仍然無法正常啟動鏡像.解決這個問題其實也非常簡單,只需要把環境變量COMPlus_EnableDiagnostics的值設置為0即可.

FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 52193

FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY . .
WORKDIR "/src"
RUN dotnet build "ReadOnlyTest.csproj" -c Release -o /app

FROM build AS publish
RUN dotnet publish "ReadOnlyTest.csproj" -c Release -o /app

FROM base AS final
WORKDIR /app
ENV DOTNET_RUNNING_IN_CONTAINER=true
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1

ENV COMPlus_EnableDiagnostics=0

COPY --from=publish /app .
ENTRYPOINT ["dotnet", "ReadOnlyTest.dll"]

我們對這些環境變量進行簡單介紹

  • DOTNET_RUNNING_IN_CONTAINER值設置為true時則表示應用運行在容器內,方便我們獲取程序的運行環境,然後根據環境做出不同決策(比如單元測試的時候,可能要根據項目是運行在windows,linux或者linux容器做出不同的測試策略).當然,你也可以設置其它的環境變量來方便自己使用,比如你鍵名稱設置為IsRunningInDocker,但是DOTNET_RUNNING_IN_CONTAINER

  • DOTNET_CLI_TELEMETRY_OPTOUT是否輸出遙測信息,如果設置為1則是關閉,這樣dotnet.exe就不會向調試窗口輸出遙測信息.

  • COMPlus_EnableDiagnostics目前沒有找到太多關於這個參數的詳細信息,只是查閱資源發現這開啟這項配置可以創建只讀權限 aspnet 應用程序.

微軟官方基礎鏡像里還包含一項名稱叫作ASPNETCORE_VERSION的環境變量,我們可以直接讀取它,這樣使用公共的環境變量一來避免息手動設置和更新的麻煩,二來便於和社區交流(自己定義約束的只能用於內部團隊交流)

我們如何使用這些環境變量呢,其它可以在程序裏面暴露一個helper方法,比如

private bool InDocker { get { return Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true";} }

這樣我們就可以根據實際的需求來使用它.

上面我們介紹了如何使用docker run命令以及docker-compose創建只讀文件系統.然而在kubernetes集群里,我們需要使用k8s的編排方法來創建只讀文件系統.那麼在k8s里如何創建只讀文件系統.其實這裏涉及到了另一個高級主題:那就是k8s的安全策略(Pod Security Policies)我們將在下一節介紹它.

【精選推薦文章】

如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

想要讓你的商品在網路上成為最夯、最多人討論的話題?

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務

想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師"嚨底家"!!

RPC – 麻雀雖小,五臟俱全

說起 RPC (遠程過程調用),大家應該不陌生。隨着微服務、分佈式越來越流行,RPC 應用越來越普遍。常見的 RPC 框架如:Dubbo、gRPC、Thrift 等。本篇文章不是介紹各種 RPC 的使用和對比。而是深入剖析一個 RPC 包含哪些內容。我最近在 Hadoop 的源碼,正好把 Hadoop RPC 看完了。感覺 Hadoop 的 RPC 框架設計的還是比價優秀的。Hadoop 作為大數據技術的基石,如果沒有一個高性能、高可靠的 RPC 框架,很難支撐上千台服務器規模的集群。因此,本篇文章就以 Hadoop RPC 為例,介紹一個 RPC 框架會涉及的技術。

架構設計

RPC 的架構涉及客戶端、網絡、服務端三大組件。網絡一般使用 socket ,更多的是基於現有的網絡框架進行參數的設置達到最優的目的。但是客戶端和服務端需要我們自己設計,並且對於分佈式框架來說,設計的架構應該有高性能、高可用以及可擴展的特點。

  • 高性能:由於客戶端同時發起多個請求,這就要求系統能夠快速處理,降低響應延遲。也就是高吞吐、低延遲。從客戶端角度來說,由於創建客戶端到服務端的連接成本較高。因此可以緩存連接資源,從而實現多個客戶端復用相同的連接資源,避免每個客戶端都來創建而降低性能;從服務端角度來說,可以啟動多線程來併發處理客戶端請求。除了多線程,可以採用 Reactor 編程模式,提高多線程併發的性能。
  • 高可用:當我們的服務端掛了,能不能有備用節點繼續提供服務。Hadoop 2.x 實現了 NameNode 的高可用。當客戶端需要通過 RPC 調用 NameNode 服務的過程中,如果主 NameNode 宕機,那麼備用 NameNode 會升級成活動節點。同時會將 RPC 的請求發送的當前活躍的 NameNode,從而繼續提供可用的服務,而這個過程對客戶端來說是透明的。
  • 可擴展性:一個框架需要不斷地優化、不斷升級。需要在架構設計時明確不變的需求點,以及可變的需求點,對於可變的需求需要能夠有良好的可擴展性。以 RPC 涉及的序列化為例。由於不同序列化框架適用場景不同,因此這需要被當成可變的需求點,應該將其設計成可擴展的,能夠容易地支持不同的序列化框架。目前,Hadoop RPC 支持自身的序列化框架(Writable)和 Protoc Buffer。

設計模式

設計模式更多地與上面提到的可擴展性相呼應。良好的設計模式可以提高代碼復用性、增強可擴展性,同時能夠降低 BUG 數量。Hadoop RPC 中涉及的設計模式比較多,大概包括:工廠模式、代理模式、適配器模式、裝飾者模式和命令模式等。以代理模式為例,當客戶端調用遠程方法時,實際上是通過代理,將方法名和參數通過網絡發送到服務端。但這個過程對客戶端是透明的,對於客戶端來說就像調用本地方法一樣。

除了設計模式,在工程實踐中還應該注意遵循常見的設計原則。

多線程

在任何一個系統中多線程都比較常見。通過多線程併發處理,提高系統的吞吐量。在 Hadoop RPC 中,客戶端與服務端都用到了多線程技術。客戶端開啟多線程,每個線程處理一類請求,並且緩存連接資源。服務端也是多線程併發處理客戶端的請求,使用 Reactor 編程模式提高併發性能。

談到多線程就不得不提另一個話題 —— 線程安全。Hadoop RPC 中用了不少的技術來保證線程安全,包括:synchronized、concurrent併發包、atomic併發包和 nio 工具包。從優秀框架中學習線程安全,對我們以後併發編程有不少好處。

序列化與反序列化

由於 RPC 涉及數據在網絡上傳輸,因此需要一個優秀的序列化框架,既能夠高效的編碼與解碼,且編碼后的數據大小又盡可能小。不同的序列化框架主要是在編解碼效率和編碼大小兩個主要方面做權衡。Hadoop RPC 目前支持兩種序列化框架,一個是 Hadoop 自己實現的 Writable 框架,另一個是 Protocol Buffer。Hadoop RPC 雖然支持 Writable 序列化框架,但還是以 Protocol Buffer 為主。因為 Protocol Buffer 從編解碼效率和編碼大小方便都是比較優秀的。當然常見的序列化包括 Avro、Kryo 等,有興趣的讀者可以查一下它們之間的性能對比。

其他

一個 RPC 框架,除了包含上面提到比較主要的方面。還有一些其他的方面

  • 語言層面:利用好 Java 語言的繼承、組合、封裝、多態等特性。甚至包括泛型、註解等。
  • 代碼規範:良好的工程實現應該有一個良好的代碼規範。在 Hadoop 中,代碼風格比較統一,且每個重要的類都有詳細的註釋,在關鍵的方法或者屬性上也有明確的註釋。我在自己的工程中會使用阿里的 Java 代碼規約插件,也會為了讓自己的代碼更規範。
  • 異常處理:對於一個優秀的框架異常處理很關鍵,什麼時候需要拋出異常、拋出什麼樣的異常以及什麼時候需要處理異常。在 RPC 中除了需要處理本地異常還要處理遠程服務的異常。因此,在程序中如何優雅的處理異常也是體現一個程序員能力的地方。
  • 網絡編程:RCP 中涉及的網絡編程一般用 socket,Hadoop RPC 使用的 Reactor 模式的網絡編程,並且 Netty 也在使用這種框架。我們有必要會用並且掌握它。

 這一段寫的比較雜,想到哪寫到哪。最近有跟朋友聊過在看 RPC 相關的東西,朋友說:“一個 RPC 能夠涉及多少東西?值得研究?”。其實我一開始也是這樣想的,無非就是客戶端將請求序列化,通過網絡發給服務端,服務端反序列化調用函數后再返回。但是看了 Hadoop RPC 代碼后,我發現這樣框架涉及的知識還是特別多的,並且還比較系統,基本上包含了我們平時編程涉及的方方面面。同時它不再是一個單機程序,而是一個 C/S 架構的程序。如果我們有興趣還可以繼續研究他的高可用,從而對分佈式應用有更深入的了解。

我覺得 RPC 是麻雀雖小五臟俱全。由於它涉及了我們編程的方方面面,所以我想基於 Hadoop RPC 做一個詳細的教程,把它涉及的每個重要部分都進行詳細的分析,上面提到的內容基本都會涵蓋。對於想了解 RPC 的讀者,能夠感受到一個 RPC 框架更清晰的面貌。對於僅有 Java 基礎的讀者來說,能夠學到編寫一個框架所涉及的具體編程技術,同時能夠從世界頂級開源項目學到優秀設計和工程經驗。

小結 

本篇文章主要介紹了 RPC 框架涉及的知識。包括:架構設計、設計模式以及設計原則、多線程併發以及線程安全、序列化框架和一些其他的內容。我覺得學習最好的方式就是從優秀的框架中學習、模仿。好比我們練書法基本都要經過臨摹這一步。當然直接看別人的代碼確實需求花費更多的時間和經歷,並且有時候投入與產出並不成正比。所以,我想把我在 Hadoop RPC 框架中學到的優秀的設計和實現能夠整理成教程,以便有興趣的讀者學習。如果有任何建議歡迎與我交流。公眾號有福利

公眾號「渡碼」

 

【精選推薦文章】

自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

評比前十大台北網頁設計、台北網站設計公司知名案例作品心得分享

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

膨脹、驕傲,程序員轉項目經理的原罪

目錄

  • 一、前言:謙卑與狂妄
  • 二、尊重:遠與近
  • 三、原罪:膨脹與驕傲

作者簡介:妖生。 坐標合肥,一個普通程序員。十年職業生涯,8年編程。經歷了許多事,也有點想法,現在將它們變成文字,書於紙上。如果能幫助到你,那將不勝榮幸。

膨脹、驕傲,程序員轉項目經理的原罪

一、前言:謙卑與狂妄

突然就想到這麼個主題,回想我在2016下、2017上的時候,確實好像有那麼一段時間,有點膨脹,感覺自己無所不能,又太過驕傲,表面謙虛、內心傲氣,似乎誰都不放在眼裡。

做管理的沒我技術好,做技術的沒我會管理。

現在想想,真是可笑啊。

由己及彼,會不會也有不少做了三五年程序員,做了小組長,突然成為管理者,有了團隊里的一些話語權,然後膨脹的呢?

那麼我這樣去總結下,能不能給一些正在經歷這個階段的程序員們,甚至非程序員們的小夥伴們,予以一點點啟發呢?

嗯,晚上想就這個話題寫篇長文,回憶下我當年的情景,復盤當年,說一說我一些錯誤的做法,和之後怎樣警醒與走出來的。

二、尊重:遠與近

2.1 近之則不遜

2015年,金三大潮席捲而來,全國的稅務系統都要升級,我也有幸參與了這樣的歷史性進程。

我代表安徽的大集中征管稅務系統承建商,與金三征管中標商中軟公司進行系統對接。趕赴深圳南海,在國家稅務總局的信息中心進行征管數據的遷移工作。

在深圳南海的時候,我帶領的小組負責為數據遷移工作作數據校驗、數據確認、遷移腳本編寫等工作。

在這樣的時刻,安徽基地突然打電話過來,也是當時的項目經理——老趙,打電話對我說,要我負責金三本地保留系統的改造工作。

我當時一頭霧水,滿心不爽,不知道為什麼這樣的工作就落到了我的頭上。並且人在深圳,有心無力。所以說話有點沖,說我不知道怎麼做,直接就懟了幾句。

老趙忽然說了一句:“如果是XX你會這樣跟他說嗎?”XX是我們的項目總監。

我腦子里突然有一道電閃了一下。

是啊,如果是XX這樣跟我說,給我安排這樣的任務。我會這樣去懟嗎?
我是不是就默默接受了?

我突然想起來,2014年,我與老趙坐在一塊,因為某件事,我引用了孔子的一句話:“女子與小人難養也。遠之則怨,近之則不遜”。

老趙讚賞地說,很有道理。

可是這樣的話,我為何說了,卻不記得,做不到,無法知行合一呢?

我對老趙,就是近之則不遜了。

我沉默了大概十秒鐘,隨後對他說:“你放心,我肯定完成任務。沒有什麼完不成的。”
在轉變態度與思路后,我對怎麼解決金三本地保留的改造有了切實可行的思路與方法。

我在電話中將我的想法與老趙溝通后,老趙對我表示了讚賞,“就是要有這種舍我其誰的氣勢。除了你,沒人能搞定。”

這通電話很長,也在我以後的職業生涯中,對我做事做人的方法進行了一次很大的改造。

但是在這個故事里我卻還是有一點沒學到,也是我最近在反思的一個問題。

我為什麼對於別人,總是吝嗇於讚賞?

老趙在與我的相處中,一直對我進行鼓勵、讚賞,讓我有了一次次面對問題的自信,得到了極大的成長。

遇到這樣一個半師半友的領導,何其之幸也。

這段往事我總結了兩個點:

一是與上級相處時可以開玩笑、插科打諢,但是在工作上應該把上級當做上級,給予應有的尊重。

二是在與下級相處時,應該不吝嗇表揚,尤其在現在85后、90后當家的年代,表揚會比批評更能激發他們的工作激情,賦予他們超出當前能力的創造力。

2.2 遠之則怨

在2016年的時候,老趙罹患重疾,在長時間病情反覆、無法工作的情況下,拿了公司的補償金,主動離職。

誰來接替這個項目經理的位置?

當時其實是有三個人選,一個是運維經理Y,一個是技術經理H,還有一個就是我這個開發經理。

在H主動退出的情況下,我與Y開始競爭這個職位。

怎麼說呢?我其實一開始並不care這個項目經理的職位。我只想安安靜靜地做開發,不願意去做什麼項目管理。因為有權利的同時,意味着麻煩。

如果是技術經理H,也就是我在电子稅務局的老搭檔坐了項目經理這個職位,我是不想去競爭的。

但是現在是他退出,我倆競爭,而令我萬分不爽的是,在一開始,項目總監X便帶了濃烈的主觀傾向,他是屬意運維經理來坐這個職位的,這樣的傾向我後來的判斷是八二。

我直接去找了總監X,說出了這樣的話:“他何德何能,憑什麼做項目經理,拿什麼來領導我?”

我後來才知道,就在這個競爭的過程中,X找了項目組運維、開發的骨幹同事們,一一諮詢了對於項目經理這個職位人選的意見。

或許結果令他大失所望,90%的人都對他屬意的人選投了反對票。

真的是我牛逼?我人緣好嗎?我當時真的是這麼覺得。

後來細想,恐怕並不是如此。開發投我可以理解,為什麼運維的同事也投我呢?

我想,是因為這位Y先生不得手下人心吧。在對待客戶、對待上級方面,這位運維負責人把自己的工作幹得很出色,客戶關係維繫的很好。

但是在對待下屬方面,我則聽到了好幾次關於Y的抱怨。

一位同事說某個工作是他來解決的,處理的很出色,結果Y卻跟客戶說是Y來解決的,讓他心生怨懟。

另一位同事說他在處理某個線上的緊急問題,一時沒解決,結果總監X將Y調了過來,看着他處理,後來事情解決了,而Y在這個過程中什麼都沒幹。導致這位同事覺得 總監X 是不是太信任 Y 了,反而對 Y 敬謝不敏。

這兩位同事在16年已經成長為項目組的骨幹員工,也在這次競爭事件中投了 Y 的反對票。

有時候,其實不是你成功,而是對手打敗了自己。用現在流行的話來說,叫 同行襯托 吧。

我舉這兩個例子,是覺得這裏面有兩個點需要去反思和總結的。

一是遠之則怨,在對待下屬時如果為了保持上級的威嚴,而拉開了距離,則導致互相不了解,很容易產生芥蒂。

二是攬功諉過是領導者的大忌,其實團隊做好了事情就是你的功勞,至於是不是你個人解決的有什麼關係呢?作為領導者,個人成功,團隊失敗,其實都是失敗者。

在這裏面還有一點是我想提出的,就是在職場工作中,如果競爭的機會,要敢於競爭,競爭說明了你的決心。

我當時找 總監X 的談話中,還說了這樣一段話:

“我覺得如果我來選,我選自己做項目經理。”
“相比他,我肯定會做的更好。”
“如果他來帶這個團隊,那不用多久,這個團隊就要人心散了。”

是不是很狂妄?然而真的就是我內心的想法。你要麼空降一個項目經理過來,要麼就選我。

有時候在與上級的溝通中,沒必要遮掩自己內心的想法。當然,如果你的上級只愛聽阿諛諂媚的話,便保留意見,默默前行吧。

幸而,我覺得我一路遇到的都是貴人。從第一家公司的保總,到神碼的斌哥、蔡總、曹工、老趙、總監X,我覺得無比幸運,總是能遇到眼界比你高、願意提點你的老闆、導師、同事與朋友。

那麼回過頭來,我對 總監X 說的這番話到底有多大的影響呢?我相信在八二的傾向比重中,得到了兩分吧。再加上同事們的支持,最終是我勝出,成了【代】項目經理。

至於為什麼是【代】,呵呵,那便是另外一個故事了。

三、原罪:膨脹與驕傲

3.1 驕傲:無所不能與一天十會

在成為【代】項目經理后,我開始了馬不停蹄的開會生涯。最多的時候,一天十會,感覺什麼都要我來拍板。

2017年,政府開始提出“互聯網+”的概念,這便開始了我們開發項目爆發的一年。

智能辦公、大數據、移動APP、數據質量、統一門戶、短信平台、外部交換等等。

從需求到架構,全都是我來主導,業務人才的極度缺乏,導致我陷入了局方的會議大海。

後來有一位從別的區調過來幫助我的資深業務專家說了一句:

從技術人升上來的管理者可能都有一個毛病,凡事都要親力親為。何不放手讓下面的人乾乾呢?說不定干出的效果會超過你想象。

但是當時的我,並沒有把這話聽進去。

在前幾天看到雷軍的一篇文章,說為什麼小米手機干不過華為,甚至遠遜於OV呢?一是待遇問題,導致招不到行業最頂尖的人才;二是雷軍凡事親力親為,哪裡的業務不行了,就自己出山擼起袖子干。

很有感觸,何其相似。雷軍也是干技術出身的啊。同樣做技術出身,還是馬化騰夠瀟灑。

當然,我自然是比不了大佬的。可是反思下,我真的是這麼無所不能嗎?

或許只是我所在的位置,導致我接收的信息足夠多,而無法被下面的同事所取代?

或許並非我的能力太強,只是我的眼界、層次還不夠,還沒有做好一個管理者的轉變。

一個好的管理者,應該是大音希聲、大象無形,讓團隊在沒有你的情況下,也能按照預定計劃穩步前行。

3.2 膨脹:程序員的原罪

在無所不能的這一段時間中,因為開發項目的過多,還有大數據這樣新興技術的項目,從公司層面協調來了一位技術總監,也是我們安徽項目組以前出去的一位同事。暫且叫他 L 吧。

因為以前共在一個項目組,也算是半熟不生。而我並沒有充分意識到怎樣去尊敬這樣一位技術達人、前輩。

因為在這個公司,實行的是項目負責制,項目經理掌握着一票否決權。而我,這樣一位剛剛上任,新鮮熱乎着的【代】項目經理,開始膨脹了。

在這位 L 前輩研究spark + strom 解決實時倉庫問題時,我總是時不時打斷了他,喊他去參與大數據的需求會議、去解決APP的流量高峰問題、去參與統一門戶的架構設計。

誠然這一切,都應是技術總負責人應該去參與負責的事情。但是我在請求 L 去解決的時候,未免有些不夠尊重。

有時候,你外在不經意的一些表現其實正是你內心想法的一種體現。而人與人的相處,是奇妙而玄幻的。這種看不見、摸不着的氣氛是真的會影響彼此的心情與態度。

更何況,L 更是將技術人簡單、直接的性格放大到極致。

在某次赴地市的會議上,我與L一起討論確定了辦公系統與外部系統的對接與集成方式后,便借口項目組有事先走了,讓 L 留下參與後續與另一家公司的對接。

這件事直接導致 L 的不滿爆發,在幾天後的一次微信群的工作安排上直接懟了我,將我拉黑。

當然,這樣的過程都是我後來回憶、反思得出的結論,而我當時是懵逼的。

為什麼?會有這麼激烈的回應?我一臉懵逼地請求項目總監X的幫助,才讓 L 重新恢復參與了後續的工作。

我後來想想,這一切好像是偶然發生的,但卻也是必然會產生的,如果不是 L ,可能事情不會這麼激烈。然而其他沒有拉黑我的下屬和同事們,是不是其實心裏也在怨懟我呢?

在升上了項目經理之後,我是不是也開始遠離初心,“遠之則怨”了呢?

特別是在並非管理序列的上升通道中,突然從技術崗轉為了管理崗,從單純的管事變成了管人,有了財政、生殺大權,怎樣消去產生的膨脹與浮躁感呢?

有時候能力與眼界是位置帶來的,但是位置帶來的能力不是天生就來的,而是慢慢培養、逐漸形成的。

我回憶、反思這段往事的時候,覺得我在坐上項目經理的時候,缺失了一名老趙這樣的導師,想起他說的一句話:“扶上馬,再送一程”。

在很多公司,其實包括我當時所在的這家公司,也都有項目經理的培養與彙報機制,但是很多時候都淪為了背景色、走過程,是不是哪怕再忙,也該有這麼一課呢?

當然,有可能也並沒有什麼鳥用。因為紙上得來終覺淺,絕知此事要躬行。

我從這件事後,開始反思,重新整理人事,縮減彙報對象與被彙報對象。分離主要事務、次要事務。有意培養各開發組長的全棧能力。

當然,這一切其實都是不夠的,甚至在2017下2018上的時候,我開始怠惰,徹底放權,走入了另一個極端。

而在環境的演變下與艱難的掙扎中,我在2018下半年選擇重新投入了技術崗,但是在兩年已經極少碰代碼的情況下,我能重新拾起,並引領項目組前行嗎?

這是另一個故事了,但是最後想說的是:

沒做到技術總監、架構師,以後還想做技術,別去碰項目經理這個職位,繁雜瑣事,耗費精力,徒增煩惱。

嗯,這又可以寫篇文章了,下一篇就叫《技術人,別做項目經理,有毒!》,哈哈哈。

歡迎加入我的知識星球,掃一掃下方二維碼,目前免費哦。

【精選推薦文章】

智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

帶您來看台北網站建置,台北網頁設計,各種案例分享

廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

Linux下Jenkins與GitHub自動構建NetCore與部署

今天我們來談談NetCore在Linux底下的持續集成與部署。NetCore我就不多介紹了,持續集成用的是Jenkins,源代碼管理器用的是GitHub。我們就跟着博文往下走吧。

        • 1.Linux環境
        • 2.Jenkins自動構建
          • 定時構建
          • 觸發構建
        • 3.NetCore部署
        • 4.總結一下

1.Linux環境

在進行自動構建之前,我們需要一個可以運行的Linux環境,並保證環境安裝了NetCoreSDK,Git,Jenkins等環境,那在這邊就不多說環境的安裝,提供相對應的安裝教程,大家按照安裝就可以,不過對Jenkins的安裝,我會多啰嗦一句。

環境 地址
NetCore https://dotnet.microsoft.com/download/linux-package-manager/centos/sdk-current
Git https://www.cnblogs.com/imyalost/p/8715688.html
Jenkins https://www.cnblogs.com/loveyouyou616/p/8714544.html
Supervisor https://www.cnblogs.com/miskis/p/6026452.html

啰嗦的那一句:
我在安裝Jenkins成功的時候進入網頁,網頁會一直保持在下面圖這個狀態,沒辦法進入到開始頁面,那解決辦法是:

Jenkins等待

  1. 進入到Jenkins的工作目錄/var/lib/jenkins/修改hudson.model.UpdateCenter.xml文件
  2. 將http://updates.jenkins-ci.org/update-center.json修改為http://mirror.xmission.com/jenkins/updates/update-center.json
  3. 重啟下Jenkins

2.Jenkins自動構建

  • 定時構建
    1. 創建Jenkins項目

    2. 配置好自定義工作目錄

    3. 填寫源代碼路徑

    4. 配置定時任務

      字段 * * * * *
      含義 分鐘 小時 日期 月份 星期
      取值範圍 0-59 0-23 1-31 1-12 0-7
      示例
      每隔15分鐘執行一次 H/15 * * * *
      每隔2個小時執行一次 H H/2 * * *
      每隔3天執行一次 H H H/3 * *
      每隔3天執行一次(每月的1-15號) H H 1-15/3 * *
      每周1,3,5執行一次 H H * * 1,3,5
      規則
      指定時間範圍 a-b
      指定時間間隔 /
      指定變量取值 a,b,c
    5. 配置構建命令

      1. dotnet restore 
      2. dotnet build 
      3. dotnet publish 
    6. 查看構建,並運行NetCore
      注意:
      因為你建立的自定義工作目錄,對於”jenkins”沒有讀寫權限,所以可能就會導致構建失敗,執行下面命令,給Jenkins賦予讀寫權限。

      1. chown jenkins /ftpfile -R  
      2. chmod 777 /ftpfile -R 

  • 觸發構建
    1. 配置GitHub
      針對我們的項目,我們要在自己的項目內添加webhook,並配置好webhook的地址。

      1. 在項目設置中找到webhook

      2. 設置Jenkins的hook地址到剛才添加的webhook中,

        Jenkins的hook地址為:http://你的地址(端口)/github-webhook,當然這個地址應該是外網可以訪問的

    2. Personal access tokens
      對於任何第三方訪問GitHub的項目,是需要授權的,Jenkins也是一樣的,所以我們需要先獲取一個accessToken

      1. 進入Settings頁面,找到左邊列表的Developer settings,點擊進入,可以看到如下:
      2. 點擊生成,然後勾選,,保存即可,就可以獲得token,順便說下,token要記下來,不然關閉頁面就看不到了。
    3. Jenkins配置
      上面的配置完成之後,我們需要返回Jenkins,進行再次設置。為Jenkins添加GitHub服務器。

      1. 添加GitHub服務器

        2.添加GitHub的憑據,也就我們剛才拿到得accessToken

    4. Jenkins任務配置
      因為我們已經將觸發構建的前置步驟都做完了,那接着我們就要去修改之前的定時構建的配置了

      1. 切換成觸發構建
      2. 使用密文模式,並添加綁定,選擇剛才添加的憑據
    5. 修改代碼,上傳GitHub
      終於我們將之前的定時構建改成了觸發構建,也就是我們每次Push代碼都會觸發構建,接下來我們試下。

      1. 提交代碼
      2. 查看Jenkins

3.NetCore部署

終於的終於,我們把構建這個步驟做好了,那麼接下來當然就是要部署我們網站咯。

  • 運行NetCore
    運行NetCore最簡單了,我們只需要在發布后的目錄運行dotnet ***.dll就可以了,當然,前提,你要裝CoreSDK。

  • nginx託管
    雖然說上面那樣已經運行起來,但是由於我們的環境在騰旭雲上,要訪問可以用nginx進行方向代理下,下面就簡單貼下配置就可以了。

    1. server { 
    2. listen 80; 
    3. location / { 
    4. proxy_pass http://localhost:5000; 
    5. proxy_http_version 1.1; 
    6. proxy_set_header Upgrade $http_upgrade; 
    7. proxy_set_header Connection keep-alive; 
    8. proxy_set_header Host $host; 
    9. proxy_cache_bypass $http_upgrade; 
    10. } 
    11. } 
  • 守護進程
    我們已經知道要運行Core,需要使用命令在控制台運行,但是一旦退出了,Core自然就退出了,所以我們在Linux底下需要一個類似IIS的,來託管我們運行Core的進程,守護進程,讓其在後台運行,自動重啟等等功能,這個就是supervisor

    supervisors是C/S架構的進程控制系統,可使用戶在類UNIX系統中監控、管理進程。常用於管理與某個用戶或項目相關的進程。

    安裝教程可以參考開始表格supervisor的鏈接,同樣怎麼對於怎麼對Core進行守護進程,文章內也有講,一步一步按照教程來就可以了。不過我這邊還是會講下我在使用supervisor中遇到的坑。

    1. 一開始,我百度了挺多的,發現百度中的文章,對於supervisor的日誌文件,很多都說在/etc/log/supervisor/supervisord.log這個文件內,但其實我去找的時候,發現並沒有,查看配置文檔,發現默認是在tmp中,我不知道是centeros 的版本問題,還是supervisor版本問題。

    2. 還有一個就是,在根據教程走完,配置后,要進行啟動時,可能會出現Unlinking stale socket /tmp/supervisor.sock 這個錯誤,我們只需要解鎖下就可以了unlink /tmp/supervisor.sock

4.總結一下

該篇文章簡單介紹了下Linux下Jenkins與GitHub自動構建NetCore與部署,很多細點我可能沒講出來,我把大大的教程貼出來,按照教程走起,就可以了,這也是我實踐過了的,肯定可以用的,非常感謝大大們,然後呢,我也想拋磚引玉下,在我Jenkins構建時有兩個問題,不知道誰可以幫我解答下。

  1. 比如我們的解決方案文件.sln並沒有在git項目的根目錄下,我們要指定需要構建的解決方案呢?

  2. 還有一個就是,在命令dotnet publish我們怎麼指定發布到另一個文件夾內呢,我知道有個參數-o|--output <OUTPUT_DIRECTORY>,我試了下,發現不行 dotnet publish -o /ftpfile/netCore/netCoreJenkins/JenkinsNetCore,會有以下的錯誤。

最後的最後,我的下一篇文章是《Linux下Jenkins與GitHub自動構建Node項目(Vue)》,期待0.5下吧。

【精選推薦文章】

如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

想要讓你的商品在網路上成為最夯、最多人討論的話題?

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務

想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師"嚨底家"!!

Akka-CQRS(13)- SSL/TLS for gRPC and HTTPS:自簽名證書產生和使用

  到現在,我們已經完成了POS平台和前端的網絡集成。不過,還是那句話:平台系統的網絡安全是至關重要的。前一篇博客里我們嘗試實現了gRPC ssl/tls網絡連接,但測試時用的證書如何產生始終沒有搞清楚。現在akka-http開發的ws同樣面臨HTTPS的設置和使用問題。所以,特別抽出這篇博文討論一下数字證書的問題。

在正式的生產環境里数字證書應該是由第三方公證機構CA簽發的,我們需要向CA提出申請。数字證書的申請、簽發和驗證流程如下:

1) 服務⽅ S 向第三⽅方機構CA提交公鑰、組織信息、個⼈信息(域名)等資料提出認證申請 (不需要提供私鑰) 2) CA 通過各種手段驗證申請者所提供信息的真實性,如組織是否存在、 企業是否合法,是否擁有域名的所有權等 3) 如信息審核通過,CA 會向申請者簽發認證文件-證書。 證書包含以下信息:申請者公鑰、申請者的組織信息和個⼈信息、簽發機構 CA 信息、有效時間、證書序列號等信息的明⽂,同時包含一個簽名的產⽣生算法:首先,使用散列函數計算出證書中公開明文信息的信息摘要,然後, 採用 CA 的私鑰對信息摘要進⾏加密,這個密⽂就是簽名了 4) 客戶端 C 向服務器 S 發出請求時,S 返回證書文件 5) 客戶端 C 讀取證書中的相關的明⽂信息,采⽤相同的散列函數計算得到信息摘要, 然後,利用對應 CA 的公鑰解密簽名數據,對比證書的信息摘要,如果一致,則可以確認證書的合法性,即公鑰合法 6) 客戶端 C 然後檢驗證書相關的域名信息、有效時間等信息 7) 客戶端 C 應內置信任 CA 的證書信息(包含公鑰),如果 CA 不被信任,則找不到對應 CA 的證書,證書也會被判定非法 8) 內置 CA 對應的證書稱為根證書,頒發者和使⽤者相同,用 CA ⾃⼰的私鑰簽名,即⾃簽名證書(此證書中的公鑰即為 CA 的公鑰,可以使用這個公鑰對證書的簽名進行校驗,⽆需另外⼀份證書)

服務器端在通信中建立SSL加密渠道過程如下:

1)客戶端 C 發送請求到服務器端 S 2) 服務器端 S 返回證書和公開密鑰到 C,公開密鑰作為證書的一部分傳送 3)客戶端 C 檢驗證書和公開密鑰的有效性,如果有效,則⽣成共享密鑰並使⽤公開密鑰加密發送到服務器端 S 4) 服務器端 S 使⽤私有密鑰解密數據,並用收到的共享密鑰加密數據,發送到客戶端 C 5) 客戶端 C 使⽤用共享密鑰解密數據 6) SSL 加密通信渠道建立 ...

應該說,需要在客戶端進行認證的應用場景不多。這種情況需要在客戶端存放数字證書。像支付寶和一些銀行客戶端一般都需要安裝證書。

好了,還是回到如何產生自簽名證書示範吧。下面是一個標準的用openssl命令產生自簽名證書流程:

在產生證書和密鑰的過程中所有系統提問回答要一致。我們先假設密碼統一為:123456

1、生成根證書私鑰: rootCA.key:  openssl genrsa -des3 -out rootCA.key 2048 

2、根證書申請 rootCA.csr:openssl req -new -key rootCA.key -out rootCA.csr

3、用申請rootCA.csr生成根證書 rootCA.crt:openssl x509 -req -days 365 -sha256 -extensions v3_ca -signkey rootCA.key -in rootCA.csr -out rootCA.crt

4、pem根證書 rootCA.pem:openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem

5、創建⼀個v3.ext⽂件,目的是產生X509 v3證書,主要目的是指定subjectAltName選項:

  authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment subjectAltName = @alt_names [alt_names] DNS.1 = localhost IP.1 = "192.168.11.189" IP.5 = "192.168.0.189" IP.2 = "132.232.229.60" IP.3 = "118.24.165.225" IP.4 = "129.28.108.238"

注意subjectAltName,這些都是可以信任的域名或地址。

6、構建證書密鑰 server.key:openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout server.key

7、用根證書rootCA產生自簽證書 server.crt:openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 -sha256 -extfile v3.ext

上面這個過程需要不斷重複回答同樣的問題,很煩。可以用配置文件來一次性產生:

先構建一個ssl.cnf文件:

  [req]
  prompt = no
  default_bits = 4096
  default_md = sha256
  distinguished_name = dn
  x509_extensions = v3_req
  [dn]
  C=CN
  ST=GuangDong
  L=ShenZhen
  O=Bayakala
  OU=POS
  CN=www.bayakala.com
  emailAddress=admin@localhost
  [v3_req]
  keyUsage=keyEncipherment, dataEncipherment
  extendedKeyUsage=serverAuth
  subjectAltName=@alt_names
  [alt_names]
  DNS.1 = localhost
  IP.1 = "192.168.11.189"  
  IP.5 = "192.168.0.189"
  IP.2 = "132.232.229.60"
  IP.3 = "118.24.165.225"
  IP.4 = "129.28.108.238"

然後:openssl req -new -newkey rsa:2048 -sha1 -days 3650 -nodes -x509 -keyout server.key -out server.crt -config ssl.cnf

一個指令同時產生需要的server.crt,server.key。

除aubjectAltName外還要關注CN這個字段,它就是我們經常會遇到系統提問:你確定信任“域名”嗎?中這個域名,也就是對外界開放的一個使用了数字證書的域名。

把crt,key抄寫到main/resources目錄下,然後在gRPC服務器配置證書:

trait gRPCServer { val serverCrtFile = new File(getClass.getClassLoader.getResource("server.crt").getPath) val serverKeyFile = new File(getClass.getClassLoader.getResource("server.key").getPath) def runServer(service: ServerServiceDefinition): Unit = { val server = NettyServerBuilder .forPort(50051) .addService(service) .useTransportSecurity(serverCrtFile,serverKeyFile) .build .start // make sure our server is stopped when jvm is shut down
    Runtime.getRuntime.addShutdownHook(new Thread() { override def run(): Unit = { server.shutdown() server.awaitTermination() } }) } }

啟動gRPC服務,運作正常。在看看客戶端代碼:

    val clientCrtFile = new File(getClass.getClassLoader.getResource("server.crt").getPath)
 //或者   val clientCrtFile = new File(getClass.getClassLoader.getResource("rootCA.pem").getPath)

//這樣也行 val clientCrtFile: InputStream = getClass.getClassLoader.getResourceAsStream("rootCA.pem")

    val sslContextBuilder = GrpcSslContexts.forClient().trustManager(clientCrtFile)

    //build connection channel
    val channel = NettyChannelBuilder
      .forAddress("192.168.11.189",50051)
      .negotiationType(NegotiationType.TLS)
      .sslContext(sslContextBuilder.build())
//      .overrideAuthority("192.168.1.3")
      .build()

測試連接,gRPC SSL/TLS成功!

現在開始了解一下https證書的配置使用方法吧。看了一下akka-http關於server端HTTPS設置的例子,證書是嵌在HttpsConnectionContext類型裏面的。還有就是akka-http使用的https證書格式只支持pkcs12,所以需要把上面用openssl產生的自簽名證書server.crt轉成server.p12。這個轉換又需要先產生證書鏈certificate-chain chain.pem:

1)產生certificate-chain:  cat server.crt rootCA.crt > chain.pem

2) server.crt轉換成server.p12: openssl pkcs12 -export -name servercrt -in chain.pem -inkey server.key -out server.p12

https server 測試代碼:

//#imports
import java.io.InputStream import java.security.{ SecureRandom, KeyStore } import javax.net.ssl.{ SSLContext, TrustManagerFactory, KeyManagerFactory } import akka.actor.ActorSystem import akka.http.scaladsl.server.{ Route, Directives } import akka.http.scaladsl.{ ConnectionContext, HttpsConnectionContext, Http } import akka.stream.ActorMaterializer import akka.http.scaladsl.Http import akka.http.scaladsl.server.Directives._ //#imports


object HttpsDemo extends App { implicit val httpSys = ActorSystem("httpSystem") implicit val httpMat = ActorMaterializer() implicit val httpEC = httpSys.dispatcher val password: Array[Char] = "123456".toCharArray // do not store passwords in code, read them from somewhere safe!
 val ks: KeyStore = KeyStore.getInstance("PKCS12") val keystore: InputStream = getClass.getClassLoader.getResourceAsStream("server.p12") ks.load(keystore, password) val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509") keyManagerFactory.init(ks, password) val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509") tmf.init(ks) val sslContext: SSLContext = SSLContext.getInstance("TLS") sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new SecureRandom) val https: HttpsConnectionContext = ConnectionContext.https(sslContext) val route = get { complete("Hello world!") } val (port, host) = (50081,"192.168.11.189") val bindingFuture = Http().bindAndHandle(route,host,port,connectionContext = https) println(s"Https Server running at $host $port. Press any key to exit ...") scala.io.StdIn.readLine() bindingFuture.flatMap(_.unbind()) .onComplete(_ => httpSys.terminate()) }

用safari連接https://192.168.11.189:50081/, 彈出窗口一堆廢話后還是成功連接上了。

 

【精選推薦文章】

自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

評比前十大台北網頁設計、台北網站設計公司知名案例作品心得分享

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

SpringBoot啟動流程分析(一):SpringApplication類初始化過程

SpringBoot系列文章簡介

SpringBoot源碼閱讀輔助篇:

  Spring IoC容器與應用上下文的設計與實現

SpringBoot啟動流程源碼分析:

  1. SpringBoot啟動流程分析(一):SpringApplication類初始化過程
  2. SpringBoot啟動流程分析(二):SpringApplication的run方法
  3. SpringBoot啟動流程分析(三):SpringApplication的run方法之prepareContext()方法
  4. SpringBoot啟動流程分析(四):IoC容器的初始化過程
  5. SpringBoot啟動流程分析(五):SpringBoot自動裝配原理實現
  6. SpringBoot啟動流程分析(六):IoC容器依賴注入

筆者註釋版Spring Framework與SpringBoot源碼git傳送門:請不要吝嗇小星星

  1. spring-framework-5.0.8.RELEASE
  2. SpringBoot-2.0.4.RELEASE

一、SpringApplication初始化過程

  1.1、SpringBoot項目的mian函數

  常規的這個主類如下圖所示,我們一般會這樣去寫。

 

  在這個類中需要關注的是

  • @SpringBootApplication
  • SpringApplication.run()

  關於 @SpringBootApplication 註解,在後面分析SpringBoot自動裝配的章節會展開去分析。

  本章節中我們需要關注的就是 SpringApplication.run() 方法。

  查看run()方法的實現,如下面代碼所示,我們發現其實其首先是創建了 SpringApplication 的實例,然後調用了 SpringApplication 的run()方法,那本章我們關注的就是 SpringApplication 創建實例的過程。

/**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified sources using default settings and user supplied arguments.
     *
     * @param primarySources the primary sources to load
     * @param args           the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Class<?>[] primarySources,
                                                     String[] args) {
        return new SpringApplication(primarySources).run(args);
    }

   

  1.2、 SpringApplication() 構造方法

  繼續查看源碼, SpringApplication 實例化過程,首先是進入但參數的構造方法,最終回來到兩個參數的構造方法。

 1 public SpringApplication(Class<?>... primarySources) {
 2     this(null, primarySources);
 3 }
 4 
 5 @SuppressWarnings({"unchecked", "rawtypes"})
 6 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
 7     this.resourceLoader = resourceLoader;
 8     Assert.notNull(primarySources, "PrimarySources must not be null");
 9     this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
10     //推斷應用類型,後面會根據類型初始化對應的環境。常用的一般都是servlet環境
11     this.webApplicationType = deduceWebApplicationType();//2.2.1
12     //初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer
13     setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//2.2.2
14     //初始化classpath下所有已配置的 ApplicationListener
15     setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//2.2.3
16     //根據調用棧,推斷出 main 方法的類名
17     this.mainApplicationClass = deduceMainApplicationClass();
18 }

 

   1.2.1、deduceWebApplicationType();該方法推斷應用的類型。 SERVLET REACTIVE NONE 

 1 //常量值
 2 private static final String[] WEB_ENVIRONMENT_CLASSES = {"javax.servlet.Servlet",
 3             "org.springframework.web.context.ConfigurableWebApplicationContext"};
 4 
 5 private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
 6         + "web.reactive.DispatcherHandler";
 7 
 8 private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
 9         + "web.servlet.DispatcherServlet";
10 
11 private static final String JERSEY_WEB_ENVIRONMENT_CLASS = "org.glassfish.jersey.server.ResourceConfig";
12 
13 /**
14  * 判斷 應用的類型
15  * NONE: 應用程序不是web應用,也不應該用web服務器去啟動
16  * SERVLET: 應用程序應作為基於servlet的web應用程序運行,並應啟動嵌入式servlet web(tomcat)服務器。
17  * REACTIVE: 應用程序應作為 reactive web應用程序運行,並應啟動嵌入式 reactive web服務器。
18  * @return
19  */
20 private WebApplicationType deduceWebApplicationType() {
21     //classpath下必須存在org.springframework.web.reactive.DispatcherHandler
22     if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
23             && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
24             && !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
25         return WebApplicationType.REACTIVE;
26     }
27     for (String className : WEB_ENVIRONMENT_CLASSES) {
28         if (!ClassUtils.isPresent(className, null)) {
29             return WebApplicationType.NONE;
30         }
31     }
32     //classpath環境下存在javax.servlet.Servlet或者org.springframework.web.context.ConfigurableWebApplicationContext
33     return WebApplicationType.SERVLET;
34 }

  返回類型是WebApplicationType的枚舉類型, WebApplicationType 有三個枚舉,三個枚舉的解釋如其中註釋

  具體的判斷邏輯如下:

  • WebApplicationType.REACTIVE  classpath下存在org.springframework.web.reactive.DispatcherHandler

  • WebApplicationType.SERVLET classpath下存在javax.servlet.Servlet或者org.springframework.web.context.ConfigurableWebApplicationContext

  • WebApplicationType.NONE 不滿足以上條件。

  

  1.2.2、 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); 

  初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer。

 1 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
 2     return getSpringFactoriesInstances(type, new Class<?>[]{});
 3 }
 4 
 5 /**
 6  * 通過指定的classloader 從META-INF/spring.factories獲取指定的Spring的工廠實例
 7  * @param type
 8  * @param parameterTypes
 9  * @param args
10  * @param <T>
11  * @return
12  */
13 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
14                                                       Class<?>[] parameterTypes, Object... args) {
15     ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
16     // Use names and ensure unique to protect against duplicates
17     //通過指定的classLoader從 META-INF/spring.factories 的資源文件中,
18     //讀取 key 為 type.getName() 的 value
19     Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
20     //創建Spring工廠實例
21     List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
22             classLoader, args, names);
23     //對Spring工廠實例排序(org.springframework.core.annotation.Order註解指定的順序)
24     AnnotationAwareOrderComparator.sort(instances);
25     return instances;
26 }

 

  看看 getSpringFactoriesInstances 都幹了什麼,看源碼,有一個方法很重要 loadFactoryNames() 這個方法很重要,這個方法是spring-core中提供的從META-INF/spring.factories中獲取指定的類(key)的同一入口方法。

在這裏,獲取的是key為 org.springframework.context.ApplicationContextInitializer 的類。

  debug看看都獲取到了哪些

 

  上面說了,是從classpath下 META-INF/spring.factories中獲取,我們驗證一下:

  發現在上圖所示的兩個工程中找到了debug中看到的6條結果。 ApplicationContextInitializer 是Spring框架的類, 這個類的主要目的就是在   ConfigurableApplicationContext 調用refresh()方法之前,回調這個類的initialize方法。通過  ConfigurableApplicationContext 的實例獲取容器的環境Environment,從而實現對配置文件的修改完善等工作。

  關於怎麼實現自定義的 ApplicationContextInitializer 請看我的另一篇專門介紹該類的博客。

 

  1.2.3、 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 

  初始化classpath下 META-INF/spring.factories中已配置的 ApplicationListener。

   ApplicationListener 的加載過程和上面的 ApplicationContextInitializer 類的加載過程是一樣的。不多說了,至於 ApplicationListener 是spring的事件監聽器,典型的觀察者模式,通過 ApplicationEvent 類和 ApplicationListener 接口,可以實現對spring容器全生命周期的監聽,當然也可以自定義監聽事件。為了梳理springboot的啟動流程在這裏先不說這個了。後面有時間的話再介紹。

   關於ApplicationContextInitializer的詳細介紹請看<SpringBoot之ApplicationContextInitializer的理解和使用>

 二、總結

  關於 SpringApplication 類的構造過程,到這裏我們就梳理完了。縱觀 SpringApplication 類的實例化過程,我們可以看到,合理的利用該類,我們能在spring容器創建之前做一些預備工作,和定製化的需求。

比如,自定義SpringBoot的Banner,比如自定義事件監聽器,再比如在容器refresh之前通過自定義 ApplicationContextInitializer 修改配置一些配置或者獲取指定的bean都是可以的。。。

  下一節開始分析SpringBoot容器的構建過程,也就是那個大家多少都看過的run();方法。

   

  原創不易,轉載請註明出處。

  如有錯誤的地方還請留言指正。

【精選推薦文章】

智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

帶您來看台北網站建置,台北網頁設計,各種案例分享

廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

進程知多少?

目錄

  • 進程為什麼出現?
  • 進程的組成
  • 如何競爭資源(調度算法)
    • FCFS
    • RR
    • SPN
    • SRT
    • HRRN
    • FB
  • 進程狀態
    • 三態圖
    • 五態圖
    • 七態圖
  • 進程關係
    • 父子關係
    • 殭屍進程
    • 孤兒進程
  • 執行模式
  • 進程間通訊
    • 管道(Pipe)
    • 流管道(Flow Pipe)
    • 有名管道(Named Pipe)
    • 信號量(Semophore)
    • 信號(Signal)
    • 消息隊列(Message Queue)
    • 共享內存(Shared Memory)
    • 套接字(Socket)
  • 總結

文章首發:進程知多少?

Java 多線程系列文章第 1 篇

要講線程,一般都得講一講進程,進程是何方神聖呢?下面來簡單介紹一下。

先通過任務管理器看看 Windows 系統下的進程。

從圖片來看,每一個進程都佔有 CPU、內存、磁盤、網絡等資源。站在操作系統的角度,進程是分配資源的基本單位,也是最小單位。

進程為什麼出現?

引入進程的目的:為了使多個程序能併發執行,以提高資源的利用率和系統的吞吐量。怎麼理解這句話呢?一個程序在運行過程中會涉及很多操作,利用 CPU 計算、通過磁盤 IO 進行數據傳輸等等,我們知道當程序在進行磁盤 IO 的時候,因為速度問題,會比較慢,所在在這個過程中 CPU 會空閑下來,這會造成資源的浪費,正因為引入進程,在 A 進程進行磁盤 IO 的時候,會讓出 CPU 給 B 進程,合理地利用了 CPU 資源,使得程序之間可以併發執行。

從 CPU 角度,執行過程是這樣子的:CPU 一直在負責執行指令,進程之間互相競爭 CPU 資源,下圖有 A 和 B 進程,在一個時間點,CPU 只執行一個進程的指令,因為 CPU 運行很快,所以在咱們看起來,像是多個進程在同時跑。這就是進程帶來的好處:提高資源利用率,併發地執行多個程序。

當然引入進程也不是有益無害,它增加了系統的時間和空間開銷。空間開銷這個好理解,進程有自己的組成部分(下面會講),這個就佔用了空間。時間開銷則是進程切換需要時間。

進程的組成

進程由 3 個部分組成,分別是程序代碼、數據集、棧和進程控制塊(Process Control Block)。

各自的作用如下:

  1. 程序代碼:描述了進程需要完成的功能。
  2. 數據集、棧:程序在執行時所需要的數據和工作區。
  3. 進程控制塊:包含進程的描述信息和控制信息,它是進程存在的唯一標識。

如何競爭資源(調度算法)

進程之間需要競爭資源,一般都是競爭 CPU 資源,因為 CPU 運行速度太快了,其他介質都趕不上。有了競爭就需要有規則,就像遊戲一樣,每個遊戲都需要規則,不同規則會有不同的側重點,這個看過“最強大腦”這個節目的朋友就非常清楚,每道題都有不同的考核側重點,有些是側重空間思維、有些側重邏輯推算等等。下面我們就簡單地一一講解競爭資源的遊戲規則。

FCFS

First In First Out(先來先服務):最先進入就緒隊列的進程,先運行,運行到完成或者阻塞時,再重新調度。一般情況下,這種調度算法會和優先級策略結合,比如每個優先級一條隊列,每條隊列中的調度都使用 FCFS。

特點:簡單、比較偏於長進程、相對於其他調度算法平均周轉時間長。

RR

Round Robin(輪轉):進程按提交順序存在就緒隊列,依次輪流佔用 CPU 資源,運行一段固定的時間,時間到后如果還沒執行完,就繼續進入就緒隊列隊尾,排隊等待下次執行。

特點:公平、對進程的響應時間較短。

SPN

Shortest Process Next(最短進程優先):將預期佔用運行時間最短的進程優先執行,直到運行完成或阻塞時,再重新調度。

特點:有利於短進程。

SRT

Shortest Remaining Time(最短剩餘時間優先):新進程進來時,如果新進程的預計運行時間比當前進程的剩餘運行時間更短,就搶佔當前進程,

特點:有利於短進程,和 SPN 的差別在於搶佔這個一點,因為搶佔,所以效率會比 SPN 好一些。

HRRN

Highest Response Ratio Next(最高響應比優先):當前運行的進程完成或者阻塞時發生調度,每次調度前,計算所有就緒進程的響應比,響應比高的進程優先運行。

響應比公式如下所示:

特點:有利於短進程,服務時間相同的進程,先來的服務會優先執行,長進程因為在等待的過程中,優先級越來越高,所以不會一直不執行。

FB

Feedback (反饋):由多個就緒隊列組成的反饋機制,它有如下規則:

  1. 在同一個隊列的進程,按 FCFS 算法調度,最後一個就緒隊列按 RR 算法調度;
  2. 優先級越高的隊列,時間片越小;
  3. 進程在一個時間片內未運行完,則降到下一個隊列末尾;
  4. 只有上級隊列無就緒進程時,才運行本級就緒隊列,本級就緒隊列無進程時,才運行下級就緒隊列,以此類推

進程執行過程如下圖所示

特點:短進程有非常大的優勢,排在前面的隊列都是時間較短的。

以上就是幾個搶佔資源的調度算法的說明。

進程狀態

上面我們講到,進程之間是在競爭資源,得到資源就運行,沒得到就等待,這個需要有狀態來維護,像很多系統一樣,需要一個狀態機。

三態圖

三態圖也是描述進程狀態最簡單最基礎的圖,它包含了進程的最基本的 3 個狀態,分別是:就緒態、運行態和阻塞態。

Read(就緒態):進程已得到除 CPU 以外的其他所需資源。
Running(運行態):進程的指令正被執行。
Blocked(阻塞態):進程正等待資源或某事件發生。

就緒態的進程在被調度的時候,進入了運行態,如果時間片運行完或者有更高級別進程搶佔資源,則變成就緒態等待再次被調度;如果發生事件(比如 IO 事件),則從運行態轉到阻塞態,進入阻塞態的進程只能等待事件解除重新進入就緒態。

五態圖

基於三態圖,新增了 2 個狀態,分別是:新建態和退出態。

New(新建態):進程正被創建。分配內存后將被設為就緒態。

Exit(退出態):進程已正常結束或出現異常結束。回收資源。

新進程剛創建還沒有分配資源的時候是新建態,等到分配了資源,被加載后就進入就緒態。當進程運行完后,就從運行態進入退出態。

七態圖

基於五態圖,新增了 2 種掛起態,分別是就緒掛起態和阻塞掛起態。

就緒掛起態:另叫外存就緒態。由於內存容量有限,將原位於內存的就緒進程轉存到外存(磁盤)上。

阻塞掛起態:另叫外存阻塞態。一樣因為內存容量有限,將原位於內存的阻塞進程轉存到外存(磁盤)上。

我們可以看出,圖中新增了解除掛起的狀態轉換過程,一般是由於掛起進程優先級比較高或者內存空間足夠,把位於外存(磁盤)的進程轉存到內存中。

進程關係

進程之間其實比較獨立,比如我們在日常使用的 QQ 和微信,它們運行起來的進程有什麼關係么?其實除了互相競爭資源之外,沒有任何關係。

父子關係

雖然上面說的進程之間沒有關係,但是有一個特殊關係需要講,就是父子關係。

先做個試驗,驗證進程的父子關係。操作步驟:

  1. 打開 CMD 命令行程序,將當前的窗口設置為 Father,在 Father 窗口通過命令start cmd啟動另一個 CMD 命令行程序;
  2. 將新開的 CMD 命令行程序的窗口設置為 Son,在 Son 窗口通過命令start cmd啟動另一個 CMD 命令行程序;
  3. 將新開的 CMD 命令行程序的窗口設置為 Grandson。

操作過程如下圖所示。

通過 ProcessExplorer 可以很清晰看到這 3 個 CMD 進程之間的關係。(想要 ProcessExplorer 插件可以通過百度網盤下載鏈接:https://pan.baidu.com/s/19531gf5tD_of1CWxpFR9Dg 提取碼:qhc6)

我們看到 Father、Son、Grandson 三個進程呈現出我們預料中的樹形。那麼什麼是父子進程呢?簡單的說就是在進程中創建出新的進程,這個新的進程就是子進程,一個進程可以有多個子進程,但是只能有一個父進程。在 Unix 系統中,父進程通過調用 fork() 創建子進程,父子進程有如下特點:

  1. 父、子進程併發執行;
  2. 父、子進程共享父進程的所有資源;
  3. 子進程複製父進程的地址空間,甚至有相同的正文段和程序計數器 PC 值;
  4. 利用寫時複製(Copy On Write)技術減少不必要的複製:fork 時父子共用父空間,當一方試圖修改時才複製。

這裏重點講一下Copy On Write,使用了這個技術,父進程創建子進程的時候不會複製所有數據到子進程,省了複製的時間以及減少了大量的內存。這個複製不是必要的,因為如果應用程序在進程複製之後立即加載新程序,那之前的複製工作就是浪費時間和內存了。

講了進程父子關係,就免不了提一下殭屍進程和孤兒進程,下面分別介紹一下。

殭屍進程

殭屍進程:子進程退出后,父進程沒有調用 wait 或 waitpid 獲取子進程的狀態信息,子進程的進程描述符仍保存在系統中,這種進程叫殭屍進程。

殭屍進程的危害:殭屍進程會一直佔用進程號,系統能使用的進程號又是有限的,如果有大量的殭屍進程,會因為沒有可用進程號導致無法創建新的進程。

孤兒進程

孤兒進程:父進程結束退出,而它的子進程還在運行,這時的子進程就叫做孤兒進程。孤兒進程就被 init 進程(進程號為 1)收養,init 進程將對孤兒進程完成狀態收集工作。

孤兒進程沒有危害,因為被 init 進程託管了,init 進程會處理孤兒進程的收集工作。

執行模式

指令分為特權指令(只能由操作系統內核使用的指令)和非特權指令(只能由用戶程序使用的指令),因為指令有特權和非特權之分,所以 CPU 也分為 2 種執行模式:系統態(可以執行所有指令,使用所有資源以及改變 CPU 狀態)和用戶態(只能執行非特權指令)。

CPU 的系統態和用戶態之間的切換。

進程間通訊

當進程之間需要數據傳輸、共享數據時,進程間就需要互相通訊,通訊方式有如下幾種,這裏只是簡單概括一下,不展開講,咱的重點在於多線程,進程咱們簡單了解一下就可以,感興趣的同學可以根據要點進行深入學習。

管道(Pipe)

管道是半雙工通訊,數據是單向流動,要建立進程間互相通訊,則需要 2 個管道,這種通訊方式只能在親戚關係的進程間使用,比如父子進程。

流管道(Flow Pipe)

流管道是管道進化來的,數據不再是單向流動,可以雙向流動,但是依舊是只能在親戚關係的進程間使用。

有名管道(Named Pipe)

有名管道提供了新的功能,就是給管道設置名字,它改善了上面 2 種管道通訊方式,支持了非親戚關係的進程通訊。

信號量(Semophore)

信號量相當於計數器,利用它來控制多個進程訪問共享資源,當一個進程A在訪問共享資源時,信號量防止其他進程來訪問,只有當進程A不訪問共享資源了,其他進程才能訪問。

信號(Signal)

信號可以在任何時候發給某一進程,不需要知道該進程當前的狀態,如果對方進程未執行,信號會存在內核中,直到進程執行後傳遞給它;如果對方進程是阻塞,則信號會延遲傳遞,等到對方進程阻塞取消后才傳遞給它。

消息隊列(Message Queue)

消息隊列是存放在內核中的鏈表,可以有多個進程對這個鏈表進行寫入和讀取,它解決了信號傳遞信息少、管道只能傳輸無格式字節流和緩衝區大小受限的缺點。目前有 POSIX 消息隊列和 System V 消息隊列。

共享內存(Shared Memory)

共享內存即為一段能被其他進程訪問的內存,多個進程訪問同一個內存,達到了通訊的效果。

套接字(Socket)

套接字就是我們網絡編程裏面的那個套接字,可以通過網絡也可以在本機進行通信,它的好處在於可以跨主機進行通信。

總結

總的來說,進程是程序在一個數據集上的一次執行過程,它就是程序運行起來的表現。這是我們學習多線程的開篇,希望通過這篇文章,讓大家簡單地了解進程是什麼,後面我們再來深入了解多線程。

推薦閱讀

設計模式看了又忘,忘了又看?

公眾號後台回復『設計模式』可以獲取《一故事一設計模式》电子書

覺得文章有用幫忙轉發&點贊,多謝朋友們!

【精選推薦文章】

如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

想要讓你的商品在網路上成為最夯、最多人討論的話題?

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務

想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師"嚨底家"!!

如何從現在開始寫博客?

  • 《構建之法》讀書筆記四-關於寫博客的想法

  在前不久知名博主《純潔的微笑》在博客園分享了他寫博客的心得,獲得了讀者圈的廣泛共鳴,同時也引起了一些老生常談的爭議,這樣的討論在我們長沙.NET技術社區內部也經常發生。

1、寫博客的好處

  在《軟技能·代碼之外的生存指南》這本書中,作者提到作為軟件開發人員,最好的推銷自己的媒介就是博客,他認為每個在乎自己職業生涯的軟件開發人員都應該建立一個博客,他把他職業生涯的大部分成功都歸功於他的博客。

  在我之前寫過的一篇博客中,介紹了長沙.NET技術社區的優秀開發者,全棧工程師《ASP.NET企業級開發實戰》的作者鄒瓊俊老師的成長經歷,在他大學畢業後去找工作屢屢碰壁時,有幸加入了由長沙社區組織者周尹老師開設的學習班,並向老師請教如何才能提高自己的技能時,老師告訴他,你去寫博客,堅持寫博客,堅持五年,一定能獲得成功。果不其然,他筆耕不輟、堅持學習和記錄自己發現的問題,並成為了一位出版了兩本開發技能書籍的暢銷書作者。

  除了他之外,還有我在博客園結識的Java全棧工程師,《Web全棧開發進階之路》的作者,沉默王二,也是一位愛好寫博客的人,多年前他在蘇州的日企工作,偶爾會做一些筆記,但自從回到小城洛陽之後,就開始瘋狂的學習技術和寫博客,多年的堅持沒有白費,終於得以打造這本優秀的Java開發者們值得擁有的寶貴書籍。

  在技術社區有一次的閑談中,有人說起一個故事,說他以前有一次和他的同學一起去面試一份工作,然後面試官問你覺得你平時有什麼比較好的學習習慣么?他說基本上回家之後就是看看書,玩玩遊戲什麼的。而他的同學卻說:回家之後會看看書,逛逛博客園、寫寫博客。於是毫無疑問,他的同學得到了面試官的青睞並獲得了這份Offer。或許他的同學以前並沒有寫博客,但是相信得到這份Offer之後,他一定會開始寫博客、並培養自己寫博客的習慣的。

  在《軟技能》這本書裏面也說了,他去給軟件開發人員做演講,每當他問開發者有多少位開了博客並每周更新的請举手時,一百個開發者,頂多只有一位举手。他認為,堅持寫博客、持之以恆的撰寫優質內容,能讓你輕鬆在開發者中脫穎而出。

2、不要給自己太大的壓力、從小問題開始記錄

  許多人說曾經開過博客賬戶,甚至心血來潮使用wordpress\hexo等博客平台搭建過自己的博客網站,但是最終都無疾而終了,主要原因是不知道寫什麼內容,以及擔心自己寫的內容會被人吐槽說質量不好、自己忙於工作,根本沒有時間寫博客等。

  有時候開發者總是會給自己過大的壓力,其實這樣的壓力毫無必要,坦率而言,在大部分技術網站中活躍的優秀開發者,他們剛剛參加工作時,並非每個人都是學霸、一開始就能寫出優秀的博客,他們善於發現和記錄在工作中自己發現的一些點,並持之以恆,最終讓他們脫穎而出。

  每一位開發者本質上沒有那麼大的區別,並不是所有的開發者都有機會經歷大併發等互聯網的牛逼場景,但是我們總有機會發現或親歷一些只有自己才能看到的場景。在開發者這個包容心最強的社會群體中,只要你勇於寫下自己的博客,一定會獲得其他開發者的認同。(當然,有一些網絡噴子,請不要在意他們的說法)

  例如,我個人認為,我們可以這樣嘗試。

  1、不要擔心自己的文筆不夠好、不要過度在意別人的看法,只要寫博客,讓自己滿意,就是一個開端。

  2、學會記錄,形成素材。想到什麼,就記錄什麼,提前頭腦風暴出不同的想法,隨時更新在自己筆記中記錄的點子、並適當的進行擴展,就是一個非常不錯的話題。

  3、無需花費太多時間,每天花半個小時思考和總結問題,一周就有3個半小時,足夠寫出一篇千字以內的總結了。

  4、通過博客與其他人進行討論。如果遇到想不明白的問題,還可以通過博客的形式,分享出來,邀請大家進行討論,這樣的過程簡直不要太開心。

  在《構建之法》這本書中,將寫博客當成是開發者形成良好習慣的開始,並稱之為“做中學”,我們也可以在edu.cnblogs.com這個站點中,看到許多年輕的未來開發者們,他們通過博客園這個平台,將自己軟件工程學習過程中的問題、解決問題的方法和經驗分享出來,不同學校間還可以互相交流,形成了一個非常積極活躍的技術氛圍,我覺得這是一種令人愉悅的體驗。

  當然,我也很遺憾在我讀書的那些年沒有機會體驗這樣的機會,以至於走了一些彎路,到今天我的博客依然寫得比較少,所以這篇博客其實也是寫給自己的一種警醒,提醒自己應該堅持寫博客,不管年紀多大,只要今天開始堅持,堅持五年,總能取得不錯的成功,雖然不一定能像其他人一樣成功,但至少會比今天的自己成功。

3、寫博客的原則

  我曾經針對寫博客的問題,有幸請教過《構建之法》的作者鄒欣老師,我說我最近也寫了一些博客,但是總感覺都是一些毫無乾貨,讀起來感覺沒什麼意思,請問如何才能寫出有乾貨的內容呢?

  • 老師回答:說清楚一個具體問題,解決一個具體問題。

  這是一個充滿哲理的回答,讓我茅塞頓開。寫博客不是寫小說,不用長篇大論,不用引經據典,不用引用華麗的詞藻,只需講清楚一個問題即可。再簡單的問題,也是一個問題,每個人的理解都不一定相同,只需用鍵盤敲下你的理解,就可以成為一篇博客。

 

  在閱讀《浪潮之巔》第二卷的過程中,我看到了一樁關於博客的軼事,說甲骨文收購了Sun公司之後,甲骨文老闆 Ellison這樣吐槽:

  “Sun的工程師團隊是如此優秀,但是他們獲得的指引方向卻異常糟糕,這是導致他們無法成功的原因。花哨的博客並不能取代優秀的微處理器,也不能取代任何軟件,博客文章多頁無法帶來好的銷售業績。”

  Sun是一家曾經是一家優秀的互聯網公司,開創的許多領域時至今日依然讓開發者們收益,但是為什麼十年前卻突然死亡,最終賣身給Oracle?在《浪潮之巔》中有比較深入的闡述,而在Sun破產之前, Schwartz這位Sun的末代領袖,卻試圖通過博客來治理這麼大的公司,經常使用十餘種語言寫博客,實在是一位有意思的商界領袖。(還有那位喜好用推特治國的川普,也挺有意思的,嗯,川普和Schwartz應該是筆友。)

  ps:我是一位.NET開發者,在過去十年間,大概.NET是受Java衝擊最嚴重的開發技術吧,但是創造出Java如此優秀語言的Sun公司,究竟是什麼原因讓他被歷史的浪潮打翻的?真的只是因為CEO愛寫博客嗎?這是一個很有意思的故事,大家也可以去《浪潮之巔》中看看,正好《浪潮之巔》第四版新書也上市了,我已經買了一套了。哈哈。

【精選推薦文章】

自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

評比前十大台北網頁設計、台北網站設計公司知名案例作品心得分享

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

上周熱點回顧(6.17-6.23)

熱點隨筆:

· 升維打擊,設計之道(Artech)
· 藉助FreeHttp為任意移動端web網頁添加vConsole調試(lulianqi15)
· 你為什麼有那麼多時間寫博客?(純潔的微笑)
· 看完此文,媽媽還會擔心你docker入不了門?(sullivan06)
· 我和 HelloGitHub(削微寒)
· 簡歷上如果出現過於高大上的項目,反而過猶不及:再論如何通過項目引出技術(hsm_computer)
· 程序員與醫生(道友留步`)
· 一個理想主義的程序員(沉默王二)
· 高考完?入門級的開源項目帶你開啟編程之旅(削微寒)
· 短信驗證碼“最佳實踐”(GUOKUN)
· .NET CORE下最快比較兩個文件內容是否相同的方法(WAKU)
· 從CLR GC到CoreCLR GC看.NET Core對雲原生的支持(艾心❤)

熱點新聞:

· “地震波還有61秒到達”,08年籌建的技術,在這次四川地震中立功了
· 微軟,奪回王位
· 我們從未見過它的真面目,直到一群科學家拍了張照片
· 華為自研SSD揭秘:國內唯一殺入全球TOP10
· 無需固態電池,一條假魚靠“血液”續航36小時
· 5G來了,需要更換SIM卡嗎?
· 屠呦呦團隊放“大招”:“青蒿素抗藥性”等研究獲新進展
· 羅永浩再談收購蘋果:還需要一點時間
· 國產最先進X86處理器KX-6000發布:8核3.0GHz 力壓酷睿i5
· 美國欲剝奪華為在美專利權,涉及至少 3195 件專利
· 四川發生強震 成都提前61秒收到預警
· 聯想 ThinkPad P 系列筆記本預裝 Ubuntu 系統

【精選推薦文章】

智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

帶您來看台北網站建置,台北網頁設計,各種案例分享

廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益