特斯拉Model S在港正式銷售 入門級57.9萬港幣

昨(13)日有消息稱,特斯拉Model S車型已正式在香港銷售,其中入門級配置僅售57.9萬港幣。此外有報導稱,比亞迪將與ABB集團及德國戴姆勒共建全球最大電動汽車快速充電網,ABB將在未來6年內為兩家公司提供直流快速充電樁。

上述消息為A股正紅火的新能源汽車相關概念再添一把柴。截至昨天收盤,充電樁概念逆市上漲0.67%,漲幅居前,奧特迅領漲,漲幅為7.60%。特斯拉概念表現較為抗跌僅下挫0.51%,金瑞科技漲停,比亞迪也大漲8.26%。鋰電池概念跌0.93%,其中南洋科技漲9.48%。

證券方面人士認為,在政府大力建設新型城鎮化、發展城市公共交通的背景下,對新能源汽車的政策扶持將會給電池業、電池電機上游的鋰和稀土資源行業、充電站行業、乘用車企業帶來機遇。

但需要注意的是,未來的上漲空間將取決於政策的落實情況和上市公司的基本面,並且投資者不可對其期望過高,尤其是目前市場處於震盪的情形下,一旦后續資金追高乏力,相關個股很難擺脫大勢的影響。

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

【其他文章推薦】

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

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

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

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

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

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

萬向集團出價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/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

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

曠世提出類別正則化的域自適應目標檢測模型,緩解場景多樣的痛點 | CVPR 2020

論文基於DA Faster R-CNN系列提出類別正則化框架,充分利用多標籤分類的弱定位能力以及圖片級預測和實例級預測的類一致性,從實驗結果來看,類該方法能夠很好地提升DA Faster R-CNN系列的性能

來源:曉飛的算法工程筆記 公眾號

論文: Exploring Categorical Regularization for Domain Adaptive Object Detection

  • 論文地址:https://arxiv.org/pdf/2003.09152.pdf
  • 論文代碼:https://github.com/Megvii-Nanjing/CR-DA-DET

Introduction

  由於標註成本大,在訓練好檢測算法后,面對差異較大的新場景(類別不變),若想獲取大量的帶標註圖片進行再訓練是很不方便的。對於這種情況,無監督的域自適應方法能夠靈活地自適應新場景,從包含豐富標註信息的源域轉移到無標註的目標域。其中,域自適應方法中比較有代表性的是Donamin Adaptive(DA) Faster R-CNN系列,利用對抗訓練來對齊圖片和實例的分佈,使得模型能夠做到域不變性,具體可以看上一篇介紹。
  但是這些方法大都把無法轉化的背景內容也進行了對齊,而且在實例對齊時,沒有從包含較多低質量的proposal集合中識別出難樣本。為了解決上面的問題,論文提出類別正則化框架,幫助DA Faster R-CNN專註於對齊跨域中的關鍵區域和重要目標。
  論文的主要貢獻如下:

  • 提出新的類別正則化框架,作為域自適應目標檢測算法的插件,不需要額外的標註和超參數。
  • 設計了兩個正則化模塊,分別用於榨取卷積分類器的弱定位能力以及圖像級別預測和實例級別預測間的類別一致性,能夠幫助分類器專註於對齊目標相關區域以及難對齊實例。
  • 對多種域轉移場景進行實驗,驗證論文提出的方法的有效性。從實驗結果來看,類別正則化框架能夠提出DA Faster R-CNN系列方法的性能,並在基礎數據集上達到SOTA。

Approach

Framework Overview

  論文方法的整體架構如圖2,在DA Faster R-CNN基礎上添加了ICR(image-level categorical regularization)和CCR(categorical consistency regularization),能夠更好地對齊域間的關鍵區域和重要實例。

Image-Level Categorical Regularization

  ICR的主要目的是提高主幹網絡的目標特徵提取能力,同時降低背景的激活。結構如圖2b所示,ICR使用源域數據進行有監督訓練,對主幹網絡的特徵輸出進行全局池化,再使用多標籤分類器($1\times 1$卷積)進行分類,損失函數使用標準交叉熵多標籤損失:

  $C$為類別總數,$yc$為GT標籤,$\hat{y}c$為預測標籤,$y^c=1$表示圖片至少包含一個類別$c$物體。

 ICR模塊利用多標籤分類器的弱定位能力,能夠有監督地引導主幹網絡只激活類相關特徵。如圖3所示,類相關的特徵會有較高的激活值。在圖像級對齊時,能夠對齊域間關鍵區域,同時,由於背景沒有參与到圖像級多標籤分類器中,能夠有效減少擬合不可對齊的源背景的可能性。

