一文看懂 K8s 日誌系統設計和實踐

作者 | 元乙  阿里雲存儲服務技術專家

導讀:上一篇文章中我們介紹了為什麼需要一個日誌系統、為什麼雲原生下的日誌系統如此重要以及雲原生背景下日誌系統的建設難點,相信 DevOps、SRE、運維等同學看了之後深有體會。本篇文章單刀直入,會直接跟大家分享一下如何在雲原生的場景下搭建一個靈活、功能強大、可靠、可擴容的日誌系統。

需求驅動架構設計

技術架構,是將產品需求轉變為技術實現的過程。對於所有的架構師而言,能夠將產品需求分析透徹是非常基本也是非常重要的一點。很多系統剛建成沒多久就要被推翻,最根本的原因還是沒有解決好產品真正的需求。

我所在的日誌服務團隊在日誌這塊有近10年的經驗,幾乎服務阿里內部所有的團隊,涉及電商、支付、物流、雲計算、遊戲、即時通訊、IoT等領域,多年來的產品功能的優化和迭代都是基於各個團隊的日誌需求變化。

有幸我們最近幾年在阿里雲上實現了產品化,服務了數以萬計的企業用戶,包括國內各大直播類、短視頻、新聞媒體、遊戲等行業Top1互聯網客戶。產品功能從服務一個公司到服務上萬家公司會有質的差別,上雲促使我們更加深入的去思考:究竟哪些功能是日誌這個平台需要去為用戶去解決的,日誌最核心的訴求是什麼,如何去滿足各行各業、各種不同業務角色的需求…

需求分解與功能設計

上一節中我們分析了公司內各個不同角色對於日誌的相關需求,總結起來有以下幾點:

  1. 支持各種日誌格式、數據源的採集,包括非K8s
  2. 能夠快速的查找/定位問題日誌
  3. 能夠將各種格式的半結構化/非結構化日誌格式化,並支持快速的統計分析、可視化
  4. 支持通過日誌進行實時計算並獲得一些業務指標,並支持基於業務指標實時的告警(其實本質就是APM)
  5. 支持對於超大規模的日誌進行各種維度的關聯分析,可接受一定時間的延遲
  6. 能夠便捷的對接各種外部系統或支持自定義的獲取數據,例如對接第三方審計系統
  7. 能夠基於日誌以及相關的時序信息,實現智能的告警、預測、根因分析等,並能夠支持自定義的離線訓練方式以獲得更好的效果

為滿足上述這些功能需求,日誌平台上必須具備的功能功能模塊有:

  1. 全方位日誌採集,支持DaemonSet、Sidecar各種採集方式以應對不同的採集需求,同時支持Web、移動端、IoT、物理機/虛擬機各種數據源的採集;
  2. 日誌實時通道,這個是為了對接上下游所必備的功能,保證日誌能夠被多種系統所便捷的使用;
  3. 數據清洗(ETL: Extract,Transform,Load),對各種格式的日誌進行清洗,支持過濾、富化、轉換、補漏、分裂、聚合等;
  4. 日誌展現與搜索,這是所有日誌平台必須具備的功能,能夠根據關鍵詞快速的定位到日誌並查看日誌上下文,看似簡單的功能卻最難做好;
  5. 實時分析,搜索只能完成一些定位到問題,而分析統計功能可以幫助快速分析問題的根因,同時可以用於快速的計算一些業務指標;
  6. 流計算,通常我們都會使用流計算框架(Flink、Storm、Spark Stream等)來計算一些實時的指標或對數據進行一些自定義的清洗等;
  7. 離線分析,運營、安全相關的需求都需要對大量的歷史日誌進行各種維度的關聯計算,目前只有T+1的離線分析引擎能夠完成;
  8. 機器學習框架,能夠便捷、快速的將歷史的日誌對接到機器學習框架進行離線訓練,並將訓練后的結果加載到線上實時的算法庫中。

開源方案設計

藉助於強大的開源社區,我們可以很容易基於開源軟件的組合來實現這樣一套日誌平台,上圖是一個非常典型的以ELK為核心的日誌平台方案:

  • 利用FileBeats、Fluentd等採集Agent實現容器上的數據統一收集。
  • 為了提供更加豐富的上下游以及緩衝能力,可以使用kafka作為數據採集的接收端。
  • 採集到的原始數據還需要進一步的清洗,可以使用Logstash或者Flink訂閱Kafka中的數據,清洗完畢后再寫入kafka中。
  • 清洗后的數據可以對接ElasticSearch來做實時的查詢檢索、對接Flink來計算實時的指標和告警、對接Hadoop來做離線的數據分析、對接TensorFlow來做離線模型訓練。
  • 數據的可視化可以使用grafana、kibana等常用的可視化組件。

為什麼我們選擇自研

採用開源軟件的組合是非常高效的方案,得益於強大的開源社區以及龐大用戶群體的經驗積累,我們可以很快搭建出這樣一套系統,並且可以滿足我們絕大部分的需求。

當我們把這套系統部署好,能夠把日誌從容器上採集上來、elasticsearch上能夠查到、Hadoop上能夠成功執行SQL、Grafana上能看到圖、告警短信能收到……完成上述流程打通后,加加班可能只需要花費幾天的時間,當系統終於跑通的時候,這時候終於可以長舒一口氣,躺在辦公椅上放鬆放鬆。

然而理想很豐滿現實很骨感,當我們預發通了,測試完了上到生產,開始接入第一個應用,逐漸更多的應用接入,越來越多的人開始使用……這時候很多問題都可能暴露出來:

  • 隨着業務量的上漲,日誌量也越來越大,Kakfa和ES要不斷擴容,同時同步Kafka到ES的Connector也需要擴容,最煩的是採集Agent,每台機器上部署的DaemonSet Fluentd根本沒辦法擴容,到了單Agent瓶頸就沒辦法了,只能換Sidecar,換Sidecar工作量大不說,還會帶來一系列其他的問題,比如怎麼和CICD系統集成、資源消耗、配置規劃、stdout採集不支持等等。
  • 從剛開始上的邊緣業務,慢慢更多的核心業務接入,對於日誌的可靠性要求越來越高,經常有研發反應從ES上查不到數據、運營說統計出來的報表不準、安全說拿到的數據不是實時的……每次問題的排查都要經過採集、隊列、清洗、傳輸等等非常多的路徑,排查代價非常高。同時還要為日誌系統搭建一套監控方案,能夠即時發現問題,而且這套方案還不能基於日誌系統,不能自依賴。
  • 當越來越多的開發開始用日誌平台調查問題時,經常會出現因為某1-2個人提交一個大的查詢,導致系統整體負載上升,其他人的查詢都會被Block,甚至出現Full GC等情況。這時候一些大能力的公司會對ES進行改造,來支持多租戶隔離;或者為不同的業務部門搭建不同的ES集群,最後又要運維多個ES集群,工作量還是很大。
  • 當投入了很多人力,終於能夠把日誌平台維持日常使用,這時候公司財務找過來了,說我們用了非常多的機器,成本太大。這時候開始要優化成本,但是思來想去就是需要這麼多台機器,每天大部分的機器水位都在20%-30%,但是高峰的水位可能到70%,所以不能撤,撤了高峰頂不住,這時候只能搞搞削峰填谷,又是一堆工作量。

上述這些是一家中等規模的互聯網企業在日誌平台建設中經常會遇到的問題,在阿里這些問題會放大非常多倍:

  • 比如面對雙十一的流量,市面上所有的開源軟件都無法滿足我們那麼大流量的需求。
  • 面對阿里內部上萬個業務應用,幾千名工程師同時使用,併發和多租戶隔離我們必須要做到極致。
  • 面對非常多核心的訂單、交易等場景,整個鏈路的穩定性必須要求3個9甚至4個9的可用性。
  • 每天如此大的數據量,對於成本的優化顯得極為重要,10%的成本優化帶來的收益可能就有上億。

阿里K8s日誌方案

針對上述的一些問題,我們經過多年的時間,開發並打磨出這樣一套K8s日誌方案:

  1. 使用我們自研的日誌採集Agent Logtail實現K8s全方位的數據採集,目前Logtail在集團內有數百萬的全量部署,性能、穩定性經過多次雙十一金融級考驗。
  2. 化繁為簡,數據隊列、清洗加工、實時檢索、實時分析、AI算法等原生集成,而不是基於各種開源軟件搭積木的形式實,大大降低了數據鏈路長度,鏈路長度的降低也意味着出錯可能性的減少。
  3. 隊列、清洗加工、檢索、分析、AI引擎等全部針對日誌場景深度定製優化,滿足大吞吐、動態擴容、億級日誌秒級可查、低成本、高可用性等需求。
  4. 對於流式計算、離線分析場景這種通用需求,無論是開源還是阿里內部都有非常成熟的產品,我們通過無縫對接的方式來支持,目前日誌服務支持了數十種下游的開源、雲上產品的對接。

這套系統目前支撐了整個阿里集團、螞蟻集團、雲上上萬家企業的日誌分析,每天寫入的數據量16PB+,開發、運維這樣一套系統問題和挑戰非常多,這裏就不再展開,有興趣的同學可以參考我們團隊的技術分享:。

總結

本篇主要從架構層面去介紹如何搭建一套K8s的日誌分析平台,包括開源方案以及我們阿里自研的一套方案。然而實際這套系統落地到生產環境並有效運行還有很多工作要做:

  1. K8s上以什麼樣的姿勢來打日誌?
  2. K8s上的日誌採集方案選擇,DaemonSet or Sidecar?
  3. 日誌方案如何與CICD去集成?
  4. 微服務下各個應用的日誌存儲如何劃分?
  5. 如何基於K8s系統的日誌去做K8s監控?
  6. 如何去監控日誌平台的可靠性?
  7. 如何去對多個微服務/組件去做自動的巡檢?
  8. 如何自動的監控多個站點並實現流量異常時的快速定位?

後續文章我們會一步一步來和大家分享如何把這套系統落地,敬請期待。

