2017PHP開發者大會
寫在最前
大會干貨很多,講師陣容也很強大,一場接著一場,連續兩天,還是學到了不少東西的,用主持人的話說就是我們干貨很干,硬廣很硬
。不過竊以為講師里面還是夾雜了“合作方”的,推銷一些平臺和書籍,不過無傷大雅,權作休息。
不過由于時間和地點的原因不少朋友沒能及時參加,今年也不知道是否會有大會視頻,所以我打算根據現場朋友用打賞堆出來的排名來對大會進行一個簡單的總結,自己復習鞏固一下,也希望能給其他人一些幫助。
瓜子后端技術架構的變遷(紀鵬程)
這個分享是熱度最高的,原因可能在于它是最接地氣的。不僅完整的介紹了瓜子二手車架構一步步由小到大的發展過程,以及這期間的經驗教訓,還逐條分享了各種最佳實踐,可以說他的分享基本可以指導一個新興業務,技術架構的快速發展方向。
石器時代,業務發展早期
這段時期的第一目的、最高目的是活下去
技術人員要把精力集中在業務上,快速迭代,實時響應業務需求,可能要做不少Quick but dirty work
。此時也不要忙著造輪子和優化,能花錢買就花錢買,能用開源的就用開源的
過早優化和造輪子是萬惡之源 —— 尼古拉斯·趙四
也不要著急上緩存。一是多一層依賴就多一層的風險,早期快速迭代時,盡量保持架構的簡單。二是緩存可能隱藏掉一些代碼性能、邏輯缺陷,導致一些性能問題可能到架構復雜到一定程度時才暴露出來,此時的修改的成本就十分高昂了。
盡早確定數據庫規范、數據字典。因為在后期業務代碼還有修改空間,底層數據庫和一些約定一旦養成是很難改動的。
保密工作也要做好。對外開放的數據一定要做好加密和脫敏工作,前期激烈競爭階段,一個泄密可能拖垮一個公司。
這個階段的問題也很突出。代碼質量差,文檔缺失,新人上手難度高?;A服務脆弱,缺少各種管理平臺(日志、部署)。不過沒關系,堅持過這個階段,業務只要能活下來,這些問題都不是問題。
鐵器時代,業務發展期
熬過業務早期,進入快速發展階段后,業務種類與規模會迅速增加,各種通用服務的重要性也逐漸顯現,同時用戶量也會大幅增長,服務性能壓力開始出現。這個時期的架構也要做出相應的調整。
- 業務拆分。包括代碼和數據庫,業務之間通過http接口調用
- redis集群。使用Twemproxy來做中間代理
- php性能監控。使用xhprof和各種日志,指定統一的日志規范
這個階段常見的問題是,業務快速調整,接口混亂,調用方式混亂、性能缺乏監控;還缺少可靠的持續交付流程
蒸汽時代,業務穩定期
各項業務逐漸穩定,各種痛點持續的暴露。
- 業務進行細分,通用服務、業務獨立管理。
- 統一的接口調用方式,性能監控。
- 規范持續交付流程,代碼的上線、回滾,數據庫的建立、評審,統一的監控、測試框架,代碼之間的隔離
- 健壯的第三方,一些通用平臺沒必要由專人維護,可以使用第三方平臺,不過最好同時使用多家
工作效率實踐
本部分講師分享了實踐過程中能夠提高效率一些經驗方法,比較實用
代碼的藝術
- 代碼分層,遵循psr1 psr2 psr4規范
- 代碼提交添加統一hook,控制格式
- compsor,包括封裝公司的內部庫
單測
單測的重要性被反復提及。完整的單測是代碼發布、升級的重要支撐。尤其是核心代碼,覆蓋率要保證。這里他推薦使用PATest代替PHPUnit作為主要單測工具
RPC
業務龐大后RPC的場景必不可少。最重要最基本的要求是調用方式統一。業界通用的解決方案一般是使用http接口,他們使用了guzzle
基礎上進行封裝,支持了簽名、并發請求和超時設置。另一個常用的方案是鳥哥開源的Yar
最后再啰嗦一句。RPC方法一定要跟普通的方法有明確區別!!!
,否則可能被誤用、濫用
服務解耦
服務級別,Rabbitmq & Kafka
數據表級別,Canal+Kafka
前后端分離
使用nodejs負責UI層展示與數據準備。php則負責業務邏輯與架構。明確職責范圍,提高各自效率。
THE NEXT GENERATION OF PHP(惠新宸)
鳥哥本次分享的主要內容是,在php7發布的這兩年期間他們的主要工作,包括release的7.1和正在開發中的jit分支。說實話,由于本人水平有限,鳥哥分享的內容只能大概聽懂意思,知道他們在做什么,但具體原理細節,鳥哥分享的我還真聽不懂。這里就對鳥哥的分享內容做個總結。
php7之后還有什么?JIT
php7于15年正式發布,他的最大賣點是,無感知的100%性能提升,包含了運行速度與內存消耗。那么在此之后php該往哪里發展呢?目前已經在開發的一個大方向就是JIT
JIT是什么?為什么是JIT?
鳥哥并沒有做過多的解釋。我就談一些我的膚淺認識,給phper們提供些參考。
首先JIT(just in time)并非是新技術,一大批語言如java早已實現。JIT的思想很簡單,即在程序運行時動態對程序進行編譯,生成平臺相關的機器碼,從而加快程序運行速度。
php文件的執行流程大致是首先引擎加載php文件,解釋器逐條解釋執行代碼。引入JIT后,前面一樣,重點是JIT編譯器會根據Runtime信息對熱點代碼進行動態編譯生成機器碼,然后這部分代碼以后就可以直接執行了,而不需要解釋器逐條解釋執行了,運行效率便得到了提升
看到這里不知道大家是否和我有一樣的疑問,既然編譯為機器碼執行的效率那么高,為何不在項目正式部署前全部進行編譯,何必在運行時編譯?
要知道運行時編譯也會增加程序的執行時間的。我在查閱了一些資料和一番思考后,有以下一些淺見
代碼發布前先編譯,是比JIT更早的通用辦法,稱為AOT(ahead of time)
,c語言便是這種執行模式。關于這兩種模式孰優孰劣,學術界一直爭論不休,目前也沒有定論。但JIT相比AOT有這樣幾個優點
- 發布速度快。不用每次都編譯,發布速度自然快
- 優化效率更好。因為JIT是基于Runtime信息,比AOT更“了解”代碼,優化的效率更好。比如分析Runtime得知某個變量雖然聲明是10個字節,但運行過程中一直是1個字節,那么就可以減小程序內存消耗;再比如某段代碼始終未被執行,JIT則可以直接將其忽略
- 粒度更精細。JIT可以只針對hotspot(熱點)進行編譯,熱點可能是一個函數或者只是一個代碼段
- 對碼農透明。JIT無須碼農自己對程序根據不同平臺進行編譯發布,只需要寫高級代碼即可
基于以上幾個優點,再結合php一貫的簡單易用原則,我想JIT確實是不錯的選擇。不過php也是支持AOT的,有興趣的同學可以查一下。
但JIT技術也絕不是靈丹妙藥,即便是編譯也是需要時間的,當代碼編譯的時間消耗大于運行收益時,程序反而會變慢!
會有這種情況嗎?有的,比如某個項目中,熱點并不明顯,JIT編譯的代碼執行次數都很少,那么編譯帶來的收益是有可能小于編譯本身的消耗的
以下是在標準測試中引入JIT技術后,php運行效率比7.2有100%的性能提升,不過在實際生產環境中效果不會有這么好
php7.1做了什么?類型預測
php要想實現JIT,有一個難題必須解決,那就是變量的類型預測
。試想如果在動態編譯時還要進行大量的類型檢查,性能將會大打折扣。php7中已經可以對變量類型進行控制,7.1則是更加完善了這個機制,可以說目前php已經是半強類型語言了。但由于php的弱類型歷史,仍有大量代碼運行前是無法得知變量類型的,所以在7.1中鳥哥進行了大量變量類型預測的工作,為后續JIT打基礎
變量預測
比較簡單的一種辦法是數據流分析,即分析代碼的上下文,推斷出變量的可能類型,比如
function calc ($a1, $b2) { // $a1: [ANY], $b2: [ANY]
$T3 = $a1 * 2; // $T3: [LONG, DOUBLE]
$a4 = $T3 % 1000; // $a4: [LONG]
$T5 = $b2 * 3; // $T5: [LONG, DOUBLE]
$b6 = $T5 % 1000; // $b6: [LONG]
$T7 = $a4 + $b6; // $T7: [LONG, DOUBLE]
return $T7;
}
其實這還是很困難的,鳥哥列舉了一些開發過程中遇到的困難。比如變量的變量,$$var_name
,或者頂層代碼(即寫在函數和類之外的代碼)等等。php的歷史包袱還是很重的。解決這些問題的簡單辦法就是強類型,但這又會降低開發效率,因為優化而影響phper的開發效率
這是鳥哥所不愿意的,他認為業務永遠是優先的,優化只是支線
目前鳥哥的解決辦法就是對JIT進行分級,通過配置實現不同程度的動態編譯,從而降低類型預測的難度。另外就是針對具體的場景,進行垂直優化
問答環節
鳥哥的問答環節也非常精彩,原定一小時的分享最終超了一小時,下面我就憑著記憶對一些問題復現一下,可能存在偏差,將來我可不負責
php7.1那個詭異的函數返回類型限定是如何考慮的?
鳥哥:沒什么特別考慮,投票投出來的。首先說明一點,我投的是反對票。包括php的命名空間反斜杠我也是非常反對的,但可能由于我并沒有對這方面太深的認識,沒有理解其他開發者的意圖。不過這些問題用習慣了也不是什么大的問題
升級php7后,遇到了一個詭異的引用計數的問題。具體記不清了,大致是他們發現有個應該回收的變量在升級后沒有回收
鳥哥:我現在不能給你準確答復,有可能是個bug,這個我隨后跟進一下。但我想說的是你剛才介紹了你們在調試過程中對引用數的反復推算,其實不必糾結這,引用數用于垃圾回收時只有0和非0兩種區別,我們在增加引用計數時可能有時候不是加1,而是加2,所以不要太在意具體是多少,確定大于0就行
一位學生提問者表示自己對高并發、分布式感興趣,如何提升這方面的技能呢?
鳥哥:這里你有一個誤區。我們研究學習技術并不是為了學習而學習,而是為了解決實際的業務問題。你沒有接觸過這方面的業務,自然沒有這方面的經驗,等你真正有這個業務需求時,好多東西原理都很簡單,使用方法也很成熟,自然就會了,這是個水到渠成的過程,不必刻意去追求那個“術”。另外,我多說一句是,其實當你真正處在這樣的業務中時,你會發現這些事情很少需要你操心的,OP通過各種集群就已經把這些問題給屏蔽了。
鳥哥你是怎樣看待php的前景呢?現在黑php的這么多人
鳥哥:php的前景不要問我,要問你和我,整個php生態。天峰貢獻一個swoole,php就有了高性能網絡請求功能,xx貢獻個php-ml,php就有了大數據處理功能,我今天貢獻一個jit,php就有了動態編譯能力。php發展到今天就是大家你一個小貢獻,他一個小貢獻積累出來的,所以php的前景好不好,要看我們生態,也希望大家踴躍貢獻。至于黑php,我現在都懶得反駁了,有句話說的好,“黑php之前,先數數他給你掙了多少錢”,我一直認為業務是技術存在的理由,能不能快速響應需求、實現業務才是最根本的。
目前php沒有連接池,非常不方便,不知道官方是否有支持計劃?
鳥哥:目前沒有。不過這不正是一個給社區做貢獻的機會嗎?你們開發一個連接池,貢獻到社區既方便了自己,也方便了大家。天峰昨天的分享PHP-X,不就是為了這樣的事
鳥哥你是怎樣看待全棧工程師這個概念的?
鳥哥:我并不認同這個概念,我認為這是個偽命題。全棧這個概念最早是前端工程師提出來的,認為從前端到后端這是“全?!?,但我理解的全棧應該是對一個領域從底層原理到上層應用,這不才更應該叫做棧?自稱全棧工程師的大部分屬于只對各個領域多少有些認識而已。優秀的工程師不必刻意去追求全棧,你只需要在你的領域里不斷深入就行,深度達到了,自然就有了廣度,廣度是深度的副產品
,推而廣之,就是所謂的全棧工程師是當你在一個領域深入到一定階段后的副產品,而不是刻意在各個領域學出來的
php7對性能壓榨已經比較徹底了,未來php是繼續提高性能呢,還是增加新的特性?
鳥哥:你想太多了,目前并未任何打算。JIT開發就非常困難了,這個是否能夠成功還是未知數,下次大會如果JIT沒有完成,我就沒啥可分享的了。
現在在北京很難安家,將來回到二三線城市,php很難找工作,不知道鳥哥有什么看法嗎?
鳥哥:不必過于擔心,不光是程序猿,其實還有好多公司也很難承受一線城市的成本,也在不斷的往二三城市分流,所以找工作問題還是不大的。另外至于你擔心php難找工作,那你可以換java、換go啊,一個程序猿不應該給自己打上標簽,“xx程序猿”,你作為一個工程師,至少要精通3種以上的語言,而且要有良好的學習能力
鳥哥你是如何放松你的部下呢?會請他們去大保健嗎?
鳥哥:這個我沒太多經驗,不過就我自己來說,有時候加班多了還是比較累的,我有段時間脖子特別疼,一周得去至少三次按摩院按摩才能緩解,當然我說的是盲人按摩。后來我真的研究了頸椎康復指南,不是開玩笑,我是真研究了。人的腦袋大概12斤重,你想你整天頂個西瓜,要是頸椎肌肉不行的話,能不難受嗎?所以我后來經常去健身房,鍛煉頸椎,后來才慢慢好了
使用c++11開發php7擴展(韓天峰)
簡單說就是天峰在多年的php擴展開發中,感受到基于c的zend api十分不方便,所以基于c++11對zend api進行了封裝,既降低了php擴展的開發難度,提高了開發效率,同時也為php帶了更多的想象空間,并把這個項目命名為php-x
什么是php擴展?
定義
php本身是c語言開發的,其實可以想象我們是使用php這個高級語言去驅動一個c開發的引擎(zend engine)去做事情的。php擴展就是允許我們使用c為整個引擎開發新的插件來擴展php的功能,比如網絡請求的curl擴展,使用mysql的mysqli擴展等。這種機制有利于社區為php的發展添磚加瓦,可以說zend engine本身就是一個個插件堆積出來的
如何開發擴展?
- 使用ext_skel生成擴展開發骨架
- 編輯config.m4文件
- 在extension.h頭文件中定義擴展函數
- 修改extension.c源文件,實現擴展函數邏輯
- 利用phpize configure make & make install安裝擴展
這樣當php程序中調用擴展函數時,zend引擎會去調用你所開發的擴展。當然,你在擴展邏輯中只能使用c語言通過zend api來對傳入的php變量進行解析、處理,最終再通過zend api返回php變量,繼續原流程
目前擴展開發有何痛點?
zend api不好用
- 大量使用宏
- api名稱太長,參數太多,容易出錯
- api分散在眾多.h和.c文件中
- 需要精通c語言,大量的指針場景,容易出錯
- 沒有任何,任何的教程或手冊!
古老的c語言
- 50年歷史的古老編程語言
- 面向過程的風格,封裝性差
- 指針容易出錯
- 缺少原生的數據結構支持
- 僅適合編寫底層軟件
php-x做了哪些工作?
鑒于前面說的問題,php-x使用c++11對zend api進行了封裝,選擇c++11是因為
- c++是現代編程語言
- 面向對象風格,封裝性好
- 模板泛型編程
- STL容器
- 通用編程,適用范圍廣
可以簡單對比一下封裝后的代碼
// 變量操作
ZVAL_LONG(&a, 1234); // zend api
Variant a = 1234; // php-x
// 類型推斷
Z_TYPE_P(value) == IS_LONG; // zend api
value.isLong(); // php-x
// 類型轉換
convert_to_long(value); // zend api
value.toInt() // php-x
此外,php-x還提供了數組、字典、資源等多種常見數據結構和方法封裝。而且還提供了在c++中嵌套php的功能
,c++中也可以訪問諸如_POST等上下文,配合通過php擴展賦予了php調用c++的能力,實際上php-x完全打通了php與c++
實戰php7擴展
本小節利用php-x實現一個擴展my_ext,包含my_ext_class類和my_ext_test函數,對php-x開發php7擴展做一個簡單的示例
php7擴展注冊
PHPX_EXTENSION() {
Extension *extension = new Extension("my_ext", "0.0.1");
extension->onStart = [extension]() {
extension->registerConstant("MY_EXT_VERSION", "0.0.1");
extension->registerClass(my_ext_class);
};
extension->registerFunction(PHPX_FN(my_ext_test));
return extension;
}
實現擴展函數
PHPX_FUNCTION(my_ext_test) {
for (int i = 0; i < args.count(); i++) {
php::echo("arg[%d] type is %d\n", i, args[i].type());
}
Variant v1 = arg[0];
Array arr(v1);
arr.set(1, 'efg');
retval = arr;
}
實現擴展類
Class *my_ext_class = new Class('myClass');
my_ext_class->addMethod(PHPX_ME(myClass, test), STATIC);
my_ext_class->addMethod(PHPX_ME(myClass, test2));
my_ext_class->addProperty('name', 'Rango');
extension->registerClass(my_ext_class);
實現類方法
PHPX_METHOND(myClass, test2) {
Variant res = newResource('String', new String('hello'));
_this.set('resource', res);
php::error(E_WARNING, 'err message');
}
注冊phpinfo
extension->info({'gtk support', 'enabled'}, {
{'author', 'Rango'},
{'version', ext->version},
});
然后和一般擴展一樣編譯安裝就行
php的想象空間
php-x打通了php與c++,理論上c++可以做的事,php同樣也能做到。同時php擴展的開發難度也大大降低,可以應用于更多的場景
- 可以用c++擴展實現多線程
- 程序中計算密集型部分可以輕松使用擴展實現
- 可以通過擴展加密商業軟件
- Fackbook、Google、Microsoft、Tencent等巨頭的開源c++庫可以為php所用了
PHP安全開發:從白帽角度做安全(湯青松)
本節講師分享了網絡安全現狀,和常見的安全問題及應對措施
嚴峻的現狀
目前網絡黑產發展迅速,聊天工具中搜索‘漏洞、低價、線報’能夠找到大量的賣家。同時他表示他認識一個17歲的小孩,不讀書,只做黑產,月入8k+
另一個是簡單搜索會發現,國內開放3306端口的服務器數量有180萬之多。有500T以上的mysql數據庫此刻正在使用root賬號裸奔!
漏洞的分類
講師對常見的漏洞進行了分類,我感覺大致可以分為以下三類
- 代碼漏洞。即你所寫的代碼沒有實現你的意圖??赡苁悄闼讲恍?,也可能是語言不行
- 邏輯漏洞。即你設計的業務邏輯存在你沒想到的情況
- 第三方漏洞。即你麻痹大意了,所托非人~
代碼漏洞
常見的代碼漏洞包括
- 代碼注入
- sql注入
- xss
- csrf
- 文件包含
- 命令執行
總的來說,代碼漏洞主要原因是盲目相信用戶輸入
,一條安全口訣始終不能忘記就是用戶輸入、程序輸出始終過濾
。另外就是不必要的權限不要開,不使用的代碼不加載
比如代碼漏洞的一般防護措施是,open_basedir限制程序訪問范圍,過濾點、斜杠、反斜杠,禁止加載遠程文件,eval這種高危函數禁用,使用代碼檢查工具如Taint
以下幾個環節是事故高發區,應著重檢查
- 文件上傳
- 全局變量
- 裸寫sql
- include參數文件
- 執行參數代碼
邏輯漏洞
常見的邏輯漏洞包括
- 越權訪問
- 連續id暴露重要信息
- 身份認證、密碼找回
業務安全問題的幾個重災區是
- 授權
- 身份認證
- 用戶輸入限制
- 密碼找回
- 驗證碼
據統計,產品發布上線后的bug修復成本大約是開發階段的30倍!
第三方漏洞
我們在平時開發中會用到大量的第三方框架與類庫,雖然大部分都經過了大量實戰測試,但仍然會時不時爆發漏洞,國內常見的php框架ECshop,WordPress,Discuz,Dedecms,PHPcms等都曾報過漏洞。此時我們更多需要做的是保證使用最新版,保證第一時間修復漏洞
- 溯源檢查。代碼版本不能存在已知漏洞
- 版本更新。不要遺漏安全補丁更新
- 后臺隱藏。不要使用默認的地址與賬號密碼
查看一下16年國內漏洞占比
最后,系統的安全性不是取決于最強的地方有多強,而是取決于最弱的地方有多弱!
swoole2.0原生協程高性能開發實踐(思超)
該分享的主要學的是swoole1.0已經實現了協程,不過是用yield和generator,來實現函數的中途退出與再入
但這樣存在很多問題,如對程序員不透明,需要掌握yield和gen的用法,其次要求整個流程都要使用這種方式。swoole2.0則是原生支持協程,使用新client可以方便的實現協程特性
mysql5.7不求人(葉金榮)
葉老師核心是講發布四年的5.7可以代替5.6了,據官方測試,5.7的性能要優于5.6三倍
5.7的主要特性包括
- 增加json數據類型
- 支持隱藏索引
- varchar可以動態加長
- innodb讀性能優化
- innodb支持全文索引
- innodb支持地理位置索引
- 默認使用sql嚴格模式
- select支持超時設置
- 支持多主復制
- 更多詳情可以見另一篇mysql文章(mysql5.7特性分析)
是時候告別myisam了
最后,5.7的下一個版本將是8.0,也已經開始進入測試階段了