Categorical Consistency Regularization

  CCR負責發現難對齊實例,調整實例級對齊損失的權重,基於兩點考慮:

  • 由於不能區分前景和後景,實例對齊模塊可能被低質量背景proposal佔據。
  • 添加的圖像級分類器和實例檢測head是互補的,前者負責獲取所有圖像級上下文信息,後者使用精確的RoI特徵,當兩者預測不一致時,該實例就是難樣本。

  基於以上考慮,論文採用圖像級預測和實例級預測的類別一致性作為目標分類難易程度的判斷,並在目標域中使用該一致性作為正則因子,調節難對齊樣本在實例對齊中的權重。假定$\hat{p}{c}_j$為預測第$j$個實例為類別$c$的概率,$\hat{y}c$為實例預測包含類別$c$的概率,類別一致性的計算為

  使用公式5來加權實例級對抗損失

  需要注意,僅對目標域的檢測head預測為前景的實例使用公式5加權,源域的所有實例和目標域的背景實例均使用$d_j=1$,前者因為是有監督的,而後者則是因為不重要。

Integration with DA Faster R-CNN Series

  將論文提出的方法加入到DA Faster R-CNN中,ICR為直接加入,CCR為對原損失的修改,最終的損失函數為

  論文也對比了另外一種主流的DA -Faster改進SW-Faster,該方法使用弱全局對齊模型來提升DA-Faster的強圖像對齊模塊,直接加入ICR和CCR,最終的損失函數為

Experiments

Comparison Results

  Faster R-CNN(Source)僅使用源域訓練,Faster R-CNN(Oracle)僅使用目標域訓練。

  • Weather Adaptation

  這裏對比模型對天氣的自適應性。

  • Scene Adaptation

  這裏對比模型對不同城市的場景的自適應性。

  • Dissimilar Domain Adaptation

  這裏對比模型對真實圖片和卡通圖片的自適應性。

Visualization and Analyses

  對前面對比實驗的目標域測試圖片進行了可視化。

  將特徵降維並可視化,藍點為源域樣本,紅點為目標域樣本,可以看到論文的方法能夠讓域間的同分類實例距離更近。
  論文也計算了域間距離,使用Earth Movers Distance (EMD) 測量,SW-Faster, SW-Faster-ICR and SW-FasterICR-CCR的結果分別是8.84、8.59和8.15。

CONCLUSION

  論文基於DA Faster R-CNN系列提出類別正則化框架,充分利用多標籤分類的弱定位能力以及圖片級預測和實例級預測的類一致性,從實驗結果來看,類該方法能夠很好地提升DA Faster R-CNN系列的性能。



如果本文對你有幫助,麻煩點個贊或在看唄~
更多內容請關注 微信公眾號【曉飛的算法工程筆記】

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

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

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

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

一對多分頁的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維修中心

LeetCode 79,這道走迷宮問題為什麼不能用寬搜呢?

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是LeetCode專題第48篇文章,我們一起來看看LeetCode當中的第79題,搜索單詞(Word Search)。

這一題官方給的難度是Medium,通過率是34.5%,點贊3488,反對170。單從這份數據上來看,這題的質量很高,並且難度比之前的題目稍稍大一些。我個人覺得通過率是比官方給的題目難得更有參考意義的指標,10%到20%可以認為是較難的題,30%左右是偏難的題。50%是偏易題,所以如果看到某題標着Hard,但是通過率有50%,要麼說明題目很水,要麼說明數據很水,總有一點很水。

題意

廢話不多說,我們來看題意:

這題的題面挺有意思,給定一個二維的字符型數組,以及一個字符串,要求我們來判斷能否在二維數組當中找到一條路徑,使得這條路徑上的字符連成的字符串和給定的字符串相等?

樣例

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

Given word = "ABCCED", return true.
Given word = "SEE", return true.
Given word = "ABCB", return false.

比如第一個字符串ABCCED,我們可以在數組當中找到這樣一條路徑:

題解

不知道大家看到題面和這個樣例有什麼樣的感覺,如果你刷過許多題,經常思考的話,我想應該不難發現,這道題的本質其實和走迷宮問題是一樣的。

我們拿到的這個二維的字符型數組就是一個迷宮, 我們是要在這個迷宮當中找一條“出路”。不過我們的目的不是找到終點,而是找到一條符合題意的路徑。在走迷宮問題當中,迷宮中不是每一個點都可以走的,同樣在當前問題當中,也不是每一個點都符合字符串的要求的。這兩個問題雖然題面看起來大相徑庭,但是核心的本質是一樣的。

我們來回憶一下,走迷宮問題應該怎麼解決?