“ 阿里巴巴雲原生微信公眾號(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術公眾號。”

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

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

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

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

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

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

ASP.NET Core 3.0 gRPC 身份認證和授權

一.開頭聊騷

本文算是對於 ASP.NET Core 3.0 gRPC 研究性學習的最後一篇了,以後在實際使用中,可能會發一些經驗之文。本文主要講 ASP.NET Core 本身的認證授權和gRPC接入,認證方式採用目前主流的 JWT 結合 IdentityServer4。

二.服務端配置

我們首先需要在服務端配置認證和授權。gRPC基於此文的Demo來開始: ,IdentityServer 基於此文Demo: 。

配置

1.首先啟動 IdentityServer4 地址為:

2.為gRPC項目安裝Jwt組件:Install-Package Microsoft.AspNetCore.Authentication.JwtBearer -Version 3.0.0

3.為gRPC項目配置認證和授權服務

在 Startup 類的 ConfigureServices 方法中,配置如下代碼

services.AddAuthorization(options =>
{
    options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, policy =>
    {
        policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
        policy.RequireClaim("sub");
    });
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "http://localhost:5000";
        options.RequireHttpsMetadata = false;
        options.Audience = "grpc1";
    });

4.啟用認證授權中間件

在 Startup 類的 Configure 方法中,配置如下代碼

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

請務必注意中間件順序

5.為gRPC服務啟用授權

我們在 LuCatService 的 SuckingCat 方法上,加上 [Authorize]特性,就和在MVC中一樣。

測試

運行客戶端調用服務端來進行測試,發現服務端返回了授權失敗,客戶端同樣獲得了錯誤。這證明我們的服務端配置是沒有問題的

三.客戶端配置

配置

客戶端首先需要從 IdentityServer 申請 Token,然後在調用 gRPC 服務時傳遞過去,這和 HTTP Api 調用一樣。

1.客戶端項目安裝組件 IdentityModel 獲得基於 HttpClient 的和 IdentityServer 的交互的封裝。

2.獲取Token

// discover endpoints from metadata
var client = new HttpClient();

var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
if (disco.IsError)
{
    Console.WriteLine(disco.Error);
    return;
}

// request token
var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
    Address = disco.TokenEndpoint,
    ClientId = "ro.client",
    ClientSecret = "secret",

    UserName = "alice",
    Password = "password",
    Scope = "grpc1"
});

if (tokenResponse.IsError)
{
    Console.WriteLine(tokenResponse.Error);
    return;
}

Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");

3.為 gRPC 客戶端請求設置 Token

和 HTTP Api 調用一樣,gRPC也是放在頭部的

var headers = new Metadata {{"Authorization", $"Bearer {tokenResponse.Json["access_token"]}"}};

var catClient = new LuCat.LuCatClient(channel);
var catReply = await catClient.SuckingCatAsync(new Empty(), headers);

主要就是在調用 SuckingCatAsync方法時,傳入了header。

測試

可以看到成功的進行了調用。

四.結束

本文所用代碼地址:

gRPC in Asp.Net Core :

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

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

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

台灣海運大陸貨務運送流程

兩岸物流進出口一站式服務

打造最可靠的自動駕駛基礎架構

文章作者:莫璐怡 Pony.ai

編輯整理:Hoh Xil

內容來源:Pony.ai & DataFun AI Talk

出品社區:DataFun

注:歡迎轉載,轉載請在留言區留言。

導讀:本次分享的主題為打造最可靠的自動駕駛基礎架構。主要內容包括如何做 Pony.ai 自動駕駛系統的基礎架構,涉及到的技術困難,以及我們是如何克服的。

首先先了解下傳統互聯網公司的基礎架構:

數據基礎設施,會包括大規模的數據庫、分佈式的文件系統;

計算平台,可能會需要大量的服務器、大數據平台、容器的管理機制;

Web 服務管理,同時還會有各種各樣的 Web Service,不停的迭代來滿足新的業務發展。

這是傳統互聯網公司要做的事情,但是對於自動駕駛公司和 Pony.ai,在這樣的架構基礎上我們還會做哪些事情?

這是 Pony.ai 的基礎架構,包含了所有傳統互聯網公司要做的事情,除此之外,還需要做如下事情:

自動駕駛車載系統,如何支持各種各樣的AI技術、算法,如何控制車輛,這都依賴於自動駕駛車載系統來完成。

大規模仿真平台,Pony.ai 每天至少會跑 30W 公里的仿真測試(很多自動駕駛公司一年跑的里程可能只有百萬級別),這點對於自動駕駛測試來說非常重要。

車隊運營基礎平台,Pony.ai 要打造自己的移動出行服務,需要基礎平台來支持 Robotaxi 的運營。

可視化平台與人機接口,可視化平台是幫助我們了解系統到底是如何思考、運作的,或者當測試工程師做各種測試的時候都依賴於可視化平台;人機接口,自動駕駛車輛最終是要提供出行服務,是有乘客在裏面,這時會有一個可視化的界面,來告訴乘客車所感知的周圍環境,以及接下來的駕駛操作等等信息,同時還會提供人機交互的功能,讓乘客也能控制車輛,比如輸入目的地,或者需要停車等等。

Pony.ai 的目標是打造自動駕駛移動出行平台,我們希望可以在不同的城市,可以提供大規模自動駕駛車輛的運營,那麼我們的基礎架構會面臨以下挑戰:

車輛數量的增加,目前廣州已經有幾十輛車在進行測試,同時還在不停的增長着;

運營區域的擴大,剛開始只是在很小的區域進行測試,目前已經在幾百平方公里的區域進行測試;

數據量的增長,我們有很多的傳感器,以及車輛和運營區域的增加,都使得數據量的增長非常非常非常大;

工程師數量的增長,目前 Pony.ai 有廣深、北京、美國四個 office,工程師的數量每周都在增長,所以導致模塊數量和內部代碼的數量也在增長。

所有的這些增長都要求我們的技術棧是具有可擴展性的,來滿足快速增長所帶來的挑戰。

剛剛講了整個基礎架構,其中重要的一點就是車載系統,在講車載系統之前,先簡單介紹下自動駕駛系統:

傳感器及其他硬件:激光雷達、高分辨率攝像頭、毫米波雷達、GNSS/IMU、運算平台,我們可看到圖中標了不同的顏色,目前這些傳感器是通過 Supplier Partner 來得到的,我們自己不做傳感器,我們需要去購買他們的產品,但是購買之後需要做數據進一步的分析和整合,然後做後面的處理,然後對於運算平台除了 supplier 的一些應用外,我們自己也會做一些優化。傳感器主要要做的事情就是接收真實世界的數據,然後傳遞給 Pony.ai 自動駕駛系統中。

自動駕駛系統:首先,要做傳感器融合,進行時間同步,將多傳感器的數據融合在一起;然後是感知模塊,用來感知周圍的環境有什麼樣的障礙物和物體;接下來會進行行為預測,預測這樣的障礙物或物體之後的行為會是什麼樣的;然後才到我們的決策規劃模塊,按照之前的預測來決定之後車輛的動作,如急剎車、讓路、超車等動作;最後,就是我們的控制模塊,他會按照決策規劃模塊,告知我們的系統要怎麼做,然後決定怎麼踩剎車、油門,怎麼打方向盤。

車輛,我們本身是不造車的,所以車輛是由 OEM 提供的,但是整個控制的算法,是我們自研去做的。

除此之外,還有高精地圖與定位模塊,以及數據與系統架構(數據的處理,以及控制數據在不同模塊的流動)。

這裏介紹的是各個模塊,但最後把他們串聯起來,靠的是我們的自動駕駛軟件系統,這就是自動駕駛的車載系統。很多自動駕駛企業使用的是 ROS 的一套工業系統,而 Pony.ai 是從第一行代碼開始,寫了一套 PonyBrain,自研的多層次自動駕駛車載系統,最主要的做的事情有:

多模塊的調度運行,所有模塊的調度運行都是 Pony.ai 自己去做的。

模塊間的消息通信,如何把數據從激光雷達傳遞到傳感器融合的模塊,再把融合的結果放到感知模塊中,然後感知的數據怎麼告訴行為預測、決策規劃等等模塊,以及如何拿到高精地圖與定位的信息。

車載計算資源的分配與管理,對於自動駕駛來說反應速度是非常重要的,這就需要我們對內存、CPU、GPU 等有足夠的優化,做到定製化的車載計算資源分配與管理。

日誌記錄,同時我們需要完善的日誌記錄,我們所有的測試數據回來都需要一整套的 Pipeline 去做自動化的分析,然後幫我們評判出有意義的數據,給到測試工程師或者研發工程師,進行進一步的分析去使用,然後進一步提升我們的模型。

監控與報警,保證了我們自動駕駛的安全性。

車載系統的挑戰:

① 可靠性:車載系統必須足夠的可靠,不能有任何的內存泄露、代碼邏輯的錯位,這種都是零容忍的,一旦發生了這樣的事情,對整個自動駕駛系統來說是非常嚴重的事故,是有可能影響到安全性的,對於 Pony.ai 自動駕駛系統技術的發展來說,安全永遠是我們的第一位,所以所有影響安全性的事情,我們都是零容忍的,同時他也會影響車隊運營的效率;所以我們還需要系統監控與異常報警,一旦系統出現任何問題,我們需要及時提醒安全員,做出車輛接管的操作。

② 高性能:滿足模塊間通信的海量數據壓力,同時實現低延遲。

③ 靈活性:支持多種不同類型的計算資源的接入,以及不同類型模塊的接入,需要有靈活的系統來支持計算資源的高速迭代。

車載系統的實踐:

可靠性:

① 代碼質量要求高:對於可靠性來說我們有非常嚴格的 code review 和 unit test,相信這是在國內互聯網公司不太容易見到的一件事情,雖然會非常耗時,但是對可靠性的提升是有非常大的幫助的。

② 合理使用工具幫助發現問題:同時我們也會使用非常多的工具,如靜態分析、ASAN 等等,來做離線的分析,來保證系統的可靠性。

③ 多重系統可靠性檢查:包括系統啟動前校驗,系統運行時實時監控,系統運行后數據分析等。

④ 這是我們的持續集成與發布的平台:對於每一次代碼的修改,我們都會進行仿真測試;然後對於研發的迭代,我們每周會有 Release 版本的更新,保障版本的穩定性,同時,剛剛我們整個測試包括封閉,半封閉,高峰期的測試,整個測試流程怎麼持續集成與發布,也是保證系統可靠性的一種方法。

高性能:

① 合理的架構避免大數據拷貝等嚴重影響性能的邏輯。

② 依據模塊邏輯分配合適的計算資源,如內存、CPU、GPU 等。

③ 定期對整個系統 Profile 分析系統的性能瓶頸。

靈活性:

① 定義足夠通用的模塊公共接口。

② 定義足夠通用的消息通信接口。

為什麼需要仿真系統?因為仿真系統可以使得我們車還沒有上路的時候,就已經做了大規模的自動駕駛測試,無需路測和人力接入就可以評價系統的性能變化;由於沒有進行路測,不會引起路面事故;同時,仿真系統還提供了基於數據驅動快速迭代算法的可行性,新的算法可以先在仿真平台上做驗證,一些具體的指標和測試的信息都會在仿真平台上有所體現。

