日產燃料電池車進化,結合生質能每趟可跑800公里

日產汽車(Nissan)日前發表新氫燃料電池車(FCV)技術,結合生質能燃料的「e-Bio Fuel-Cell」。充填燃料後,每趟最高可行駛800公里,較日產現行電動車Leaf的280公里續航力高上三倍。

e-Bio Fuel-Cell 以甘蔗、玉米等植物所生產的生物乙醇做為燃料,將生物乙醇透過改質器分解出氫和二氧化碳,再用電池堆進行氫氧化學反應並產生電力。由於生質能原料在成長過程中會吸收二氧化碳,因此整套能量循環是碳中和的。

最特別的是,e-Bio Fuel-Cell採用固態氧化物燃料電池(SOFC)做為車用電池堆,充填一次燃料最高可行駛800公里,每公里行駛費用估計僅約3.1日圓,與電動車的2.9日圓相差不多、而遠低於汽油車的9日圓。

日產已開始測試新款FCV,計畫率先將e-Bio Fuel-Cell技術應用於宅配等領域的商用車輛上,預計在2020年投入市場。屆時希望將開賣時的車輛售價壓低到一般電動車的水準。

《日經》指出,未來新能源汽車的版圖主要將分為以特斯拉(Tesla)為首的純電動車,以及以豐田、本田為主的FCV汽車等兩大陣營。豐田汽車已推出燃料電池車Mirai,本田也推出了Clarity Fuel Cell,皆以高純度氫為燃料。

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

【其他文章推薦】

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

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

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

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

高性能Web動畫和渲染原理系列(4)“Compositor-Pipeline演講PPT”學習摘要

目錄

示例代碼託管在:

博客園地址:

華為雲社區地址:

附件PPT來自開發文檔。術語里的cc指的是Chromium Compositor

一直以來都想了解瀏覽器合成層的運作機制,但是相關的中文資料大多比較關注框架和開發技術,這方面的資料實在是太少了,後來在chromium官方網站的文檔里找到了項目組成員malaykeshav在 2019年4月的一份關於瀏覽器合成流水線的演講PPT,個人感覺裏面講的非常清楚了,由於沒有找到視頻,有些部分只能自行理解,本文僅對關鍵信息做一些筆記,對此感興趣的讀者可以在文章開頭的github倉庫或附件中拿到這個PPT自行學習。

摘要

1.合成流水線

合成流水線,就是指瀏覽器處理合成層的工作流程,其基本步驟如下:

大致的流程就是說Paint環節會生成一個列表,列表裡登記了頁面元素的繪製指令,接着這個列表需要經過Raster光柵化處理,並在合成幀中處理紋理,最後的Draw環節才是將這些紋理圖展示在瀏覽器內容區。

2. 預定義UI層

chromium中預定義了一些指定類型的UI層,大致分為:

  • Not Drawn – 為了處理透明度或濾鏡效果、transform變形或者clip剪裁的非繪製層
  • Solid color layer – 固有顏色層
  • Painted texture layer – Texture紋理會在這個層執行paint渲染和後續的rasterized光柵化任務
  • Transferable resource layer – 共享資源層,可能是GPU裏面的Texture紋理也可能未來會發給GPU的位圖
  • Surface layer – 臨時佔位層,因為自頂向下遍歷layer樹時子樹都還沒處理,需要先佔位最後再填充
  • Nine patch layer – 用於實現陰影的層

3. paint是什麼意思

每個層layer是由若干個views組成的,所謂paint,就是每個views將自己對應圖形的繪製指令添加到層的可展示元素列表Display Item List里,這個列表會被添加到一個延遲執行的光柵化任務中,並最終生成當前層的texture紋理(可以理解為當前層的繪製結果),考慮到傳輸性能以及未來增量更新的需求,光柵化的結果會以tiles瓦片形式保存。在chrome中也可以看到頁面瓦片化拆分的結果:

4. 分層的優勢和劣勢

分層的優勢和劣勢也在此進行了說明,和之前我們主動思考的答案基本一致(暗爽一下)。

5. 視圖屬性及其處理方式

views中支持的屬性包含Clip剪裁,transform變換,effect效果(如半透明或濾鏡等),mask遮罩,通常按照後序遍歷的方式自底向上進行遍歷處理。

clip剪裁的處理方式是在父節點和子節點之間插入一個剪裁層,用來將其子樹的渲染結果剪裁到限定的範圍內,然後再向上與父級進行合併;

transform變換直接作用於父節點,處理到這個節點時其子樹都已經處理完畢,直接將整體應用變形即可;

effect效果一般直接作用於當前處理的節點,有時也會產生交叉依賴的場景;

PPT第40頁中在介紹effect效果處理時描述了兩種不同的透明度處理需求,從而引出了一個Render Surface的概念,它相當於一個臨時的層,它的子樹需要先繪製在這個層上,然後再向上與父節點進行合併,屏幕就是是根級的Render Surface

6. Quads

Layer遍歷處理輸出的結果被稱為Quads(從意思上理解好像就是指輸出了很多個矩形方塊),每個quad都持有它被繪製到目標緩衝區所需要的資源,根據它持有的資源不同可以分為:

  • Solid Color-固定顏色型
  • Texture– 紋理型
  • Tile– 瓦片型
  • Surface– 臨時繪圖表面型
  • Video – 視頻幀型
  • Render PassRender Surface類型的佔位區,Render Surface子樹處理完后填充到關聯的Render Pass

7. Compositor Frame

合成層真正的工作要開始了,主角概念Compositor Frame(合成幀)登場,它負責將quads合併繪製在一起,膠片里59-62頁非常清楚地展示了合成的過程,最終輸出的結果就是根節點的紋理。

chromium是多進程架構,Browser Process瀏覽器進程會對菜單欄等等容器部分的畫面生成合成幀來輸出,每個網頁的Render Process渲染進程會對頁面內容生成合成幀來輸出,最終的結果都被共享給GPU ProcessGPU進程進行聚合併生成最終完整的合成表面,接着在Display Compositor環節將最後的位圖展示在屏幕上。

8. 關於光柵化以及渲染方式

膠片里並沒有描述具體的光柵化的處理過程,但是layer輸出的quads看起來應該是光柵化以後的結果,推測應該是處理Display Item List中的繪圖指令時也和WebGL類似,經過頂點着色器片元着色器的遍歷式處理機制,並在過程中自動完成像素插值。

9.【重要】軟件渲染和硬件渲染的區別

聲明:本節內容是個人理解,僅用作技術交流,不保證對!

