小米有品野小獸健腹輪 J20 眾籌推出:智慧數字顯示、遠距回彈,眾籌價約 860 元_貨運

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

除了疫情影響,最近寒流來襲也讓民眾降低出門運動的意願,不過如果想在家自主培養運動習慣,選購一款體積小、收納不佔用空間的健身器材就相當重要,像是健腹輪就是款能幫助用戶在家鍛煉腹肌、燃燒脂肪的運動器材就是不錯的選擇。最近小米有品最新一期眾籌的「野小獸健腹輪 J20」,不僅能顯示數字資訊、還能自動回彈讓新手也能輕鬆駕馭。

小米有品野小獸健腹輪 J20 眾籌推出:智慧數字顯示、遠距回彈,眾籌價約 860 元

最近在小米有品推出的野小獸健腹輪 J20 採用人體工學設計,能搭配智慧訓練模式和完整的訓練計劃達成訓練目標。

野小獸健腹輪 J20 具備自動超強回彈機制,回彈力道足以抵銷運動時身體向前的衝力,降低受傷風險、即便新手也能駕馭。

當運動到最遠處時,彈簧可提供等效的回彈力道來支撐身體, 1.8 公尺的安全回彈距離,能有效保障運動安全。

野小獸健腹輪 J20 配備智慧 LED 顯示螢幕,能即時顯示藍芽狀態、剩餘電量、運動次數等運動數據。

在野小獸健腹輪 J20 採用一體成型設計,在機身內部採用全包覆式多重防護結構,能有效防止拉伸、變形、捲縮、分裂,達到安全防護作用。

另外,採用多重消音裝置讓運動更靜音,通過內建鋼片發條自動回彈發力,確保滾動更順暢。相較於傳統發條受力更均衡、壽命也更長。

滾輪內裡採用多層加厚複合材質,具備韌性強、耐磨行滑的特性,在滾動來回之間不傷地板、無噪音。

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

用戶也能野小獸 App 觀看教學課程,在 App 端也能即時顯示運動數據:

售價方面,野小獸健腹輪 J20 目前在小米有品展開眾籌,眾籌價為人民幣 199 元(約合新台幣 860 元),建議售價則為人民幣 299 元(約合新台幣 1,287 元)。

圖片/消息來源:小米有品

延伸閱讀:
Redmi K40 Pro 最新渲染圖曝光:可能是最便宜的 S888 旗艦 5G 手機之一

小米11 Lite 通過 FCC 認證,相關規格提前曝光!

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

微軟將推出對使用者更友善、更容易使用的檔案恢復工具_網頁設計公司

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

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

人嘛!難免有錯手刪掉不該刪檔案的時候,在 2020 年初時,Microsoft 發表了一個供 Windows 系統恢復檔案的工具,允許用戶反會撤銷決定,恢復從各地永久刪除的檔案,包括資源回收桶。雖然這是個福音,但對於大多數用戶來說還是有不算低的門檻,不過接下來,Microsoft 將讓它變得更友善、更容易使用。

微軟將推出對使用者更友善、更容易使用的檔案恢復工具

在之前推出的 Windows 檔案恢復工具,其實是一個指令工具,它的工作方式與其他第三方應用不太一樣,只能輸入指令來使用它,雖說是免費使用,但因為對使用者友好程度低,使得這個工具立意良善卻普及度不如預期。根據消息,Microsoft 有計畫讓這工具的使用更加簡單,新的改良正在路上。

這項工具目前已經對測試人員釋出,根據 Windows Latest 報導,在最新測試版本中除了速度變得更快,還支援一般與廣泛兩種模式。使用新的「一般」模式,你可以掃描磁碟機 (NTFS 檔案系統) 以尋找被刪除檔案並嘗試還原。Microsoft 指出,一般模式是使用者的標準恢復選項,適用於恢復近期刪除的檔案。任何具備一點指令基本知識的人都能夠恢復他們的檔案,雖然一般模式對使用者更友好,但如果硬碟中的可用空間已經被其他檔案所覆蓋,它可能就失去作用了,尤其是如果你的電腦中是使用 SSD 的話。

Microsoft 表示,從 2020 年首度發表檔案恢復工具以來,陸續收到許多使用者的意見回饋,未來幾個月內,該工具將會推出更多改良,第一個大更新會在 2021 年初與 Windows 10 2004 更新或更新的版本推出給普羅大眾。

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

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

◎資料來源:Windows Latest

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

POCO M3 即將在台發表:6000mAh 超大電量,規格重點搶先看!_租車

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

幾天前,我們曾報導過 POCO 台灣的官方 Facebook 粉絲專頁和官網上線的消息,這幾天 POCO 官方也開始在預告新機即將推出的消息,種種跡象也正暗示著 POCO 品牌獨立後的首款在台發表的新機,就是內建 6000mAh 超大電量的 POCO M3 。定位為中階入門的大電量手機, POCO M3 也早已在上個月就通過台灣 NCC 認證。

▲圖片來源:小米香港官網

POCO M3 即將在台發表:6000mAh 超大電量,規格重點搶先看!

隨著 POCO Taiwan 粉專和官網曝光後,最近在 POCO Taiwan 的 Facebook 粉絲專頁也開始推出問答抽獎活動,截至目前的兩題的答案都是「6000」,這數字正是 POCO M3 的電池容量。另外,這項活動只到 1 月 10 日,也代表在不久後 POCO M3 就會正式在台亮相。(更新:POCO M3 將於 1 月 21 日在台發表)

▲圖片來源:POCO Taiwan(Facebook)

其實早在去年 12 月初, POCO M3 就以型號 M2010J19CG 通過台灣 NCC 認證, POC M3 預計銷售模式將比照目前小米10T、小米10T Pro 方式,由官方授權通路進行販售。

▲圖片來源:NCC

在 NCC 認證通過的資料附件也能見到產品外觀照,確認它就是 POCO M3 :

▲圖片來源:NCC

雖然 POCO M3 只支援 18W 快速充電,不過充電器仍將標配 22.5W 快速充電器:

▲圖片來源:NCC

至於 POCO M3 的規格重點有哪些呢?目前 POCO M3 已在印度、香港等地開賣,這裡也提前為大家簡單介紹 POCO M3 的規格特色:

▲圖片來源:小米香港官網

POCO M3 產品定位為中階入門的 4G 智慧型手機,採用 6.53 吋 FHD+ 解析度的水滴全螢幕:

▲圖片來源:小米香港官網

相機方面, POCO M3 配備 4800 萬像素 AI 三鏡頭主相機,擁有 4800 萬像素 f/1.79 光圈主鏡頭、200 萬像素微距鏡頭以及 200 萬像素景深鏡頭的搭配。前相機則為 800 萬像素自拍鏡頭。

▲圖片來源:小米香港官網

硬體規格方面, POCO M3 搭載 Qualcomm Snapdragon 662 八核心處理器、配備 4GB RAM 和最高 128GB ROM :

▲圖片來源:小米香港官網

POCO M3 超大電量則是 POCO M3 的特色之一, POCO M3 內建 6000mAh 大電量電池並支持 18W 快速充電。 POCO M3 的續航換算日常使用情境,待機可達到 583 小時、通話 40 小時、音樂 196 小時、影片 17 小時。

▲圖片來源:小米香港官網

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

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

音效方面, POCO M3 內建雙揚聲器,並通過 Hi-Res Audio 高解析度音效認證。另外, POCO M3 仍保留 3.5mm 耳機插孔,對有線耳機需求的用戶來說更為方便:

