導入高通無線快充技術!BMW i8 充滿電僅需一小時

  隨著新一季電動方程式 (Formula E) 賽車比賽 10 月將在北京開跑,身為主要贊助廠商之一的 Qualcomm 高通也在賽前活動中發表了將應用在賽季中的新無線充電技術,透過 Halo DD 技術的強化,讓原本需要 2 小時才能完全充滿電的 BMW i8 SafetyCar 安全車,充電時間減少至 1 小時內即可完全充飽!   Qualcomm 所推出的這款 Halo DD 進階版車用無線充電系統,利用鋪設在地面上的磁感應線圈對電動車進行無線充電,不過新版的 Halo DD 透過磁感應線圈的最佳化,將原本 3.6 kW 的規格提升到 7.2 kW,雖然仍比不上汽車加油般在數分鐘內就可加滿,但在技術上已經是相當大的進步,倘若這項技術普及到一般環境中,只要在停車場、路邊停車格都鋪設了此種磁感應充電線圈,相信對電動車的普及將會更有幫助。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

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

 受惠特斯拉新車交車 F-貿聯本季營運可展現新車效益

美國電動車大廠特斯拉(Tesla)宣布新款 SUV 車將於 29 日交車,F-貿聯在電動車大客戶 Tesla 新車帶動下,預料本季營運可望展現新車效益,公司表示,今年車用與資訊類應用均正面發展,公司布局電動車獲得大客戶認同後今年產品品項增加,包含儲能系統設備線組將由 F-貿聯供應,而儲能應用中家用與企業用需求逐漸增長,能源基礎建設需求將隨著電動車與新能源電廠已見到雛形。   除了一般電動車外,F-貿聯表示,目前已送樣電動車線組給商用電動車廠,由於美國政府要求公家單位使用電動車比例須增加,政府要求下看好商用電動車市場銷量增加,公司可望持續擴展電動車業績。   F-貿聯強調,電動車線組技術門檻高且汽車應用重視品質與安全穩定度,不怕同業競爭。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

什麼是回調,回調在編程中的含義

回調函數的最初需求背景

回調函數我能想到的最古老的場景就是系統編程會用到。

編程分為兩類:

  • 系統編程(system programming)
  • 應用編程(application programming)

什麼是系統編程:
  所謂系統編程,簡單來說,就是編寫各種各樣的功能庫。比如Windows裏面的win32、gdi32庫,win32就能調用主機硬件和系統層的功能,gdi32能用來繪製圖形相關。這些庫就等着那些做應用的人來調用就行。

什麼是應用編程:
  而應用編程就是利用已經寫好的各種系統功能庫、語言功能庫來編寫具某種業務功能用的程序,就是應用。比如一個基礎的爬蟲程序,可以利用python語言和requests庫來完成,一個基礎的Web站點可以利用Java語言和Java Servlet庫來完成。

系統編程和回調的關係

  系統程序員會給自己寫的庫留下一些接口,即API,以供應用程序員使用。所以在抽象層的圖示里,庫位於應用的底下。當程序跑起來時,一般情況下,應用程序會時常通過API調用庫里所預先備好的函數。但是有些庫函數卻要求應用先傳給它一個函數,好在合適的時候調用,以完成目標任務。這個被傳入的、后又被調用的函數就稱為回調函數。

如果你看文字看得比較懵,那麼你看我畫的圖(下面是圖1):

(圖1)

理解回調前,先理解同步調用

同步調用是以一種阻塞式調用,簡單來說就是從上往下,按照順序去執行。 而回調就是一種非同步調用式順序。

  同步式調用的具體案例,可以聯想到古代的烽火台。古代長城的烽火傳遞的機制就和同步調用差不多,現在我們假設每個烽火只能看到相鄰的烽火狀態,每個烽火的狀態只有亮(點火狀態)和暗(不點火狀態)。

  現在有A、B、C、D四個烽火台,A首先點亮,B看到A的烽火亮了,立馬去點火,花了2秒點亮。但是這時候負責C烽火的人在睡覺,可是這時候所有人都在等待C點亮,終於C睡了2個小時候看到了B點亮,然後去點亮。D由於長期沒有點亮,導致烽火出現問題,因此整個過程都在等待D的完成。(由此也引發一些思考,同步調用有時也容易掉鏈子,如果上一步掉鏈子了,下一步之後的操作都完蛋了。)

同步調用的案例代碼:

print("start.")
print(123)
print(456)

a = 7
if a > 6:
    print(789)

print(91011)
print("end.")

回調需要解決的問題

  常見的系統都會開發出很多庫,庫裏面有很多函數。而有些函數,需要調用者根據自己的需求來寫入要調用的函數。因為這個在編寫庫的時候沒法預測,只能由調用者輸入,所以就需要回調機制。

  回調機制是用來完善同步調用機制的一種方式,用來完善同步調用機制的還有異步調用機制。(後面會寫文章介紹這種更重要的異步)