軟件渲染和硬件渲染的區別對筆者而言一直非常抽象,只是知道基本概念。後來在(國內可能無法訪問)中《Compositor Thread Architecture》這篇合成器線程架構的文章中找到了一些相關描述,也解開了筆者心中一直以來的疑惑,相關部分摘抄如下:

Texture Upload

One challenge with all these textures is that we rasterize them on the main thread of the renderer process, but need to actually get them into the GPU memory. This requires handing information about these textures (and their contents) to the impl thread, then to the GPU process, and once there, into the GL/D3D driver. Done naively, this causes us to copy a single texture over and over again, something we definitely don’t want to do.

We have two tricks that we use right now to make this a bit faster. To understand them, an aside on “painting” versus “rasterization.”

  • Painting is the word we use for telling webkit to dump a part of its RenderObject tree to a GraphicsContext. We can pass the painting routine a GraphicsContext implementation that executes the commands as it receives them, or we can pass it a recording context that simply writes down the commands as it receives them.
  • Rasterization is the word we use for actually executing graphics context commands. We typically execute the rasterization commands with the CPU (software rendering) but could also execute them directly with the GPU using Ganesh.
  • Upload: this is us actually taking the contents of a rasterized bitmap in main memory and sending it to the GPU as a texture.With these definitions in mind, we deal with texture upload with the following tricks:
  • Per-tile painting: we pass WebKit paint a recording context that simply records the GraphicsContext operations into an SkPicture data structure. We can then rasterize several texture tiles from that one picture.
  • SHM upload: instead of rasterizing into a void* from the renderer heap, we allocate a shared memory buffer and upload into that instead. The GPU process then issues its glTex* operations using that shared memory, avoiding one texture copy.The holy grail of texture upload is “zero copy” upload. With such a scheme, we manage to get a raw pointer inside the renderer process’ sandbox to GPU memory, which we software-rasterize directly into. We can’t yet do this anywhere, but it is something we fantasize about.

大概翻譯一下,方便英語水平一般的小夥伴理解,GPU處理圖片的方式是按照Texture進行貼圖的,對此不熟悉的小夥伴可以查看筆者以前發的有關Three.js相關的博文。

紋理上傳:
處理紋理的挑戰之一就是它是在渲染進程(可以理解為單個Tab網頁的進程)的主線程里進行的,但是最終需要將其放入GPU內存。這就需要將紋理數據遞交給合成器線程,然後再交給GPU進程(Chromium架構里有專門的GPU進程用來專門處理和GPU之間的協作任務),最後再傳遞給底層的Direct3DOpenGL(也就是圖形學的底層技術),如果只是按照常規流程來處理,就會需要一次又一次來複制生成的紋理數據,這顯然不是我們想要的。
我們現在使用了兩個小方法來使這個流程變得快一點。它們分別作用於painting(繪製)和rasterization(光柵化)兩個階段。

  • 1號知識點!!!Painting我們用來告訴webkit為RenderObject Tree的來生成對應的GraphicsContext。通過給painting routine(繪製流程)傳遞一個GraphicsContext的具體實現來執行這些已經編排好的繪製命令,也可以傳遞一個record context(記錄上下文)只是簡單地把繪圖命令都記錄下來。
  • 2號知識點!!!Rasterization(光柵化)是指Graphics context關聯的繪圖命令實際被執行的過程。通常我們使用CPU(也就是軟件渲染的方式)來執行光柵化任務,也可以直接使用GPU來渲染(也就是硬件渲染的方式)。
  • 上傳:指在主線程存儲區獲取到光柵化以後的位圖內容然後將它作為紋理上傳給GPU的過程,考慮到上述已經提及的定義,上傳過程是如下來處理的:
    • 瓦片繪製:我們在webkit中使用recording context來簡單地記錄Graphics Context的操作指令,將它存儲為SkPicture類型(直接使用軟件光柵化時生成的是SkBitmap類型),隨後可以從一張picture裏面光柵化處理得到多個紋理瓦片
    • 共享內存:在軟件渲染的方式中,光柵化的結果會被存儲在renderer進程的堆內存里,現在不這樣搞了,我們重新分配了一塊共享緩衝區,然後通過它來傳遞相關對象,GPU進程隨後在獲取紋理時直接從共享內存中獲取就行了,這樣就避免了數據的拷貝。
      總的來說,紋理上傳的過程幾乎是零拷貝的。利用這樣的結構,我們在renderer進程(也就是網頁的渲染進程)的沙箱環境內也可以獲取到指向GPU 內存的指針,而在軟件光柵化的過程中,是直接將位圖結果放在這裏的。
  • Painting: this is the process of asking Layers for their content. This is where we ask webkit to tell us what is on a layer. We might then rasterize that content into a bitmap using software, or we might do something fancier. Painting is a main thread operation.
  • Drawing: this is the process of taking the layer tree and smashing it together with OpenGL onto the screen. Drawing is an impl-thread operation.
  • painting:表示的過程是向Layers對象查詢層內容,也就是讓webkit告訴我們每一層上面到底有什麼。接下來我們就可以使用軟件光柵化的方式將這些內容處理為位圖,也可以做一些更牛的事情,painting是一個主線程行為。
  • drawing:是指將Layer中的內容用OpenGL繪製在屏幕上的過程,它是另一個線程中的操作。

概念比較多沒有基礎的讀者可能理解起來有難度,我嘗試用自己的話複述一下:

【軟件渲染】的模式下,在paint時會直接利用Graphics Context繪圖上下文將結果繪製出來,在一個SkBitmap實例中保存為位圖信息;【硬件渲染】的模式下,在paint時傳入一個SkPicture實例,將需要執行的繪圖命令保存在裏面先不執行,然後通過共享內存將它傳給GPU進程,藉助GPU來最終去執行繪圖命令,生成多個瓦片化的位圖紋理結果(OpenGL中頂點着色器向片元着色器傳遞數據時可以自動進行數據插值,完成光柵化的任務)。 純軟件渲染里嚴格說是沒有合成層概念的,因為最終輸出的只有一張位圖,按照順序從下往上畫,和畫到一個新層上再把新層貼到已有結果上其實是一樣的。

不管使用哪種途徑,paint動作都是得到位圖數據,而最終的draw這個動作是藉助OpenGL和位圖數據最終把圖形显示在显示器上。

所以【硬件渲染】就是渲染進程把要做的事情和需要的數據都寫好,然後打包遞給GPU讓它去幹活。

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

【其他文章推薦】

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

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

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

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

奧迪展開純電動車電池量產

德國奧迪母公司德國福斯爆出柴油車廢氣檢驗造假爭議後,宣布將加強生產純電動車的方針。20日,奧迪汽車宣布將在比利時布魯塞爾工廠開始量產純電動SUV汽車以及車用電池,建立電動車一條龍生產線。