▲圖片來源:小米香港官網

POCO M3 採用獨立三卡槽設計,可同時支持 4G+4G 雙卡雙待和 microSD 記憶卡擴充容量(最高可擴充至 512GB):

▲圖片來源:小米香港官網

POCO M3 採用側邊指紋辨識,結合於電源鍵更方便於快速解鎖:

▲圖片來源:小米香港官網

即便 POCO M3 內建 6000mAh 大電量電池,仍將重量控制在 200 克以內,機身重量為 198 克:

▲圖片來源:小米香港官網

延伸閱讀:
小米有品推出 Lydsto 手持吸塵打氣機,眾籌價約 860 元

小米11 Lite 通過 FCC 認證,相關規格提前曝光!

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

小米發表中階 Redmi Note 9T 與入門 Redmi 9T,效能與長續航合而為一_包裝設計

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

好久沒有紅米系列的好消息了,在今晚(1/8)的發表會中,小米一口氣發表了號稱中階、入門機皇的兩款新機 Redmi Note 9T 與 Redmi 9T,以震撼 5G 手機市場的價格提供 5G + 5G 雙卡雙待,超乎同級競爭對手的效能表現與長效續航力,攝影與外型上也絲毫不妥協。

小米發表中階 Redmi Note 9T 與入門 Redmi 9T,效能與長續航合而為一

Redmi Note 9T
這款機型擁有特別打造的機身外型,具備 Unibody 3D 曲面機背,具紋理的聚碳酸酯纖維提高抓握感和抗指紋等特性,加上康寧第五代大猩猩玻璃的保護,讓你用起來更安心,共有日暮黑與晨曦紫兩款顏色,典雅大方。配備 6.53 吋 FHD+ DotDisplay 螢幕,並通過德國萊因低藍光認證,在給你更好的視覺享受同時保護你的眼睛。

搭載支援 5G 的聯發科天璣 800U 行動平台,處理速度較前代快 100%,以八核心處理器及 7nm 製程技術帶來絕佳的運行效能。而 Redmi Note 9T 也是整個 Redmi Note 系列中首款隻原 5G+5G 雙卡雙待的機型;配備 5,000mAh 電池和高效處理器技術,在消除電池容量煩惱同時為 5G 時代兼顧效能與電源效率兩個方面。Redmi Note 9T 支援 18W 快速充電,手機隨盒附上 22.5W 充電器,加上高循環週期電池技術,在日常情況下可確保 3 年電量儲存不縮水。

Redmi Note 9T 配備 4,800 萬像素 AI 三鏡頭後置相機,以 4,800 萬像素主鏡頭、200 萬像素景深鏡頭和 200 萬像素微距鏡頭,讓攝影新手也能輕鬆拍出專業攝影作品。以 1/2 吋感光元件和旗艦級 ISP 影像處理器架構,提升影像品質與提供更快的成像速度。同時,Redmi Note 9T 擁有多款全新攝影模式,如夜景模式、Pro+、RAW、HDR和人像模式等,在任何時刻均能捕捉完美的照片。

Redmi Note 9T 提供 4GB+64GB 及 4GB+128GB 兩種容量選擇,售價分別為 229 歐元(約合新台幣 8,085 元)及 269 歐元(約合新台幣 9,495 元)。

Redmi 9T
Redmi 9T 採用圓角及防指紋機背的現代簡約設計,配備 6.53 吋外覆康寧第三代大猩猩玻璃的 FHD+ 水滴螢幕,帶來清晰的觀看體驗,同時提供有效的防裂和防刮保護,共提供碳纖灰、暮光藍、日出橙與海洋綠四款顏色。

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

配備 6,000mAh 大電量電池及 Qualcomm Snapdragon 662 行動平台,以 11nm 製程的節能處理器,讓 Redmi 9T 在產生更少熱能及更少功耗的情況下,比前幾代帶來更高的效能。此外,MIUI的省電模式及反向有線充電功能,讓使用者能全日或多日使用。而 Redmi 9T 與 Redmi Note 9T 同樣支援 18W 快充,且隨盒一樣附贈 22.5W 充電器。Redmi 9T 支援雙卡及可擴充 microSD 的儲存空間,提供高達 512GB 內建儲存空間,並能彈性擴充,讓用戶可以將喜愛的應用程式、遊戲、照片及影片儲存於同一手機;另外,Redmi 9T 支援紅外線遙控功能,增加操控其他裝置的方便性。

Redmi 9T 配備 4,800 萬像素 AI 四鏡頭,除主鏡頭外,還有 800 萬像素超廣角鏡頭能在無需裁剪的情況下拍攝大型團體照及寬廣的風景照,而 200 萬像素微距鏡頭及 200 萬像素景深鏡頭可拍攝出具專業水平的特寫照片。Redmi 9T 提供全新電影相框功能,在無需編輯的情況下讓照片呈現電影質感。同時,更擁有全新縮時攝影功能,拍攝可設定不同速度和持續時間,在無需使用數位單眼相機或其他專業相機的情況下,拍攝出創意亮眼的縮時照片。

Redmi 9T 提供 4GB+64GB、4GB+128GB 及 6GB+128GB 三種容量選擇,售價分別為 159 歐元(約合新台幣 5,615 元)、189 歐元(約合新台幣 6,670 元)及 199 歐元(約合新台幣 7,025 元)。

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

Pixel 的雷達黑科技傳將被用在新世代 Google 智慧顯示器上_台中搬家

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

曾在 Google Pixel 旗艦機上作為新進手勢感應功能的關鍵感應器 Soli Motion 雷達感測器,來到了最新的 Pixel 5 上,卻疑似因為產品定位而沒有提供,更引起 Google 可能已經放棄這方面發展的猜測。不過現在又有了它可能會透過更多 Nest Home 裝置復活的跡象。繼續閱讀 Pixel 的雷達黑科技傳將被用在新世代 Google 智慧顯示器上報導內文。

▲圖片來源:Google

Pixel 的雷達黑科技傳將被用在新世代 Google 智慧顯示器上

其實在外媒去年訪談 Google 的時候,官方就有表示並沒有放棄 Soli 雷達技術。至於可能的應用方向,當時就有猜測很可能會被用在物聯產品方面 — 後續也真的先被 Nest 溫控器所採納。而隨著支援超音波偵測點亮的 Nest Home mini 推出之後,Google 似乎也打算要將更進階的手勢辨識功能給應用在更多的 Nest 物聯產品之上。

▲圖片來源:Google

相對於將 Soli Motion 感測器用在手機上,進行隔空用手勢切換音樂與關閉通知等用途 — 其實現在的智慧顯示器也已經可以透過攝影機來提供類似的手勢功能了。根據外媒 9to5Google 的消息,Google 2021 年 Smart Display 智慧顯示器,將很可能融入 Soli Motion 的雷達感應帶來更進階的機能,會將其應用在睡眠追蹤記錄功能。

誒… 雖然大家應該心裡第一反應就是「那我睡覺的資料不就讓OOO知道了」。不過與其擔心穿戴裝置是否會在睡眠過程中沒電,還有一定得要持續穿戴的麻煩。說真的,對在意這方面監測的使用者而言,透過隨時對著床並且不用擔心充電問題的 Google Home 物聯裝置,也算是解決了不少的麻煩 — 而且還能放放照片、播播音樂跟詢問問題(比讚)。

▲圖片來源:Google