仿真系統數據的倆個不同來源:

① 支持真實路測收集的場景,我們的路測數據非常的多,數據回來之後,通過 Data Pipeline 自動更新這些有意義和有意思的場景,我們會根據當時的場景改動相應的模塊,然後會在仿真系統重跑當時的場景,來判斷新的方法是否 work;

② 支持人工和隨機生成的場景,這樣的一些仿真的場景,也是非常的重要的,因為雖然我們在做大規模的路測,但是不代表可以遇到所有的場景,很多場景無法在路測中收集到,這就需要我們通過人工去創造這樣的場景出來,給我們的系統一些樣本,來學習如何處理這樣的場景,保證我們新的 feature 在這樣的場景不會出現問題。

仿真平台的挑戰與實踐:

① 仿真結果的可靠性:首先仿真的結果必須是可靠的,如果不可靠,用它檢測出來的結果是沒有任何的意義的。整個仿真是在服務端模擬車載環境跑的,同時在服務端構建車輛動力學模型,保證測試的數據足夠可靠。

② 仿真數據的選擇與管理:當然我們會選擇合適的路測數據來幫助算法的迭代(這裏的選擇不是人工的選擇,是全自動化的選擇,幫我們在茫茫數據中挑選出有意義的數據);另外,我們還會規範的依據類別管理大規模的仿真數據,比如感知模塊的一些改動,到底需要測試哪些數據,才會更加的體現這個改動帶來多少影響,這裏我們會有內部的一個分類,我們不會對所有的數據進行無差別的仿真(這樣做意義不大)。

③ 仿真系統的性能:我們將整個仿真系統并行部署在分佈式計算平台中,這才可能滿足我們單天 30W 公里以上的仿真測試,並且這個數據還在不斷增長。

數據基礎架構:

數據是自動駕駛技術進步的核心驅動力,沒有數據,我們就看不到現在如此多的測試車輛在進行路測,數據本身有幾個重要的點:

① 如何存儲海量的數據,如何支持快速的訪問。

② 如何進行數據處理。

③ 如何進行數據同步,如何把不同區域、路測數據、車載數據同步到數據集中,如何讓不同辦公區的工程師都可以使用這些數據,對數據同步來說是一個很大的挑戰。

核心挑戰:

① 數據量大:我們有 PB 級別的數據,這裏只是以攝像頭為例,還包括其他傳感器數據,以及系統運作的中間數據等等。

② 數據屬性不同於互聯網數據:我們的數據由客戶端產生,有大量的傳感器數據、大量的模塊運行日誌,這與互聯網數據有本質的區別,所以對整個數據架構的要求也是不一樣的。

數據存儲的挑戰:

① 依據特定的使用場景設計合理的存儲格式的設計:以便於車載系統記錄、大規模數據分析(數據回來之後,需要有方法進行分析,找出有意義的數據)、部分數據訪問、文件系統存儲(如何高效的利用文件系統)等。

② 選擇合適的存儲系統:

針對冷/熱數據選擇不同方案

選擇高可用的存儲系統

選擇易於水平擴展,因為車輛規模是不停的在變大的,運營時間越來越長,數據的增長速度是遠超想象的,所以需要易於水平擴展的存儲系統。

控製成本,不能用過於昂貴的設備。

數據處理可以幫助收集性能指標,有 MPI(平均每次接管所需里程)、模塊運行效率、乘客舒適度體驗等,還有就是路測有趣場景的挖掘,如接管、急剎、感知算法識別、不合理的變道策略等用於模型訓練和仿真。

數據處理的挑戰:

① 減小數據採集到處理的全流程時間:如何以最快的速度把數據從車傳到中間處理系統,Data Pipeline 運行完之後,上傳到數據中心,這裏面我們做了非常多的工作。

② 依據不同類型數據處理任務選擇合適的處理系統:計算量要求比較高的我們選擇 CPU 密集型系統來處理;更多的會是車載的數據,我們會選擇 IO 密集型系統進行處理。

③ 通用的任務定義以支持靈活的添加新任務:幫我們檢測出來更多有意義的數據。

車隊運營基礎平台:

我們有一個 Pony Pilot 項目,在我們廣州所有的內部員工都可以使用,同時在北京和美國加州,也有同樣的服務已經上線,那麼支持這樣的服務,我們需要做哪些事情:

Fleet Control Center,車隊控制中心

Pony Pilot APP

Onboard system

各種各樣的 webapp,幫助我們觀察整個車隊的運營情況,幫助管理測試的車輛和人員。

車隊運營基礎平台的挑戰:

需要支持複雜需求變化的 web 框架,同時我們有大量的 web service 的部署與管理,這都需要我們去完善 web 服務通用組件,例如部署工具、日誌記錄平台隨時排查問題、監控平台保證所有 service 平台的高可容性。

容器與服務調度平台:

通過 Kubernetes 來幫我們做各種各樣的服務調度和集群支持。

可視化平台:

① 目標:方便人類理解無人車系統看到的世界

② 挑戰:首先,需要足夠的靈活,易於適配不同需求的工具;其次,需要有高性能的現實,如 3D 實時渲染的高效實現;最後,支持跨平台的可視化框架,如桌面系統、移動系統、Web 等多平台。

人機接口:

方便乘客使用的用戶界面,同時可以看到自動駕駛是如何了解世界,如何做決策,如何規劃之後的行為等等,給乘客更多的信息和信任。

總結:

① Pony.ai 的基礎架構工作包括:

傳統互聯網公司所需要解決的基礎架構挑戰。

自動駕駛技術特定的基礎架構挑戰。

② 在這裏工作你可以:

接觸自動駕駛系統的各個方面。

設計並實現滿足通用需求的單機和分佈式系統。

系統的保障自動駕駛技術的持續進步。

這是一個非常有意思的 team,裏面有很多有意思的工作,非常歡迎大家與我們一起來工作,推動整個自動駕駛的發展,謝謝大家!

歡迎關注DataFunTalk同名公眾號,收看第一手原創技術文章。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

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

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

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

小三通海運與一般國際貿易有何不同?

小三通快遞通關作業有哪些?

Flutter 構建的 Mac 桌面應用上無法發出網絡?

在上一篇文章中我們分享了,如何開發桌面應用。在本章文章中,來解決一下為何在 Mac 中無法發出網絡情況的原因。

起因

事情​起因是這樣的:我總覺得寫一個 Demo 不足以體現我們開發同學的能力。直到最近,我發現了一個可以改善的小點,可幫助我們的測試同學提高測試效率。

大體情況就是在某天晚上,客戶端的一個小問題,需要進行驗證。但是呢,測試同學要做回歸測試,把所有的數據都重新創建了一遍。但是此過程用了很久,理論來說,應該十來分鐘搞定的事情。我就靜靜的在她身旁看着,內心捉急萬分,畢竟我想早點回家(睡覺,打遊戲,看電視)。最後晚上 11 點多打車回家,一路上我都在想,這個問題應該很快就能驗證好吧,為何要折騰這麼就呢?也許是流程真這麼長吧,那我們無法改變流程,那就做出能提升效率的工具吧。

次日,我把我的想法告訴了她,但是她說我們有自己的網頁工具,可以解決啊,只是現在沒時間創建腳本。隨後我擱置這個想法,但是這種提升效率的想法一直縈繞在我的心頭。

再然後,某天突然意識到,同樣是工具,大家都會使用順手好用的,幹嘛使用哪種傳統老土的工具呢?基於這一點,我開啟了新的探索。

我不僅要做客戶端的,還要做網頁的,更要做桌面應用(測試同學最關注)。我要體驗更好,不僅方便我自己測試,還要給所有的測試同學提供便利,減少加班是我的最終目的,哈哈哈。

想法有了,那就開始實施吧。之前遇到一個問題,一直沒解決,但是此刻必須解決了。那就是之前 Flutter 構建的 Mac 應用后,無法發出網絡請求。

在桌面應用無法發出請求?

添加 Dio 以後,會遇到這種情況:

提示,當前系統不支持該操作。

為何會有如此問題,在 Dio 上支持 Other,也就是支持 Mac ,Windows, Linux, 為何我們直接運行有錯誤呢?

具體原因

實際是因為 Mac 應用有沙箱限制,需要在對應文件中開啟即可。

<key>com.apple.security.network.client</key>
<true/>

添加以後,效果如下:

具體參考: https://github.com/google/flutter-desktop-embedding/issues/546

代碼示例:

import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

// Sets a platform override for desktop to avoid exceptions. See
// https://flutter.dev/desktop#target-platform-override for more info.
void _enablePlatformOverrideForDesktop() {
  if (!kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux)) {
    debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
  }
}