日經新聞報導,奧迪在2015年9月推出純電動SUV概念車,並規劃2018年開始在歐洲銷售的計畫。這款純電動SUV的行駛距離可達500公里,且奧迪也預計將向各公司供應電池以降低電池成本,並加速純電動車問世、普及的腳步。

在布魯塞爾電池廠開始量產後,奧迪將可建立電動車一條龍產線,強化自己的電動車市場競爭力。而同一工廠也將納入歐洲區的小型車生產體制重組規劃,將原先在該工廠生產的奧迪A1轉移到西班牙廠生產,原先在西班牙廠生產的小型SUV「Q3」則將轉至匈牙利生產。

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

【其他文章推薦】

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

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

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

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

中國大陸電動車銷量年增169% 私家車成長幅度大

大陸公安部交管局公布,截至去(2015)年底,大陸機動車保有量達2.79億輛,其中汽車1.72億輛。在新能源汽車部分,截至去年底,大陸新能源汽車保有量已達58.32萬輛,年增169.48%;其中,純電動汽車保有量33.2萬輛,占56.93%,其保有量與2014年相比增幅達317.06%。

隨著大陸經濟持續發展,民眾購車剛性需求旺盛,汽車保有量繼續呈快速增長趨勢,去年新註冊登記的汽車達2,385萬輛,保有量淨增1,781萬輛,均為歷史最高水準。汽車占機動車的比率迅速提高,近5年汽車占機動車比率從47.06%提高到61.82%。

大陸全國已有40個城市的汽車保有量超過百萬輛,北京、成都、深圳、上海、重慶、天津、蘇州、鄭州、杭州、廣州、西安等11個城市汽車保有量超過200萬輛。

截至去年底,大陸小型載客汽車達1.36億輛,其中,私家車達1.24億輛,占91.53%;與2014年相比,私家車增加1,877萬輛,增長17.77%。全國平均每百戶家庭擁有31輛私家車,北京、成都、深圳等大城市每百戶家庭擁有私家車超過60輛。

(本文內容由授權使用)

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

【其他文章推薦】

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

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

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

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

詳解Spring Security的HttpBasic登錄驗證模式

一、HttpBasic模式的應用場景

HttpBasic登錄驗證模式是Spring Security實現登錄驗證最簡單的一種方式,也可以說是最簡陋的一種方式。它的目的並不是保障登錄驗證的絕對安全,而是提供一種“防君子不防小人”的登錄驗證。

就好像是我小時候寫日記,都買一個帶小鎖頭的日記本,實際上這個小鎖頭有什麼用呢?如果真正想看的人用一根釘子都能撬開。它的作用就是:某天你的父母想偷看你的日記,拿出來一看還帶把鎖,那就算了吧,怪麻煩的。

舉一個我使用HttpBasic模式的進行登錄驗證的例子:我曾經在一個公司擔任部門經理期間,開發了一套用於統計效率、分享知識、生成代碼、導出報表的Http接口。純粹是為了工作中提高效率,同時我又有一點點小私心,畢竟各部之間是有競爭的,所以我給這套接口加上了HttpBasic驗證。公司里隨便一個技術人員,最多只要給上一兩個小時,就可以把這個驗證破解了。說白了,這個工具的數據不那麼重要,加一道鎖的目的就是不讓它成為公開數據。如果有心人破解了,真想看看這裏面的數據,其實也無妨。這就是HttpBasic模式的典型應用場景。

二、spring boot2.0整合Spring security

spring boot 2,x版本maven方式引入Spring security坐標。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

三、HttpBasic登錄認證模式

如果使用的Spring Boot版本為1.X版本,依賴的Security 4.X版本,那麼就無需任何配置,啟動項目訪問則會彈出默認的httpbasic認證.

我們現在使用的是spring boot2.0版本(依賴Security 5.X版本),HttpBasic不再是默認的驗證模式,在spring security 5.x默認的驗證模式已經是表單模式。所以我們要使用Basic模式,需要自己調整一下。並且security.basic.enabled已經過時了,所以我們需要自己去編碼實現。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   
   @Override
   protected void configure(HttpSecurity http) throws Exception {
      http.httpBasic()//開啟httpbasic認證
      .and()
      .authorizeRequests()
      .anyRequest()
      .authenticated();//所有請求都需要登錄認證才能訪問
   }

}

啟動項目,在項目後台有這樣的一串日誌打印,冒號後面的就是默認密碼。

Using generated security password: 0cc59a43-c2e7-4c21-a38c-0df8d1a6d624

我們可以通過瀏覽器進行登錄驗證,默認的用戶名是user.(下面的登錄框不是我們開發的,是HttpBasic模式自帶的)

當然我們也可以通過application.yml指定配置用戶名密碼

spring:
    security:
      user:
        name: admin
        password: admin

四、HttpBasic模式的原理說明

  • 首先,HttpBasic模式要求傳輸的用戶名密碼使用Base64模式進行加密。如果用戶名是 "admin"  ,密碼是“ admin”,則將字符串"admin:admin" 使用Base64編碼算法加密。加密結果可能是:YWtaW46YWRtaW4=。
  • 然後,在Http請求中使用Authorization作為一個Header,“Basic YWtaW46YWRtaW4=“作為Header的值,發送給服務端。(注意這裏使用Basic+空格+加密串)
  • 服務器在收到這樣的請求時,到達BasicAuthenticationFilter過濾器,將提取“ Authorization”的Header值,並使用用於驗證用戶身份的相同算法Base64進行解碼。
  • 解碼結果與登錄驗證的用戶名密碼匹配,匹配成功則可以繼續過濾器後續的訪問。

所以,HttpBasic模式真的是非常簡單又簡陋的驗證模式,Base64的加密算法是可逆的,你知道上面的原理,分分鐘就破解掉。我們完全可以使用PostMan工具,發送Http請求進行登錄驗證。

期待您的關注

  • 博主最近新寫了一本書:
  • 本文轉載註明出處(必須帶連接,不能只轉文字):。

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

【其他文章推薦】

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

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

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

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

[ASP.NET Core 3框架揭秘] 依賴注入[9]:實現概述

《》、《》和《》主要從實現原理的角度對.NET Core的依賴注入框架進行了介紹,接下來更進一步,看看該框架的總體設計和實現。在過去的多個版本更迭過程中,依賴注入框架的底層實現一直都在發生改變,加上底層的涉及的大都是內容接口和類型,所以我們不打算涉及太過細節的層面。

一、ServiceProviderEngine & ServiceProviderEngineScope