是說,這也不是 Nest 首度採用 Soli 感應器了。其實在他們的自動溫控產品上,就已經有導入來做為更精確感應居家動靜的用途。講起來,這樣的技術好像在物聯產品上反而更發光發熱,創造出不少不錯的應用呢。感覺可以期待一下新世代 Smart Display 可以因此獲得什麼樣的有趣創新功能。

引用來源

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

延伸閱讀:

蘋果 AirTags 防丟追蹤器還沒出,但已有皮革鑰匙圈與眼鏡環三方配件照流出

沒錯 Facebook 又改版了,差在哪你看得出來嗎?

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

汽機車「聲音照相」科技執法懶人包_台中搬家公司

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

為改善高噪音車輛擾寧問題,環保署推動「聲音照相」科技執法來抓汽、機車噪音車,35套系統預計已經在2021年元旦於18個縣市正式上路。環保署「聲音照相」科技執法首日,各縣市都有斬獲,其中又以新北市首日就告發221台車輛噪音超標戰果輝煌。

環保署「聲音照相」科技執法是什麼? 目前有哪些路段開始實施「聲音照相」科技執法? 以下作一整理與說明:

掌握最新電信資費訊息,請加入小丰子3C俱樂部粉絲頁!

小丰子3C俱樂部

 

針對高噪音車輛擾寧問題,目前採搭配攔查檢測或通知車主到指定地點進行原地噪音量測,但偶有碰到在路上很吵但原地噪音卻合格的狀況加上攔查檢測非常耗人力物力,有鑑於此,環保署特參考超速照相執法模式,推動聲音照相直接開罰措施,並完成「機動車輛行駛噪音量測方法-影像輔助法」、「機動車輛噪音管制標準」及「使用中機動車輛噪音管制辦法」等三項法規修訂工作,在聲音照相都已符合目前檢測標準在精密度和準確度的相關規定下,「聲音照相」科技執法已經在2021年1月1日公告正式上路,違規可處新臺幣1800元至3600元。

 

以下針對環保署「聲音照相」執法的方式與全台執法熱點地段做一整理:

1.環保署「聲音照相」科技執法是什麼?
「聲音照相」是透過噪音照相系統(設備包含噪音計、風速計、魚眼攝影機、車牌辨識攝影機、控制箱體…),來紀錄行經車輛產生的噪音是否超標的系統。只要行經檢測地點產生的噪音超標準,照相機就會「聽音」”喀擦”一聲拍下車牌,改裝機車噪音也能「聽音」拍下車牌。

 

為避免環境噪音影響「聲音照相」執法結果,針對由聲音照相系統紀錄到的噪音超標行為,經背景音修正、比對前後三秒確認排除環境干擾、且氣候為無雨、風速小於5m/s者,作為噪音是否超標的判定標準。

 

若行車產生的噪音超標,各地環保局就會根據噪音管制法第26條以不當操駕進行開罰;若從照片中辨認使用非原廠或認證排氣管等違法改裝者,地方環保局將再要求到檢與限期改善。

 

環保署表示:「聲音照相」執法是從受體保護(民眾)角度出發,以「抓吵」為原則,車輛只要沒有過度不當改裝或刻意製造噪音的操駕行為,不會有超標的疑慮,駕駛人只要配合「順順騎、順順開,油門輕、不怕拍」,並不用擔憂受罰!

 

2.開罰標準:

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

開罰的標準為:速限50km/h路段,不得超過86分貝;速限50~70km/h路段,不得超過90分貝。至於速限70km/h以上路段,大多離人口密集處較遠,噪音影響相對小,目前未納入「聲音拍照」科技執法範圍。只要噪音超標,不當操駕的超標車輛將以《噪音管制法》開罰1800至3600元罰鍰,不當改造車輛也將限期改

 

不過, 執行公務車輛、非機動車輛自身設備產生的聲音及多車同時經過會被排除。

 

3.「聲音照相」執法有哪些路段?

環保署表示目前35套「聲音照相」系統已經在2021年元旦於18個縣市正式上路。「聲音照相」系統分成「固定式高噪音車輛偵測攝影系統」與「移動式聲音照相系統」,前者主要固定架設在民眾陳情熱區之特定路段設置以長期監視取締;後者則採非定時、非定點,於民眾常陳情噪音車輛出沒之熱區,執行機動性聲音照相取締工作。

 

在「聲音照相」執法路段,在架設點前方100公尺至300公尺間,會設置「前有聲音照相 請降低音量行駛」的告示牌,提醒汽機車駕駛。

 

以下是已知各縣市重點取締汽機車噪音的路段與取締熱點,請習慣故意發出吵雜聲音的汽機車主,看好自己的荷包!

Note: 以上資訊純供參考,實際熱點以各地方環保局公告為準

 

整體而言,環保署推動的「聲音照相」科技執法主要是針對一些違法改裝的車主及故意不當操駕的跑車或大型重型機車車主而來,對於正常行駛的汽、機車車主基本上是沒有影響的。有了這項可以取締故意製造噪音的科技,對於經常身受車輛排氣管高分貝的音量打擾的民眾將是一大福音。

 

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

ClickHouse源碼筆記1:聚合函數的實現_網頁設計公司

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

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

由於工作的需求,後續筆者工作需要和開源的OLAP數據庫ClickHouse打交道。ClickHouse是Yandex在2016年6月15日開源了一個分析型數據庫,以強悍的單機處理能力被稱道
筆者在實際測試ClickHouse和閱讀ClickHouse的源碼過程之中,對”戰鬥民族”開發的數據庫十分欣賞。ClickHouse不僅是一個很好的數據庫學習材料,而且同時應用了大量的CPP17的新特性進行開發,也是一個大型的Modern CPP的教導資料。
筆者接下來會陸續將閱讀ClickHouse的部分心得體會與通過源碼閱讀筆記的方式和大家分享,坦白說,這種源碼閱讀筆記很難寫啊。(多一分繁瑣,少一分就模糊了~~)
第一篇文章,我們就從聚合函數的實現開始聊起~~ 上車!

1.基礎知識的梳理

什麼是聚合函數?

聚合函數: 顧名思義就是對一組數據執行聚合計算並返回結果的函數。
這類函數在數據庫之中很常見,如:count, max, min, sum等等。