這個答案應該已經非常確定了,當然是搜索算法。我們需要搜索解可能存在的空間去尋找存在的解,也就是說我們面臨的是一個解是否存在的問題,要麼找到解,要麼遍歷完所有的可能性發現解不存在。確定了是搜索算法之後,剩下的就簡單了,我們只有兩個選項,深度優先或者是廣度優先。

理論上來說,一般判斷解的存在性問題,我們使用廣度優先搜索更多,因為一般來說它可以更快地找到解。但是本題當中有一個小問題是,廣度優先搜索需要在隊列當中存儲中間狀態,需要記錄地圖上行走過的信息,每有一個狀態就需要存儲一份地圖信息,這會帶來比較大的內存開銷,同樣存儲的過程也會帶來計算開銷,在這道題當中,這是不可以接受的。拷貝狀態帶來的空間消耗還是小事,關鍵是拷貝帶來的時間開銷,就足夠讓這題超時了。所以我們別無選擇,只能深度優先。

明確了算法之後,只剩下了最後一個問題,在這個走迷宮問題當中,我們怎麼找到迷宮的入口呢?因為題目當中並沒有規定我們起始點的位置,這也不難解決,我們遍歷二維的字符數組,和字符串開頭相匹配的位置都可以作為迷宮的入口。

最後,我們來看代碼,並沒有什麼技術含量,只是簡單的回溯法而已。

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        fx = [[0, 1], [0, -1], [1, 0], [-1, 0]]
        def dfs(x, y, l):
            if l == len(word):
                return True
            for i in range(4):
                nx = x + fx[i][0]
                ny = y + fx[i][1]
                # 出界或者是走過的時候,跳過
                if nx < 0 or nx == n or ny < 0 or ny == m or visited[nx][ny]:
                    continue
                if board[nx][ny] == word[l]:
                    visited[nx][ny] = 1
                    if dfs(nx, ny, l+1):
                        return True
                    visited[nx][ny] = 0
            return False
                
        n = len(board)
        if n == 0:
            return False
        m = len(board[0])
        if m == 0:
            return False
        
        visited = [[0 for i in range(m)] for j in range(n)]
        
        for i in range(n):
            for j in range(m):
                # 找到合法的起點
                if board[i][j] == word[0]:
                    visited = [[0 for _ in range(m)] for _ in range(n)]
                    visited[i][j] = 1
                    if dfs(i, j, 1):
                        return True
                    
        return False

總結

如果能夠想通回溯法,並且對於回溯法的實現足夠熟悉,那麼這題的難度是不大的。實際上至今為止,我們一路刷來,已經做了好幾道回溯法的問題了,我想對你們來說,回溯法的問題應該已經小菜一碟了。

相比於回溯法來說,我覺得更重要的是我們能夠通過分析想清楚,為什麼廣度優先搜索不行,底層核心的本質原因是什麼。這個思考的過程往往比最後的結論來得重要。

如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

本文使用 mdnice 排版

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

Blazor帶我重玩前端(一)

寫在前面

曾經我和前端朋友聊天的時候,我說我希望有一天可以用C#寫前端,不過當時更多的是美好的想象,而現在這一切正變得真實……

 

什麼是Blazor

我們知道瀏覽器可以正確解釋並執行JavaScript代碼,那麼瀏覽器是如何執行C#代碼的呢?答案是通過WebAssembly。通過WebAssembly,我們可以讓瀏覽器運行很多的高級語言,如 C#、C、C++、GO等,並使他們運行在基於內存安全的沙箱環境中。如下圖所示:

作為一個已經五六年沒有寫過前端的.NET程序員,遇到Blazor實在是幸運中的幸運。它又讓我可以很愉快的寫前端了,而且還是用C#去寫,我也就不用再分出精力去學習其他的JS框架了。

我們可以認為Blazor是.NET對WebAssembly的實現。通過使用Blazor,我們可以使用C#語言來取代JS去開發交互式Web UI。

值得一提的是,Blazor是由Browser和Razor這兩個單詞合併而成的,意思就是Blazor可以基於客戶端執行Razor視圖后將HTML呈現給瀏覽器。所以想要更好的理解Blazor,就要首先更好的了解瀏覽器和Razor。

Blazor有以下幾個優點:
  • 使用C#來取代JavaScript創建豐富的交互式UI
  • 基於.NET及其生態編寫服務器端和客戶端應用程序邏輯
  • 糅合現有HTML和CSS技術,提供了廣泛的瀏覽器支持,包括移動瀏覽器(注意:Blazor取代的是基於JavaScript的UI交互,而其他部分如HTML、CSS,這些是我們的技術基礎)
  • 與現代託管平台(例如Docker)集成。
  • Blazor是開源的,其源碼位置在GitHub上