void main() {
  _enablePlatformOverrideForDesktop();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  String requestData = "";

  void _incrementCounter() {
    setState(() {
      _counter++;
      getHttp();
    });
  }

  void getHttp() async {
    try {
      Response response = await Dio().get("http://www.gdky005.com");
      requestData = response.toString();
      print(requestData);
    } catch (e) {
      print(e);
      requestData = e.toString();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
            Container(
              height: 400,
              child: ListView(
                children: <Widget>[
                  Text(
                    '$requestData',
                  )
                ],
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

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

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

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

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

小三通物流營運型態?

※快速運回,大陸空運推薦?

netty源碼解析(4.0)-29 Future模式的實現

  Future模式是一個重要的異步併發模式,在JDK有實現。但JDK實現的Future模式功能比較簡單,使用起來比較複雜。Netty在JDK Future基礎上,加強了Future的能力,具體體現在:

  1. 更加簡單的結果返回方式。在JDK中,需要用戶自己實現Future對象的執行及返回結果。而在Netty中可以使用Promise簡單地調用方法返回結果。
  2. 更加靈活的結果處理方式。JDK中只提供了主動得到結果的get方法,要麼阻塞,要麼輪詢。Netty除了支持主動get方法外,還可以使用Listener被動監聽結果。
  3. 實現了進度監控。Netty提供了ProgressiveFuture、ProgressivePromise和GenericProgressiveFutureListener接口及其實現,支持對執行進程的監控。

  吹了那麼多牛,有一個關鍵問題還沒弄清楚:Future到底是幹嘛的?io.netty.util.concurrent.Future代碼的第一行註釋簡潔第回答了這個問題:Future就是異步操作的結果。這裏面有三個關鍵字:異步,操作,結果。首先,Future首先是一個“結果”;其次這個結果產生於一個“操作”,操作具體是什麼可以隨便定義;最後這個操作是”異步”執行的,這就意味着“操作”可能在另一個線程中併發執行,也可能隨後在同一個線程中執行,什麼時候產生結果是一件不確定的事。

  異步調用過程的一般過程是:調用方喚起一個異步操作,在接下來的某個恰當的時間點得到的異步操作操作的結果。要正確地完成上述步驟,需要解決以下幾個問題:

  • 怎樣維護這個調用狀態?
  • 如何獲取異步操作的結果?
  • 何時處理結果?

  io.netty.util.concurrent.DefaultPromise是Future的默認實現,以上三個問題的答案都能在這個類的代碼中找到。

 

DefaultPromise的派生體系

  下面是DefaultPromis及其父類,接口的聲明:

  public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> 

  public abstract class AbstractFuture<V> implements Future<V>

  public interface Promise<V> extends Future<V> 

  public interface Future<V> extends java.util.concurrent.Future<V> 

   可以看出,DefaultPromise派生自AbstractFuture類,並實現了Promise接口。抽象類型AbstractFuture派生自Future, 接口Promise派生自Future。Future派生自JDK的Future接口。

  和JDK的Future相比,Netty的Future接口增加一些自己的方法:

   /**
     當操作成功時返回true*/
    boolean isSuccess();

    /**
   只有當操作可以被取消時返回true
*/ boolean isCancellable(); /** 返回操作的異常*/ Throwable cause(); /** 添加一個監聽器到future。當操作完成(成功或失敗都算完成,此事isDone()返回true)時, 會通知這個監聽器。如果添加時操作已經完成,
   這個監聽器會立即被通知。
*/ Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener); /** 和上個方法一樣,可以同時添加多個監聽器*/ Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners); /** 刪除指定的監聽器, 如果這個監聽器還沒被通知的話。*/ Future<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener); /** 功能和上個方法一樣,可以同時刪除多個監聽器。*/ Future<V> removeListeners(GenericFutureListener<? extends Future<? super V>>... listeners); /** 同步等待直到操作完成。會被打斷。 */ Future<V> sync() throws InterruptedException; /**    同步等着知道操作完成。不會被打斷。 */ Future<V> syncUninterruptibly(); /** 同sync*/ Future<V> await() throws InterruptedException; /** 同synUniterruptibliy*/ Future<V> awaitUninterruptibly(); /** 等待,直到操作完成或超過指定的時間。會被打斷。*/ boolean await(long timeout, TimeUnit unit) throws InterruptedException; /** 同上*/ boolean await(long timeoutMillis) throws InterruptedException; /** 同上,不會被打斷。*/ boolean awaitUninterruptibly(long timeout, TimeUnit unit); /** 同上。*/ boolean awaitUninterruptibly(long timeoutMillis); /** 立即得到結果,不會阻塞。如果操作沒有完成或沒有成功,返回null*/ V getNow();

  Netty的Future最大的特點是增加了Listener被動接收任務完成通知,下面是兩個Listener接口的定義:

public interface GenericFutureListener<F extends Future<?>> extends EventListener {
    void operationComplete(F future) throws Exception;
}

public interface GenericProgressiveFutureListener<F extends ProgressiveFuture<?>> extends GenericFutureListener<F> {
    void operationProgressed(F future, long progress, long total) throws Exception;
}

  把一個listener添加到future之後。當異步操作完成之後,listener會被通知一次,同時會回調operationComplete方法。參數future是當前通知的future,這意味這,一個listener可以被添加到多個future中。

  當異步操作進度發送變化時,listener會被通知,同時會回調operationProgressed方法。progress是當前進度,total是總進度。progress==total表示操作完成。如果不知道何時完成操作progress=-1。

  Promise定義的方法:

    /**
    設置結果。把這個future設置為success,通知所有的listener,
  如果這個future已經是success或failed(操作已經完成),會拋出IllegalStateException
*/ Promise<V> setSuccess(V result); /**
同上。只有在操作沒有完成的時候才會生效,且會返回true */ boolean trySuccess(V result); /** 設置異常。把這個future設置為failed狀態,通知所有的listener.
如果這個future已經完成,會拋出IllegalStateException
*/ Promise<V> setFailure(Throwable cause); /** 同上。只有在操作沒有完成時才會生效,且返回ture */ boolean tryFailure(Throwable cause); /** 設置當前前future的操作不能被取消。這個future沒有完成且可以設置成功或這個future已經完成,返回true。否則返回false */ boolean setUncancellable();

 

DefaultPromise的設計

關鍵屬性

  volatile Object result;

  異步操作的結果。可以通過它的值知道當前future的狀態。

  final EventExecutor executor;

  通知listener的線程。

  Object listeners;

  維護添加到當前future的listener對象。

  short waiters;

  記錄當前真正等待結果的線程數量。

  boolean notifyingListeners;

  是否正在通知listener,防止多線程併發執行通知操作。

 

狀態管理

  future有4種狀態: 未完成, 未完成-不能取消,完成-成功,完成-失敗。使用isDone()判斷是否完成,它代碼如下:

1     @Override
2     public boolean isDone() {
3         return isDone0(result);
4     }
5 
6     private static boolean isDone0(Object result) {
7         return result != null && result != UNCANCELLABLE;
8     }

  第7行是判斷當前完成狀態的。result != null 且 result != UNCANCELLABLE,表示處於完成狀態。

  result默認是null, 此時future處於未完成狀態。可以使用setUncancellable方法把它設置成為完成-不能取消狀態。

1     @Override
2     public boolean setUncancellable() {
3         if (RESULT_UPDATER.compareAndSet(this, null, UNCANCELLABLE)) {
4             return true;
5         }
6         Object result = this.result;
7         return !isDone0(result) || !isCancelled0(result);
8     }

  第3行,使用原子操作設置result的值,只有result==null時才能把result設置成UNCANCELLABLE。當result==UNCANCELLABLE時,不允許取消異步操作。

  使用isSuccess方法判斷future是否處於完成-成功狀態。

1     @Override
2     public boolean isSuccess() {
3         Object result = this.result;
4         return result != null && result != UNCANCELLABLE && !(result instanceof CauseHolder);
5     }

  第4行是完成-成功狀態result的取值:除null, UNCANCELLABLE和CauseHolder對象的任何值。

  只有滿足isDone() && !isSuccess()時,future處於完成失敗狀態,可以使用cause方法獲取異常。

  調用setSuccess和trySuccess方法,能夠把狀態轉換成完成-成功。

 1     @Override
 2     public Promise<V> setSuccess(V result) {
 3         if (setSuccess0(result)) {
 4             notifyListeners();
 5             return this;
 6         }
 7         throw new IllegalStateException("complete already: " + this);
 8     }
 9     
10     private boolean setSuccess0(V result) {
11         return setValue0(result == null ? SUCCESS : result);
12     }

  第3行嘗試把狀態設置成完成-成功狀態。如果可以,在第4行通知所有的listener。否則第7行拋出錯誤。第11行給出了成功的默認值SUCCESS。trySuccess少了第7行,不會拋出異常。

  調用setFailure和tryFailure方法,能夠包狀態轉換成完成-失敗狀態。

 1     @Override
 2     public Promise<V> setFailure(Throwable cause) {
 3         if (setFailure0(cause)) {
 4             notifyListeners();
 5             return this;
 6         }
 7         throw new IllegalStateException("complete already: " + this, cause);
 8     }
 9 
10     private boolean setFailure0(Throwable cause) {
11         return setValue0(new CauseHolder(checkNotNull(cause, "cause")));
12     }

  第3行嘗試把專題設置成完成-失敗狀態。如果可以,在第4行通知所有listener。否則在第7行拋出異常。第11行把異常包裝成CauseHolder對象。tryFailure少了第7行,不會拋出異常。

 

獲取異步操作的結果

  當異步操作完成時,調用Promise提供的setSuccess和trySuccess設置成功的結果,調用setFailure和tryFailure設置異常結果。不論什麼結果,都會使用setValue0方法保存到result屬性上。

1     private boolean setValue0(Object objResult) {
2         if (RESULT_UPDATER.compareAndSet(this, null, objResult) ||
3             RESULT_UPDATER.compareAndSet(this, UNCANCELLABLE, objResult)) {
4             checkNotifyWaiters();
5             return true;
6         }
7         return false;
8     }

  第2,3行,使用原子操作設置result的值,只有result==null或result==UNCANCELLABLE時,才能設置成功。如果設置成功,在第4行喚醒所有等待中的線程。可以使用get方法得到result值。如果isSucess()==true, result的值是SUCCESS或異步操作的結果。否則result的值是CauseHolder對象,此時可以調用cause方法得到異常對象。

  使用get或cause,只有在異步操作完成后才能順利得到結果。可以使用listener,被動等待操作完成通知。

 

使用listener異步通知處理結果

  Future的listener是必須實現GenericFutureListener接口,調用方法可以在operationComplete方法中處理異步操作的結果。

  listeners屬性用來保存使用addListener,addListeners方法添加到future的listener。listeners可能使用一個GenericFutureListener對象,也可能是一個GenericFutureListener數組。所有添加listener方法都會調用addListener0方法添加listener。

1     private void addListener0(GenericFutureListener<? extends Future<? super V>> listener) {
2         if (listeners == null) {
3             listeners = listener;
4         } else if (listeners instanceof DefaultFutureListeners) {
5             ((DefaultFutureListeners) listeners).add(listener);
6         } else {
7             listeners = new DefaultFutureListeners((GenericFutureListener<? extends Future<V>>) listeners, listener);
8         }
9     }

  這段代碼中使用了一個DefaultFutureListeners類,它內部維護了一個GenericFutureListener數組。

  當一次操作完成時,會調用notifyListeners方法通知listeners中所有的listener,並調用listener的operationComplete方法。只有當isDone()==true時才會調用notifyListeners方法。觸發點在下面的一些方法中:

  addListener, addListeners。

  setSuccess, trySuccess。

  setFailure, tryFailure。

  notifyListeners的代碼如下:

 1     private void notifyListeners() {
 2         EventExecutor executor = executor();
 3         if (executor.inEventLoop()) {
 4             final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get();
 5             final int stackDepth = threadLocals.futureListenerStackDepth();
 6             if (stackDepth < MAX_LISTENER_STACK_DEPTH) {
 7                 threadLocals.setFutureListenerStackDepth(stackDepth + 1);
 8                 try {
 9                     notifyListenersNow();
10                 } finally {
11                     threadLocals.setFutureListenerStackDepth(stackDepth);
12                 }
13                 return;
14             }
15         }
16 
17         safeExecute(executor, new Runnable() {
18             @Override
19             public void run() {
20                 notifyListenersNow();
21             }
22         });
23     }

  這段代碼的作用是調用notifyListenersNow。如果當前線程就是executor的線程,在第9行直接調用notifyListenerNow,否則在第20行,把notifyListnerNow放在executor中執行。第4-7行和11行的作用是防止遞歸調用導致線程棧溢出,MAX_LISTENER_STACK_DEPTH就是listener遞歸調用的最大深度。

  notifyListenerNow的作用是,確保沒有併發執行notifyListener0或notifyListners0方法,且所有的listener只能被通知一次。

 1     private void notifyListenersNow() {
 2         Object listeners;
 3         synchronized (this) {
 4             // Only proceed if there are listeners to notify and we are not already notifying listeners.
 5             if (notifyingListeners || this.listeners == null) {
 6                 return;
 7             }
 8             notifyingListeners = true;
 9             listeners = this.listeners;
10             this.listeners = null;
11         }
12         for (;;) {
13             if (listeners instanceof DefaultFutureListeners) {
14                 notifyListeners0((DefaultFutureListeners) listeners);
15             } else {
16                 notifyListener0(this, (GenericFutureListener<? extends Future<V>>) listeners);
17             }
18             synchronized (this) {
19                 if (this.listeners == null) {
20                     // Nothing can throw from within this method, so setting notifyingListeners back to false does not
21                     // need to be in a finally block.
22                     notifyingListeners = false;
23                     return;
24                 }
25                 listeners = this.listeners;
26                 this.listeners = null;
27             }
28         }
29     }

  第3-11行的作用是防止多個線程併發執行11行之後的代碼。

  結合第5,9,10行可知, listeners中的所有listener只能被通知一次。

  13-17行,通知所有listeners。notifyListener0通知一個listener,notifyListeners0通知所有的listener。

  最後,18-27行,檢查在通知listeners的過程中,是否有新的listener被添加進來。如果有,25,26行得到所有新添加的listener並清空listeners屬性,13-17行繼續通知新添加的listener。否則,運行22,23行結束通知過程。

 1     private void notifyListeners0(DefaultFutureListeners listeners) {
 2         GenericFutureListener<?>[] a = listeners.listeners();
 3         int size = listeners.size();
 4         for (int i = 0; i < size; i ++) {
 5             notifyListener0(this, a[i]);
 6         }
 7     }
 8 
 9     @SuppressWarnings({ "unchecked", "rawtypes" })
10     private static void notifyListener0(Future future, GenericFutureListener l) {
11         try {
12             l.operationComplete(future);
13         } catch (Throwable t) {
14             logger.warn("An exception was thrown by " + l.getClass().getName() + ".operationComplete()", t);
15         }
16     }

  1-7行,notifyListeners0對每個listener調用一次notifyListener0,參數是當前的future。

  10-16,調用listener的operationComplete方法,捕獲了所有的異常,確保接下來可以繼續通知下一個listener。

 

使用await機制同步等待結果

  可以使用一系列的await,awaitXXX方法同步等待結果。這些方法可以分為: 能被打斷的,不能被打斷的。一直等待的,有超時時間的。await0方法是最複雜的等待實現,所有帶超時時間的await方法都會調用它。

 1     private boolean await0(long timeoutNanos, boolean interruptable) throws InterruptedException {
 2         if (isDone()) {
 3             return true;
 4         }
 5 
 6         if (timeoutNanos <= 0) {
 7             return isDone();
 8         }
 9 
10         if (interruptable && Thread.interrupted()) {
11             throw new InterruptedException(toString());
12         }
13 
14         checkDeadLock();
15 
16         long startTime = System.nanoTime();
17         long waitTime = timeoutNanos;
18         boolean interrupted = false;
19         try {
20             for (;;) {
21                 synchronized (this) {
22                     if (isDone()) {
23                         return true;
24                     }
25                     incWaiters();
26                     try {
27                         wait(waitTime / 1000000, (int) (waitTime % 1000000));
28                     } catch (InterruptedException e) {
29                         if (interruptable) {
30                             throw e;
31                         } else {
32                             interrupted = true;
33                         }
34                     } finally {
35                         decWaiters();
36                     }
37                 }
38                 if (isDone()) {
39                     return true;
40                 } else {
41                     waitTime = timeoutNanos - (System.nanoTime() - startTime);
42                     if (waitTime <= 0) {
43                         return isDone();
44                     }
45                 }
46             }
47         } finally {
48             if (interrupted) {
49                 Thread.currentThread().interrupt();
50             }
51         }
52     }

  這個方法返回的條件有: (1)isDone()==true;(2)允許被打斷(interrupted==true)的情況下被打斷;(3)已經超時。2-12行分別檢查了這3種情況。

  25,35行管理waiters屬性,這個屬性用來記錄當前正在等待的線程數。inWaiters方法正常情況下會把waiters加1,當檢查到waiters==Short.MAX_VALUE時會拋出異常,防止過多的線程等待。

  27行,調用wait等待,經歷waitTime后超時返回。在等待過程中,會被setValue0方法調用notifyAll喚醒。

  29-33行,處理被打斷的異常,如果運行被打斷,在30行拋出這個異常返回。

  38-45行,不論什麼原因線程被喚醒,檢查是否滿足返回條件,如果不滿足,繼續循環等待。

  沒有超時的wait方法實現要簡單一些,只需判讀返回條件(1)(2)。

 

跟蹤異步操作的執行進度

  如果想要跟蹤異步操作的執行進度,future需要換成DefaultProgressivePromise對象,listener需要換成GenericProgressiveFutureListener類型。DefaultProgressivePromise派生自DefaultPromise同時實現了ProgressivePromise接口。GenericProgressiveFutureListener接口派生自GenericFutureListener接口。

  ProgressivePromise定義了setProgress和tryProgress方法用來更新進度,是不是很眼熟,和Promise接口定義返回結果的方法很類似。

ProgressivePromise<V> setProgress(long progress, long total);
boolean tryProgress(long progress, long total);

  GenericProgressiveFutureListener定義了operationProgressed方法用來處理進度更新通知。

     void operationProgressed(F future, long progress, long total) throws Exception;

  

  DefaultProgressivePromise自己只實現了setProgress和tryProgress方法,其它都是復用了DefaultPromise的實現。

 1     @Override
 2     public ProgressivePromise<V> setProgress(long progress, long total) {
 3         if (total < 0) {
 4             // total unknown
 5             total = -1; // normalize
 6             if (progress < 0) {
 7                 throw new IllegalArgumentException("progress: " + progress + " (expected: >= 0)");
 8             }
 9         } else if (progress < 0 || progress > total) {
10             throw new IllegalArgumentException(
11                     "progress: " + progress + " (expected: 0 <= progress <= total (" + total + "))");
12         }
13 
14         if (isDone()) {
15             throw new IllegalStateException("complete already");
16         }
17 
18         notifyProgressiveListeners(progress, total);
19         return this;
20     }

  3-12行,檢查progress和total的合法性。

  14行,如isDone()==true,拋出異常。只有在操作還沒完成的是否更新進度才有意義。

  18行,調用notifyProgressiveListeners觸發進度更新通知,這個方法在DefaultPromise中實現。

  notifyProgressiveListeners實現了觸發進度更新通知的主要流程:

 1     void notifyProgressiveListeners(final long progress, final long total) {
 2         final Object listeners = progressiveListeners();
 3         if (listeners == null) {
 4             return;
 5         }
 6 
 7         final ProgressiveFuture<V> self = (ProgressiveFuture<V>) this;
 8 
 9         EventExecutor executor = executor();
10         if (executor.inEventLoop()) {
11             if (listeners instanceof GenericProgressiveFutureListener[]) {
12                 notifyProgressiveListeners0(
13                         self, (GenericProgressiveFutureListener<?>[]) listeners, progress, total);
14             } else {
15                 notifyProgressiveListener0(
16                         self, (GenericProgressiveFutureListener<ProgressiveFuture<V>>) listeners, progress, total);
17             }
18         } else {
19             if (listeners instanceof GenericProgressiveFutureListener[]) {
20                 final GenericProgressiveFutureListener<?>[] array =
21                         (GenericProgressiveFutureListener<?>[]) listeners;
22                 safeExecute(executor, new Runnable() {
23                     @Override
24                     public void run() {
25                         notifyProgressiveListeners0(self, array, progress, total);
26                     }
27                 });
28             } else {
29                 final GenericProgressiveFutureListener<ProgressiveFuture<V>> l =
30                         (GenericProgressiveFutureListener<ProgressiveFuture<V>>) listeners;
31                 safeExecute(executor, new Runnable() {
32                     @Override
33                     public void run() {
34                         notifyProgressiveListener0(self, l, progress, total);
35                     }
36                 });
37             }
38         }
39     }

  第3行,從listeners中選出GenericProgressiveFutureListener類型的listener。

  10-38行。調用notifyProgressiveListeners0, notifyProgressiveListener0通知進度跟新。11-17行,在當前線程中調用。

  19-37行,在executor中調用。notifyProgressiveListener0隻是簡單地調用listener的operationProgressed方法。notifyProgressiveListeners0是對每個listener調用一次notifyProgressiveListener0。

  和完成通知相比,進度更新通知要更加簡單。進度更新通知沒有處理併發問題,沒有處理棧溢出問題。

  

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

【其他文章推薦】

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

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

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

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

台中搬家,彰化搬家,南投搬家前需注意的眉眉角角,別等搬了再說!

小胖李的面試之旅(二)

上一篇:

#0 前言

上一篇寫完之後我就給媳婦做飯去了。。沒看,然後瀏覽量居然又過2000了。好開心!謝謝大家的鞭策。其中又100%的回復都非常的中肯。這是我必學的內容,

所以我就想問問各位回復的老鐵。你們是怎麼知道我是瀋陽的?????

 

#1 降薪

經過上一周的面試經驗總結,講各大招聘網站的薪資全面下降500-1000的檔次,效果非常明顯,周五一天的面試電話等於平時2天的量,

所以側面證明我要的薪資過高,直接讓HR把我pass了。

 

#1 小工廠

公司性質:工業工廠

福利:五險一金(最低)+雙休

面試職位:軟件開發工程師

簡歷途徑:智聯招聘

面試方式:face to face

這是一家做熱電的工廠企業,小工廠,但是根據企查查等app查看,是一家外企,離家比較近,去了之後發現是一家佔地面積不大的工廠,廠房設備老舊。

說他小,其實應該並不小,可能是我這幾年去過的工廠都太大了,這家顯得比較小。

並在車上反覆溫習之前的面試題,進到廠區后給HR打電話,不一會一個男的過來問我是面試的吧,我說,對。

他說:你寫一下你基本情況,給你筆。上2樓,有個休息區,你在哪等我就行,我一會就上去找你。

我說:好的

上了二樓就是樓梯的位置有一個桌子,我一猜,估計就是這了,坐下后,刷刷刷寫。

一直寫到了:

 

猶豫了一下,寫個8500,我就想知道,他怎麼問,因為這個薪資是他們能接受的範圍。他們給出的薪資是 8k-10k

在寫到了:

 

 我真的很想寫  唱,跳,rep,籃球。。但是我控制住了。

等了能有20分鐘吧,HR帶着技術總監來了。

技術總監很嚴肅

進去之後,HR先給自己研磨了一杯咖啡,我TM以為給我的呢!

我和技術總監面前什麼都沒有,做了一個簡單的自我介紹。

技術總監:你會EF嗎?

小胖李:會,但是我們一般不用,因為有一定的局限性

技術總監(點頭):的確有一些局限性

小胖李:那種框架的東西,學起來也簡單,一般我們底層用ibatis

技術總監:會oracle嗎?

小胖李:不會,但是oracle的語法和mssql是很相似的,類似c#與java的關係

技術總監(點頭):是很相似但是還是有不一樣的地方

小胖李:的確,例如oracle沒有視圖的概念,存儲過程和函數也是有一定的區別的

技術總監(點頭):你說你以前是做能源的,你們是怎麼做的

小胖李:通過三種方式:1 2 3

技術總監 and HR (頻頻點頭)

技術總監:我沒問題了

HR:你期望薪資為什麼比你現在的工資還低?

小胖李:我未來的職業發展我想定位資深技術開發工程師,其次,我現在因為經常不休息,所以當前工作給的工資比較多,如果到一個可以正常休息雙休和福利保險的公司。

那麼我不會要太高的價格

HR:哪你能接受出差嗎?

小胖李:一個月以內的可以接受

HR:回去等通知吧

總結:

這一次面試我是完全賭的成份,因為我不會EF。但是這一次感覺沒問什麼東西,但是感覺又問了一些東西,只不過沒有答到面試者的那個點上,不過新的突破就是終於輪到HR問薪資的問題了,比之前強了一點點。

 

 

#2 玄學公司

公司性質:市政監控和道路監控

福利:五險一金(最低)+雙休

面試職位:軟件開發工程師

簡歷途徑:BOSS

面試方式:face to face

這家單位是周末找到我並且邀請我去面試,BOSS和我聯繫的人向我推了他們技術總監的微信,加了之後看一眼朋友圈,整個一個玄學大師。研究易經的,我都懷疑我是不是被騙子公司給找到了。

看這情況,就有點不想去了,所以什麼都沒準備,直接去面了。

技術總監:介紹一下自己

小胖李:巴拉巴拉巴拉

技術總監:問你幾個問題。。。什麼是事件?

小胖李:(我要說 按鈕的 click 是事件是不是就是純扯淡?)我們很少用 事件,委託這些。

技術總監(閉眼):你知道L……I……N……Q嗎?

小胖李(3秒蒙逼):您說是 linq吧?

技術總監(睜眼):對,你知道是做什麼的嗎

小胖李:我知道,linq我的個人理解是程序級的的快速擴展性查詢,一般都用在集合上,我一般常用的where,desc等這些和lambda差不多,沒lambda那麼強大而已。。。。。

技術總監(不耐煩):不用說了,假設查詢 表某一列大於500的 數據總數怎麼查?

小胖李:where 那一列 500, count總數就可以了

技術總監:表連接有哪些

小胖李:左聯,右連,內連

技術總監:堆棧有什麼區別

小胖李:一個是存值類型一個存引用類型

技術總監:不準確

小胖李:嗯。。。。。。。

技術總監:站在內存角度考慮一下

小胖李:(老子c#也不管內存了啊)。。。。。

技術總監:我沒問題了

尷尬的沉默時間。。不說讓我走也不說後面還有啥。。我就尷尬的看着他和老闆,我決定打破這個沉默
小胖李:咱們公司是有五險一金和雙休的吧

老闆:有的,你想要多少薪資?

小胖李:8500到9000

老闆:嗯。。。

又他媽就沉默……

小胖李:哪沒問題的話我就先走了?

老闆(如負釋重):行,你回去等通知吧,這两天就告訴你

小胖李:好的

面試題:

1,事件,委託是什麼東西

  事件是一種特殊的委託,委託是將方法作為變量

2,堆棧的區別是什麼

  棧:是一個先進后出的數據結構,通常用於保存方法(函數)中的參數,局部變量. 

    堆:是一個可動態申請的內存空間(其記錄空閑內存空間的鏈表由操作系統維護)

總結

這次面試雖然也問到了價錢這一關,但是也出現新的問題,要對一些不常用的東西的理論知識有所了解。

 

#3 又是一個新的外包公司

公司性質:市政監控和道路監控

福利:五險一金(最低)+雙休+福利假(?)

面試職位:軟件開發工程師

簡歷途徑:BOSS

面試方式:電話面試

和上一篇文章的大型外包公司是同類型,同地點的,只不過又換了一家分包商,本來不是很在意的,因為我知道過的可能性很低,但是該準備還是要準備的。一邊玩遊戲,這邊電話就來了

PM:你在項目一般都是什麼職位

小胖李:項目經理,開發者,需求分析師

PM:能說說嘛

小胖李:一般項目交給我之後我負責前期需求溝通,中期的開發,以及後期的培訓溝通會等等,一直到項目驗收階段為止,不負責收錢,其他基本都是我負責。

PM:也就是項目的主要負責人被

小胖李:對,主要負責人之一

…… 沒了,整個電話面試8分鐘左右,沒有討論任何技術問題,只是對我個人做一個了解,我也和他實話實說,我面過你們的總包,只不過被pass了,1選3。

結果:面試通過。。。很意外的面試通過。。毫無準備的那種。12月份入職

總結:有時候不吹牛逼,聲音沉穩一些,對方可能就認為你是個大佬。

 

#4 電腦城的一家互聯網公司

公司性質:面向全市老百姓的互聯網公司

福利:五險一金(最低)+雙休(忙季 單休)+福利假

面試職位:軟件開發工程師

簡歷途徑:前程

面試方式:筆試+face to face

這個是在上一家外包公司沒有給我結果的時候答應人家去面試的,所以做人不能食言該去就要去,去了之後做了20道筆試題,筆試題如下鏈接:

和這個差不多內容,反正就是百度能查得到的內容。

PM(翻看我的筆試題):你覺得我們的面試題怎麼樣?

小胖李:都是百度的內容,基本就是比較熱門的面試題,屬於基礎內容,題目一部分出自於博客園一部分出自黑馬

PM:…….你怎麼知道

小胖李:我在來之前在家看了。。上面很多題我都看了,但是我沒有按照原答案寫,我就挑我會的在項目中使用過的寫了一下,而且是按照我的理解寫的

PM:那你說一下什麼時候用抽象類什麼時候用接口

小胖李:不會,但是大部分的項目都是用接口吧?畢竟接口是多態的一種。

PM:抽象類難道不是嗎?

小胖李:沒具體研究過,因為實際項目中不會用到抽象類,這個東西沒印象

PM:哪抽象類的關鍵字是什麼?

小胖李:。。。。波浪線?

PM:不是。波浪線是析構函數,你學會c++?

小胖李:沒有,我就忽然想起來,c#裏面有個波浪線的寫法

PM:析構函數在c++和c#的作用和方式是一樣的

小胖李:受教了

PM:哪你這道題 string s=new string(“xyz”) 實現了幾個 string object 為什麼寫了一個呢?

小胖李:因為他就 new了一次呀

PM:哪 “xyz”呢

小胖李:難道您的意思是指 new了一個新的對象之後 xyz指向了這個的對象嗎?

PM:差不多這個意思

PM:attribute 是做什麼用的?

小胖李:方法或者類的特性

PM:具體一點呢

小胖李:嗯。。。沒怎麼了解過,但是我的項目中是把這個作為一種事物回滾的時候使用

PM:???具體說一下什麼是回滾

小胖李:回滾就是同時發生2件以上的事情,有一個成功一個失敗的情況下為了讓數據統一減少臟數據,講成功的那件事情回滾回去

PM:嗯。。為什麼不直接用sql的事物呢

小胖李:因為我們框架帶呀

PM:。。。。特性可不是用來做這個的,這個東西是用來做反射的

小胖李:哦 哦,,但是不是說反射不安全么?動態的講程序級映射出去

PM:哪有,哈哈哈

小胖李:看來我這方面知識挺匱乏

PM:你看你寫的 const 和 readonly的區別,一個是只讀一個不是,常量難道不是只讀嗎?

小胖李:那也是只讀的話這倆還有啥區別了呢?

PM:對啊,你說呢

小胖李:嗯。。。。。。

PM:一個是在編譯前就知道結果,一個是編譯中知道結果

小胖李:原來是這樣。哈哈哈

PM:能說說out和ref的區別嗎

小胖李:ref呢。。就是 傳進去一個變量,在方法結束后 在方法外可以訪問這個變量並且知道結果

PM:out呢

小胖李:具體不太了解,就用過一次,是我的方法需要返回兩個結果才這麼用的

PM:看字面意思也是用於返回兩個結果

小胖李:哦,哦,哪為什麼不用list,hashtable返回多個結果呢

PM:你的意思是在創建一個對象返回多個結果?

小胖李:對啊

PM:。。。。嗯。。。

 

PM:你看我們的面試題還行吧

小胖李:看着沒難度,但是理論知識要的還是蠻多的,我理論知識還是比較匱乏的

PM:也不完全是,比如CLR這個很多人都不知道啥意思,你至少寫出來了。

小胖李:哈哈

PM:給你XXXX,行嗎

小胖李:我回去考慮一下哈

結果:面試通過,但是給的比我要的還要低。。

總結:一共聊了能有半個多小時吧,中途還接一個面試邀請電話,但是這家單位側重於理論知識還是很多的,其實能通過面試題和面試題的擴展知道自己的知識點差在什麼地方,只不過是自己忽略了。

並且在平時的工作中沒有使用這些複雜的東西。自己的知識面停留在幾年前,而且基礎知識有一些偏差性,不是全理論覆蓋。。這個也是需要補充學習的。

 

 

#5 躲在居民區的小作坊

公司性質:真 · 小作坊

福利:五險(最低)+(單休)

面試職位:軟件開發工程師

簡歷途徑:前程

面試方式:筆試+face to face

這家單位在購物街的後面的小區里,雖然外面掛着大牌子寫着孵化基地,裏面明顯是改造的樓,一大堆小公司,看起來就不是很正規的樣子。進去之後先讓我做一套筆試題。

這套筆試題5分鐘答完。掃一眼就知道答案,而且全是選擇題。。。我也不知道這個公司怎麼想的,最後還通過他們的網站收集了一波我的個人信息,包括手機號,照片,身份證。。。

本來不想面了,因為公司不大,領導倒是挺有派頭,,,不過用啤酒杯喝茶也是奇葩。。沒問我任何技術問題,只是隨便東扯扯,西扯扯。找他們要了一個離譜的價格我就撤退了

總結:這家單位有詐騙的嫌疑,在前程上寫的是 五險一金+雙休+各種福利,結果去了之後告訴我只有五險和單休。還有就是 太簡單的實踐類問題的企業就不要去了,基本很小很騙人。

 

#6 彩蛋

為了提升自己在智聯的簡歷排名,花了88塊錢買了個簡歷置頂15天。結果是簡歷瀏覽數從0變成1。。。看我的這家還是我主動投的。這是我花的最大頭的88塊錢。

但是有意思的是,花了這88之後,前程和boss電話和消息就沒斷。。。花一份錢享受3家VIP。。賺了!~

 

#7 福利好的互聯網企業

公司性質:房產互聯網企業

福利:五險一金(全額)+雙休+福利假+附加福利假

面試職位:軟件開發工程師

簡歷途徑:BOSS

面試方式:筆試+機試+face to face

說實話,這家企業在我年輕的時候去面過一次沒過,這次又去面還是沒過。哈哈哈哈,但是他家的待遇是真的不錯,分為筆試和機試,初試是筆試,但是很明顯筆試沒過,就算我這麼抄百度都沒過。。。哎。。

對話也沒什麼營養,描述一下為什麼在前幾家單位離職,對方和我說一下他們想招的是什麼級別的人,雖然勉強能夠上,但是人家不是很想勉強的要我。哈哈哈,沒關係等以後有機會在來面試。 

這家單位真的挺值得去的,可以和他們一起學習技術,通過我內部得到的消息,他們在做技術轉型很多技術還是大家一起學習的,但是福利好也意味着規矩多。和我一個城市的小夥伴可以自己考慮考慮。

 

#8 航空企業

公司性質:民航企業下屬單位

福利:五險一金(全額)+雙休+福利假

面試職位:軟件開發工程師

簡歷途徑:前程

面試方式:face to face

主要問的內容偏向於,是否能獨立搭建框架,能否處理大數據,回答的磕磕巴巴。不過福利待遇是真的很棒,先以外包身份進去,

然後會過一段時間轉為這個公司的正式員工,上級公司是民航。。目前還在招人的狀態,估計開春會有一個大面積招人的可能。有興趣的小夥伴可以在前程上更新一下自己的簡歷。

 

#9 最後一節

我已經選擇了外包公司,準備入職,所以不再面試。上一篇博客很多小夥伴猜到我是哪個城市了,那麼我說的這幾家單位大部分都能猜到,後面幾個沒有寫具體的對話內容是因為我是一天内面了這3家,太累了,一天答了兩份筆試

叨叨3次自己經歷,路上多次差點追尾別人。。給大家能帶來的總結就是

技術要掌握 

net core 

mvc 4 

mvc 5

那麼基本問題都不大,最後說的航空和互聯網企業,我真的蠻看好的,互聯網企業是穩定的技術更新換代,他們肯定也不會在短期內把平台換成java,因為他們家是專註做net c#,航空這個應該屬於新成立的,一切都在起步階段

他們的未來發展前進也很廣,同樣也很穩定,基本干到50歲不愁的那種。雖然最後只有2家面試通過,但是真的在面試中學習很多知識。理論的知識點,實際的操作內容。語言的溝通等等。

 

年底有一些前瞻性的公司已經開始做人才儲備,也就是屬於那種提前錄取批次,有想法的朋友可以更新一下簡歷和做一些筆試題。年後還應該是常年那樣的爆發一波招聘。

 

好了,祝大家前程似錦

 

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

【其他文章推薦】

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

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

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

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

Mybatis精講(二)—生命周期

目錄

回顧

  • 上一章節我們通過xml和代碼的方式實現了Mybatis環境的配置。代碼方式只是簡單介紹下。我們也知道我們大部分情況使用的是xml方式的配置。在實際開發中我們那樣開發顯然是不合理的。

  • 上章節提到的組件显示不可能每次執行sql都要重新創建的。這樣性能上肯定是過不去的。今天我們就來簡單聊聊SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、Mapper這些組件的生命周期吧。

SqlSessionFactoryBuilder

  • 通過觀察分析這個類我們就知道既然是Builder模式的類,那他的作用就是構建起(孵化器).換句話說這個類不是那麼的重要,因為他唯一的作用就是孵化SqlSessionFactory。在Spring與Mybatis整合的框架中,我相信Spring一定是在構建了SqlSessionFactory之後就將這個類進行回收了。因為後面就不需要了。這裏純屬個人猜想。

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      inputStream.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}
  • 上面就是我們通過加載xml配置文件的源碼。我們不難發現build核心是通過XMLConfigBuilder這個類去負責解析mybatis-config.xml配置文件並生成Configuration對象。

SqlSessionFactory

  • 看名字就知道是工廠模式。在這裏我們可以把他看成數據庫連接池。既然是工廠就肯定得有產品。SqlSessionFactory的產物就是SqlSession。SqlSession是與數據庫的一次連接。管理數據庫的連接的自然就是連接池了。
  • Mybatis中使用的SqlSessionFactory是DefaultSqlSessionFactory 。 以連接池的角度看待我們不難推斷出SqlSessionFactory應該是個單例 。SqlSessionFactory對應的是數據庫。一個數據庫原則上應該對應一個SqlSessionFactory來管理。這點在Spring中正好無縫連接。把SqlSessionFactory交由spring管理。spring默認是單例模式bean.

openSessionFromDataSource

  • SqlSessionFactory通過openSession方法獲取SqlSession.SQLSession實際上可以看做是一次數據庫的連接。下面我們通過源碼的方式去看看工廠是如何生產SqlSession的。
<!--定義數據庫信息,默認使用development數據庫構建環境-->
<environments default="development">
    <environment id="development">
        <!--jdbc事物管理-->
        <transactionManager type="JDBC"></transactionManager>
        <!--配置數據庫連接信息-->
        <dataSource type="POOLED">
            <property name="driver" value="${database.driver}"/>
            <property name="url" value="${database.url}"/>
            <property name="username" value="${database.username}"/>
            <property name="password" value="${database.password}"/>
        </dataSource>
    </environment>
</environments>

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  //定義一個事物對象
  Transaction tx = null;
  try {
    //通過配置對象獲取事先配置好的環境對象   這裏對應了xml中的environments標籤  。environments默認develop.所以是develop的environment
    final Environment environment = configuration.getEnvironment();
    //通過環境獲取事物。在environment里配置了JDBC類型的事物==JdbcTransactionFactory;如果沒有配置則默認採用ManagedTransactionFactory
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    //構建事物對象 , 實際就是屬性的賦值
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    //獲取執行器 BatchExecutor、ReuseExecutor、SimpleExecutor , 選擇SimpleExecutor
    //因為默認有緩存,這裡會用CachingExecutor包裹原始Executor , 之後會加載各種插件
    final Executor executor = configuration.newExecutor(tx, execType);
    //返回DefaultSqlSession。寫死
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    closeTransaction(tx); // may have fetched a connection so lets call close()
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

Executor

  • Mybatis的數據庫執行器。Mybatis提供了一共四中Executor.這裏嚴格意義上應該說是三種 BatchExecutor、ReuseExecutor、SimpleExecutor。還有一個CachingExecutor。這裏為什麼不把他算上了。因為這個是一個全局的開關。在settings標籤的cacheEnabled設置的。說道這個標籤大家都知道這個就是二級緩存的開關。所以這裏CachingExecutor就不做介紹了。
  • SimpleExecutor是一種常規執行器,每次執行都會創建一個statement,用完后關閉。
  • ReuseExecutor是可重用執行器,將statement存入map中,操作map中的statement而不會重複創建statement。
  • BatchExecutor是批處理型執行器,doUpdate預處理存儲過程或批處理操作,doQuery提交並執行過程。
  • 關於Executor的選取也是在settings標籤控制的。defaultExecutorType。 默認是simple

SqlSession

  • 每個線程都有一個屬於自己的Sqlsession對象。這裏我們看成是一次Connection。他的生命周期應該是一次完成的事物處理過程。他是一個線程不安全的對象。在多線程操作的時候我們需要注意事物的隔離級別。我們操作時需要注意的是每次處理玩需要將他關閉。否則會造成資源浪費。在Mybaits中已經通過finnally把我們將他釋放了。

Mapper

  • Mapper是一個接口,我們可以將xml看成他的一個實現類。這裏的實現類虛化。通過Java代碼的調用實際將xml對應的sql發送給數據庫並獲取數據結果。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}
  • 通過上述代碼我們能夠發現。在獲取Mapper的時候先通過類型看是否被註冊了。然後根據類別獲取代理實例。

總結

  • 關於生命周期其實沒什麼重點。這一章節也比較簡單。我們只需要知道我們最終需要的Mapper。然後是如何從配置到獲取Mapper.這過程中哪些是全局的。哪些又適合做成復用的。

# 加入戰隊

微信公眾號

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

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

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

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

純羊絨毛衣展現顯瘦有具有設計感巧思的視覺美感

Giordano ladies的服裝以高端的材質和簡約的設計近幾年迅速在時裝界闖出名號,2019年秋冬Giordano ladies以線條與幾何造型為概念基調,融入豐富的材質,以不同紋理、透明度、觸感、色調,呈現出一系列優雅又現代的新品。這次有哪些必收商品推薦呢?一次告訴妳!

多層次雪紡展現輕盈感

本季設計中大量運用薄紗、雪紡等材質,呈現出女性的不同風格面貌、身型曲線與穿搭層次。黑色拼雪紡的nappa小牛夾克外套搭配多層次裙襬的下身造型,「重量對與層次上的堆棧」演繹出代表都市女性的柔中帶剛力量,此外也使簡約的黑色系衣着產生更豐富的細節變化。

克什米爾質料超柔軟

克什米爾(Cashmere)織料來自生長在海拔三千尺以上高寒地區的山羊,為抵抗極寒冬,牠們的羊毛髮展得更為綿密富有彈性,珍貴且得來不易,克什米爾製成的織品非常除了兼具保暖及舒適外,更具有溫潤的光澤及矜貴感,廣受女性的歡迎。本季giordano ladies運用cashmere設計出多款版型極佳的毛衣,帶給大家時尚的秋冬。

特殊剪裁超顯瘦

採用特殊的幾何剪裁做呈現,以修身的純羊絨毛衣展現顯瘦有具有設計感巧思的視覺美感,另外高領的毛衣搭配上傘狀的長裙,異材質與對比剪裁的搭配也讓整體造型更輕盈有氣勢。

馬毛此次換上了半透明的織法,形成更柔和細膩的觸感,此外,仿絨的純白ballerina毛衣及鉛筆裙,軟棉棉的手感犹如冬日的愷愷白雪,搭配一條具設計感不規則排列的珍珠項錬,質感立刻更上一階。

最後,冬季大衣一定不少!一身杏白的雙面羊毛大衣造型,透過材質與毛冷飾邊等細節,展示不同明暗的層次點綴與變化,沒有過多裝飾卻更加優雅自若,讓大家展現與眾不同的個人風格。

本站聲明:網站內容來源於http://www.shelive.net/,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

※聽過「電子菸」嗎?想知道與一般傳統香菸有何不同嗎?

電子煙能幫助戒菸嗎?專家學者以健康觀點帶您來了解 !

※新手該如何選擇電子菸口味及濃度呢?

※你應該要知道的電子煙懶人包!

電子煙有爭議?真相解密

※全台最大電子煙交易平台?

陳妍希內搭紅色T恤,感覺熱情的文藝風範

說道陳妍希大家都會想到她飾演《神鵰俠侶》吧,她雖在裏面飾演的小龍女深受大家爭議,但是也深受大家的喜愛,並在這部電視劇里收穫愛情和陳曉在2016年走向婚禮的殿堂並成為一名合格的媽媽。

陳妍希不僅有着外表清純斯文,笑容甜美,緊緻的五官和優美的線條,她的穿搭也是極富有吸引力的,她的穿搭盡顯女人的嬌媚和迷人的魅力。接下來就跟小編來扒一扒她的穿搭方式吧!

黑色西裝+紅色T恤+牛仔短褲

黑色西裝,穿出時尚休閑范,沒有往常西裝的沉悶感,把下身包裹着,特別有吸引力,內搭紅色T恤,感覺熱情的文藝風範,把休閑的西裝突顯出一絲柔和感。

牛仔短褲,彰顯青春的氣息,露出纖細的大長腿,盡顯身高,高跟鞋穿出了十足的女人味,整體穿搭出溫婉可人的休閑感。

深藍色西裝+短褲(帥氣)

深藍色西裝,後面採用褶皺來設計,突顯出時尚與個性,腰間腰鏈的設計,把不僅腰線展示出來還起着裝飾的作用,十分好看。

下身搭配同色系短褲,露出纖細白皙的美腿,十分養眼,手拿個性包包,整體穿搭出乾淨利落時尚不失帥氣的感覺。

拼色衛衣+拼色半身裙+高跟鞋(甜美)

拼色上衣,採用紅藍兩種顏色結合非常有協調性,上面的卡通圖案清新可人,彰顯青春感。

裙子也是採用兩種顏色搭配起來,前面是條紋,後面是白色卡通的穿出了與眾不同的個性化。

腳上灰色長腿襪極為吸睛的效果,整體穿搭出清新脫俗,娉婷動人的感覺。

牛仔套裝+白色T恤(灑脫 率真)

牛仔上衣,線條突顯出時尚與個性也不失整體的造型感,金色扣子和口袋上的鉚釘設計,灑脫不羈的性格,內搭白色T恤,感覺色彩上的不突兀和協調性特別的好。

同色系牛仔裙,前面的一排扣子,彰顯出裙子的造型與裝飾,高腰的設計拉長腿部盡顯身高,還能把腰部線條展示出來。整體穿搭出瀟灑率真的氣質。

黑色T恤+軍綠色風衣+短裙(飄逸 輕盈 )

黑色T恤,百搭簡約不失大方感,外穿軍綠色大衣,用同色系的寬大的腰帶束縛着,突顯出強大氣場和飄逸的感覺,腰帶設計特別的有吸引力,時尚不乏個性感。

黑色皮裙,是帥氣與性感並存的一件裙子,露出纖細的美腿,黑色高跟鞋,整體穿搭出輕盈飄逸的帥氣的感覺。

白色西裝+黑色打底+黑色半身裙(幹練 知性)

白色西裝,穿出仙氣,簡約大方的穿搭出乾淨利落的感覺,黑色打底,V領的設計露出性感鎖骨和脖頸,盡顯性感迷人的效果。

黑色半身裙,周邊全是白色鑽,極為耀眼,穿上黑色高跟鞋,露出纖細的大長腿,太誘惑了,整體的穿搭出女性幹練與優雅的知性美。

黑色皮衣+黑色針織衫+小腳褲(炫酷 時尚)

黑色皮衣,是彰顯時尚潮流的服飾,率真颯爽的感覺,黑色打底,百搭又大方,底下的小腳褲,緊身設計,感覺不拖沓利落的感覺,整體穿搭出青春活力張揚的個性。

黑色皮衣+白色襯衣+牛仔褲(青春 活力)

黑色皮衣,短款的設計盡顯嬌巧,不進行任何修飾的皮衣盡顯時尚大方,白色襯衣,不規則的襯衣打破了以往襯衣沉悶感,破洞牛仔褲盡顯青春與張揚,這樣穿搭出街休閑個性不是清純活力。

條紋風衣+白色T恤+黑色小腳褲(大氣 休閑)

條紋風衣,本身風衣會顯得成熟大氣的感覺,融合條紋的元素,反而顯得柔和增添了一襲時尚元素,白色T恤,簡約百搭不失文藝范。

黑色小腳褲,緊緻利落感十足,突顯出纖細的大長腿,白色休閑鞋,增添舒適和時尚感,整體穿搭出女性的大氣風格不失休閑時尚感。

黑色印花風衣+黑色打底+小腳褲(時尚 )

黑色風衣,上面的印花設計搭配打破了風衣的沉穩反而增添了美感,讓風衣變得時尚有個性,黑色打底搭配小腳褲,純黑的裝扮讓人看起來纖細苗條,整體穿搭出俏皮時尚感十足的風格。

灰色西裝+銀白色連體褲(高級 吸睛)

本站聲明:網站內容來源於http://www.shelive.net/,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

※聽過「電子菸」嗎?想知道與一般傳統香菸有何不同嗎?

電子煙能幫助戒菸嗎?專家學者以健康觀點帶您來了解 !

※新手該如何選擇電子菸口味及濃度呢?

※你應該要知道的電子煙懶人包!

電子煙有爭議?真相解密

※全台最大電子煙交易平台?

冬季的運動風不僅很舒服,簡單大方又保暖

冬季的衣服一層接着一層,你會不會感覺到了壓力,喜歡自由,討厭舒服的妹子們,今天一定要注意了,冬季的運動風不僅很舒服,還很美麗。所以不要把衣櫃里休閑的衣服收起來,利用起來用心的搭配一下,就會發現其中的奧妙。

第一套穿搭乍一看就非常驚艷,簡簡單單的衣服,但是搭配起來以後,就有一種高級感,運動風也是十足。破洞的牛仔外套,內搭白色秋衣,在加上黑色馬甲,搭配起來非常協調,讓整體看起來非常清新,減齡效果很好。

這樣的風格真的是應了那句話,可鹽可甜,非常帥氣的一身穿着,但是卻同樣露出一種青春甜美的氣息。女生必備的緊身黑褲,和牛仔衣搭配起來不僅顯腿長,腿直的女生真的會把優點放大,非常吸引人。一雙增高運動鞋,更是讓整體多了幾分高級感。

一雙長筒襪,露在運動鞋外面,讓整體有了層次感,這樣的一身穿搭,學生黨看到了一定很愛,當然對於喜歡外出的女生來說,也是非常適合的。非常完美的一身搭配,把平時簡單的牛仔外套,緊身黑褲,和運動鞋搭配的很協調,有一種時尚大氣的感覺。

第二套穿搭看起來就很甜美,喜歡粉色的女生,就可以選擇像這樣的一款羽絨外套,短款外套顯得整個人乾淨利落,這種顏色也很高級,不會顯俗氣。所以少女心還在的女孩子們,一定不要選錯了粉色,不一樣的粉色,穿出來的感覺也是不一樣的,一定不要雷了。

這樣一款寬鬆的外套,配上白色的緊身內搭,顯得很乾凈文雅,當然運動風最重要的是舒適,那麼外出時搭配上一頂帽子,可以遮住油頭,也會起到保暖的作用。那麼褲子的選擇也很簡單,黑色寬鬆的褲型會讓腿看起來很瘦也很直,就像下面這樣。

這樣的牛仔褲穿上很舒適,再配上一雙長筒條紋襪,加上一雙運動鞋,休閑又好看,這樣的搭配真的很。可以說是學生黨的首選了,不過事實上,無論是什麼風格的妹子,穿上都會很適合的。

接下來的最後一身,也是非常簡約的一種搭配,黑色的衛衣,黑色的緊身褲,顯瘦又百搭,吸收陽光帶來的溫暖,會讓整個人健康許多。紅色的包包,讓整體多了一種色彩,非常亮眼,黑色鉛筆褲非常修腿型,看上去也非常好看。一雙簡單的經典帆布鞋,可以說是所有女生必備的一雙鞋了,高幫帆布鞋和緊身黑褲搭配,更是會顯高顯瘦,視覺上增加了身高。

一件簡單的加絨牛仔外套,讓女生們不再瑟瑟發抖,非常休閑又好看,總體來說,這一身衣服並不是太繁雜的搭配,但是卻非常休閑,也不會顯庸俗。以上這幾套運動風穿搭,非常適合所有熱愛舒適和自由的女生,當然學生黨穿起來更是非常適合了。愛運動的女生運氣不會太差的,既美麗又健康。

本站聲明:網站內容來源於http://www.shelive.net/,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

※聽過「電子菸」嗎?想知道與一般傳統香菸有何不同嗎?

電子煙能幫助戒菸嗎?專家學者以健康觀點帶您來了解 !

※新手該如何選擇電子菸口味及濃度呢?

※你應該要知道的電子煙懶人包!

電子煙有爭議?真相解密

※全台最大電子煙交易平台?