ClickHouse的實現接口
  • IAggregateFunction接口
    在ClickHouse之中,定義了一個統一的聚合函數接口:IAggregateFunction.(在ClickHouse之中,所有的接口類都是以大寫的I開頭的。) 上文筆者提到的聚合函數,則都是作為抽象類IAggregateFunction的子類實現的。其中該接口最為核心的方法是下面這5個方法:
    • add函數:最為核心的調用接口,將對應AggregateDataPtr指針之中數據取出,與列columns中的第row_num的數據進行對應的聚合計算。(這裏可以看到ClickHouse是一個純粹的列式存儲數據庫,所有的操作都是基於列的數據結構。)
    • merge函數:將兩個聚合結果進行合併的函數,通常用在併發執行聚合函數的過程之中,需要將對應的聚合結果進行合併。
    • serialize函數與deserialize函數:序列化與反序列化的函數,通常用於spill to disk或分佈式場景需要保存或傳輸中間結果的。
    • addBatch函數:這是函數也是非常重要的,雖然它僅僅實現了一個for循環調用add函數。它通過這樣的方式來減少虛函數的調用次數,並且增加了編譯器內聯的概率。(虛函數的調用需要一次訪存指令,一次查表,最終才能定位到需要調用的函數上,這在傳統的火山模型的實現上會帶來極大的CPU開銷。
  /** Adds a value into aggregation data on which place points to.
     *  columns points to columns containing arguments of aggregation function.
     *  row_num is number of row which should be added.
     *  Additional parameter arena should be used instead of standard memory allocator if the addition requires memory allocation.
     */
    virtual void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena) const = 0;

    /// Merges state (on which place points to) with other state of current aggregation function.
    virtual void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const = 0;

    /// Serializes state (to transmit it over the network, for example).
    virtual void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const = 0;

    /// Deserializes state. This function is called only for empty (just created) states.
    virtual void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const = 0;
    // /** Contains a loop with calls to "add" function. You can collect arguments into array "places"
      *  and do a single call to "addBatch" for devirtualization and inlining.
      */
    virtual void addBatch(size_t batch_size, AggregateDataPtr * places, size_t place_offset, const IColumn ** columns, Arena * arena) const = 0;

  • 抽象類IColumn
    上面的接口IAggregateFunction的函數使用到了ClickHouse的核心接口IColumn類,這裏也進行簡要的介紹。 IColumn 接口表達了所有數據在ClickHouse之中的用內存表達的數據結構,其他帶有具體數據類型的如ColumnUInt8、ColumnArray 等, 都實現了對應的列接口,並且在子類之中具象實現了不同的內存布局。
    IColumn的子類實現細節很瑣碎,筆者這裏就暫時不展開講了,筆者這裏就簡單講講涉及到聚合函數調用部分的IColumn接口的對應方法:
    這裏columns是一個二維數組,通過columns[0]可以取到第一列。(這裏只有涉及到一列,為什麼columns是二維數組呢?因為處理array等列的時候,也是通過對應的接口,而array就需要應用二維數組了. )
    注意這裡有一個強制的類型轉換,column已經轉換為ColVecType類型了,這是模板派生出IColumn的子類。
    然後通過IColumn子類實現的getData方法獲取對應row_num行的數據進行add函數調用就完成了一次聚合函數的計算了。
    void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena *) const override
    {
        const auto & column = static_cast<const ColVecType &>(*columns[0]);
        this->data(place).add(column.getData()[row_num]);
    }
  • IAggregateFunctionHelper接口
    這個接口是上面提到 IAggregateFunction的輔助子類接口,它很巧妙的通過模板的類型派生,將虛函數的調用轉換為函數指針的調用,這個在實際聚合函數的實現過程之中能夠大大提高計算的效率。
    函數addFree就實現了我上述所說的過程,但是它是一個private的函數,所以通常我們都是通過getAddressOfAddFunction獲取對應的函數地址。這在聚合查詢的過程之中能夠提高20%左右的執行效率。
template <typename Derived>
class IAggregateFunctionHelper : public IAggregateFunction
{
private:
    static void addFree(const IAggregateFunction * that, AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena)
    {
        static_cast<const Derived &>(*that).add(place, columns, row_num, arena);
    }

public:
    IAggregateFunctionHelper(const DataTypes & argument_types_, const Array & parameters_)
        : IAggregateFunction(argument_types_, parameters_) {}

    AddFunc getAddressOfAddFunction() const override { return &addFree; }
  • AggregateFunctionFactory類
    顧名思義,這個是一個生成聚合函數的工廠類。它的邏輯很簡單,所有ClickHouse之中所相關的聚合函數都是通過這個工廠類註冊並且獲取,然後進行調用的。
class AggregateFunctionFactory final : private boost::noncopyable, public IFactoryWithAliases<AggregateFunctionCreator>
{
public:

    static AggregateFunctionFactory & instance();

    /// Register a function by its name.
    /// No locking, you must register all functions before usage of get.
    void registerFunction(
        const String & name,
        Creator creator,
        CaseSensitiveness case_sensitiveness = CaseSensitive);

    /// Throws an exception if not found.
    AggregateFunctionPtr get(
        const String & name,
        const DataTypes & argument_types,
        const Array & parameters = {},
        int recursion_level = 0) const;

2.聚合函數的註冊流程

有了上述的背景知識,我們接下來舉個栗子。來看看一個聚合函數的實現細節,以及它是如何被使用的。

AggregateFunctionSum

筆者這裏選取了一個很簡單的聚合算子Sum,我們來看看它實現的代碼細節。
這裏我們可以看到AggregateFunctionSum是個final類,無法被繼承了。而它繼承了上面提到的IAggregateFunctionHelp類的子類IAggregateFunctionDataHelper類。

這裏我們就重點看,這個類override了getName方法,返回了對應的名字sum。並且實現了我們上文提到的四個核心的方法。

  • add
  • merge
  • seriable
  • deserialize
template <typename T, typename TResult, typename Data>
class AggregateFunctionSum final : public IAggregateFunctionDataHelper<Data, AggregateFunctionSum<T, TResult, Data>>
{
public:
    using ResultDataType = std::conditional_t<IsDecimalNumber<T>, DataTypeDecimal<TResult>, DataTypeNumber<TResult>>;
    using ColVecType = std::conditional_t<IsDecimalNumber<T>, ColumnDecimal<T>, ColumnVector<T>>;
    using ColVecResult = std::conditional_t<IsDecimalNumber<T>, ColumnDecimal<TResult>, ColumnVector<TResult>>;

    String getName() const override { return "sum"; }

    AggregateFunctionSum(const DataTypes & argument_types_)
        : IAggregateFunctionDataHelper<Data, AggregateFunctionSum<T, TResult, Data>>(argument_types_, {})
        , scale(0)
    {}

    AggregateFunctionSum(const IDataType & data_type, const DataTypes & argument_types_)
        : IAggregateFunctionDataHelper<Data, AggregateFunctionSum<T, TResult, Data>>(argument_types_, {})
        , scale(getDecimalScale(data_type))
    {}

    DataTypePtr getReturnType() const override
    {
        if constexpr (IsDecimalNumber<T>)
            return std::make_shared<ResultDataType>(ResultDataType::maxPrecision(), scale);
        else
            return std::make_shared<ResultDataType>();
    }

    void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena *) const override
    {
        const auto & column = static_cast<const ColVecType &>(*columns[0]);
        this->data(place).add(column.getData()[row_num]);
    }

    void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena *) const override
    {
        this->data(place).merge(this->data(rhs));
    }

    void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override
    {
        this->data(place).write(buf);
    }

    void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena *) const override
    {
        this->data(place).read(buf);
    }

    void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const override
    {
        auto & column = static_cast<ColVecResult &>(to);
        column.getData().push_back(this->data(place).get());
    }

private:
    UInt32 scale;
};

接下來,ClickHouse實現了兩種聚合計算:AggregateFunctionSumDataAggregateFunctionSumKahanData。後者是用Kahan算法避免float類型精度損失的,我們可以暫時不細看。直接看SumData的實現。這是個模板類,之前我們講到AggregateFunction的函數就是通過AggregateDataPtr指針來獲取AggregateFunctionSumData的地址,來調用add實現聚合算子的。我們可以看到AggregateFunctionSumData實現了前文提到的add, merge, write,read四大方法,正好和接口一一對應上了。

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

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

template <typename T>
struct AggregateFunctionSumData
{
    T sum{};

    void add(T value)
    {
        sum += value;
    }

    void merge(const AggregateFunctionSumData & rhs)
    {
        sum += rhs.sum;
    }

    void write(WriteBuffer & buf) const
    {
        writeBinary(sum, buf);
    }