另外需要注意的,Blazor和Silverlight不可混為一談,Blazor是基於開放標準而構建的,本身不需要任何額外插件。而Silverlight帶有太多自有特性,所以不得不在瀏覽器上安裝插件以更好的支持其運行。

什麼是WebAssembly

概覽

WebAssembly是一種二進制格式的指令集,其設計目標是能夠在解釋或者將其編譯為本地機器代碼並執行他們的機器上運行,這類似於我們.NET編譯后的IL。

WebAssembly可以作為編譯高級編程語言的可移植目標,通過節省大小和加載時間,充分利用各種平台(移動平台和IOT平台)上的通用應用功能,使得WebAssembly可以以接近於本機(接近於本機的英語單詞是:near-native,在語言學里意思是精通語言的人,所說的話和說母語的人沒有什麼區別)的運行速度運行。

支持

WebAssembly已經獲得了大部分瀏覽器的支持。詳細內容可以移步至Can I Use

手寫一個例子

接下來我們看一個例子,方便起見,我們直接使用在線的WebAssembly編譯工具,地址是:https://mbebenita.github.io/WasmExplorer/。目前,這個工具只支持C和C++。不過也沒有什麼關係,我們寫一個簡單的方法用於測試即可。

  • 首先我們定義了一個計算兩個數和的方法:
1 int Addition(int a, int b)
2 {
3   return a + b; 
4 }
  • 然後點擊COMPILE

在中間的框里會生成WAT(即WebAssembly文本格式)的代碼,最右邊的是二進制了。中間的代碼部分可以幫助我們查看在編譯的過程中發生了什麼,會看到生成了一個名為_Z8Additionii的function,其中8表示這個方法名的長度,後面的i表示有多個參數,接下來我們會去調用它。

 1 (module
 2  (table 0 anyfunc)
 3  (memory $0 1)
 4  (export "memory" (memory $0))
 5  (export "_Z8Additionii" (func $_Z8Additionii))
 6  (func $_Z8Additionii (; 0 ;) (param $0 i32) (param $1 i32) (result i32)
 7   (i32.add
 8    (get_local $1)
 9    (get_local $0)
10   )
11  )
12 )
  • 點擊Download,下載.WAT文件

  • 接下來我們再寫一個HTML網頁出來,就用那種最簡單的HTML代碼,代碼如下:
 1 <HTML>
 2 <HEAD>
 3     <TITLE>WebAssembly Sample: Call C++ Code</TITLE>
 4     <script type="text/javascript">
 5         let addition = fetch('test.wasm')
 6             .then(response => response.arrayBuffer())
 7             .then(buffer => WebAssembly.compile(buffer))
 8             .then(module => { return new WebAssembly.Instance(module) })
 9             .then(instance => { addition = instance.exports._Z8Additionii }); 
10     </script>
11 </HEAD>
12 <BODY BGCOLOR="FFFFFF">
13     <h1>WebAssembly Sample: Call C++ Code</h1>
14 </BODY>
15 </HTML>
  • 最終的效果圖

通過以上示例,我們基本上對Blazor和WebAssembly的部分運行機制有了一個比較清晰的認識了,接下來,我們繼續討論有關Blazor的內容。

參考鏈接:

https://webassembly.org/

https://webassembly.github.io/spec/js-api/index.html

https://caniuse.com/#search=wasm

https://webassembly.github.io/spec/js-api/index.html


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

【其他文章推薦】

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

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

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

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

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

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

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)超輕量級快速開發架構

內容

在我的項目中有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

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

【其他文章推薦】

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

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

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

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

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

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

特斯拉今年或沿京滬高速布局充電設施

據阿思達克財經報導,特斯拉產品專家表示,特斯拉預計第二或者第三季度在上海開店,同時,特斯拉在中國的充電設施首先會沿著京滬高速布局,只是具體布局時間暫不確定。

上述人士表示,特斯拉(Tesla)中國客戶現在訂車,將於今年年底拿到車。上海客戶屆時則可以直接在上海提車,不需要自己出錢把車從北京提到上海。另外,特斯拉客戶可以用自己的燃油車車牌置換,或者參與上海市拍牌。

據介紹,特斯拉在中國大陸目前有6輛,北京店有3輛上牌車,還有3輛試駕車。

針對客戶關心的充電問題,該產品專家表示,客戶購買特斯拉的車價裡面已經包含了充電樁費用,消費者不會再另外花錢購買充電樁,隻需要再支付從物業拉電到車位的材料、人工等費用。

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

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

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

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