對於依賴注入的底層設計和實現來說,ServiceProviderEngine和ServiceProviderEngineScope是兩個最為核心的類型。顧名思義,ServiceProviderEngine表示提供服務實例的提供引擎,服務實例最終是通過該引擎提供的,在一個應用範圍內只存在一個全局唯一的ServiceProviderEngine對象。ServiceProviderEngineScope代表服務範圍,它利用對提供服務實例的緩存實現對生命周期的控制。ServiceProviderEngine實現了接口IServiceProviderEngine,從如下的代碼片段可以看出,一個ServiceProviderEngine對象同時也是一個IServiceProvider對象,還是一個IServiceScopeFactory對象。

internal interface IServiceProviderEngine :  IServiceProvider, IDisposable, IAsyncDisposable
{
    void ValidateService(ServiceDescriptor descriptor);
    IServiceScope RootScope { get; }
}

internal abstract class ServiceProviderEngine : IServiceProviderEngine, IServiceScopeFactory
{
    public IServiceScope RootScope { get; }    
    public IServiceScope CreateScope();
    ...
}

ServiceProviderEngine的RootScope屬性返回的IServiceScope對象是為根容器提供的服務範圍。作為一個IServiceScopeFactory對象,ServiceProviderEngine的CreateScope會創建一個新的服務範圍,這兩種服務範圍都通過一個ServiceProviderEngineScope對象來表示。

internal class ServiceProviderEngineScope : IServiceScope, IDisposable,  IServiceProvider, IAsyncDisposable
{
    public ServiceProviderEngine Engine { get; }
    public IServiceProvider ServiceProvider { get; }
    public object GetService(Type serviceType);
}

如上面的代碼片段所示,一個ServiceProviderEngineScope對象不僅是一個IServiceScope對象,還是一個IServiceProvider對象。在《》中,我們說表示服務範圍的IServiceScope對象是對一個表示依賴注入容器的IServiceProvider對象的封裝,實際上兩者合併為同一個ServiceProviderEngineScope對象,一個ServiceProviderEngineScope對象的ServiceProvider屬性返回的就是它自己。換句話說,我們所謂的子容器和它所在的服務範圍引用的都是同一個ServiceProviderEngineScope對象。

下圖進一步揭示了ServiceProviderEngine和ServiceProviderEngineScope之間的關係。對於一個通過調用ServiceProviderEngine對象的CreateScope創建的ServiceProviderEngineScope來說,由於它同時也是一個IServiceProvider對象,如果我們調用它的GetService<IServiceProvider>方法,該方法同樣返回它自己。如果我們調用它的GetService<IServiceScopeFactory>方法,它返回創建它的ServiceProviderEngine對象,也就是該方法和Engine屬性返回同一個對象。

依賴注入框架提供的服務實例最終是通過ServiceProviderEngine對象提供的。從上面給出的代碼片段可以看出,ServiceProviderEngine是一個抽象類,.NET Core依賴注入框架提供了如下四個具體的實現類型,默認使用的是DynamicServiceProviderEngine。

  • RuntimeServiceProviderEngine:採用反射的方式提供服務實例;
  • ILEmitServiceProviderEngine:採用IL Emit的方式提供服務實例;
  • ExpressionsServiceProviderEngine:採用表達式樹的方式提供服務實例;
  • DynamicServiceProviderEngine:根據請求併發數量動態決定最終的服務實例提供方案(反射和者IL Emit或者反射與表達式樹,是否選擇IL Emit取決於當前運行時是否支持Reflection Emit)。

4.4.2. ServiceProvider

調用IServiceCollection集合的擴展方法BuildServiceProvider創建的是一個ServiceProvider對象。作為根容器的ServiceProvider對象,和前面介紹的ServiceProviderEngine和ServiceProviderEngineScope對象,一起構建了整個依賴注入框架的設計藍圖。

在利用IServiceCollection集合創建ServiceProvider對象的時候,提供的服務註冊將用來創建一個具體的ServiceProviderEngine對象。該ServiceProviderEngine對象的RootScope就是它創建的一個ServiceProviderEngineScope對象,子容器提供的Singleton服務實例由它維護。如果調用ServiceProvider對象的GetService<IServiceProvider>方法,返回的其實不是它自己,而是作為RootScope的ServiceProviderEngineScope對象(調用ServiceProviderEngineScope對象的GetService<IServiceProvider>方法返回的是它自己)。

ServiceProvider和ServiceProviderEngineScope都實現了IServiceProvider接口,如果我們調用了它們的GetService<IServiceScopeFactory>方法,返回的都是同一個ServiceProviderEngine對象。這一個特性決定了調用它們的CreateScope擴展方法都會創建一個新的ServiceProviderEngineScope對象作為子容器。綜上所述,我們針對依賴注入框架總結出如下的特性:

  • ServiceProviderEngine的唯一性:整個服務提供體系只存在一個唯一的ServiceProviderEngine對象。
  • ServiceProviderEngine與IServiceFactory的同一性:唯一存在的ServiceProviderEngine會作為創建服務範圍的IServiceFactory工廠。
  • ServiceProviderEngineScope和IServiceProvider的同一性:表示服務範圍的ServiceProviderEngineScope同時也是作為服務提供者的依賴注入容器。

為了印證我們總結出來的特性,我們編寫的測試代碼。由於設計的ServiceProviderEngine和ServiceProviderEngineScope都是內部類型,我們只能採用反射的方式得到它們的屬性或者字段成員。上面總結的這些特徵體現在如下幾組調試斷言中。

class Program
{
    static void Main()
    {
        var (engineType, engineScopeType) = ResolveTypes();
        var root = new ServiceCollection().BuildServiceProvider();
        var child1 = root.CreateScope().ServiceProvider;
        var child2 = root.CreateScope().ServiceProvider;

        var engine = GetEngine(root);
        var rootScope = GetRootScope(engine, engineType);

        //ServiceProviderEngine的唯一性
        Debug.Assert(ReferenceEquals(GetEngine(rootScope, engineScopeType), engine));
        Debug.Assert(ReferenceEquals(GetEngine(child1, engineScopeType), engine));
        Debug.Assert(ReferenceEquals(GetEngine(child2, engineScopeType), engine));

        //ServiceProviderEngine和IServiceScopeFactory的同一性
        Debug.Assert(ReferenceEquals(root.GetRequiredService<IServiceScopeFactory>(), engine));
        Debug.Assert(ReferenceEquals(child1.GetRequiredService<IServiceScopeFactory>(), engine));
        Debug.Assert(ReferenceEquals(child2.GetRequiredService<IServiceScopeFactory>(), engine));

        //ServiceProviderEngineScope提供的IServiceProvider是它自己
        //ServiceProvider提供的IServiceProvider是RootScope
        Debug.Assert(ReferenceEquals(root.GetRequiredService<IServiceProvider>(), rootScope));
        Debug.Assert(ReferenceEquals(child1.GetRequiredService<IServiceProvider>(), child1));
        Debug.Assert(ReferenceEquals(child2.GetRequiredService<IServiceProvider>(), child2));

        //ServiceProviderEngineScope和IServiceProvider的同一性
        Debug.Assert(ReferenceEquals((rootScope).ServiceProvider, rootScope));
        Debug.Assert(ReferenceEquals(((IServiceScope)child1).ServiceProvider, child1));
        Debug.Assert(ReferenceEquals(((IServiceScope)child2).ServiceProvider, child2));
    }