    void read(ReadBuffer & buf)
    {
        readBinary(sum, buf);
    }

    T get() const
    {
        return sum;
    }
};

ClickHouse在Server啟動時。main函數之中會調用registerAggregateFunction的初始化函數註冊所有的聚合函數。
然後調用到下面的函數:

void registerAggregateFunctionSum(AggregateFunctionFactory & factory)
{
    factory.registerFunction("sum", createAggregateFunctionSum<AggregateFunctionSumSimple>, AggregateFunctionFactory::CaseInsensitive);
    factory.registerFunction("sumWithOverflow", createAggregateFunctionSum<AggregateFunctionSumWithOverflow>);
    factory.registerFunction("sumKahan", createAggregateFunctionSum<AggregateFunctionSumKahan>);
}

這裏又調用了 factory.registerFunction("sum", createAggregateFunctionSum<AggregateFunctionSumSimple>, AggregateFunctionFactory::CaseInsensitive);來進行上述我們看到的聚合函數的註冊。這裡有一點很噁心的模板代碼,筆者這裏簡化了一下,把註冊的部分函數拉出來:

createAggregateFunctionSum(const std::string & name, const DataTypes & argument_types, const Array & parameters)
{
    AggregateFunctionPtr res;
    DataTypePtr data_type = argument_types[0];
    if (isDecimal(data_type))
        res.reset(createWithDecimalType<Function>(*data_type, *data_type, argument_types));
    else
        res.reset(createWithNumericType<Function>(*data_type, argument_types));
    return res;

這裏的Function模板就是上面的AggregateFunctionSumSimple, 而它又是下面的模板類型:

template <typename T> using AggregateFunctionSumSimple = typename SumSimple<T>::Function;

template <typename T>
struct SumSimple
{
    /// @note It uses slow Decimal128 (cause we need such a variant). sumWithOverflow is faster for Decimal32/64
    using ResultType = std::conditional_t<IsDecimalNumber<T>, Decimal128, NearestFieldType<T>>;
    using AggregateDataType = AggregateFunctionSumData<ResultType>;
    using Function = AggregateFunctionSum<T, ResultType, AggregateDataType>;
};

不知道讀者被繞暈了沒,最終繞回來還是new出來這個AggregateFunctionSum<T, ResultType, AggregateDataType>
也就是完成了這個求和算子的註冊,後續我們get出來就可以愉快的調用啦。(這裏這部分的模板變化比較複雜,如果看不明白可以回到源碼梳理一下~~~)

3. 小結

好了,關於聚合函數的基礎信息,和它是如何實現並且通過工廠方法註冊獲取的流程算是搞明白了。
關於其他的聚合算子,也是大同小異的方式。筆者就不再贅述了,感興趣的可以回到源碼之中繼續一探究竟。講完了聚合函數的實現,下一篇筆者就要繼續給探究聚合函數究竟在ClickHouse之中是如何和列存結合使用,並實現向量化的~~。
筆者是一個ClickHouse的初學者,對ClickHouse有興趣的同學,也歡迎和筆者多多指教,交流。

4. 參考資料

官方文檔
ClickHouse源代碼

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

最佳實踐之“全身而退”_如何寫文案

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

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。

全身而退,是一種境界,更是一種智慧!編程,特別是框架設計,需要的就是哲學思維。“全身而退”這四個字,還在當年的內存泄露的踩坑過程中就悟出來了,有圖有真相:

    

有人會問,內存泄露和這個“全身而退”好像搭不上邊?從某些角度看也是,要不我不會單獨放在這裏講。但是也有一點關係,內存泄露不就是內存有分配沒有釋放,何時釋放,一般是程序退出之時。當年作為踩坑專業戶,踩過的坑遠遠不止前面有關“踩坑系列”系列文章提到的,可謂五花八門,也許可以套用一句話:“你們看到過的官場小說遠沒有我經歷過的精彩”:)大家可以看前面一系列踩坑經歷的總結。 

當時踩過很多崩潰的坑,時不時的崩潰,我形容我當時是救火隊長。而當時經過不少的踩坑分析有一個共同的現象,竟然都是退出的時候,就莫名其妙崩潰了。然後這個問題其實也容易重現,你不停的啟動和退出就行了,至少百分之五十的概率。只是因為運行的時間不同,退出崩潰的堆棧還不一樣。

正是這樣子,我們總結開玩笑說,就是經不起折騰。也造就了後面,做項目時,寫完一個類,一定會測試退出。整個系統分不同的時間點反覆重啟、反覆插拔網線。基本達到着魔的程度,不是?原來我控制欲這麼強:)事實也證明了,經過這樣反覆重啟的折騰,新產品落地投入市場,基本沒有這些亂七八糟的問題了!踩坑經驗(二)有截圖作證:

     

很久才會出現一個Bug,而且也可以輕鬆搞定。一般都是新加的功能,或者很特別的情況沒考慮到。

所以,也通過真實的項目驗證了,全身而退的重要性。當然現在和當年的心境也完全不一樣了!事實上,後面經過分佈式洗禮,我的這種方法是正確的。分佈式就是解決這種單點故障。當然分佈式單點故障和單機故障,是不一樣的概念或者說屬性。分佈式中是考慮不可抗拒的原因,集群中單節點的穩定性肯定是集群穩定性的基礎。只是這種測試方法可以用來測試分佈式的故障切換,甚至是故障恢復!集群考慮的是集群的全身而退,事實上,我們在測試過程中也出現過集群整體失效的時候。

那麼我們可以從哪些方面去做到全身而退呢?下面我從實踐經驗中總結以下幾點。 

一、單實例的使用

1.方式一

    

說到單實例,估計絕大部分人都說很簡單。如上的例子,一般的人會說沒問題。從邏輯來說確實沒問題,很簡單,不是嗎?實現類更簡單:

    

就一句話,靜態變量再申明一下。你是不是這樣實現的呢? 這是我們重構的新平台上,依然有這樣使用的。以前的平台那就更多了,基本上是全部。導致的結果就是上面描述的崩潰就是家常便飯。當時在部門範圍內做培訓的時候,我把這個作為考題,結果能指出問題點的少之又少。

2.方式二

後面的修改:

    

這是定義,我們再看看具體實現:

     

平時我對代碼是追求簡潔的,但是在這裏我反而增加了函數,是不是看上去複雜了?No!簡潔必須建立在遵循原理的基礎之上。那為什麼要這麼改,遵循的原理是什麼?

最基本的原理就是單實例首先是一個對象,而且是類的對象,不是簡單變量。那麼類就可能涉及深拷貝,涉及到new和delete,比基礎變量要複雜得多。如果像第一種方式,那麼就可能出現編譯期間開始進行內存分配等操作,而退出的時候也在等着編譯器系統的自動回收。在這種啟動和退出之時非常容易操作已經不存在的對象和內存,而出現了崩潰。

其實,第一種方式使用的是靜態變量,可返回的是引用。從內存的布局而言,屬於系統(內核)管理,無法做到應用層的控制,面對複雜情況的內存使用很難做到全身而退。關於程序的內存布局和分配可以查閱虛擬技術和內存泄露兩篇文檔。

所以基於以上兩點,將單實例定義為類的私有變量,指針形式。那麼只有在程序啟動后要獲取的時候才開始構建類對象,完全可以掌控在自己(程序的用戶態)的手中,而且可以非常靈活的加入調試代碼,比如捕獲異常等。在程序退出之時,亦可以很方便很合理的選擇何時釋放和銷毀對象。