回調函數怎麼解決實際問題的案例

回調就是通過如下方式來解決上面說的問題。

  • 函數能變成參數
  • 靈活、自定義的方式調用

函數變參數案例

def doubel(x):
    return 2*x

def quadruple(x):
    return 4*x

# mind function
def getAddNumber(k, getEventNumber):
    return 1 + getEventNumber(k)

def main():
    k=1
    i=getAddNumber(k,double)
    print(i)
    i=getAddNumber(k,quadruple)
    print(i)

# call main
main()

輸出結果:

3
5

靈活、自定義的方式調用(酒店叫醒旅客)案例

這個案例真是回調的靈魂所在了,假設你是酒店的前台小姐姐,你不可能知道今晚入住的旅客需不需要明天要不要叫醒服務、需要什麼樣的叫醒服務。

def call_you_phone(times):
    """
    叫醒方式: 給你打電話
    :param times: 打幾次電話
    :return: None
    """
    print('已經給旅客撥打了電話的次數:', str(times))

def knock_you_door(times):
    """
    叫醒方式: 去敲你房間門
    :param times: 敲幾次門
    :return: None
    """
    print('已經給旅客敲門的次數:', str(times))

def no_service(times):
    """
    叫醒方式: 無叫醒服務. (默認旅客是選無叫醒服務)
    :param times: 敲幾次門
    :return: None
    """
    print('顧客選擇無服務.不要打擾他的好夢。')

def front_desk(times, function_name=no_service()):
    """
    這個相當於酒店的前台,你去酒店之後,你要啥叫醒方式都得在前台說
    這裡是實現回調函數的核心,相當於一个中轉中心。
    :param times:次數
    :param function_name:回調函數名
    :return:調用的函數結果
    """
    return function_name(times)

if __name__ == '__main__':
    front_desk(100, call_you_phone)  # 意味着給你打100次電話,把你叫醒

輸出:

已經給旅客撥打了電話的次數:100

實際應用(Python的requests庫自帶的事件鈎子)

這個案例就很好解決原本程序是同步機制執行的,但是通過鈎子事件,就可以優先去執行一些先行步驟。而這個鈎子事件的原理就是函數回調。

import requests

def env_hooks(response, *args, **kwargs):
    print(response.headers['Content-Type'])

def main():
    result = requests.get("https://api.github.com", hooks=dict(response=env_hooks))
    print(result.text)

if __name__ == '__main__':
    main()

輸出:

application/json; charset=utf-8
{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"...省略"}

課後思考題

看完了上面的案例,請你回答如下幾個問題

1.回調在那些場景下使用?
2.回調必須以函數為參數嗎?
3.回調和異步的差異在哪裡?

把你的思考評論在評論區裏面,我會抽空給你回復的。

參考文獻

參考:
參考:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

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

Forsaken Mail創建臨時郵箱系統| 手把手教程

場景需求

  • 不需要長時間使用的郵箱
  • 需要大量創建臨時郵箱
  • 使用匿名郵箱