    static (Type Engine, Type EngineScope) ResolveTypes()
    {
        var assembly = typeof(ServiceProvider).Assembly;
        var engine = assembly.GetTypes().Single(it => it.Name == "IServiceProviderEngine");
        var engineScope = assembly.GetTypes().Single(it => it.Name == "ServiceProviderEngineScope");
        return (engine, engineScope);
    }

    static object GetEngine(ServiceProvider serviceProvider)
    {
        var field = typeof(ServiceProvider).GetField("_engine", BindingFlags.Instance | BindingFlags.NonPublic);
        return field.GetValue(serviceProvider);
    }

    static object GetEngine(object enginScope, Type engineScopeType)
    {
        var property = engineScopeType.GetProperty("Engine", BindingFlags.Instance | BindingFlags.Public);
        return property.GetValue(enginScope);
    }

    static IServiceScope GetRootScope(object engine, Type engineType)
    {
        var property = engineType.GetProperty("RootScope", BindingFlags.Instance | BindingFlags.Public);
        return (IServiceScope)property.GetValue(engine);
    }
}

三、注入IServiceProvider對象

在《》中,我們從“Service Locator”設計模式是反模式的角度說明了為什麼不推薦在服務中注入IServiceProvider對象。不過反模式並不就等於是完全不能用的模式,有些情況下直接在服務構造函數中注入作為依賴注入容器的IServiceProvider對象可能是最快捷省事的解決方案。對於IServiceProvider對象的注入,有個細節大家可能忽略或者誤解。

讀者朋友們可以試着思考這麼一個問題:如果我們在某個服務中注入了IServiceProvider對象,當我們利用某個IServiceProvider對象來提供該服務實例的時候,注入的IServiceProvider對象是它自己嗎?以如下所示的代碼片段為例,我們定義了兩個在構造函數中注入了IServiceProvider對象的服務類型SingletonService和ScopedService,並按照命名所示的生命周期進行了註冊。

class Program
{
    static void Main()
    {
        var serviceProvider = new ServiceCollection()
            .AddSingleton<SingletonService>()
            .AddScoped<ScopedService>()
            .BuildServiceProvider();
        using (var scope = serviceProvider.CreateScope())
        {
            var child = scope.ServiceProvider;
            var singletonService = child.GetRequiredService<SingletonService>();
            var scopedService = child.GetRequiredService<ScopedService>();

            Debug.Assert(ReferenceEquals(child, scopedService.RequestServices));
            Debug.Assert(ReferenceEquals(rootScope, singletonService.ApplicationServices));
        }
    }            

    public class SingletonService
    {
        public IServiceProvider ApplicationServices { get; }
        public SingletonService(IServiceProvider serviceProvider) => ApplicationServices = serviceProvider;
    }

    public class ScopedService
    {
        public IServiceProvider RequestServices { get; }
        public ScopedService(IServiceProvider serviceProvider) => RequestServices = serviceProvider;
    }
}

我們最終利用一個作為子容器的IServiceProvider對象(ServiceProviderEngineScope對象)來提供這來個服務類型的實例,並通過調試斷言確定注入的IServiceProvider對象是否就是作為當前依賴注入容器的ServiceProviderEngineScope對象。如果在Debug模式下運行上述的測試代碼,我們會發現第一個斷言是成立的,第二個則不成立

再次回到兩個服務類型的定義,SingletonService和ScopedService中通過注入IServiceProvider對象初始化的屬性分別被命名為ApplicationServices和RequestServices,意味着它們希望注入的分別是針對當前應用程序的根容器和針對請求的子容器。當我們利用針對請求的子容器來提供針對這兩個類型的服務實例時,如果注入的當前子容器的話,就與ApplicationServices的意圖不符。所以在提供服務實例的注入的IServiceProvider對象取決於採用的生命周期模式,具體策略為:

  • Singleton:注入的是ServiceProviderEngine的RootScope屬性表示的ServiceProviderEngineScope對象。
  • Scoped和Transient:如果當前IServiceProvider對象類型為ServiceProviderEngineScope,注入的就是它自己,如果是一個ServiceProvider對象,注入的還是ServiceProviderEngine的RootScope屬性表示的ServiceProviderEngineScope對象。

基於生命周期模式注入IServiceProvider對象的策略可以通過如下這個測試程序來驗證。最後還有一點需要補充一下:我們將調用IServiceCollection集合的BuildServiceProvider擴展方法創建的ServiceProvider對象作為根容器,它對應的ServiceProviderEngine對象的RootScope屬性返回作為根服務範圍的ServiceProviderEngineScope對象,ServiceProvider、ServiceProviderEngine和ServiceProviderEngineScope這三個類型全部實現了IServiceProvider接口,這三個對象都可以視為根容器。

class Program
{
    static void Main()
    {
        var serviceProvider = new ServiceCollection()
            .AddSingleton<SingletonService>()
            .AddScoped<ScopedService>()
            .BuildServiceProvider();
        var rootScope = serviceProvider.GetService<IServiceProvider>();
        using (var scope = serviceProvider.CreateScope())
        {
            var child = scope.ServiceProvider;
            var singletonService = child.GetRequiredService<SingletonService>();
            var scopedService = child.GetRequiredService<ScopedService>();

            Debug.Assert(ReferenceEquals(child, child.GetRequiredService<IServiceProvider>()));
            Debug.Assert(ReferenceEquals(child, scopedService.RequestServices));
            Debug.Assert(ReferenceEquals(rootScope, singletonService.ApplicationServices));
        }
    }
}

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

【其他文章推薦】

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

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

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

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

特斯拉第 2 季銷量暴增逾 5 成 世德及和大受惠