當然,一般編碼規範的公司就會直接用第二種方式,其他人使用的時候直接copy改改就行。同樣的如果是“先輩們”使用了第一種,錯誤也會傳承下去。。。而且大多時候錯誤的東西還很難糾正過來,就說第一種方式竟然被發現在代碼重構后!

所以,編程的時候我們需要有懷疑的精神、反思的精神和專業的引導

二、架構設計

1.模塊與接口

模塊和層級清晰,接口簡單易用。都有利於全身而退。我們新版的存儲系統中,核心業務有三塊,IO+rewrite,而僅僅只有一個寫模塊I和rewrite模塊之間的接口!讀模塊O和其他都是完全分開。

2.類的五大函數

你聽說的都是類的三大函數吧,我這裏咋變成了五大函數,而且我的五大不是包括原來的三大。

2.1 構造函數和析構函數

只是單純的對成員變量進行賦值。

2.2 初始化函數和反初始化函數

例如內存的分配和釋放,多線程的創建啟動和停止。都應該放在這裏面,而不是上面提到的構造和析構中。在內存泄露中也提到過。其實只有一個目的,好控制!

2.3 複位函數

有異常情況下,不是被動退出,而是能夠主動控制,最壞的情況是複位。將各種變量、狀態、內存和多線程恢復到初始狀態。有的人可能會說,那怎麼不直接用初始化函數呢?這兩種功能還是不一樣的。初始化是系統啟動的時候用的。而複位可能承載更多的判斷,例如打印一些重要變量的信息,以利於開發人員跟蹤異常情況。

※別再煩惱如何寫文案,掌握八大原則!

什麼是銷售文案服務?A就是幫你撰寫適合的廣告文案。當您需要販售商品、宣傳活動、建立個人品牌,撰寫廣告文案都是必須的工作。

五大函數是我建立一個類最基本的模板,也是做到全身而退基本的保障

一個類要盡量做到職責單一,其實一個函數也是類似的。例如說初始化函數不能省,很多人將初始化和構造放一起,或者複位和初始化放一起。

析構函數只是簡單的初始化變量而已,而例如內存的分配、線程和定時器的啟動最好都到初始化函數中;

假如採用單例模式,則只需用到構造函數,像上面提到的單實例模式用的不好,如果構造函數中包括太多內存和多線程的使用,對退出將是一個災難。

而從另一個角度,虛擬內存的原理,內存只有分配和使用的時候才開始產生缺頁中斷即才開始尋找物理內存。所以何時初始化,何時使用內存可以掌握在開發人員的手中,而不是依賴系統來控制。

所以我認為這也是C++相比JAVA來說更好的優勢,可能有的人認為C++就是很難控制,像內存、指針、繼承,而且還沒有GC機制等等。而我認為剛剛相反,例如指針就是一把雙刃劍,你懂它,它就威力無窮!你看很多開源庫,比如經典的MySQL、高效的Redis、一直很火的Nginx,是吧,指針發揮了太大太大的作用,你能說這些庫不穩定嗎?雖然說我也一直關注和學習Java和Spring,主要學習它們的抽象思想!

3.鎖的設計

細心的你可以發現,上面單實例使用的第二種方式,加了一把鎖。我一般的類設計中,鎖的數量是2~3,大多情況下是2。單實例模式使用一把鎖,狀態機用一把鎖。在死鎖的踩坑經驗總結中曾經提到過,鎖的使用個數少,範圍少。所以你可以回頭看看狀態機和單實例模式鎖的使用。該加的時候一定加鎖,該不要加的時候一定不能加,不能模糊。

鎖或者說多線程同步使用不當,是很容易出問題的,不僅是運行過程中可能出現死鎖,退出的時候也可能出現,可能的情況就是退出失敗,同樣需要手動進行殺進程。

4.時序

前面的函數、接口、類、內存、多線程與鎖都設計好了,對於全身而退,或許已經成功了一半。還有一半在哪裡,時序。就是將這些對象和接口如何串起來,如果做到有序最常見的是退出和啟動的順序即時序是相反的,這是基本的要求,而對於複雜的情況需要分兩方面來討論。

一方面,每個類、每個模塊都要做到全身而退,即每個類和模塊都需要有序的退出。所以上面類的設計,五大函數最終是為了全身而退,為了有序的釋放內存、停止線程等。我們上文提到的有限狀態機最終也是為了更好的控制線程、為了線程的退出,鎖的設計同樣可以減少干擾提高效率。所以系統的全身而退主要依賴於每個類、每個模塊的全身而退

另一方面,程序最後的退出其實也是要封裝和集中在一兩個函數中,不能太散。即層級關係要非常明確,類似於樹的結構,最後集中到根節點,也就是一個函數。所以層級設計的目的也應該是越到外層越好控制

以上提到的幾點,包括時序,包括單實例模式,也包括最近一系列踩坑經歷,我最後都會歸納到框架或設計,所以也可以說這一切都是為了最終的全身而退

總之,能隨時隨地的全身而退的代碼是一種境界,經得起各種折騰的代碼才是好代碼,經得起各種折騰的設計才是好的設計!

  

最後,萬事萬物的發展都離不開一個簡單的道理,例如讀一本書: 

從簡單,到複雜,最後到簡單。但這兩個簡單的心境完全不一樣,前者我認為更多的是無知者無畏,後者則是一切瞭然於心。 

 

計算機最終歸結於二進制的運算,最終就是01的組合,組合成各種數據結構和協議,組合成各種調度和算法,等等。

而對應於中國智慧,就是道,抓住事物的本質,以不變應萬變,陰陽論,太極八卦論;對應於中國的萬事開頭難,和全身而退!

也可以對應軟件兩大難點,就是原理的把握和框架的設計。

有了對原理的理解,能抓住事物的本質,就會下筆如有神助,也能最終全身而退!

當然你要達到這一點,得不停的”悟”!

悟的速度依賴”實踐+思考”!

 

肯定不能只依賴CRUD,也不能僅僅依賴改Bug的多少、僅僅依賴參与項目的多少。

有的改10個Bug比不上別人一個Bug,有的參与了10個項目不如比別人開發一個項目。

還是那句話,如果不讀書,行萬里路,也只是一個郵差。

所以,我認為多去“實踐+思考”。

 

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

※教你寫出一流的銷售文案?

銷售文案是什麼?A文案是廣告用的文字。舉凡任何宣傳、行銷、販賣商品時所用到的文字都是文案。在網路時代,文案成為行銷中最重要的宣傳方式,好的文案可節省大量宣傳資源,達成行銷目的。

如何在Spring Boot應用啟動之後立刻執行一段邏輯_網頁設計公司

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

1. 前言

不知道你有沒有接到這種需求,項目啟動后立馬執行一些邏輯。比如簡單的緩存預熱,或者上線后的廣播之類等等。如果你使用 Spring Boot 框架的話就可以藉助其提供的接口CommandLineRunnerApplicationRunner來實現。

2. CommandLineRunner

org.springframework.boot.CommandLineRunnerSpring Boot提供的一個接口,當你實現該接口並將之注入Spring IoC容器后,Spring Boot應用啟動后就會執行其run方法。一個Spring Boot可以存在多個CommandLineRunner的實現,當存在多個時,你可以實現Ordered接口控制這些實現的執行順序(Order 數值越大優先級越低)。接下來我們來聲明兩個實現並指定順序:

優先執行:

package cn.felord;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

