標籤: 新北清潔

  • 中國電動車購買補助高於預期 特斯拉股價攀新高

    據新浪財經消息,受中國財政部宣布的電動汽車補貼縮減幅度低於市場預期,可望為特斯拉下月起在中國銷售Model S創造良好環境的影響,特斯拉汽車(TSLA)昨(10)日股價漲5.38%至196.56美元的新高。特斯拉方面表示,雖然Model S不在直接補貼範圍內,但希望中國也能考慮給予直接補貼待遇。

    據《新華社》報導,中國去(2013)年向電動車消費者每台車補貼約 3.5 至 6 萬元人民幣,而今年與明 (2015) 年補貼金額將分別減少至 5% 與 10%,僅為原先公布補貼縮減幅度的一半。

    特斯拉上月宣布的報告指出,去年第 4 季一共售出 6900 輛 Model S 車款,銷售輛季增 25%,也比先前預期高出 20%,也帶動公司股價穩定回升。

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

    【其他文章推薦】

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

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

    ※台北網頁設計公司全省服務真心推薦

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

  • 搶先日產 比亞迪全電動計程車隊進入倫敦

    中國汽車廠商比亞迪(BYD)今(11)日宣布,首支英國倫敦史上全電動計程車隊正式上路,在2018年前批量供應零排放出租車的競爭中,搶在了日產(Nissan)等國際競爭對手之前。不到2個月前,比亞迪還交付了倫敦史上首批全電動公共汽車。

    據《金融時報》報導,倫敦市長鮑里斯約翰遜(Boris Johnson)設定了全市計程車必須在2018年前實現零排放的目標,引發汽車廠商爭相開發新車。

    比亞迪趕在該期限之前率先打入了倫敦交通市場。比亞迪將推出20輛電動汽車組成的車隊,由出租車公司Thriev營運。

    另一方面,日本電動車廠商日產(Nissan)與英國經典黑出租車製造商倫敦出租車公司(London Taxi Company) ,也準備趕在2018年期限之前開發出全電動車型。

    著名的股神華倫•巴菲特(Warren Buffett)持有比亞迪9.9%股份。

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

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

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

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

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

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準

  • 萬向集團出價1.49億美元 最終競得菲斯科

    萬向集團出價1.49億美元 最終競得菲斯科

    據外電報道,中國汽車零件製造商萬向集團擊敗李澤楷旗下的Hybrid Tech,在美國豪華混合動力汽車製造商Fisker的破產資產拍賣中勝出。萬向出價1.492億美元,大約是Fisker最初尋求的收購價的6倍。

    這次拍賣持續了三天,經歷了19輪投標。美國破產法官Kevin Gross按計劃將於2月18日批准此次出售。

    菲斯科在2009年獲得了美國能源部的5.29億美元綠色貸款。但能源部在2011年中期凍結支付,稱菲斯科在開發新車型上狀況頻出,一再拖延。

    2013年11月,菲斯科申請破產,並要求破產法官準許Hybrid Tech以2500萬美元的低價,向美國能源部購入Fisker原本總值1.6億多美元的貸款。Hybrid Tech從而成為Fisker的高級擔保貸款人,更表明有意進一步收購Fisker。

    但無擔保債權人反對這一報價,從而幫助中國最大的汽車零部件供應商於12月進入到交易環節中。

    在此筆交易達成后,萬向集團將努力重振菲斯科在中國汽車市場的發展,該集團也獲得了一個打進美國市場的入口點。據資料顯示,萬向為中國投資美國製造業、新能源和房地產的先行者,在過去20年,萬向於美國的投資遍布美國14個州,涉及汽車零件製造、不動產、新能源和私募基金等。

    2012年底時,萬向曾以近2.6億美元擊敗江森自控,成功拍得美國破產電池生產商A123鋰電池公司資產,這家公司正是Fisker的電池供應商。經營汽車零部件業務的萬向集團一直期望進軍整車制造領域。

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

    【其他文章推薦】

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

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

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

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

  • 英最大單電動車合約易主 比亞迪遺憾出局

    據《英國每日電訊》昨(17)日報導,比亞迪推出倫敦首支全電動出租車隊的計劃受挫,倫敦第二大出租車服務商綠番茄出租車公司(Greentomatocars)表示,決定不再和比亞迪進行合作,而改為準備測試現代汽車的一款燃料電池汽車。

    這是英國史上最大一筆電動車交易。去年曾有傳言稱,倫敦充電樁設施不全,導致這批出租車無法按時上路。

    比亞迪已向綠番茄公司交付了20輛E6汽車,這批車輛隨后將由另一家運營商Thriev接盤。Thriev已在倫敦建造兩個快速充電站,可為E6汽車在兩個小時內完全完電。據比亞迪介紹,E6在充電完畢之后能續航186英里,比倫敦市場上日產聆風的124英里要高出不少。

    Thriev公司發言人還表示,公司將在18-24個月內打造一支由1000輛電動汽車所組成的電動車隊。Thriev還與英國天然氣集團進行了接洽,商談如何在倫敦建立多個電動汽車充電站事宜。

    倫敦市長辦公室曾表示,出租車貢獻了倫敦所有尾氣排放的逾三分之一,推廣零排放出租車是政府將英國打造成重要電動汽車市場舉措的一部分。倫敦市長鮑里斯•約翰遜也設定了全市出租車必須在2018年前實現零排放的目標。

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

    【其他文章推薦】

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

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

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

    ※幫你省時又省力,新北清潔一流服務好口碑

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

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

  • 北汽集團將成為美國新能源公司Atieva第一大股東

    北京汽車集團有限公司(北汽集團)昨(17)日宣布,與美國新能源公司Atieva簽署股份認購協議,北汽集團將收購Atieva公司25.02%的股份。收購完成後,北汽將成為Atieva的第一大股東,雙方預計在第3年推出與奧迪A6L同等級的電動汽車。

    由於美國電動車大廠特斯拉也正和北汽股份洽談合作事項,昨日北汽旗下的上市公司福田汽車,股價也跟著漲停。

    去年,北汽集團新成立了新能源汽車公司,而此次收購的美國Atieva公司是一家新能源汽車核心系統提供商,曾主要參與過Tesla Roadster純電動跑車、雪佛蘭Volt插電式混合電動車、奧迪R8純電動跑車的開發。

    北汽集團方面也表示,此次收購主要是為進一步提升北汽集團及下屬公司在新能源汽車尤其是高端純電動汽車領域的設計、研發和制造的能力和水平。

    據悉,北汽由6家股東組成,除北汽集團以51%的股比成為控股股東外,首鋼股份有限公司以18.31%的股比成為第二大股東,其他股東包括北京市國資公司、現代創新控股公司及京能集團,而北京市國資委直屬的投融資平台-北京國有資本經營管理中心也持股5%。

    另據外媒稍早報導,北汽可望在2014年第2季在香港IPO上市,籌資額度或達到20億美元。

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

    【其他文章推薦】

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

    ※超省錢租車方案

    FB行銷專家,教你從零開始的技巧

  • 第二屆新能源車與充電技術中國國際論壇

    地點:上海綠地萬豪酒店 /中國上海黃浦區江濱路99號(靠近打浦路)
    會議背景:

        新能源汽車是國務院確定的重要戰略性新興產業,是我國最具發展潛力的重要領域之一。近日來中國大面積飽受霧霾天氣困擾的嚴峻形勢,更使得加快新能源汽車的發展迫在眉睫。隨著新一輪新能源車補貼政策的出臺以及首批28個示範運行城市的公佈,新能源汽車產業展現出良好的發展態勢。

        中國政府和車企致力於電動車在全國範圍內的大規模推廣。工信部發佈,我國2015年應有規模將達到50萬輛,到2020年將達到500萬輛。據專家預測,中國十年內將有望成為最大的新能源車市場。為適應電動汽車發展要求,國家將在“十二五”期間,建設充換電站2351座,充電樁22萬股,初步建成覆蓋公司經營區域的智慧充換電服務網路。與此同時,行業在積累了一些寶貴的經驗的同時也出現了在技術路徑、政策導向、基礎設施、商業模式、行業標準等方面急需解決的一些突出問題。

        比亞迪,奇瑞,大眾,通用,富豪等車企都在為滿足新一輪中國客戶對新能源汽車的需求謀劃新局,為更好的享受政府補貼而將新能源汽車技術和生產實現本土化。

      由全國電力技術市場協會機構指導,OPPLAND MEDIA策劃組織的第二屆新能源車與充電技術中國國際論壇將於2014年4月29-30日在上海舉辦。大會網站:

        第二屆新能源車與充電技術中國國際論壇2014旨在加快新能源車推廣,促進中國電動車產業發展,進一步加強行業交流,深入探討技術創新,共同分享中國現有電動車示範城市專案運營經驗,為中國新能源車發展及環境治理出一份力。

        論壇將圍繞“清除環境與政策上的霧霾”為主題,就新能源汽車關鍵技術發展與應用,產業創新研發機制,示範運營與市場推廣、基礎配套設施建設、國際合作與交流等一系列熱點問題展開廣泛地探討與合作,推動新能源汽車產業化、市場化、國際化。本次論壇將邀請國家發展改革委等國家有關部門領導,歐洲汽車工業協會,美國加州大學、美國電動車聯盟等國際組織、中國汽車工業協會、中國汽車工程學會、中國汽車技術研究中心、清華大學、同濟大學等行業組織和研究機構的專家學者,美國加州,挪威、倫敦、北京、上海、天津、廣州、深圳等中外地方政府官員,以及國內外各大汽車集團高層,共同圍繞節能與新能源汽車產業發展的宏觀視角、政策與管理、戰略目標與方向、汽車企業的節能與新能源戰略與行動、新能源汽車發展中的新競爭以及商業探索與經驗交流等議題進行深入研討和交流。

    會議板塊:

    1. 中國新能源車政策和市場發展
    2. 建立中國新能源車標準化體系規範之路
    3. 新能源車電池和電機控制系統科技創新
    4. 中國電動車充電基礎設施建設新進展

    會議熱點:
    中國發展新能源車市場應用的路線圖
    中國新能源車市場的投資計畫以及帶給全球OEM的機會
    中國新能源車財政補貼從地方政府到車企的政策更新
    下一輪新能源車推廣應用項目概覽
    純電動車,混動車等新能源車技術交流
    下一代新能源車新電池組件和關鍵技術
    中國充電基礎設施和車企之間的協調發展
    BMS電池管理系統迎合中國新能源車增長的精細化管理
    新能源車快速發展的產業需求和規劃
    統一存在于車企、政府和供應商之間的不同標準

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

    【其他文章推薦】

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

    ※超省錢租車方案

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

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

  • 一對多分頁的SQL到底應該怎麼寫?

    一對多分頁的SQL到底應該怎麼寫?

    1. 前言

    MySQL一對多的數據分頁是非常常見的需求,比如我們要查詢商品和商品的圖片信息。但是很多人會在這裏遇到分頁的誤區,得到不正確的結果。今天就來分析並解決這個問題。

    2. 問題分析

    我們先創建一個簡單商品表和對應的商品圖片關係表,它們之間是一對多的關係:

    然後我分別寫入了一些商品和這些商品對應的圖片,通過下面的左連接查詢可以看出它們之間具有明顯的一對多關係:

    SELECT P.PRODUCT_ID, P.PROD_NAME, PI.IMAGE_URL
    FROM PRODUCT_INFO P
             LEFT JOIN PRODUCT_IMAGE PI
                       ON P.PRODUCT_ID = PI.PRODUCT_ID
    

    按照傳統的思維我們的分頁語句會這麼寫:

        <resultMap id="ProductDTO" type="cn.felord.mybatis.entity.ProductDTO">
            <id property="productId" column="product_id"/>
            <result property="prodName" column="prod_name"/>
            <collection property="imageUrls"  ofType="string">
                <result column="image_url"/>
            </collection>
        </resultMap>
    
        <select id="page" resultMap="ProductDTO">
            SELECT P.PRODUCT_ID, P.PROD_NAME,PI.IMAGE_URL
            FROM PRODUCT_INFO P
                     LEFT JOIN PRODUCT_IMAGE PI
                               ON P.PRODUCT_ID = PI.PRODUCT_ID
            LIMIT #{current},#{size}
        </select>               
    

    當我按照預想傳入了(0,2)想拿到前兩個產品的數據,結果並不是我期望的:

    2020-06-21 23:35:54.515 DEBUG 10980 --- [main] c.f.m.mappers.ProductInfoMapper.page     : ==>  Preparing: SELECT P.PRODUCT_ID, P.PROD_NAME,PI.IMAGE_URL FROM PRODUCT_INFO P LEFT JOIN PRODUCT_IMAGE PI ON P.PRODUCT_ID = PI.PRODUCT_ID limit ?,? 
    2020-06-21 23:35:54.541 DEBUG 10980 --- [main] c.f.m.mappers.ProductInfoMapper.page     : ==> Parameters: 0(Long), 2(Long)
    2020-06-21 23:35:54.565 DEBUG 10980 --- [main] c.f.m.mappers.ProductInfoMapper.page     : <==      Total: 2
    page = [ProductDTO{productId=1, prodName='杯子', imageUrls=[http://asset.felord.cn/cup1.png, http://asset.felord.cn/cup2.png]}]
    

    我期望的兩條數據是杯子和筆記本,但是結果卻只有一條。原來當一對多映射時結果集會按照多的一側進行輸出(期望4條數據,實際上會有7條),而前兩條展示的只會是杯子的數據(如上圖),合併后就只有一條結果了,這樣分頁就對不上了。那麼如何才能達到我們期望的分頁效果呢?

    3. 正確的方式

    正確的思路是應該先對主表進行分頁,再關聯從表進行查詢。

    拋開框架,我們的SQL應該先對產品表進行分頁查詢然後再左關聯圖片表進行查詢:

    SELECT P.PRODUCT_ID, P.PROD_NAME, PI.IMAGE_URL
    FROM (SELECT PRODUCT_ID, PROD_NAME
          FROM PRODUCT_INFO
          LIMIT #{current},#{size}) P
             LEFT JOIN PRODUCT_IMAGE PI
                       ON P.PRODUCT_ID = PI.PRODUCT_ID
    

    這種寫法的好處就是通用性強一些。但是MyBatis提供了一個相對優雅的路子,思路依然是開頭所說的思路。只不過我們需要改造上面的Mybatis XML配置:

    <resultMap id="ProductDTO" type="cn.felord.mybatis.entity.ProductDTO">
        <id property="productId" column="product_id"/>
        <result property="prodName" column="prod_name"/>
         <!-- 利用 collection 標籤提供的 select 特性 和 column   -->
        <collection property="imageUrls" ofType="string" select="selectImagesByProductId" column="product_id"/>
    </resultMap>
    <!-- 先查詢主表的分頁數據    -->
    <select id="page" resultMap="ProductDTO">
        SELECT PRODUCT_ID, PROD_NAME
        FROM PRODUCT_INFO
        LIMIT #{current},#{size}
    </select>
    <!--根據productId 查詢對應的圖片-->
    <select id="selectImagesByProductId" resultType="string">
        SELECT IMAGE_URL
        FROM PRODUCT_IMAGE
        WHERE PRODUCT_ID = #{productId}
    </select>
    

    4. 總結

    大部分情況下分頁是很容易的,但是一對多還是有一些小小的陷阱的。一旦我們了解了其中的機制,也並不難解決。當然如果你有更好的解決方案可以留言討論,集思廣益。多多關注:碼農小胖哥,獲取更多開發技巧。

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

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

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

    【其他文章推薦】

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

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

    ※台北網頁設計公司全省服務真心推薦

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

  • 深入理解進程,線程,協程

    深入理解進程,線程,協程

    今日得到

    • 計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決

    • 併發:Do not communicate by sharing memory; instead, share memory by communicate. (不要以共享內存的方式來通信,相反,要通過通信來共享內存)

    1. 進程

    進程是系統進行資源分配和調度的一個獨立單位,程序段、數據段、PCB三部分組成了進程實體(進程映像),PCB是進程存在的唯一標準

    1.1 進程的組織方式:

    • 鏈接方式
      • 按照進程狀態將PCB分為多個隊列,就緒隊列,阻塞隊列等
      • 操作系統持有指向各個隊列的指針
    • 索引方式
      • 根據進程狀態的不同,建立幾張索引表
      • 操作系統持有指向各個索引表的指針

    1.2 進程的狀態

    • 創建態: 操作系統為進程分配資源,初始化PCB

    • 就緒態:運行資源等條件都滿足,存儲在就緒隊列中,等待CPU調度

    • 運行態:CPU正在執行進程

    • 阻塞態:等待某些條件滿足,等待消息回復,等待同步鎖,sleep等,阻塞隊列

    • 終止態 :回收進程擁有的資源,撤銷PCB

    1.3 進程的切換和調度

    進程在操作系統內核程序臨界區中不能進行調度與切換

    臨界資源:一個時間段內只允許一個進程使用資源,各進程需要互斥地訪問臨界資源

    臨界區:訪問臨界資源的代碼

    內核程序臨界區:訪問某種內核數據結構,如進程的就緒隊列(存儲各進程的PCB)

    進程調度的方式:

    • 非剝奪調度方式(非搶佔方式),只允許進程主動放棄處理機,在運行過程中即便有更緊迫的任務到達,當前進程依然會繼續使用處理機,直到該進程終止或者主動要求進入阻塞態
    • 剝奪調度方式(又稱搶佔方式)當一個進程正在處理機上執行時,如果有一個優先級更高的進程需要處理機,則立即開中斷暫停正在執行的進程,將處理機飯呢陪給優先級高的那個進程

    進程的切換與過程:進程的調度、切換是有代價的

    1. 對原來運行進程各種數據的保存
    2. 對新的進程各種數據恢復(程序計數器,程序狀態字,各種數據寄存器等處理機的現場)

    進程調度算法的相關參數:

    • CPU利用率:CPU忙碌時間/作業完成的總時間
    • 系統吞吐量:單位時間內完成作業的數量
    • 周轉時間:從作業被提交給系統開始,到作業完成為止的時間間隔 = 作業完成時間-作業提交時間
    • 帶權周轉時間:(由於周轉時間相同的情況下,可能實際作業的運行時間不一樣,這樣就會給用戶帶來不一樣的感覺) 作業周轉時間/作業實際運行時間, 帶權周轉時間>=1, 越小越好
    • 平均帶權周轉時間:各作業帶權周轉時間之和/作業數
    • 等待時間
    • 響應時間

    調度算法:

    算法思想,用於解決什麼問題?

    算法規則,用於作業(PCB作業)調度還是進程調度?

    搶佔式還是非搶佔式的?

    優缺點?是否會導致飢餓?

    以下調度算法是適用於當前交互式操作系統

    • 時間片輪轉(Round-Robin)
      • 算法思想:公平地、輪流地為各個進程服務,讓每個進程在一定時間間隔內可以得到相應
      • 算法規則:按照各進程到達就緒隊列的順序,輪流讓各個進程執行一個時間片(如100ms)。若進程未在一個時間片內執行完,則剝奪處理機,將進程重新放到就緒隊列隊尾重新排隊。
      • 用於作業/進程調度:用於進程的調度(只有作業放入內存建立相應的進程后,才會被分配處理機時間片)
      • 是否可搶佔?若進程未能在規定時間片內完成,將被強行剝奪處理機使用權,由時鐘裝置發出時鐘中斷信號來通知CPU時間片到達
      • 優缺點:適用於分時操作系統,由於高頻率的進程切換,因此有一定開銷;不區分任務的緊急程度
      • 是否會導致飢餓? 不會
    • 優先級調度算法
      • 算法思想:隨着計算機的發展,特別是實時操作系統的出現,越來越多的應用場景需要根據任務的進程成都決定處理順序
      • 算法規則:每個作業/進程有各自的優先級,調度時選擇優先級最高的作業/進程
      • 用於作業/進程調度:即可用於作業調度(處於外存後備隊列中的作業調度進內存),也可用於進程調度(選擇就緒隊列中的進程,為其分配處理機),甚至I/O調度
      • 是否可搶佔? 具有可搶佔版本,也有非搶佔式的
      • 優缺點:適用於實時操作系統,用優先級區分緊急程度,可靈活地調整對各種作業/及進程的偏好程度。缺點:若源源不斷地提供高優先級進程,則可能導致飢餓
      • 是否會導致飢餓: 會
    • 多級反饋隊列調度算法
      • 算法思想:綜合FCFS、SJF(SPF)、時間片輪轉、優先級調度

      • 算法規則:

        • 1.設置多級就緒隊列,各級別隊列優先級從高到底,時間片從小到大
        • 2.新進程到達時先進入第1級隊列,按照FCFS原則排隊等待被分配時間片,若用完時間片進程還未結束,則進程進入下一級隊列隊尾
        • 3.只有第k級別隊列為空時,才會為k+1級對頭的進程分配時間片
      • 用於作業/進程調度:用於進程調度

      • 是否可搶佔? 搶佔式算法。在k級隊列的進程運行過程中,若更上級別的隊列(1-k-1級)中進入一個新進程,則由於新進程處於優先級高的隊列中,因此新進程會搶佔處理機,原理運行的進程放回k級隊列隊尾。

      • 優缺點:對各類型進程相對公平(FCFS的有點);每個新到達的進程都可以很快就得到相應(RR優點);短進程只用較少的時間就可完成(SPF)的有點;不必實現估計進程的運行時間;可靈活地調整對各類進程的偏好程度,比如CPU密集型進程、I/O密集型進程(拓展:可以將因I/O而阻塞的進程重新放回原隊列,這樣I/O型進程就可以保持較高優先級)

      • 是否會導致飢餓: 會

    2. 線程

    引入線程之後,進程只作為除CPU之外的系統資源的分配單元(如:打印機,內存地址空間等都是分配給進程的)

    線程的是實現方式:

    • 用戶級線程(User-Level Thread),用戶級線程由應用程序通過線程庫是實現如python (import thread), 線程的管理工作由應用程序負責。
    • 內核級線程(kernel-Level Thread),內核級線程的管理工作由操作系統內核完成,線程調度,切換等工作都由內核負責,因此內核級線程的切換必然需要在核心態下才能完成

    進程和線程的關係:一條線程指的是進程中一個單一順序的控制流,一個進程中可以併發多個線程,每條線程并行執行不同的任務。CPU的最小調度單元是線程,所以單進程多線程是可以利用多核CPU的。

    2.1 線程模型:

    • 用戶級線程模型(一對多模型)

    多個用戶態的線程對應着一個內核線程,程序線程的創建、終止、切換或者同步等線程工作必須自身來完成。python就是這種。雖然可以實現異步,但是不能有效利用多核(GIL)

    • 內核級線程模型 (一對一)

    這種模型直接調用操作系統的內核線程,所有線程的創建、終止、切換、同步等操作,都由內核來完成。C++就是這種

    • 兩級線程模型(M:N)

    這種線程模型會先創建多個內核級線程,然後用自身的用戶級線程去對應創建的多個內核級線程,自身的用戶級線程需要本身程序去調度,內核級的線程交給操作系統內核去調度。GO語言就是這種。

    python中的多線程因為GIL的存在,並不能利用多核CPU優勢,但是在阻塞的系統調用中,如sock.connect(), sock.recv()等耗時的I/O操作,當前的線程會釋放GIL,讓出處理器。但是單個線程內,阻塞調用上還是阻塞的。除了GIL之外,所有的多線程還有通病,他們都是被OS調用的,調度策略是搶佔式的,以保證同等有限級的線程都有機執行,帶來的問題就是:並不知道下一刻執行那個線程,也不知道正在執行什麼代碼,會存在競態條件

    3. 協程

    協程通過在線程中實現調度,避免了陷入內核級別的上下文切換造成的性能損失,進而突破了線程在IO上的性能瓶頸。

    python的協程源於yield指令

    • yield item 用於產出一個值,反饋給next()的調用方法
    • 讓出處理機,暫停執行生成器,讓調用方繼續工作,直到需要使用另一個值時再調用next()

    協程式對線程的調度,yield類似惰性求職方式可以視為一種流程控制工具,實現協作式多任務,python3.5引入了async/await表達式,使得協程證實在語言層面得到支持和優化,大大簡化之前的yield寫法。線程正式在語言層面得到支持和優化。線程是內核進行搶佔式調度的,這樣就確保每個線程都有執行的機會。而coroutine運行在同一個線程中,有語言層面運行時中的EventLoop(事件循環)來進行調度。在python中協程的調度是非搶佔式的,也就是說一個協程必須主動讓出執行機會,其他協程才有機會運行。讓出執行的關鍵字 await, 如果一個協程阻塞了,持續不讓出CPU處理機,那麼整個線程就卡住了,沒有任何併發。

    PS: 作為服務端,event loop最核心的就是I/O多路復用技術,所有來自客戶端的請求都由I/O多路復用函數來處理;作為客戶端,event loop的核心在於Future對象延遲執行,並使用send函數激發協程,掛起,等待服務端處理完成返回后再調用Callback函數繼續執行。[python 協程與go協程的區別]

    3.1 Golang 協程

    Go 天生在語言層面支持,和python類似都是用關鍵字,而GO語言使用了go關鍵字,go協程之間的通信,採用了channel關鍵字。

    go實現了兩種併發形式:

    • 多線程共享內存:如Java 或者C++在多線程中共享數據的時候,通過鎖來訪問
    • Go語言特有的,也是Go語言推薦的 CSP(communicating sequential processes)併發模型。
    package main 
    
    import ("fmt")
    
    func main() {
        jobs := make(chan int)
        done := make(chan bool)  // end flag
        
        go func() {
            for {
                j, ok := <- jobs 
                fmt.Println("---->:", j, ok)
                if ok {
                    fmt.Println("received job")
                } else {
                    fmt.Println("end received jobs")
                    done <- true
                    return
                }
            }
        }()
        
        go func() {
            for j:= 1; j <= 3; j++ {
                jobs <-j
                fmt.Println("sent job", j)
            }
            close(jobs)
            fmt.Println("close(jobs)")
        }()
        
        fmt.Println("sent all jobs")
        <-done  // 阻塞 讓main等待協程完成
    }
    

    Go的CSP併發模型是通過goroutine 和 channel來實現的。

    • goroutine是go語言中併發的執行單位。
    • channel是Go語言中各個併發結構體之間的通信機制。
      • channel -< data 寫數據
      • <- channel 讀數據

    協程本質上來說是一種用戶態的線程,不需要系統來執行搶佔式調度,而是在語言測個面實現線程的調度。

    4. 併發

    併發:Do not communicate by sharing memory; instead, share memory by communicate.

    4.1 Actor模型

    Actor模型和CSP模型的區別:

    • CSP並不Focus發送消息的實體/Task, 而是關注發送消息時消息所使用的載體,即channel。
    • 在Actor的設計中,Actor與信箱是耦合的,而在CSP中channel是作為first-class獨立存在的
    • Actor中有明確的send/receive關係,而channel中並不區分這樣的關係,執行快可以任意選擇發送或者取消息

    好文推薦:Go/Python/Erlang編程語言對比分析及示例

    4.4 Go 協程調度器 GPM

    • G 指的是Goroutine,其本質上也是一種輕量級的線程
    • P proessor, 代表M所需要的上下文環境,也是處理用戶級代碼邏輯處理器。同一時間只有一個線程(M)可以擁有P, P中的數據都是鎖自由(lock free)的, 讀寫這些數據的效率會非常的高
    • M Machine,一個M直接關聯一個內核線程,可以運行go代碼 即goroutine, M運行go代碼需要一個P, 另外就是運行原生代碼,如 syscall。運行原生代碼不需要P。

    一個M會對應一個內核線程,一個M也會連接一個上下文P,一個上下文P相當於一個“處理器”,一個上下文連接一個或者多個Goroutine。P(Processor)的數量是在啟動時被設置為環境變量GOMAXPROCS的值,或者通過運行時調用函數runtime.GOMAXPROCS()進行設置

    erlang和golang都是採用CSP模型,python中協程是eventloop模型。但是erlang是基於進程的消息通信,go是基於goroutine和channel通信。

    python和golang都引入了消息調度系統模型,來避免鎖的影響和進程線程的開銷問題。

    計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決 — G-P-M模型正是此理論踐行者,此理論也用到了python的asyncio對地獄回調的處理上(使用Task+Future避免回調嵌套),是不是巧合?
    其實異步≈可中斷的函數+事件循環+回調,go和python都把嵌套結構轉換成列表結構有點像算法中的遞歸轉迭代.

    調度器在計算機中是分配工作時所需要的資源,Linux的調度是CPU找到可運行的線程,Go的調度是為M線程找到P(內存,執行票據)和可運行的G(協程)

    Go協程是輕量級的,棧初始2KB(OS操作系統的線程一般都是固有的棧內存2M), 調度不涉及系統調用,用戶函數調用前會檢查棧空間是否足夠,不夠的話,會進行站擴容,棧大小限制可以達到1GB。

    Go的網絡操作是封裝了epoll, 為NonBlocking模式,切換協程不阻塞線程。

    Go語言相比起其他語言的優勢在於OS線程是由OS內核來調度的,goroutine則是由Go運行時(runtime)自己的調度器調度的,這個調度器使用一個稱為m:n調度的技術(復用/調度m個goroutine到n個OS線程)。 其一大特點是goroutine的調度是在用戶態下完成的, 不涉及內核態與用戶態之間的頻繁切換,包括內存的分配與釋放,都是在用戶態維護着一塊大的內存池, 不直接調用系統的malloc函數(除非內存池需要改變),成本比調度OS線程低很多。 另一方面充分利用了多核的硬件資源,近似的把若干goroutine均分在物理線程上, 再加上本身goroutine的超輕量,以上種種保證了go調度方面的性能。點我了解更多

    4.5 Go 調度器的實現 以及搶佔式調度

    legendtkl阿里雲技術專家

    Golang源碼探索(二) 協程的實現原理

    相關參考文獻:

    王道操作系統

    操作系統中調度算法(FCFS、RR、SPN、SRT、HRRN)

    Python協程與Go協程的區別二

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

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

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

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

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

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準

  • 010.OpenShift綜合實驗及應用

    010.OpenShift綜合實驗及應用

    實驗一 安裝OpenShift

    1.1 前置準備

    [student@workstation ~]$ lab review-install setup

    1.2 配置規劃

    OpenShift集群有三個節點:

    • master.lab.example.com:OpenShift master節點,是一個不可調度pod的節點。
    • node1.lab.example.com:一個OpenShift節點,它可以同時運行應用程序和基礎設施pod。
    • node2.lab.example.com:另一個OpenShift節點,它可以同時運行應用程序和基礎設施pod。

    所有節點都使用帶有overlay2驅動程序的OverlayFS來存儲Docker,每個節點中的第二個磁盤(vdb)保留給Docker存儲。

    所有節點都將使用基於rpm的安裝,使用release v3.9和OpenShift image tag version v3.9.14。

    路由的默認域是apps.lab.example.com。Classroom DNS服務器已經配置為將此域中的所有主機名解析為node1.lab.example.com。

    OpenShift集群使用的所有容器image都存儲在registry.lab.example.com提供的私有倉庫中。

    使用兩個基於HTPasswd身份驗證的初始用戶:developer和admin,起密碼都是redhat,developer作為普通用戶,admin作為集群管理員。

    services.lab.example.com中的NFS卷作為OpenShift內部倉庫的持久存儲支持。

    services.lab.example.com也為集群存儲提供NFS服務。

    etcd也部署在master節點上,同時存儲使用services.lab.example.com主機提供的NFS共享存儲。

    集群必須與Internet斷開連接,即使用離線包形式。

    內部OpenShift倉庫應該由NFS持久存儲支持,存儲位於services.lab.example.com。

    master API和控制台將在端口443上運行。

    安裝OpenShift所需的RPM包由已經在所有主機上使用Yum配置文件定義完成。

    /home/student/DO280/labs/review-install文件夾為OpenShift集群的安裝提供了一個部分完成的Ansible目錄文件。這個文件夾中包含了執行安裝前和安裝後步驟所需的Ansible playbook。

    測試應用程序由Git服務器http://services.lab.example.com/phphelloworld提供。這是一個簡單的“hello, world”應用程序。可以使用Source-to-Image來部署這個應用程序,以驗證OpenShift集群是否已部署成功。

    1.3 確認Ansible

      1 [student@workstation ~]$ cd /home/student/DO280/labs/review-install/
      2 [student@workstation review-install]$ sudo yum -y install ansible
      3 [student@workstation review-install]$ ansible --version
      4 [student@workstation review-install]$ cat ansible.cfg
      5 [defaults]
      6 remote_user = student
      7 inventory = ./inventory
      8 log_path = ./ansible.log
      9 
     10 [privilege_escalation]
     11 become = yes
     12 become_user = root
     13 become_method = sudo

    1.4 檢查Inventory

      1 [student@workstation review-install]$ cp inventory.preinstall inventory		#此為準備工作的Inventory
      2 [student@workstation review-install]$ cat inventory
      3 [workstations]
      4 workstation.lab.example.com
      5 
      6 [nfs]
      7 services.lab.example.com
      8 
      9 [masters]
     10 master.lab.example.com
     11 
     12 [etcd]
     13 master.lab.example.com
     14 
     15 [nodes]
     16 master.lab.example.com
     17 node1.lab.example.com
     18 node2.lab.example.com
     19 
     20 [OSEv3:children]
     21 masters
     22 etcd
     23 nodes
     24 nfs
     25 
     26 #Variables needed by the prepare_install.yml playbook.
     27 [nodes:vars]
     28 registry_local=registry.lab.example.com
     29 use_overlay2_driver=true
     30 insecure_registry=false
     31 run_docker_offline=true
     32 docker_storage_device=/dev/vdb

    提示:

    Inventory定義了六個主機組:

    • nfs:為集群存儲提供nfs服務的環境中的vm;
    • masters:OpenShift集群中用作master角色的節點;
    • etcd:用於OpenShift集群的etcd服務的節點,本環境中使用master節點;
    • node:OpenShift集群中的node節點;
    • OSEv3:組成OpenShift集群的所有接待,包括master、etcd、node或nfs組中的節點。

    注意:默認情況下,docker使用在線倉庫下載容器映像。本環境內部無網絡,因此將docker倉庫配置為內部私有倉庫。在yml中使用變量引入倉庫配置。

    此外,安裝會在每個主機上配置docker守護進程,以使用overlay2 image驅動程序存儲容器映像。Docker支持許多不同的image驅動。如AUFS、Btrfs、Device mapper、OverlayFS。

    1.5 確認節點

      1 [student@workstation review-install]$ cat ping.yml
      2 ---
      3 - name: Verify Connectivity
      4   hosts: all
      5   gather_facts: no
      6   tasks:
      7     - name: "Test connectivity to machines."
      8       shell: "whoami"
      9       changed_when: false
     10 [student@workstation review-install]$ ansible-playbook -v ping.yml

    1.6 準備工作

      1 [student@workstation review-install]$ cat prepare_install.yml
      2 ---
      3 - name: "Host Preparation: Docker tasks"
      4   hosts: nodes
      5   roles:
      6     - docker-storage
      7     - docker-registry-cert
      8     - openshift-node
      9 
     10   #Tasks below were not handled by the roles above.
     11   tasks:
     12     - name: Student Account - Docker Access
     13       user:
     14         name: student
     15         groups: docker
     16         append: yes
     17 
     18 ...
     19 [student@workstation review-install]$ ansible-playbook prepare_install.yml

    提示:如上yml引入了三個role,具體role內容參考《002.OpenShift安裝與部署》2.5步驟。

    1.7 確認驗證

      1 [student@workstation review-install]$ ssh node1 'docker pull rhel7:latest' #驗證是否可以正常pull image

    1.8 檢查Inventory

      1 [student@workstation review-install]$ cp inventory.partial inventory		#此為正常安裝的完整Inventory
      2 [student@workstation review-install]$ cat inventory
      3 [workstations]
      4 workstation.lab.example.com
      5 
      6 [nfs]
      7 services.lab.example.com
      8 
      9 [masters]
     10 master.lab.example.com
     11 
     12 [etcd]
     13 master.lab.example.com
     14 
     15 [nodes]
     16 master.lab.example.com
     17 node1.lab.example.com openshift_node_labels="{'region':'infra', 'node-role.kubernetes.io/compute':'true'}"
     18 node2.lab.example.com openshift_node_labels="{'region':'infra', 'node-role.kubernetes.io/compute':'true'}"
     19 
     20 [OSEv3:children]
     21 masters
     22 etcd
     23 nodes
     24 nfs
     25 
     26 #Variables needed by the prepare_install.yml playbook.
     27 [nodes:vars]
     28 registry_local=registry.lab.example.com
     29 use_overlay2_driver=true
     30 insecure_registry=false
     31 run_docker_offline=true
     32 docker_storage_device=/dev/vdb
     33 
     34 
     35 [OSEv3:vars]
     36 #General Variables
     37 openshift_disable_check=disk_availability,docker_storage,memory_availability
     38 openshift_deployment_type=openshift-enterprise
     39 openshift_release=v3.9
     40 openshift_image_tag=v3.9.14
     41 
     42 #OpenShift Networking Variables
     43 os_firewall_use_firewalld=true
     44 openshift_master_api_port=443
     45 openshift_master_console_port=443
     46 #default subdomain
     47 openshift_master_default_subdomain=apps.lab.example.com
     48 
     49 #Cluster Authentication Variables
     50 openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', 'challenge': 'true', 'kind': 'HTPasswdPasswordIdentityProvider', 'filename': '/etc/origin/master/htpasswd'}]
     51 openshift_master_htpasswd_users={'admin': '$apr1$4ZbKL26l$3eKL/6AQM8O94lRwTAu611', 'developer': '$apr1$4ZbKL26l$3eKL/6AQM8O94lRwTAu611'}
     52 
     53 #Need to enable NFS
     54 openshift_enable_unsupported_configurations=true
     55 #Registry Configuration Variables
     56 openshift_hosted_registry_storage_kind=nfs
     57 openshift_hosted_registry_storage_access_modes=['ReadWriteMany']
     58 openshift_hosted_registry_storage_nfs_directory=/exports
     59 openshift_hosted_registry_storage_nfs_options='*(rw,root_squash)'
     60 openshift_hosted_registry_storage_volume_name=registry
     61 openshift_hosted_registry_storage_volume_size=40Gi
     62 
     63 #etcd Configuration Variables
     64 openshift_hosted_etcd_storage_kind=nfs
     65 openshift_hosted_etcd_storage_nfs_options="*(rw,root_squash,sync,no_wdelay)"
     66 openshift_hosted_etcd_storage_nfs_directory=/exports
     67 openshift_hosted_etcd_storage_volume_name=etcd-vol2
     68 openshift_hosted_etcd_storage_access_modes=["ReadWriteOnce"]
     69 openshift_hosted_etcd_storage_volume_size=1G
     70 openshift_hosted_etcd_storage_labels={'storage': 'etcd'}
     71 
     72 #Modifications Needed for a Disconnected Install
     73 oreg_url=registry.lab.example.com/openshift3/ose-${component}:${version}
     74 openshift_examples_modify_imagestreams=true
     75 openshift_docker_additional_registries=registry.lab.example.com
     76 openshift_docker_blocked_registries=registry.access.redhat.com,docker.io
     77 openshift_web_console_prefix=registry.lab.example.com/openshift3/ose-
     78 openshift_cockpit_deployer_prefix='registry.lab.example.com/openshift3/'
     79 openshift_service_catalog_image_prefix=registry.lab.example.com/openshift3/ose-
     80 template_service_broker_prefix=registry.lab.example.com/openshift3/ose-
     81 ansible_service_broker_image_prefix=registry.lab.example.com/openshift3/ose-
     82 ansible_service_broker_etcd_image_prefix=registry.lab.example.com/rhel7/
     83 [student@workstation review-install]$ lab review-install verify		#本環境使用腳本驗證

    1.9 安裝OpenShift Ansible playbook

      1 [student@workstation review-install]$ rpm -qa | grep atomic-openshift-utils
      2 [student@workstation review-install]$ sudo yum -y install atomic-openshift-utils

    1.10 Ansible安裝OpenShift

      1 [student@workstation review-install]$ ansible-playbook \
      2 /usr/share/ansible/openshift-ansible/playbooks/prerequisites.yml

      1 [student@workstation review-install]$ ansible-playbook \
      2 /usr/share/ansible/openshift-ansible/playbooks/deploy_cluster.yml

    1.11 確認驗證

    通過web控制台使用developer用戶訪問https://master.lab.example.com,驗證集群已成功配置。

    1.12 授權

      1 [student@workstation review-install]$ ssh root@master
      2 [root@master ~]# oc whoami
      3 system:admin
      4 [root@master ~]# oc adm policy add-cluster-role-to-user cluster-admin admin

    提示:master節點的root用戶,默認為集群管理員。

    1.13 登錄測試

      1 [student@workstation ~]$ oc login -u admin -p redhat \
      2 https://master.lab.example.com
      3 [student@workstation ~]$ oc get nodes			#驗證節點情況

    1.14 驗證pod

      1 [student@workstation ~]$ oc get pods -n default #查看內部pod

    1.15 測試S2I

      1 [student@workstation ~]$ oc login -u developer -p redhat \
      2 https://master.lab.example.com
      3 [student@workstation ~]$ oc new-project test-s2i	#創建項目
      4 [student@workstation ~]$ oc new-app --name=hello \
      5 php:5.6~http://services.lab.example.com/php-helloworld

    1.16 測試服務

      1 [student@workstation ~]$ oc get pods			#查看部署情況
      2 NAME            READY     STATUS    RESTARTS   AGE
      3 hello-1-build   1/1       Running   0          39s
      4 [student@workstation ~]$ oc expose svc hello		#暴露服務
      5 [student@workstation ~]$ curl hello-test-s2i.apps.lab.example.com	#測試訪問
      6 Hello, World! php version is 5.6.25

    1.17 實驗判斷

      1 [student@workstation ~]$ lab review-install grade #本環境使用腳本判斷
      2 [student@workstation ~]$ oc delete project test-s2i #刪除測試項目

    實驗二 部署一個應用

    2.1 前置準備

      1 [student@workstation ~]$ lab review-deploy setup

    2.2 應用規劃

    部署一個TODO LIST應用,包含以下三個容器:

    一個MySQL數據庫容器,它在TODO列表中存儲關於任務的數據。

    一個Apache httpd web服務器前端容器(todoui),它具有應用程序的靜態HTML、CSS和Javascript。

    基於Node.js的API後端容器(todoapi),將RESTful接口公開給前端容器。todoapi容器連接到MySQL數據庫容器來管理應用程序中的數據

    2.3 設置策略

      1 [student@workstation ~]$ oc login -u admin -p redhat https://master.lab.example.com
      2 [student@workstation ~]$ oc adm policy remove-cluster-role-from-group \
      3 self-provisioner system:authenticated system:authenticated:oauth
      4 #將項目創建限製為僅集群管理員角色,普通用戶不能創建新項目。

    2.4 創建項目

      1 [student@workstation ~]$ oc new-project todoapp
      2 [student@workstation ~]$ oc policy add-role-to-user edit developer	#授予developer用戶可訪問權限的角色edit

    2.5 設置quota

      1 [student@workstation ~]$ oc project todoapp
      2 [student@workstation ~]$ oc create quota todoapp-quota --hard=pods=1	#設置pod的quota

    2.6 創建應用

      1 [student@workstation ~]$ oc login -u developer -p redhat \
      2 https://master.lab.example.com						#使用developer登錄
      3 [student@workstation ~]$ oc new-app --name=hello \
      4 php:5.6~http://services.lab.example.com/php-helloworld			#創建應用
      5 [student@workstation ~]$ oc logs -f bc/hello				#查看build log

    2.7 查看部署

      1 [student@workstation ~]$ oc get pods
      2 NAME             READY     STATUS      RESTARTS   AGE
      3 hello-1-build    0/1       Completed   0          2m
      4 hello-1-deploy   1/1       Running     0          1m
      5 [student@workstation ~]$ oc get events
      6 ……
      7 2m          2m           7         hello.15b54ba822fc1029            DeploymentConfig
      8 Warning   FailedCreate            deployer-controller              Error creating deployer pod: pods "hello-1-deploy" is forbidden: exceeded quota: todoapp-quota, requested: pods=1, used: pods=1, limited: pods=
      9 [student@workstation ~]$ oc describe quota
     10 Name:       todoapp-quota
     11 Namespace:  todoapp
     12 Resource    Used  Hard
     13 --------    ----  ----
     14 pods        1     1

    結論:由於pod的硬quota限制,導致部署失敗。

    2.8 擴展quota

      1 [student@workstation ~]$ oc rollout cancel dc hello	#修正quota前取消dc
      2 [student@workstation ~]$ oc login -u admin -p redhat
      3 [student@workstation ~]$ oc project todoapp
      4 [student@workstation ~]$ oc patch resourcequota/todoapp-quota --patch '{"spec":{"hard":{"pods":"10"}}}'

    提示:也可以使用oc edit resourcequota todoapp-quota命令修改quota配置。

      1 [student@workstation ~]$ oc login -u developer -p redhat
      2 [student@workstation ~]$ oc describe quota		#確認quota
      3 Name:       todoapp-quota
      4 Namespace:  todoapp
      5 Resource    Used  Hard
      6 --------    ----  ----
      7 pods        0     10

    2.9 重新部署

      1 [student@workstation ~]$ oc rollout latest dc/hello
      2 [student@workstation ~]$ oc get pods			#確認部署成功
      3 NAME            READY     STATUS      RESTARTS   AGE
      4 hello-1-build   0/1       Completed   0          9m
      5 hello-2-qklrr   1/1       Running     0          12s
      6 [student@workstation ~]$ oc delete all -l app=hello	#刪除hello

    2.10 配置NFS

      1 [kiosk@foundation0 ~]$ ssh root@services
      2 [root@services ~]# mkdir -p /var/export/dbvol
      3 [root@services ~]# chown nfsnobody:nfsnobody /var/export/dbvol
      4 [root@services ~]# chmod 700 /var/export/dbvol
      5 [root@services ~]# echo "/var/export/dbvol *(rw,async,all_squash)" > /etc/exports.d/dbvol.exports
      6 [root@services ~]# exportfs -a
      7 [root@services ~]# showmount -e

    提示:本實驗使用services上的NFS提供的共享存儲為後續實驗提供持久性存儲。

    2.11 測試NFS

      1 [kiosk@foundation0 ~]$ ssh root@node1
      2 [root@node1 ~]# mount -t nfs services.lab.example.com:/var/export/dbvol /mnt
      3 [root@node1 ~]# ls -la /mnt ; mount | grep /mnt		#測試是否能正常掛載

    提示:建議node2做同樣測試,測試完畢需要卸載,後續使用持久卷會自動進行掛載。

    2.12 創建PV

      1 [student@workstation ~]$ vim /home/student/DO280/labs/review-deploy/todoapi/openshift/mysql-pv.yaml
      2 apiVersion: v1
      3 kind: PersistentVolume
      4 metadata:
      5  name: mysql-pv
      6 spec:
      7  capacity:
      8   storage: 2G
      9  accessModes:
     10   -  ReadWriteMany
     11  nfs:
     12   path: /var/export/dbvol
     13   server: services.lab.example.com
     14 [student@workstation ~]$ oc login -u admin -p redhat
     15 [student@workstation ~]$ oc create -f /home/student/DO280/labs/review-deploy/todoapi/openshift/mysql-pv.yaml
     16 [student@workstation ~]$ oc get pv

    2.13 導入模板

      1 [student@workstation ~]$ oc apply -n openshift -f /home/student/DO280/labs/review-deploy/todoapi/openshift/nodejs-mysql-template.yaml

    提示:模板文件見附件。

    2.14 使用dockerfile創建image

      1 [student@workstation ~]$ vim /home/student/DO280/labs/review-deploy/todoui/Dockerfile
      2 FROM  rhel7:7.5
      3 
      4 MAINTAINER Red Hat Training <training@redhat.com>
      5 
      6 # DocumentRoot for Apache
      7 ENV HOME /var/www/html
      8 
      9 # Need this for installing HTTPD from classroom yum repo
     10 ADD training.repo /etc/yum.repos.d/training.repo
     11 RUN yum downgrade -y krb5-libs libstdc++ libcom_err && \
     12     yum install -y --setopt=tsflags=nodocs \
     13     httpd \
     14     openssl-devel \
     15     procps-ng \
     16     which && \
     17     yum clean all -y && \
     18     rm -rf /var/cache/yum
     19 
     20 # Custom HTTPD conf file to log to stdout as well as change port to 8080
     21 COPY conf/httpd.conf /etc/httpd/conf/httpd.conf
     22 
     23 # Copy front end static assets to HTTPD DocRoot
     24 COPY src/ ${HOME}/
     25 
     26 # We run on port 8080 to avoid running container as root
     27 EXPOSE 8080
     28 
     29 # This stuff is needed to make HTTPD run on OpenShift and avoid
     30 # permissions issues
     31 RUN rm -rf /run/httpd && mkdir /run/httpd && chmod -R a+rwx /run/httpd
     32 
     33 # Run as apache user and not root
     34 USER 1001
     35 
     36 # Launch apache daemon
     37 CMD /usr/sbin/apachectl -DFOREGROUND
     38 [student@workstation ~]$ cd /home/student/DO280/labs/review-deploy/todoui/
     39 [student@workstation todoui]$ docker build -t todoapp/todoui .
     40 [student@workstation todoui]$ docker images
     41 REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE
     42 todoapp/todoui                   latest              0249e1c69e38        39 seconds ago      239 MB
     43 registry.lab.example.com/rhel7   7.5                 4bbd153adf84        12 months ago       201 MB

    2.15 推送倉庫

      1 [student@workstation todoui]$ docker tag todoapp/todoui:latest \
      2 registry.lab.example.com/todoapp/todoui:latest
      3 [student@workstation todoui]$ docker push \
      4 registry.lab.example.com/todoapp/todoui:latest

    提示:將從dockerfile創建的image打標,然後push至內部倉庫。

    2.16 導入IS

      1 [student@workstation todoui]$ oc whoami -c
      2 todoapp/master-lab-example-com:443/admin
      3 [student@workstation todoui]$ oc import-image todoui \
      4 --from=registry.lab.example.com/todoapp/todoui \
      5 --confirm -n todoapp					#將docker image導入OpenShift的Image Streams
      6 [student@workstation todoui]$ oc get is -n todoapp
      7 NAME      DOCKER REPO                                       TAGS      UPDATED
      8 todoui    docker-registry.default.svc:5000/todoapp/todoui   latest    13 seconds ago
      9 [student@workstation todoui]$ oc describe is todoui -n todoapp	#查看is

    2.17 創建應用

    瀏覽器登錄https://master.lab.example.com,選擇todoapp的項目。

    查看目錄。

    語言——>JavaScript——Node.js + MySQL (Persistent)。

    參考下錶建立應用:

    名稱
    Git Repository URL http://services.lab.example.com/todoapi
    Application Hostname todoapi.apps.lab.example.com
    MySQL Username todoapp
    MySQL Password todoapp
    Database name todoappdb
    Database Administrator Password redhat

    create進行創建。

    Overview進行查看。

    2.18 測試數據庫

      1 [student@workstation ~]$ oc port-forward mysql-1-6hq4d 3306:3306		#保持端口轉發
      2 [student@workstation ~]$ mysql -h127.0.0.1 -u todoapp -ptodoapp todoappdb < /home/student/DO280/labs/review-deploy/todoapi/sql/db.sql
      3 #導入測試數據至數據庫
      4 [student@workstation ~]$ mysql -h127.0.0.1 -u todoapp -ptodoapp todoappdb -e "select id, description, case when done = 1 then 'TRUE' else 'FALSE' END as done from Item;"
      5 #查看是否導入成功

    2.19 訪問測試

      1 [student@workstation ~]$ curl -s http://todoapi.apps.lab.example.com/todo/api/host | python -m json.tool	#curl訪問
      2 {
      3     "hostname": "todoapi-1-kxlnx",
      4     "ip": "10.128.0.12"
      5 }
      6 [student@workstation ~]$ curl -s http://todoapi.apps.lab.example.com/todo/api/items | python -m json.tool	#curl訪問

    2.20 創建應用

      1 [student@workstation ~]$ oc new-app --name=todoui -i todoui	#使用todoui is創建應用
      2 [student@workstation ~]$ oc get pods
      3 NAME              READY     STATUS      RESTARTS   AGE
      4 mysql-1-6hq4d     1/1       Running     0          9m
      5 todoapi-1-build   0/1       Completed   0          9m
      6 todoapi-1-kxlnx   1/1       Running     0          8m
      7 todoui-1-wwg28    1/1       Running     0          32s

    2.21 暴露服務

      1 [student@workstation ~]$ oc expose svc todoui --hostname=todo.apps.lab.example.com

    瀏覽器訪問:http://todo.apps.lab.example.com

    2.22 實驗判斷

      1 [student@workstation ~]$ lab review-deploy grade #本環境使用腳本判斷

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

    【其他文章推薦】

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

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

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

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

  • 完美解決asp.net core 3.1 兩個AuthenticationScheme(cookie,jwt)共存在一個項目中,基於領域驅動設計(DDD)超輕量級快速開發架構

    完美解決asp.net core 3.1 兩個AuthenticationScheme(cookie,jwt)共存在一個項目中,基於領域驅動設計(DDD)超輕量級快速開發架構

    內容

    在我的項目中有mvc controller(view 和 razor Page)同時也有webapi,那麼就需要網站同時支持2種認證方式,web頁面的需要傳統的cookie認證,webapi則需要使用jwt認證方式,兩種默認情況下不能共存,一旦開啟了jwt認證,cookie的登錄界面都無法使用,原因是jwt是驗證http head “Authorization” 這屬性.所以連login頁面都無法打開.

    解決方案

    實現web通過login頁面登錄,webapi 使用jwt方式獲取認證,支持refreshtoken更新過期token,本質上背後都使用cookie認證的方式,所以這樣的結果是直接導致token沒用,認證不是通過token唯一的作用就剩下refreshtoken了

    通過nuget 安裝組件包

    Microsoft.AspNetCore.Authentication.JwtBearer

    下面是具體配置文件內容

    //Jwt Authentication
          services.AddAuthentication(opts =>
          {
            //opts.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            //opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
          })
          //這裡是關鍵,添加一個Policy來根據http head屬性或是/api來確認使用cookie還是jwt chema
            .AddPolicyScheme(settings.App, "Bearer or Jwt", options =>
            {
              options.ForwardDefaultSelector = context =>
              {
                var bearerAuth = context.Request.Headers["Authorization"].FirstOrDefault()?.StartsWith("Bearer ") ?? false;
                // You could also check for the actual path here if that's your requirement:
                // eg: if (context.HttpContext.Request.Path.StartsWithSegments("/api", StringComparison.InvariantCulture))
                if (bearerAuth)
                  return JwtBearerDefaults.AuthenticationScheme;
                else
                  return CookieAuthenticationDefaults.AuthenticationScheme;
              };
            })
    //這裏和傳統的cookie認證一致       .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
           {
             options.LoginPath = "/Identity/Account/Login";
             options.LogoutPath = "/Identity/Account/Logout";
             options.AccessDeniedPath = "/Identity/Account/AccessDenied";
             options.Cookie.Name = "CustomerPortal.Identity";
             options.SlidingExpiration = true;
             options.ExpireTimeSpan = TimeSpan.FromSeconds(10); //Account.Login overrides this default value
           })
            .AddJwtBearer(x =>
          {
            x.RequireHttpsMetadata = false;
            x.SaveToken = true;
            x.TokenValidationParameters = new TokenValidationParameters
            {
              ValidateIssuerSigningKey = true,
              IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Jwt:Key"])),
              ValidateIssuer = true,
              ValidateAudience = true,
              ValidateLifetime = true,
              ValidIssuer = Configuration["Jwt:Issuer"],
              ValidAudience = Configuration["Jwt:Issuer"],
            };
          });
    
     //這裏需要對cookie做一個配置
          services.ConfigureApplicationCookie(options =>
          {
            // Cookie settings
            options.Cookie.Name = settings.App;
            options.Cookie.HttpOnly = true;
            options.ExpireTimeSpan = TimeSpan.FromSeconds(10);
            options.LoginPath = "/Identity/Account/Login";
            options.LogoutPath = "/Identity/Account/Logout";
            options.Events = new CookieAuthenticationEvents()
            {
              OnRedirectToLogin = context =>
              {
               //這裏區分當訪問/api 如果cookie過期那麼 不重定向到login登錄界面
                if (context.Request.Path.Value.StartsWith("/api"))
                {
                  context.Response.Clear();
                  context.Response.StatusCode = 401;
                  return Task.FromResult(0);
                }
                context.Response.Redirect(context.RedirectUri);
                return Task.FromResult(0);
              }
            };
            //options.AccessDeniedPath = "/Identity/Account/AccessDenied";
          });        

    startup.cs

    下面userscontroller 認證方式

    重點:我簡化了refreshtoken的實現方式,原本規範的做法是通過第一次登錄返回一個token和一個唯一的隨機生成的refreshtoken,下次token過期后需要重新發送過期的token和唯一的refreshtoken,同時後台還要比對這個refreshtoken是否正確,也就是說,第一次生成的refreshtoken必須保存到數據庫里,這裏我省去了這個步驟,這樣做是不嚴謹的的.

    [ApiController]
      [Route("api/users")]
      public class UsersEndpoint : ControllerBase
      {
        private readonly ILogger<UsersEndpoint> _logger;
        private readonly ApplicationDbContext _context;
        private readonly UserManager<ApplicationUser> _manager;
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly SmartSettings _settings;
        private readonly IConfiguration _config;
    
        public UsersEndpoint(ApplicationDbContext context,
          UserManager<ApplicationUser> manager,
          SignInManager<ApplicationUser> signInManager,
          ILogger<UsersEndpoint> logger,
          IConfiguration config,
          SmartSettings settings)
        {
          _context = context;
          _manager = manager;
          _settings = settings;
          _signInManager = signInManager;
          _logger = logger;
          _config = config;
        }
        [Route("authenticate")]
        [AllowAnonymous]
        [HttpPost]
        public async Task<IActionResult> Authenticate([FromBody] AuthenticateRequest model)
        {
          try
          {
            //Sign user in with username and password from parameters. This code assumes that the emailaddress is being used as the username. 
            var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, true, true);
    
            if (result.Succeeded)
            {
              //Retrieve authenticated user's details
              var user = await _manager.FindByNameAsync(model.UserName);
    
              //Generate unique token with user's details
              var accessToken = await GenerateJSONWebToken(user);
              var refreshToken = GenerateRefreshToken();
              //Return Ok with token string as content
              _logger.LogInformation($"{model.UserName}:JWT登錄成功");
              return Ok(new { accessToken = accessToken, refreshToken = refreshToken });
            }
            return Unauthorized();
          }
          catch (Exception e)
          {
            return StatusCode(500, e.Message);
          }
        }
        [Route("refreshtoken")]
        [AllowAnonymous]
        [HttpPost]
        public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenRequest model)
        {
          var principal = GetPrincipalFromExpiredToken(model.AccessToken);
          var nameId = principal.Claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value;
          var user = await _manager.FindByNameAsync(nameId);
          await _signInManager.RefreshSignInAsync(user);
    
            //Retrieve authenticated user's details
            //Generate unique token with user's details
            var accessToken = await GenerateJSONWebToken(user);
            var refreshToken = GenerateRefreshToken();
            //Return Ok with token string as content
            _logger.LogInformation($"{user.UserName}:RefreshToken");
            return Ok(new { accessToken = accessToken, refreshToken = refreshToken });
    
    
        }
    
        private async Task<string> GenerateJSONWebToken(ApplicationUser user)
        {
          //Hash Security Key Object from the JWT Key
          var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
          var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
    
          //Generate list of claims with general and universally recommended claims
          var claims = new List<Claim>  {
               new Claim(ClaimTypes.NameIdentifier, user.UserName),
               new Claim(ClaimTypes.Name, user.UserName),
                    new Claim(JwtRegisteredClaimNames.Sub, user.Email),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                    new Claim(ClaimTypes.NameIdentifier, user.Id),
                    //添加自定義claim
                    new Claim(ClaimTypes.GivenName, string.IsNullOrEmpty(user.GivenName) ? "" : user.GivenName),
                    new Claim(ClaimTypes.Email, user.Email),
                    new Claim("http://schemas.microsoft.com/identity/claims/tenantid", user.TenantId.ToString()),
                    new Claim("http://schemas.microsoft.com/identity/claims/avatars", string.IsNullOrEmpty(user.Avatars) ? "" : user.Avatars),
                    new Claim(ClaimTypes.MobilePhone, user.PhoneNumber)
          };
          //Retreive roles for user and add them to the claims listing
          var roles = await _manager.GetRolesAsync(user);
          claims.AddRange(roles.Select(r => new Claim(ClaimsIdentity.DefaultRoleClaimType, r)));
          //Generate final token adding Issuer and Subscriber data, claims, expriation time and Key
          var token = new JwtSecurityToken(_config["Jwt:Issuer"]
              , _config["Jwt:Issuer"],
              claims,
              null,
              expires: DateTime.Now.AddDays(30),
              signingCredentials: credentials
          );
    
          //Return token string
          return new JwtSecurityTokenHandler().WriteToken(token);
        }
    
        public string GenerateRefreshToken()
        {
          var randomNumber = new byte[32];
          using (var rng = RandomNumberGenerator.Create())
          {
            rng.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
          }
        }
    
        private ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
        {
          var tokenValidationParameters = new TokenValidationParameters
          {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_config["Jwt:Key"])),
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidIssuer = _config["Jwt:Issuer"],
            ValidAudience = _config["Jwt:Issuer"],
          };
    
          var tokenHandler = new JwtSecurityTokenHandler();
          SecurityToken securityToken;
          var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out securityToken);
          var jwtSecurityToken = securityToken as JwtSecurityToken;
          if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
          {
            throw new SecurityTokenException("Invalid token");
          }
    
          return principal;
        }
    ....
    }
    }

    ControllerBase

    下面是測試

    獲取token

     refreshtoken

     

    獲取數據

     

     這裏獲取數據的時候,其實可以不用填入token,因為調用authenticate或refreshtoken是已經記錄了cookie到客戶端,所以在postman測試的時候都可以不用加token也可以訪問

     推廣一下我的開源項目

    基於領域驅動設計(DDD)超輕量級快速開發架構

    https://www.cnblogs.com/neozhu/p/13174234.html

    源代碼

    https://github.com/neozhu/smartadmin.core.urf

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

    【其他文章推薦】

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

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

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

    ※幫你省時又省力,新北清潔一流服務好口碑

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

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