環境說明

  • **` Forsaken Mail是一個臨時郵箱系統,可以供任何人接受郵件,即收即毀,支持自定義郵箱地址前綴,這裏就說下Docker和NPM兩種安裝教程,任選一種即可,有興趣或者有需求的可以玩玩。

  • Github地址:https://github.com/denghongcai/forsaken-mail

開啟25 跟3000端口

  • 發工單開 25 跟 3000端口
  • 寶塔面板放行25 跟 3000端口
  • 運營商(xx雲等)到安全組開啟機可
  • 國外VSP(如xx工等) 一般不用開

安裝Docker環境

Docker 官網

#CentOS 6
rpm -iUvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
yum update -y
yum -y install docker-io
service docker start
chkconfig docker on

#CentOS 7、Debian、Ubuntu
curl -sSL https://get.docker.com/ | sh
systemctl start docker
systemctl enable docker

Docker 運行 Forsaken Mail 鏡像

​`````` docker run --name forsaken-mail -d -p 25:25 -p 3000:3000 denghongcai/forsaken-mail 

注意:可能會出現端口25被佔用

##找出佔用端口程序PID
$ netstat -anp |grep 25
##關閉該程序
$ kill -9 PID
## 重新運行Docker 鏡像
docker start ID/name

使用 域名 代替 IP

做到前面這一步已經可以通過 VSP_IP :3000 來訪問,但是不能通過 域名:3000 進行訪問

此時就應該進行域名解析——登錄你的域名管理。

  • 一級域名解析

需要添加以下2條解析記錄。 了解MX記錄 , A 記錄可參考上一篇

#MX記錄, xx.com 是你買的域名 mx 不要更改
xx.com    MX   10     mx.xx.com
#A記錄 
mx.xx.com   A   服務器IP
  • 其實如果xx.com 被你用了的話,就需要使用 二級域名解析(比如 mail.xx.com)
#CNAME記錄
mail         CNAME     @ 
#A記錄 
mail.xx.com   A   服務器IP

配置Https訪問

如果還不滿足使用http://mx.xx.com:3000,或者想使用Https域名訪問主界面,那我們可以使用Caddy反代。這裏所使用的域名只能是上面設置MX記錄的xx.com,並提前將域名A記錄解析到服務器IP。

1、安裝Caddy
使用命令:

wget -N --no-check-certificate https://raw.githubusercontent.com/ToyoDAdoubiBackup/doubi/master/caddy_install.sh && chmod +x caddy_install.sh && bash caddy_install.sh
#備用地址
wget -N --no-check-certificate https://www.moerats.com/usr/shell/Caddy/caddy_install.sh && chmod +x caddy_install.sh && bash caddy_install.sh
2、配置Caddy

2、配置Caddy

#以下全部內容是一個整體,請修改2個域名后一起複制到SSH運行!
echo "xx.com {
 gzip
 tls admin@moerats.com
 proxy / mx.xx.com:3000
}" > /usr/local/caddy/Caddyfile

3、啟動Caddy

/etc/init.d/caddy start

最後可以打開https://xx.com訪問,使用Docker應用還是容易的。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

電、毒、炸樣樣來 2019斯里蘭卡死361頭大象 創新高

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

Gogoro進軍荷蘭!阿姆斯特丹體驗店2016夏季開張

來自台灣的Smartcooter智慧雙輪電動機車Gogoro 將進軍歐洲!2016年夏天,荷蘭阿姆斯特丹將擁有歐陸第一間Gogoro體驗店,與國際分享智慧能源網路、智慧城市的創新科技,同時在歐洲推動低碳交通工具。

Gogoro參加2015年義大利米蘭國際機車展,同時宣布將進軍歐洲,在阿姆斯特丹成立第一家海外體驗店。同時,Gogoro也受邀參與荷蘭阿姆斯特丹智慧城市體驗實驗室(Amsterdam Smart City Experienced Lab),推動更有效的都會能源使用、深化智慧城市願景。同時,Gogoro也將在阿姆斯特丹佈建GoStation電池交換站以及Smartscooter電動機車。

Gogoro共同創辦人暨執行長Horace Luke表示:「許多歐洲城市都渴望能以創新的商業模式,打造更有效率、更智慧的能源使用方式,這也是為什麼Gogoro 選擇明年至歐洲開拓市場。Smartscooter™ 智慧雙輪與Gogoro® 能源網路正是為了協助大都會能源更有效運用而設計,因此我們也正在與阿姆斯特丹以及歐洲各大城市密切合作,協助全球大型都會逐步轉型為更進步的智慧城市。」

阿姆斯特丹Gogoro體驗店是Gogoro海外拓展計劃的第一站,2016下半年起也預計在更多歐洲城市規畫成立。

國發基金增資,推動全球拓展計劃

潤泰集團、Panasonic以及行政院國家發展基金日前宣布增資1.3億美元,幫助Gogoro發產電動機車、能源管理以及智慧城市科技。增資後,Gogoro資本額成長到1.8億美元,且Smartscooter的電池Panasonic供應商也正式成為Gogoro的第一個策略投資夥伴。

潤泰集團總裁尹衍樑是Gogoro首輪與第二輪最主要的投資者。國發基金的目標則是用於投資、資助新科技重點企業或新創公司。經過增資後,Gogoro擁有更充足的資金走向全世界。

目前,全台已有近2,000輛Gogoro機車發牌,在大台北與桃園地區則擁有近90處電池交換站。

(照片來源:Gogoro)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

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

去小七換電池!Gogoro與7-ELEVEn結盟

台灣智慧電動機車Gogoro的電池交換系統GoStation宣布與統一超商結盟,未來的Gogoro騎士可望在7-ELEVEn便利商店門市快速替換電池,大舉擴大電池交換站據點。

Gogoro繼十月調降售價、日前宣布獲得國發基金等1.3 億美金挹注、且明年將到荷蘭阿姆斯特丹設置體驗館後,更在本周宣布與統一超商異業結盟,在超商門口設置電池交換站,方便騎士使用。第一處超商電池交換站已在台北市建國北路的7-ELEVEn榮鑫門市啟用,若市場接受度高,未來可望有更多7-ELEVEn門市加入電池交換行列。

統一超商目前在全台有超過五千家門市,且多數重要幹道上都有7-ELEVEn的服務據點;Gogoro與其合作,可快速拓展GoStation的服務版圖。Gogoro表示,今年的銷量目標是3000輛;而更便捷的電池交換服務,可望提高消費者的採購意願。

(照片來源:Gogoro)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

碼出優美

  一份擁有良好可讀性和拓展性的代碼是項目里的良藥,它不僅看着舒服,改起來也方便,甚至還能重用,各模塊邏輯分明。“見碼知功底”,而要達到高手那種簡潔有力的境界,需要進行大量的總結和練習,今天我們就來談談如何寫出優美的代碼。

  

命名

  好的命名應該具有如下特徵:

  1,意思正確。這是最基本的要求,不要掛羊頭賣狗肉,詞不達意,要一眼就知道什麼意思。就算一眼看不出來,複製到有道詞典翻譯一下也能知道什麼意思;

  2,單複數分明。如果是一個數組,要麼加s/es結尾表明其是複數,要麼加入list表示它是一個數組。如cars,carList都可以表達一個車的列表;

  3,慎用縮略詞。縮略詞可以讓我們的命名更加簡潔,但是一!定!要!是!業!界!通!用!縮!略!詞!比如info原意為information,msg原意為message,fn原意function,conf原意config等等,這些縮略詞都是業內傳統了,大家都知道什麼意思,切記不要自己亂造縮略詞;

  4,有具體含義。根據業務場景去命名,而不是根據抽象命名;比如getUnreadMsgList,一看就知道是獲取未讀消息列表的意思,而getData這種說了跟沒說一樣,缺乏具體含義。

 

註釋

  有表達力的代碼是不需要註釋的。比如一個init函數,一看就知道是用於做一些初始化的工作,沒必要寫多餘的註釋來說明。

  但是有一些場景註釋是非常有必要的,下面幾種場景要添加註釋:

  1,一些針對特殊業務場景而訂製的特殊邏輯。比如當我們更新個人信息的時候,由於後端的問題,需要少傳一個諸如生日的信息,或者更新頭像時要多傳一個時間戳來供其他業務以後使用。這些莫名其妙的增刪屬性,如果不加以註釋,將導致後續自己都無法理解;

  2,可能會出現隱患的代碼。由於自身水平所限,或者本身技術上就無法實現,只能通過一些特殊技巧來仿製一些效果,往往會存在安全隱患,比如:用戶操作太快會出問題,網絡太慢會出問題,某個接口調不通這頁面會全掛了,一些特殊的操作會引起的暫時無法解決的bug等等場景,都需要註釋說明;

  3,涉及到某些高深的或者生僻的技術知識。這種也要註釋,以提醒自己和其他開發者。

  一般來說,註釋基本上都是在表達“這裏我為什麼這麼做”,很少有註釋會去表達“我是一個什麼玩意兒”,如果是後者的註釋,只能說明命名沒做好。

  

函數

  函數是代碼的靈魂,也是寫邏輯的載體,以下幾個要求是判斷一個開發者函數寫的好不好的標準。

  1,是不是單一職責。一個函數應該只做一件事,而這件事應該能通過函數名就可以清晰的展示。這是一個非常好的特性,一個辣雞的函數可能動輒幾百行代碼,各種邏輯堆積在一起,看得人頭腦發暈,甚至開發者自己都理不清楚;判斷這個函數是不是單一職責的技巧很簡單:看看它還能不能再拆分。

  2,有沒有層級之分。函數與函數之間是有身份地位之分的,有負責整體大局觀的高級函數,也有專註細節的打工仔低級函數。如果不建立起層級結構,就容易迷失在細節的海洋里。比如:

  

 

 

  對於做一頓飯這個函數來說,他不需要關心諸如買菜時反覆挑選這種細節,它只需要知道,大概有三個大步驟就行了。所以這段邏輯會根據其地位拆分出不同等級的函數,假設沒有層級之分,那麼從第一步的“搭公交”一直寫到最後一步的“倒垃圾”,這東西會變得極難維護。

 

  3,承載的場景是否足夠簡單。有時候我們會遇到這樣一種場景:一個函數在很多地方都會用到,但是不同地方傳入的參數不一樣,這樣我們為了函數的通用性,就針對入參做了很多種場景的識別,導致入參非常多,裏面還需要根據不同的場景做邏輯上的細微調整。所以單單是取傳入參數都夠嗆了,一堆的if else或switch case。

  這個函數太難了,而且這已經違背了函數的單一職責原則,它在裏面做了多種場景的判斷,從而表現出不同的行為,但這些行為又不是完全不同,而是“大體相同”,如果重寫好像又會增加很多的重複的代碼。解決的方法是做更小粒度的拆分,將那些真正與業務脫鈎的部分抽離出來,而不同場景對應不同的處理函數,剛剛抽離的業務脫鈎函數正好作為這些不同場景函數公共部分!

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

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

golang 服務詭異499、504網絡故障排查

  • 事故經過
  • 排查
  • 總結

事故經過

11-01 12:00 中午午飯期間,手機突然收到業務網關非200異常報警,平時也會有一些少量499或者網絡抖動問題觸發報警,但是很快就會恢復(目前配置的報警閾值是5%,閾值跟當時的採樣窗口qps有直接關係)。

報警當時非200佔比已經過10%並且在持續升高,根據歷史規律應該很快就會恢復,我們稍微觀察了幾分鐘(一邊吃着很香的餃子一邊看着手機),但是過了幾分鐘故障沒有恢復而且佔比升高了突破50%,故障逐漸升級(故障如果不在固定時間內解決會逐漸升級,故障群每次升級都會逐層拉更高level的boss進來)手機持續報警震動已經發燙了,故障佔比已經快100%,影響面突然變大。

此時提現系統也開始報警,大量打款訂單擠壓(打款訂單擠壓突破一定閾值才會報警,所以不是實時),工位同事也反應支付系統也有少量連接錯誤,突然感覺情況複雜了,迅速停止吃飯,趕緊回公司排查。

回到工位時間差不多12:40左右,快速查看監控大盤,基本都是499、504錯誤,此類錯誤都是因為網絡超時導致。集群中的兩台機器均有錯,而且qps也比較平均,可以排除某台機器問題。

RT99線基本5s,而且連續橫盤,這5s是觸發了上游sidecar proxy調用超時主動斷開了,真正的RT時間可能更長。

故障還未見恢復,業務運維協助一起排查,此時故障群已經升級到技術中心老大,壓力瞬間大的一筆。

查看網關係統日誌,大量調用我們內部的兩個系統報出“下游服務器超時”錯誤,根據日誌信息可以判斷網絡問題導致超時,但是我們調用的是內網服務,如果是網絡問題為什麼只有我們的系統受到影響。

在12:51到13:02之間錯誤佔比情況有所好轉,但是之後錯誤佔比繼續升高。

此時業務運維同步其他部門有大量302報警,時間線有點吻合,此時時間差不多13:30。但是別的部門的系統和我們的系統沒有任何關係,太多的疑問大家開始集中坐到一起排查問題。

他們嘗試做了版本回滾未見好轉,然後嘗試將訪問返回302域名切到內網故障立馬恢復,此時正好14:00。根據他們的反饋在做實驗放量,導致在12:00的時候有一波流量高峰,但是這一波流量高峰對我的系統鏈路衝擊在哪裡,一臉懵逼,疑點重重。

本次故障持續時間太長,報警整整報了兩個小時,故障群每三種報警一次並且電話通知,報警電話幾十個,微信報警群“災難”級別的信息更多,嚴重程度可想而知。

排查

雖然故障是因為別的部門放量導致,但是還是有太多疑問沒有答案,下次還會再出現。作為技術人員,線上環境是非常神聖的地方是禁區,一定要找到每次故障的 root cause,否則沒辦法給自己一個交代,我們開始逐層剝洋蔥。

我們來梳理下疑問點:

1.302是什麼原因,為什麼做了域名切換就整體恢復了?
2.兩邊的系統在鏈路上有什麼交集?如果應用鏈路沒有交集,那麼在網絡鏈路上是否有交集?
3.我們業務網關中的“下游服務器超時”為什麼其他系統沒有影響?對日誌的解讀或者描述是否有歧義?
4.504是觸發sidecar proxy 超時斷開連接,網關服務設置的超時為什麼沒起作用?

1.302是什麼原因,為什麼做了域名切換就整體恢復了?

經過我們的運維和阿里雲專家的排查,出現大量302是因為訪問的域名觸發DDos/CC高防策略。由於訪問的域名配置了DDos/CC高防策略,大量請求觸發了其中一條規則導致拒絕請求(具體觸發了什麼規則就不方便透露),所以會返回302,通過添加白名單可以解決被誤殺的情況。
(從合理性角度講內部調用不應該走到外網,有一部分是歷史遺留問題。)

2.兩邊的系統在鏈路上有什麼交集?如果應用鏈路沒有交集,那麼在網絡鏈路上是否有交集?

所有人焦點都集中在高防上,認為網關故障就是因為也走到了被高防的地址上,但是我們的網關配置里根本沒有這個高防地址,而且我們內部系統是不會有外網地址的。

排查提現系統問題,提現系統的配置里確實有用到被高防的外網地址,認為提現打款擠壓也是因為走到了高防地址,但是這個高防地址只是一個旁路作用,不會影響打款流程。但是配置里確實有配置到,所以有理由判斷肯定使用到了才會影響,這在當時確實是個很重要的線索,是個突破口。

根據這個線索認為網關係統雖然本身沒有調用到高防地址,但是調用的下游也有可能會走到才會導致整個鏈路出現雪崩的問題。

通過大量排查下游服務,翻代碼、看日誌,基本上在應用層調用鏈路沒有找到任何線索。開始在網絡層面尋找線索,由於是內網調用所以路線是比較簡單的,client->slb->gateway->slb->sidecar proxy->ecs,幾個下游被調用系統請求一切正常,slb、sidecar proxy監控也一切正常,應用層、網絡層都沒有找到答案。

sidecar proxy 因為沒有打開日誌所以看不到請求(其實有一部分調用沒有直連還是通過slb、vtm中轉),從監控上看下游的 sidecar proxy 也一切正常,如果網路問題肯定是連鎖反應。

百般無解之後,開始仔細檢查當天出現故障的所有系統日誌(由於現在流行Microservice所以服務比較多,錯誤日誌量也比較大),在排查到支付系統的渠道服務時發現有一些線索,在事故發生期間有一些少量的 connection reset by peer,這個錯誤基本上多數出現在連接池化技術中使用了無效連接,或者下游服務器發生重啟導致。但是在事故當時並沒有發布。

通過對比前一周日誌沒有發生此類錯誤,那很有可能是很重要的線索,聯繫阿里雲開始幫忙排查當時ecs實例在鏈路上是否有問題,驚喜的是阿里雲反饋在事故當時出現 nat網關 限流丟包,一下子疑問全部解開了。

限流丟包才是引起我們系統大量錯誤的主要原因,所以整個故障原因是這樣的,由於做活動放量導致高防302和出網限流丟包,而我們系統受到影響都是因為需要走外網,提現打款需要用到支付寶、微信等支付渠道,而支付系統也是需要出外網用到支付寶、微信、銀聯等支付渠道。
(由於當時我們並沒有nat網關的報警導致我們都一致認為是高防攔截了流量。)

問題又來了,為什麼網關調用內部系統會出現問題,但是答案已經很明顯。簡單的檢查了下其中一個調用會走到外網,網關的接口會調用下游三個服務,其中第一個服務調用就是會出外網。

這個問題是找到了,但是為什麼下游設置的超時錯誤一個沒看見,而且“下游服務器超時”的錯誤日誌stack trace 堆棧信息是內網調用,這個還是沒搞明白。

3.我們業務網關中的“下游服務器超時”為什麼其他系統沒有影響?對日誌的解讀或者描述是否有歧義?

通過分析代碼,這個日誌的輸出並不是直接調用某個服務發生超時timeout,而是 go Context.Done() channel 的通知,我們來看下代碼:

func Send(ctx context.Context, serverName, method, path string, in, out interface{}) (err error) {
    e := make(chan error)
    go func() {
        opts := []utils.ClientOption{
            utils.WithTimeout(time.Second * 1),
        }
        if err = utils.HttpSend(method, path, in, out, ops, opts...); err != nil {
            e <- err
            return
        }
        e <- nil
    }()

    select {
    case err = <-e:
        return
    case <-ctx.Done():
        err = errors.ErrClientTimeOut
        return
    }
}

Send 的方法通過 goroutine 啟動一個調用,然後通過 select channel 感知http調用的結果,同時通過 ctx.Done() 感知本次上游http連接的 canceled。

err = errors.ErrClientTimeOut
ErrClientTimeOut         = ErrType{64012, "下游服務器超時"}

這裏的 errors.ErrClientTimeOut 就是日誌“下游服務器超時”的錯誤對象。

很奇怪,為什麼調用下游服務器沒有超時錯誤,明明設置了timeout時間為1s。

        opts := []utils.ClientOption{
                    utils.WithTimeout(time.Second * 1),
                }
        if err = utils.HttpSend(method, path, in, out, ops, opts...); err != nil {
            e <- err
            return
        }

這個 utils.HttpSend 是有設置調用超時的,為什麼一條調用超時錯誤日誌沒有,跟蹤代碼發現雖然opts對象傳給了utils.HttpSend方法,但是裏面卻沒有設置到 __http.Client__對象上。

client := &http.Client{}
    // handle option
    {
        options := defaultClientOptions
        for _, o := range opts {
            o(&options)
        }
        for _, o := range ops {
            o(req)
        }
        
        //set timeout
        client.Timeout = options.timeout

    }

    // do request
    {
        if resp, err = client.Do(req); err != nil {
            err = err502(err)
            return
        }
        defer resp.Body.Close()
    }

就是缺少一行 client.Timeout = options.timeout 導致http調用未設置超時時間。加上之後調用一旦超時會拋出 “net/http: request canceled (Client.Timeout exceeded while awaiting headers)” timeout 錯誤。

問題我們大概知道了,就是因為我們沒有設置下游服務調用超時時間,導致上游連接超時關閉了,繼而觸發context.canceled事件。

上層調用會逐個同步進行。

    couponResp, err := client.Coupon.GetMyCouponList(ctx, r)
    // 不返回錯誤 降級為沒有優惠券
    if err != nil {
        logutil.Logger.Error("get account coupon  faield",zap.Any("err", err))
    }
    coins, err := client.Coin.GetAccountCoin(ctx, cReq.UserID)
    // 不返回錯誤 降級為沒有金幣
    if err != nil {
        logutil.Logger.Error("get account coin faield",zap.Any("err", err))
    }
    subCoins, err := client.Coin.GetSubAccountCoin(ctx, cReq.UserID)
    // 不返回錯誤 降級為沒有金幣
    if err != nil {
        logutil.Logger.Error("get sub account coin faield",zap.Any("err", err))
    }

client.Coupon.GetMyCouponList 獲取優惠券
client.Coin.GetAccountCoin 獲取金幣賬戶
client.Coin.GetSubAccountCoin 獲取金幣子賬戶

這三個方法內部都會調用Send方法,這個接口邏輯就是獲取用戶名下所有的現金抵扣權益,並且在超時時間內做好業務降級。但是這裏處理有一個問題,就是沒有識別Send方法返回的錯誤類型,其實連接斷了之後程序再往下走已經沒有意義也就失去了Context.canceld的意義。
(go和其他主流編程語言在線程(Thread)概念上有一個很大的區別,go是沒有線程概念的(底層還是通過線程在調度),都是goroutine。go也是完全隱藏routine的,你無法通過類似Thread Id 或者 Thread local線程本地存儲等技術,所有的routine都是通過context.Context對象來協作,比如在java 里要想取消一個線程必須依賴Thread.Interrupt中斷,同時要捕獲和傳遞中斷信號,在go里需要通過捕獲和傳遞Context信號。)

4.504是觸發sidecar proxy 超時斷開連接,網關服務器設置的超時為什麼沒起作用?

sidecar proxy 斷開連接有三個場景:

1.499同時會關閉下游連接
2.504超時直接關閉下游連接
3.空閑超過60s關閉下游連接

事故當時499、504 sidecar proxy 主動關閉連接,網關服務Context.Done()方法感知到連接取消拋出異常,上層方法輸出日誌“下游服務器超時”。那為什麼我們網關服務器本身的超時沒起作用。

http/server.Server對象有四個超時參數我們並沒有設置,而且這一類參數通常會被忽視,作為一個服務器本身對所有進來的請求是有最長服務要求,我們一般關注比較多的是下游超時會忽視服務本身的超時設置。

type Server struct {
    // ReadTimeout is the maximum duration for reading the entire
    // request, including the body.
    //
    // Because ReadTimeout does not let Handlers make per-request
    // decisions on each request body's acceptable deadline or
    // upload rate, most users will prefer to use
    // ReadHeaderTimeout. It is valid to use them both.
    ReadTimeout time.Duration

    // ReadHeaderTimeout is the amount of time allowed to read
    // request headers. The connection's read deadline is reset
    // after reading the headers and the Handler can decide what
    // is considered too slow for the body.
    ReadHeaderTimeout time.Duration

    // WriteTimeout is the maximum duration before timing out
    // writes of the response. It is reset whenever a new
    // request's header is read. Like ReadTimeout, it does not
    // let Handlers make decisions on a per-request basis.
    WriteTimeout time.Duration

    // IdleTimeout is the maximum amount of time to wait for the
    // next request when keep-alives are enabled. If IdleTimeout
    // is zero, the value of ReadTimeout is used. If both are
    // zero, ReadHeaderTimeout is used.
    IdleTimeout time.Duration
}

這些超時時間都會通過setDeadline計算成絕對時間點設置到netFD對象(Network file descriptor.)上。
由於沒有設置超時時間所以相當於所有的連接關閉都是通過sidecar proxy觸發傳遞下來的。

我們已經知道 sidecar proxy 關閉連接的1、2兩種原因,第3種情況出現在http長連接上,但是這類連接關閉是無感知的。

默認的tcpKeepAliveListener對象的keepAlive是3分鐘。

func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
    tc, err := ln.AcceptTCP()
    if err != nil {
        return nil, err
    }
    tc.SetKeepAlive(true)
    tc.SetKeepAlivePeriod(3 * time.Minute)
    return tc, nil
}

我們服務host是使用endless框架,默認也是3分鐘,這其實是個約定90s,過小會影響上游代理。

func (el *endlessListener) Accept() (c net.Conn, err error) {
    tc, err := el.Listener.(*net.TCPListener).AcceptTCP()
    if err != nil {
        return
    }

    tc.SetKeepAlive(true)                  // see http.tcpKeepAliveListener
    tc.SetKeepAlivePeriod(3 * time.Minute) // see http.tcpKeepAliveListener

    c = endlessConn{
        Conn:   tc,
        server: el.server,
    }

    el.server.wg.Add(1)
    return
}

sidecar proxy 的超時是60s,就算我們要設置keepAlive的超時時間也要大於60s,避免sidecar proxy使用了我們關閉的連接。
(但是這在網絡不穩定的情況下會有問題,如果發生HA Failover 然後在一定小概率的心跳窗口內,服務狀態並沒有傳遞到註冊中心,導致sidecar proxy重用了之前的http長連接。這其實也是個權衡,如果每次都檢查連接狀態一定會影響性能。)

這裡有個好奇問題,http是如何感知到四層tcp的狀態,如何將Context.cancel的事件傳遞上來的,我們來順便研究下。

type conn struct {
    // server is the server on which the connection arrived.
    // Immutable; never nil.
    server *Server

    // cancelCtx cancels the connection-level context.
    cancelCtx context.CancelFunc
}
func (c *conn) serve(ctx context.Context) {
    
    // HTTP/1.x from here on.
    
    ctx, cancelCtx := context.WithCancel(ctx)
    c.cancelCtx = cancelCtx
    defer cancelCtx()

    c.r = &connReader{conn: c}
    c.bufr = newBufioReader(c.r)
    c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)

    for {
        w, err := c.readRequest(ctx)

        if !w.conn.server.doKeepAlives() {
            // We're in shutdown mode. We might've replied
            // to the user without "Connection: close" and
            // they might think they can send another
            // request, but such is life with HTTP/1.1.
            return
        }

        if d := c.server.idleTimeout(); d != 0 {
            c.rwc.SetReadDeadline(time.Now().Add(d))
            if _, err := c.bufr.Peek(4); err != nil {
                return
            }
        }
        c.rwc.SetReadDeadline(time.Time{})
    }
}
// handleReadError is called whenever a Read from the client returns a
// non-nil error.
//
// The provided non-nil err is almost always io.EOF or a "use of
// closed network connection". In any case, the error is not
// particularly interesting, except perhaps for debugging during
// development. Any error means the connection is dead and we should
// down its context.
//
// It may be called from multiple goroutines.
func (cr *connReader) handleReadError(_ error) {
    cr.conn.cancelCtx()
    cr.closeNotify()
}
// checkConnErrorWriter writes to c.rwc and records any write errors to c.werr.
// It only contains one field (and a pointer field at that), so it
// fits in an interface value without an extra allocation.
type checkConnErrorWriter struct {
    c *conn
}

func (w checkConnErrorWriter) Write(p []byte) (n int, err error) {
    n, err = w.c.rwc.Write(p)
    if err != nil && w.c.werr == nil {
        w.c.werr = err
        w.c.cancelCtx()
    }
    return
}

其實tcp的狀態不是通過主動事件觸發告訴上層http的,而是每當http主動去發現。

read時使用connReader來感知tcp狀態,writer時使用checkConnErrorWriter對象來感知tcp狀態,然後通過server.conn對象中的cancelCtx來遞歸傳遞。

type conn struct {
    // server is the server on which the connection arrived.
    // Immutable; never nil.
    server *Server

    // cancelCtx cancels the connection-level context.
    cancelCtx context.CancelFunc
}

總結

此次故障排查了整整两天半,很多點是需要去反思和優化的。

1.所有的網絡調用沒有拋出最原始error信息。(經過加工之後的日誌會嚴重誤導人。)
2.超時時間的設置未能起到作用,未經過完整的壓測和故障演練,所以超時時間很容易無效。
3.內外網域名沒有隔離,需要區分內外網調用,做好環境隔離。
4.http服務器本身的超時沒有設置,如果程序內部出現問題導致處理超時,併發會把服務器拖垮。
5.對雲上的調用鏈路和網絡架構需要非常熟悉,這樣才能快速定位問題。

其實系統一旦上雲之後整個網絡架構變得複雜,干擾因素太多,排查也會面臨比較大的依賴,監控告警覆蓋面和基數也比較大很難察覺到個別業務線。(其實有些問題根本找不到答案。)
所有無法復現的故障是最難排查的,因為只能事後靠證據一環環解釋,涉及到網絡問題情況就更加複雜。

作者:王清培(趣頭條 Tech Leader)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

Acer 首款電動車竟然是輛沙灘車

老牌 PC 廠商 Acer 在尋求業務轉型的過程中,除了推出智慧型手機外,還在雲端技術平台研發各種物聯網產品,近日 Acer 在德國 eCarTex 展上展出了一款電動車 X Terran,車身設計接近沙灘車,能適應各類地形行駛。

Acer 公司旗下擁有筆電、平板電腦、智慧型手機等消費電子產品,這家 PC 廠商正在積極開拓其他業務領域,比如基於物聯網技術的電動車產品。

在德國慕尼克 eCarTech 展會上,Acer 展出了自主研發的首款電動車,這款產品的設計相當特別,Acer 並沒有採用四輪轎車的常規設計,而是做出了一輛類似沙灘車的四輪電動車。Acer X Terran 全地形電動車由 Acer 的 BYOC 部門與獨立設計公司 Studio X-Gene 合作研發,Acer 提供 MPS Energy 電池管理系統,硬體模組包括主機板和三個電池組,每個電池組由 256 個標準鋰電池構成,可提供 8.7KW/H 電能,在軟體層面提供基於 BYOC 平台的車聯網服務。   電動車產品線是 Acer 進軍電動汽車市場和能源市場的重要一步,2015 年 7 月 Acer 成立了 MPS Energy 子公司,專注於智慧型電動車的電池和車聯網技術研發。目前該團隊有 10 名來自中華汽車的工程師,也有一些來自 Acer 原有產品線的團隊,未來將以合作的方式,與中小汽車廠商在電動車領域進行合作。

(本文授權轉載自《》─〈〉)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

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