/**
 * 優先級最高
 * 該類期望在springboot 啟動后第一順位執行
 * @author felord.cn
 * @since 12:57
 **/
@Slf4j
@Component
public class HighOrderCommandLineRunner implements CommandLineRunner, Ordered {
    @Override
    public void run(String... args) throws Exception {
        for (String arg : args) {
            log.info("arg = " + arg);
        }
        log.info("i am highOrderRunner");
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE+1;
    }
}

第二順序執行:

package cn.felord;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

/**
 * 優先級低於{@code HighOrderCommandLineRunner}
 * @author felord.cn
 * @since 12:59
 **/
@Slf4j
@Component
public class LowOrderCommandLineRunner implements CommandLineRunner, Ordered {

    @Override
    public void run(String... args) throws Exception {
        log.info("i am lowOrderRunner");
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE+1;
    }
}

然後啟動Spring Boot應用后,控制台按照預定的順序打印出了結果:

2020-05-30 23:11:03.685  INFO 11976 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-05-30 23:11:03.701  INFO 11976 --- [           main] c.f.Application  : Started SpringBootApplication in 4.272 seconds (JVM running for 6.316)
2020-05-30 23:11:03.706  INFO 11976 --- [           main] c.f.HighOrderCommandLineRunner   : i am highOrderRunner
2020-05-30 23:11:03.706  INFO 11976 --- [           main] c.f.LowOrderCommandLineRunner   : i am lowOrderRunner

3. ApplicationRunner

Spring Boot 1.3.0又引入了一個和CommandLineRunner功能一樣的接口ApplicationRunnerCommandLineRunner接收可變參數String... args,而ApplicationRunner 接收一個封裝好的對象參數ApplicationArguments。除此之外它們功能完全一樣,甚至連方法名都一樣。 聲明一個ApplicationRunner並讓它優先級最低:

package cn.felord;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * 優先級最低
 * @author felord.cn
 * @since 13:00
 **/
@Slf4j
@Component
public class DefaultApplicationRunner implements ApplicationRunner, Ordered {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("i am applicationRunner");
        Set<String> optionNames = args.getOptionNames();
        log.info("optionNames = " + optionNames);
        String[] sourceArgs = args.getSourceArgs();
        log.info("sourceArgs = " + Arrays.toString(sourceArgs));
        List<String> nonOptionArgs = args.getNonOptionArgs();
        log.info("nonOptionArgs = " + nonOptionArgs);
        List<String> optionValues = args.getOptionValues("foo");
        log.info("optionValues = " + optionValues);
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE+2;
    }
}

按照順序打印了三個類的執行結果:

2020-06-01 13:02:39.420  INFO 19032 --- [           main] c.f.MybatisResultmapApplication  : Started MybatisResultmapApplication in 1.801 seconds (JVM running for 2.266)
2020-06-01 13:02:39.423  INFO 19032 --- [           main] c.f.HighOrderCommandLineRunner   : i am highOrderRunner
2020-06-01 13:02:39.423  INFO 19032 --- [           main] c.f.LowOrderCommandLineRunner    : i am lowOrderRunner
2020-06-01 13:02:39.423  INFO 19032 --- [           main] c.f.DefaultApplicationRunner     : i am applicationRunner
2020-06-01 13:02:39.423  INFO 19032 --- [           main] c.f.DefaultApplicationRunner     : optionNames = []
2020-06-01 13:02:39.423  INFO 19032 --- [           main] c.f.DefaultApplicationRunner     : sourceArgs = []
2020-06-01 13:02:39.423  INFO 19032 --- [           main] c.f.DefaultApplicationRunner     : nonOptionArgs = []
2020-06-01 13:02:39.423  INFO 19032 --- [           main] c.f.DefaultApplicationRunner     : optionValues = null

Ordered接口並不能被 @Order註解所代替。

4. 傳遞參數

相信很多同學看到這裏都開始對這兩個run方法的入參感興趣了。Spring Boot應用啟動時是可以接受參數的,換句話說也就是Spring Bootmain方法是可以接受參數的。這些參數通過命令行 java -jar yourapp.jar 來傳遞。CommandLineRunner會原封不動照單全收這些接口,這些參數也可以封裝到ApplicationArguments對象中供ApplicationRunner調用。 我們來認識一下ApplicationArguments的相關方法:

  • getSourceArgs() 被傳遞給應用程序的原始參數,返回這些參數的字符串數組。

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

  • getOptionNames() 獲取選項名稱的Set字符串集合。如 --spring.profiles.active=dev --debug 將返回["spring.profiles.active","debug"]

  • getOptionValues(String name) 通過名稱來獲取該名稱對應的選項值。如--foo=bar --foo=baz 將返回["bar","baz"]

  • containsOption(String name) 用來判斷是否包含某個選項的名稱。

  • getNonOptionArgs() 用來獲取所有的無選項參數。

    接下來我們試驗一波,你可以通過下面的命令運行一個 Spring Boot應用 Jar

java -jar yourapp.jar --foo=bar --foo=baz --dev.name=碼農小胖哥 java felordcn

或者在IDEA開發工具中打開Spring Boot應用main方法的配置項,進行如下配置,其他IDE工具同理。

運行Spring Boot應用,將會打印出:

2020-06-01 15:04:31.490  INFO 13208 --- [           main] c.f.HighOrderCommandLineRunner   : arg = --foo=bar
2020-06-01 15:04:31.490  INFO 13208 --- [           main] c.f.HighOrderCommandLineRunner   : arg = --foo=baz
2020-06-01 15:04:31.490  INFO 13208 --- [           main] c.f.HighOrderCommandLineRunner   : arg = --dev.name=碼農小胖哥
2020-06-01 15:04:31.490  INFO 13208 --- [           main] c.f.HighOrderCommandLineRunner   : arg = java
2020-06-01 15:04:31.490  INFO 13208 --- [           main] c.f.HighOrderCommandLineRunner   : arg = felordcn
2020-06-01 15:04:31.491  INFO 13208 --- [           main] c.f.HighOrderCommandLineRunner   : i am highOrderRunner
2020-06-01 15:04:31.491  INFO 13208 --- [           main] c.f.LowOrderCommandLineRunner    : i am lowOrderRunner
2020-06-01 15:04:31.491  INFO 13208 --- [           main] c.f.DefaultApplicationRunner     : i am applicationRunner
2020-06-01 15:04:31.491  INFO 13208 --- [           main] c.f.DefaultApplicationRunner     : optionNames = [dev.name, foo]
2020-06-01 15:04:31.491  INFO 13208 --- [           main] c.f.DefaultApplicationRunner     : sourceArgs = [--foo=bar, --foo=baz, --dev.name=碼農小胖哥, java, felordcn]
2020-06-01 15:04:31.491  INFO 13208 --- [           main] c.f.DefaultApplicationRunner     : nonOptionArgs = [java, felordcn]
2020-06-01 15:04:31.491  INFO 13208 --- [           main] c.f.DefaultApplicationRunner     : optionValues = [bar, baz]

然後你就可以根據實際需要動態地執行一些邏輯。

5. 總結

今天我們對CommandLineRunnerApplicationRunner進行了講解,從用法到順序執行,又對Spring Boot傳遞參數進行了介紹和演示,希望對你有所幫助。多多關注:碼農小胖哥,更多編程乾貨分享給你。

關注公眾號:Felordcn 獲取更多資訊

個人博客:https://felord.cn

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

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

搭建Prometheus平台,你必須考慮的6個因素_網頁設計

※推薦評價好的iphone維修中心

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