美國電動車大廠特斯拉(Tesla)第二季銷量暴增超過 50%,激勵國內相關概念股飆漲,其中世德攻上漲停價位、和大則再創歷史新高。   特斯拉公布最新數據,第二季電動車銷量增加至 1.15 萬輛,暴增 52%,比市場先前估算的 1~1.1 萬輛還多,為連續第二個季度表現優於市場預期,激勵國內大廠股價向上走高。   以世德來說,身為特斯拉供應鏈廠,隨著客戶銷量增加、推出新款車型,加上對其供貨金額占比提升 2 倍至 8%,市場看好其後市發展,對其紛紛敲下滿單。   而和大因是特斯拉電動車減速齒輪唯一供應商,隨著客戶的新車種由兩輪傳動提升為四輪傳動,需要多一顆前輪減速齒輪箱,法人預估,和大今年來自 Tesla 的訂單、營收將成長超過 1 倍。

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

【其他文章推薦】

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

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

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

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

006.Kubernetes二進制部署ETCD

一 部署ETCD集群

1.1 安裝ETCD


etcd 是基於 Raft 的分佈式 key-value 存儲系統,由 CoreOS 開發,常用於服務發現、共享配置以及併發控制(如 leader 選舉、分佈式鎖等)。kubernetes 使用 etcd 存儲所有運行數據。

  1 etcd 是基於 Raft 的分佈式 key-value 存儲系統,由 CoreOS 開發,常用於服務發現、共享配置以及併發控制(如 leader 選舉、分佈式鎖等)。kubernetes 使用 etcd 存儲所有運行數據。
  2 [root@k8smaster01 ~]# cd /opt/k8s/work
  3 [root@k8smaster01 work]# wget https://github.com/coreos/etcd/releases/download/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz
  4 [root@k8smaster01 work]# tar -xvf etcd-v3.3.13-linux-amd64.tar.gz


1.2 分發ETCD

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
  4   do
  5     echo ">>> ${master_ip}"
  6     scp etcd-v3.3.13-linux-amd64/etcd* root@${master_ip}:/opt/k8s/bin
  7     ssh root@${master_ip} "chmod +x /opt/k8s/bin/*"
  8   done


1.3 創建etcd證書和密鑰

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# cat > etcd-csr.json <<EOF
  3 {
  4     "CN": "etcd",
  5     "hosts": [
  6     "127.0.0.1",
  7     "172.24.8.71",
  8     "172.24.8.72",
  9     "172.24.8.73"
 10   ],
 11     "key": {
 12         "algo": "rsa",
 13         "size": 2048
 14     },
 15     "names": [
 16         {
 17             "C": "CN",
 18             "ST": "Shanghai",
 19             "L": "Shanghai",
 20             "O": "k8s",
 21             "OU": "System"
 22         }
 23     ]
 24 }
 25 EOF
 26 #創建etcd的CA證書請求文件



解釋:

hosts 字段指定授權使用該證書的 etcd 節點 IP 或域名列表,需要將 etcd 集群的三個節點 IP 都列在其中。

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
  3 -ca-key=/opt/k8s/work/ca-key.pem -config=/opt/k8s/work/ca-config.json \
  4 -profile=kubernetes etcd-csr.json | cfssljson -bare etcd	#生成CA密鑰(ca-key.pem)和證書(ca.pem)


1.4 分發證書和私鑰

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
  4   do
  5     echo ">>> ${master_ip}"
  6     ssh root@${master_ip} "mkdir -p /etc/etcd/cert"
  7     scp etcd*.pem root@${master_ip}:/etc/etcd/cert/
  8   done


1.5 創建etcd的systemd

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# cat > etcd.service.template <<EOF
  4 [Unit]
  5 Description=Etcd Server
  6 After=network.target
  7 After=network-online.target
  8 Wants=network-online.target
  9 Documentation=https://github.com/coreos
 10 
 11 [Service]
 12 Type=notify
 13 WorkingDirectory=${ETCD_DATA_DIR}
 14 ExecStart=/opt/k8s/bin/etcd \\
 15   --data-dir=${ETCD_DATA_DIR} \\
 16   --wal-dir=${ETCD_WAL_DIR} \\
 17   --name=##MASTER_NAME## \\
 18   --cert-file=/etc/etcd/cert/etcd.pem \\
 19   --key-file=/etc/etcd/cert/etcd-key.pem \\
 20   --trusted-ca-file=/etc/kubernetes/cert/ca.pem \\
 21   --peer-cert-file=/etc/etcd/cert/etcd.pem \\
 22   --peer-key-file=/etc/etcd/cert/etcd-key.pem \\
 23   --peer-trusted-ca-file=/etc/kubernetes/cert/ca.pem \\
 24   --peer-client-cert-auth \\
 25   --client-cert-auth \\
 26   --listen-peer-urls=https://##MASTER_IP##:2380 \\
 27   --initial-advertise-peer-urls=https://##MASTER_IP##:2380 \\
 28   --listen-client-urls=https://##MASTER_IP##:2379,http://127.0.0.1:2379 \\
 29   --advertise-client-urls=https://##MASTER_IP##:2379 \\
 30   --initial-cluster-token=etcd-cluster-0 \\
 31   --initial-cluster=${ETCD_NODES} \\
 32   --initial-cluster-state=new \\
 33   --auto-compaction-mode=periodic \\
 34   --auto-compaction-retention=1 \\
 35   --max-request-bytes=33554432 \\
 36   --quota-backend-bytes=6442450944 \\
 37   --heartbeat-interval=250 \\
 38   --election-timeout=2000
 39 Restart=on-failure
 40 RestartSec=5
 41 LimitNOFILE=65536
 42 
 43 [Install]
 44 WantedBy=multi-user.target
 45 EOF



解釋:

WorkingDirectory、–data-dir:指定工作目錄和數據目錄為 ${ETCD_DATA_DIR},需在啟動服務前創建這個目錄;

–wal-dir:指定 wal 目錄,為了提高性能,一般使用 SSD 或者和 –data-dir 不同的磁盤;

–name:指定節點名稱,當 –initial-cluster-state 值為 new 時,–name 的參數值必須位於 –initial-cluster 列表中;

–cert-file、–key-file:etcd server 與 client 通信時使用的證書和私鑰;

–trusted-ca-file:簽名 client 證書的 CA 證書,用於驗證 client 證書;

–peer-cert-file、–peer-key-file:etcd 與 peer 通信使用的證書和私鑰;

–peer-trusted-ca-file:簽名 peer 證書的 CA 證書,用於驗證 peer 證書。

1.6 修改systemd相應地址

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for (( i=0; i < 3; i++ ))
  4   do
  5     sed -e "s/##MASTER_NAME##/${MASTER_NAMES[i]}/" -e "s/##MASTER_IP##/${MASTER_IPS[i]}/" etcd.service.template > etcd-${MASTER_IPS[i]}.service
  6   done