作者簡介

Loris Degioanni,Sysdig的創始人和CTO,同時還是容器安全工具Falco的創建者。

原文鏈接
https://thenewstack.io/6-things-to-consider-in-a-prometheus-monitoring-platform/

本文轉自Rancher Labs

當前,Prometheus被許多企業和組織廣泛使用,以監控其容器和微服務。但是在這一過程中,大型公司通常會陷入困境:當應用程序數量越來越多的時候,擴展監控指標則是一個十分重大的挑戰。

不斷增長的容器使情況複雜化

相對來說,監控單體環境常常更簡單,因為靜態物理服務器和虛擬機數量是確定的,並且監控指標的數量也是有限的。但是,如今由於容器以及需要向微服務架構遷移,要跟蹤監控的實例程序數量激增。

如果說位於數據中心的服務器是寵物,需要我們不斷關注的話,那麼雲實例則更像牛(因為有很多,你不必關心單個實例),而容器則更像小蜜蜂。它們數量很多,有時每台機器有數百個容器,並且新的容器一直不斷出現,當與諸如Kubernetes的容器編排引擎一起使用時,它們的壽命可能非常短。這使得跟蹤監控它們變得更加困難,而且如果你不小心誤操作的話,它們可能會造成很多損害。

隨着複雜性和分佈式環境的增加,你需要監控的實體數量也在增加。此外,你可能希望監控更多屬性以確保你對正在發生的事情有準確的了解,或者在進行故障排除或事件響應的情況下,可以了解正在發生的事情。在短暫的環境中,後者尤其成問題,因為當你想了解問題的根本原因時,通常相關的資源已經停用,這意味着監控解決方案必須提供一種能夠存儲足夠的歷史記錄以進行取證的方法。

流行的監控工具:Prometheus

越來越多需要雲監控的團隊正在轉向Prometheus,這是一個開源的CNCF項目。Prometheus已成為開發人員用來在雲原生環境中收集和理解指標的首選監控工具。它由一個大型社區支持,有來自700多家公司的6300個貢獻者,有13500個代碼提交和7200個拉取請求。

默認情況下,典型的雲原生應用程序堆棧(如Kubernetes、Ngnix、MongoDB、Kafka、golang等)會暴露Prometheus指標。Prometheus是一個可以垂直彈性伸縮的Go程序,為單個容器或單個主機部署它時十分容易。換言之,一開始使用Prometheus極為容易,你可以輕鬆監控你的第一個Kubernetes集群,但是這也意味着隨着基礎架構的增長,監控會越來越複雜。

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

應用程序增長帶來的擴展問題

隨着環境規模增長,你需要跟蹤監控飛速增長的時間序列數據,並且在數據量達到某個點之後,單個Prometheus實例無法繼續跟蹤監控。這一情況下,最直接的選擇是在整個企業中運行一組Prometheus服務器,但這帶來了一些挑戰。例如,跨數十甚至數百台Prometheus服務器管理和合併數據並不容易。同樣,了解企業工作流程、單點登錄、基於角色的訪問控制以及遵守SLA或合規性也不是容易的問題。隨着應用程序的增長,在不中斷開發人員工作的情況下運行一個全方位的監控解決方案,這將成為一個可管理性和可靠性的問題。

為了解決這一問題,企業採用了許多方法。

簡單的方法是為每個命名空間或每個集群都準備一個單獨的Prometheus服務器。這種方法到一定規模就會難以為繼,此外,它還有一個缺點,那就是會造成大量的斷開的數據孤島。這會使故障排查變得很麻煩,因為大多數問題會跨越多個服務/團隊/集群。不但在每個環境中很難找到相同的指標,你還需要把數據拼接在一起,以試圖了解發生了什麼。

另一個常見方法是使用類似Cortex或Thanos的開源工具來集合多個Prometheus服務器。這些高效的工具可以讓你集中查詢服務器、收集數據然後在統一的dashboard中共享。然而,與任何數據密集型分佈式系統一樣,它們需要大量的技能和資源才能運行。

需要考慮的6個因素

對於那些以Prometheus為起點,然後尋求商業化解決方案以獲得全局監控的公司來說,重要的是,不丟失Prometheus上完成的所有標準化開發工作——dashboard、告警、exporter等。然而,這不是需要考慮的唯一事情,如果你繼續使用Prometheus,需要堅持以下標準:

1、 兼容性,以支持所有Prometheus功能

你的供應商/所使用的工具/SaaS解決方案需要能夠使用任何可產生Prometheus指標的實體程序中消耗數據,無論是本地Kubernetes還是雲服務。相對來說,消耗Prometheus指標微不足道,但是也不要忽略一些小事情,例如將指標提取到存儲中或增加數據時能夠重新標註指標,這樣對你的環境更有意義。這些小事加起來,能夠收集到的數據將會堆積如山、大不相同。

2、 PromQL兼容性

Prometheus查詢語言由Prometheus創建者發明,用於提取存儲在Prometheus中的信息。PromQL能讓你查詢指定服務或指定用戶的指標,它還能匯總或細分數據。例如,你可以使用它显示所有容器中每個應用的CPU使用率。或者僅显示Cassandra容器的數據,並將其显示為每個集群的單個值。可以說,PromQL釋放了Prometheus的真正價值,因此如果將Prometheus的指標集成到一個不完全支持PromQL的產品中,就完全違背了使用Prometheus的初衷。

3、 支持熱插拔

要真正與Prometheus兼容,該解決方案必須能夠支持熱插拔,以便能夠與你現有的dashboard、告警和腳本一起使用。例如,許多使用Prometheus的企業都將Grafana用於dashboard。這個開源工具能夠與Prometheus很好地集成在一起,包括在查詢級別,並且可以用於生成一系列有用的圖表和dashboard。因此,聲稱與Prometheus兼容的商業產品應與Grafana等工具兼容。僅僅說解決方案可以讓你在Grafana中查看数字是遠遠不夠的,你需要能夠按照原樣提取現有的Grafana dashboard,並將它們重新應用於商業解決方案中已安裝的數據。

4、 訪問控制

在評估工具時,訪問控制是另一個你需要考慮的安全問題。能夠使用行業標準協議(包括LDAP、Google Oauth、SAML和OpenID)保護用戶身份驗證,使公司能夠通過基於服務的訪問控制來隔離和保護資源。

5、 故障排查

Kubernetes簡化了部署、彈性伸縮和管理容器化應用程序和微服務。這有助於保持服務的正常運行,但是要識別和解決諸如性能降低、部署失敗和連接錯誤之類的根本問題,你需要能夠從整個環境中收集和可視化基礎架構、應用程序和性能數據。由於無法同時訪問實時信息和上下文數據,因此幾乎不可能關聯環境中的指標,所以你可以更快地解決問題。

6、 與現有告警兼容

最後,如果你正在尋找商業解決方案來幫助解決Prometheus可擴展性問題,請確保它支持所有級別的告警。能夠實現這一目標的關鍵是全面支持Alert Manager功能,而Alert Manager還要求100%的集成和 PromQL兼容性。

如果你找到一個能夠滿足以上標準的商業化工具,你應該能夠輕鬆將其集成到現有的Prometheus中,並且能夠避免公司遇到的可擴展性問題。開發人員有充分的理由喜愛Prometheus,因此在採用商業化方案之前進行全面、盡職的調查將確保他們仍然可以使用自己喜歡的指標。

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

網頁設計最專業,超強功能平台可客製化

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。