1.7 分發etcd systemd

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
  4   do
  5     echo ">>> ${master_ip}"
  6     scp etcd-${master_ip}.service root@${master_ip}:/etc/systemd/system/etcd.service
  7   done


二 啟動並驗證

2.1 啟動ETCD

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
  4   do
  5     echo ">>> ${master_ip}"
  6     ssh root@${master_ip} "mkdir -p ${ETCD_DATA_DIR} ${ETCD_WAL_DIR}"
  7     ssh root@${master_ip} "systemctl daemon-reload && systemctl enable etcd && systemctl restart etcd " &
  8   done


2.2 檢查ETCD啟動

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
  4   do
  5     echo ">>> ${master_ip}"
  6     ssh root@${master_ip} "systemctl status etcd|grep Active"
  7   done


2.3 驗證服務狀態

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
  4   do
  5     echo ">>> ${master_ip}"
  6     ETCDCTL_API=3 /opt/k8s/bin/etcdctl \
  7     --endpoints=https://${master_ip}:2379 \
  8     --cacert=/etc/kubernetes/cert/ca.pem \
  9     --cert=/etc/etcd/cert/etcd.pem \
 10     --key=/etc/etcd/cert/etcd-key.pem endpoint health
 11   done



2.4 查看ETCD當前leader

  1 [root@k8smaster01 ~]# source /opt/k8s/bin/environment.sh
  2 [root@k8smaster01 ~]# ETCDCTL_API=3 /opt/k8s/bin/etcdctl \
  3   -w table --cacert=/etc/kubernetes/cert/ca.pem \
  4   --cert=/etc/etcd/cert/etcd.pem \
  5   --key=/etc/etcd/cert/etcd-key.pem \
  6   --endpoints=${ETCD_ENDPOINTS} endpoint status




如上所示,當前ETCD集群的leader為172.24.8.71。本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

高德客戶端及引擎技術架構演進與思考

2019杭州雲棲大會上,高德地圖技術團隊向與會者分享了包括視覺與機器智能、路線規劃、場景化/精細化定位、時空數據應用、億級流量架構演進等多個出行技術領域的熱門話題。現場火爆,聽眾反響強烈。我們把其中的優秀演講內容整理成文並陸續發布出來,本文為其中一篇。

阿里巴巴高級無線開發專家宋照春在高德技術專場做了題為《高德客戶端及引擎技術架構演進與思考》的演講,主要分享了高德地圖客戶端技術架構沿着「上漂下沉」、「模塊化、Bundle化」的思路演進所做的一系列架構升級中的經驗和思考。

以下為宋照春演講內容的簡版實錄:

主要分享三個方面的內容:

  • 融合
  • 架構治理
  • 動態化

一、三管齊下 深度融合

高德最初有兩個端,車機版的高德導航,手機版的高德地圖,兩個團隊,一個是2B,一個是2C,分別是汽車業務和手機業務。當時在引擎/技術上,分為離線引擎和在線引擎,但兩個團隊之間交流比較少,各自有自己的研發、產品和測試,而作為一款端上的APP,兩塊業務都需要有地圖渲染、路線規劃、導航以及定位等通用能力。從公司層面看,存在較大的重複建設,整體研發效率較低。

於是我們做了一件事:利用技術手段,打通端上引擎,打造一套能同時支撐多端的APP能力。具體到執行層面,先從A團隊拉一部分人到B團隊一起建設,建設完之後再從B團隊拉到A團隊。在同時支撐好主線業務發展的情況下,通過一年左右時間,完成了引擎上的融合,做到同時支撐手機、車機以及開放平台。

這樣就從引擎的維度,實現了渲染、定位、規劃和引導的統一。具體來說,我們的各大引擎有好多套代碼,好幾個開發團隊,每個團隊有各自的開發方式和開發環境(Linux,Windows,Mac OS)。各種開發環境,工程配置文件大量重複,修改非常繁瑣。

為此,我們通過兩種方法:

1.建立了一套構建系統Abtor,通過一個配置系統實現統一構建,能夠同時支持多個子引擎,在構建集成效率上得到了很大的提升;

2.對基礎庫進行了整體重構,形成了一套涵蓋了文件I/O、KV存儲、多線程框架&異步框架、歸檔、基礎容器等一系列標準能力的基礎庫,同時也做了引擎核心架構的統一。

 

二、架構治理

通過引擎的融合同時支持多端,在研發效率上實現比較大的收益。而通過技術的抓手來實現團隊的融合,對公司發展而言,這其實是更大的收益,團隊融合的意義在於人才拉通和復用,組織效率得到了較大提升。

隨着高德業務的快速發展,業務上持續擴品類,需求量激增,高德地圖從最初的駕車導航,到後來的步行、騎行、摩托車導航等等,App所承載的業務發展非常快,而原有的架構治理模式的問題也逐漸暴露出來。

首先就是App的代碼規模變得特別大。當時一個倉庫達到了10G以上,由此導致的一個典型的問題就是編譯慢,編譯出一次安裝包需要一個小時。伴隨代碼規模的另一個問題是團隊規模快速增長。代碼增長和大團隊并行開發,最終導致合版慢,每次迭代,客戶端合版需要2天。

代碼膨脹導致的架構腐化問題特別突出,所以測試質量以及線上的質量有段時間也比較差。此外,從產品提出需求到上線,平均需要45天,版本迭代周期很長。

為解決以上架構問題,我們採取了三個手段:升級Native基礎組件,搭建Native容器和頁面框架,Bundle化分拆(微應用)。

下面重點介紹下頁面框架和微應用。

頁面框架主要借鑒和融合了Android和iOS的生命期管理機制。從高德地圖App架構看,下層模塊是一套標準地圖,所有上層業務都要基於地圖模塊開發。為確保上層業務低耦合、一致性,我們設計了一個頁面框架。

如上圖,左邊的Activity是Android的系統頁面控制器,右邊的UIViewController是iOS的系統頁面控制器,通過虛線連接比較,我們發現兩端的頁面狀態設計基本相同。

所以,我們在設計自己的頁面框架時沿用了這些系統頁面狀態,同時從命名上也保持一致,這樣可以讓Android和iOS原生開發的同學更容易理解和上手。

我們吸取了雙端各自的優點。比如,Android端頁面有四種啟動模式,但是iOS 端並沒有這些,我們就把Android的四種啟動模式運用到了iOS端;iOS端有Present特性,但是Android端沒有,那麼也把這種特性融合到Android端的頁面框架中;最後,還有一些小設計,比如Android的onResult設計,也可以借鑒融合到iOS端。

此外,我們還做了微應用,所謂微應用,首先是模塊化,就是把大模塊倉庫大模塊拆成一個個小的Bundle,除了實現模塊化,還主要實現以下幾個目標:

粒度:以業務為單位,以業務線為分組

編譯:二進制級別的產物,可獨立編譯、出包時鏈接

依賴:松耦合,以“服務”為導向,不關心模塊歸屬

而Native容器層面,要實現四個核心目標:路由管理、服務管理、UI生命期管理、微應用管理。

通過一年時間的Bundle化改造,高德地圖單端App完成了300多個頁面的建設,拆分了100多個Bundle。

從收益來看,總編譯時間從原來的60分鐘降低到了8分鐘,合版周期從原來的3天降到1天,需求上線周期降到了1個月以內,線上質量和測試質量都得到了極大的提升,崩潰率從萬分之八降低到十萬分之八。

三、動態化

隨着高德地圖業務發展沿着擴品類、在垂直品類做精做細,景區、酒店、銀行商鋪、充電樁等個性化定製需求凸顯,對前端展現提出了更高的要求,對“快速應變”要求也更高了。

實際上,在2015年,高德就開始做動態化。最早的時候業內就有React Native,團隊做了技術調研,發現不能完全滿足業務上的需要,尤其是性能方面。最後我們決定自研一套動態化技術。

具體來說,就是通過一個核心C++引擎,把兩端業務(Android、iOS)用一套JavaScript代碼解決,實現雙端歸一,Android實現業務動態化發布。

架構層面,最下面是高德App核心的地圖引擎,我們在上面搭建了一套動態化應用引擎,通過C++來實現。應用引擎的作用是為了承上啟下,上面承載動態化業務,下層完成地圖引擎的直接打通。眾所周知,GUI的核心是DOM樹,所以應用引擎不但要實現和JavaScript引擎的整合,還要負責DOM樹的核心邏輯計算。

其次,動態化的技術和前端Web技術一致:樣式、布局。應用引擎負責完成樣式的布局計算、DOM樹Diff、事件生成。而GUI的繪製,通過Diff事件,交由原生的Android以及iOS去完成。這樣,所有的GUI都是原生的組件。

在之上,我們搭建了一套前端框架,前端框架採用當前前端響應式框架做,前端框架之上又搭建了一套前端的UI卡片庫和UI組件庫,讓上層業務能夠更高效的開發。

而對於一些通過動態化的技術無法實現,或者性能上存在卡點的功能,我們就通過Native擴展能力來支撐,這樣,完整的動態化的業務能夠直接運行在Android以及iOS上。

 

JS去執行代碼之後,前端框架會產生虛擬的DOM樹,最後提交到C++引擎,形成C++的DOM樹。C++引擎去完成布局、樣式計算,Diff計算,將每個節點的屬性和坐標交給Android以及iOS,由Native來完成最終UI的渲染。

總體來說,動態化的特點:首先是它與主流前端框架融合,充分融合了大前端的生態;第二,性能、擴展性較好。因為採用C++實現整個核心邏輯,靜態和動態的語言綁定技術,能夠保證地圖引擎的能力能夠直接透出到上層,或者從上層能夠直接call底層的C++能力;第三,多端歸一和動態化,充分利用Native優勢,接近原生Native體驗。

動態化技術改造完成之後,雙端不一致的問題降低了90%,開發、測試成本降低30%,發版周期從T+30到T+0。

最後,總結下高德客戶端及引擎技術架構演進的幾個重要階段:第一個階段,通過在線&離線引擎的融合拉通,讓高德最核心的導航能力提到提升;第二階段,在客戶端發展成為“巨型”APP,代碼量發展到超大規模的時候,通過架構治理,滿足業務快速增長的訴求,解決大規模業務體量下的架構合理性問題,消除架構瓶頸;第三個階段通過動態化的技術,實現多端歸一,以及動態發版能力,為業務發展提供更大的助力。

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

【其他文章推薦】

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

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

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

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

特斯拉、Airbnb 合作,至少百間民宿將加裝電動車充電座

  對於特斯拉(Tesla)的充電式電動車而言,最大普及障礙就是充電座設施的密度,如果只能在自己家充電,那電動車的作用就相當有限了,為此,特斯拉找上了分享經濟旅遊住宿網站 Airbnb,與其旗下的民宿合作,在民宿安裝 Model S 電動車充電座,這樣一來旅遊住宿時也能充電,擴增 Model S 電動車車主的活動範圍。   特斯拉將根據資格標準審查,包括要四星級以上、並至少有 5 個房間,符合水準的 Airbnb 旗下高檔民宿,可獲得免費充電座,充電座本身價值 750 美元,不過民宿本身要負擔安裝費用,安裝費用隨地區不同,可能超過 900 美元,安裝完成後,Airbnb 將在網站上標示該民宿為特斯拉電動車可充電民宿。特斯拉初期將先從加州開始,選擇 30 家加州 Airbnb 旗下高檔民宿免費安裝充電座,之後再逐步擴大到全球,目前只有 12 戶民宿擁有特斯拉充電座,最終將至少有 100 家 Airbnb 旗下民宿可得到免費充電座。   這只是特斯拉積極建立充電站努力的其中一環,特斯拉正積極布建其 Supercharger 快速充電站,目前全球已有 487 座,也與餐廳、旅館、度假村合作,在其中建立壁式充電座,方便車主造訪時順便充電,至 2015 年 6 月,特斯拉在全球已經與 1,200 家業主合作設立充電座。   Airbnb 旗下民宿與特斯拉合作可說是雙贏選擇,雖然仍需負擔安裝費用,但安裝後,可吸引大多為高階消費者的特斯拉車主入住,不僅對生意與品牌形象有幫助,車主充電付出充電費用,一個晚上大約可增加 10 美元營收,對民宿不無小補。而對特斯拉來說,充電座越普及,車子也更好賣。   特斯拉目前大體上只在美國主要城市與幹道路線上布置充電座,經常遭到質疑與攻擊,聲稱電動車可能會在荒郊野外沒電,儘管特斯拉嚴詞反擊目前布站範圍已經涵蓋 96% 美國地區,要故意開到離充電站遠到來不及去充電的地區沒那麼容易,但特斯拉對這樣的抨擊也不敢怠慢,2015 年 3 月時推出軟體更新,整合充電站位置到導航系統之中,一旦車主離充電站距離可能超出殘餘電力可行駛里程,就發出警告,確保不會發生半途沒電的情況。   但要讓潛在消費者更安心買車,最根本之道仍是布建更多充電站,與 Airbnb 的合作代表特斯拉又向前跨出一步,未來可預期特斯拉將更積極推動類似的合作案。     本文全文授權轉載自《科技新報》─〈〉

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

【其他文章推薦】

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

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

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

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