標籤: 南投搬家公司費用

  • 商業停擺意外有好空氣 印度人30年來首見喜馬拉雅山

    摘錄自2020年4月10日自由時報報導

    許多國家運用封鎖地區、暫停商家營業等方式防止武漢肺炎疫情傳播,意外造成空氣污染減緩的狀況,印度北部甚至可以看見數十年來未見的喜馬拉雅山,讓居民相當驚喜。

    《CNN》報導,數十年來印度人首度可見喜馬拉雅山,原因在於地區封鎖減緩了空氣污染,印度北邊旁遮普邦居民現在可以看到喜馬拉雅山。有居民表示,這是近30年來第一次可以清楚看見山脈,太神奇了。近來印度因為商家關閉、汽車停駛、航空公司取消航班,空氣污染大幅降低。

    報導指出,在印度全國封閉的第一週有85個城市空氣變好,據去年報告顯示,印度在世界空氣污染最嚴重的城市中,共有6個城市排名在前10名中。

    公害污染
    空氣污染
    污染治理
    國際新聞
    印度
    武漢肺炎
    喜馬拉雅山
    疫情看氣候與能源

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

    ※回頭車貨運收費標準

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

  • 小嬰兒三更半夜找爸爸 Alexa:已經將爸爸放到購物車了

    小嬰兒三更半夜找爸爸 Alexa:已經將爸爸放到購物車了

    雖然在台灣比較少見,但是亞馬遜所推出的數位語音助理「Alexa」除了可以幫你啟動各種家電外,同時也可以幫你從亞馬遜的商場上搜尋你要的東西,並且放入購物車中。而近日就有國外的小孩,三更半夜吵著想要找爸爸,沒想到意外的將「爸爸」放到購物車中。

    根據《每日星報》報導指出,一位媽媽近日在TikTok上上傳一段影片,影片的內容是他們家的嬰兒監視器,只見影片中小寶寶從嬰兒床上爬起來,對著一旁的數位助理Alexa說「Alexa,我需要爸爸!」

    Alexa在聆聽了男童充滿童言童語的請求後,竟然是直接回應說「我應該加入什麼?」顯然的Alexa把男童的請求,聽成了某種購物的需求。而男童聽到Alexa的回應後,再一次的表示「爸爸」。沒想到Alexa竟然回應男童「好的!已經將爸爸放入購物車中,請問還需要什麼嗎?」面對Alexa的回應,男童也瞬間呆滯。

    由於男童與Alexa之間的對話實在過於暴笑,影片從18日上傳後,至今已經有370多萬次觀看,也吸引不少網友留言。小編原本還想說,如果Alexa在聽到男童的需求後,就打電話給爸爸那就真的非常厲害了。但是現在,小編更好奇的是,Alexa到底放了哪家商店提供的「爸爸」在購物車中呢?

    您也許會喜歡:

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

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

    【其他文章推薦】

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

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

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

    ※回頭車貨運收費標準

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

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

  • Pixel「車禍偵測」助翻車意外昏迷者報警脫困!雖然他開的是山貓起重機(咦)

    Pixel「車禍偵測」助翻車意外昏迷者報警脫困!雖然他開的是山貓起重機(咦)

    雖然因為還不開放其他國家使用,而讓人覺得這是「真 · 他國事務」(苦笑)。不過 Google Pixel 傳說中的車禍偵測功能(Car Crash Detection),現在也真的像許多其他的科技裝置的緊急功能那樣,開始有了拯救人命的案例。繼續閱讀 Pixel「車禍偵測」助翻車意外昏迷者報警脫困!雖然他開的是山貓起重機(咦)報導內文。

    ▲圖片來源:9to5Google

    Pixel「車禍偵測」助翻車意外昏迷者報警脫困!

    科技觸角延伸到了各種領域後,不僅對於生活有了新的幫忙,更能在緊急事件時發揮不同於以往的對應可能。除了特斯拉能讓一家取暖在車庫渡過寒冬夜晚外,像是 Apple Watch 等穿戴裝置的健康偵測功能,也隨著產品被更多人所用,而出現即時提醒就診而避免更嚴重健康問題的實例。這次,Pixel 手機更新獲得的「車禍偵測」功能,也讓因為翻車事故而短暫失去意識的 Chuck Walker,能夠獲得及時的幫助。

    ▲圖片來源:Reddit

    他在 Reddit 分享了這個不算真正「車禍」但仍讓自己陷入險境的意外事故。至於為什麼說不算一般車禍事故呢?主要是因為 Chuck Walker 開的是起重機(Bobcat Loader),而且進行相關作業時,並非在一般道路,而是自家土地區域內。然而這樣的「條件」,反而讓隻身在家的他在意外時陷入更難求救的窘境。

    去年 11 月時,Chuck Walker 在美國密蘇里州的自家土地開著山貓進行工作時,意外翻覆下小山坡。事故發生當下整台車的狀態是倒栽蔥的狀態,過程也導致 Walker 失去意識。此時,他所使用的 Google Pixel 4 XL 因為偵測到了類似車禍的狀態(一般車禍大概也不會有這麼嚴重的…「位移?」),因此啟動了緊急報警的功能。

    ▲圖片來源:9to5Google

    漸漸恢復意識的他,則是在聽到了藍牙耳機傳來救護單位的聲音後發出求救訊號。幸運的是,救護單位在收到這個緊急通話後早已機警派出救援人員前往。同時也是前護理人員的 Walker 描述,雖然他判斷自己的情況並不會有立即的生命危險,事後也的確僅花了數週醫治骨折狀況。但他回憶自己當時的處境。

    不僅預期可能得要等數個小時的時間才會被人發現(畢竟算是在家裡,其他人很難經過…),手機也被甩到了卡在車中的他無法觸及的位置;不論傷勢的話,如果想自己脫困,起重機的緊急逃生口被遮蔽,原本的出口也呈現半遮蔽狀態。

    簡而言之,就是如果沒有車禍偵測功能及時報案,也許相對於骨折的痛,他還得要想辦法在長時間的低溫環境生存,更別說車輛可能會有漏油起火的危險。

    不過講起來,要是真的沒有車禍偵測,其實以他的狀況來說應該還是可以用「OK, Google」指令撥打緊急電話?只是除了得真的在意外當下能冷靜思考到此方法,危急的情況下也的確是分秒必爭(不然救護單位也不會還沒確定就先派人前往了),真的是不怕一萬只怕萬一。

    唯一可惜的是,這個 Pixel 3 以上機型應該都支援的功能,目前還僅支援美國、英國與澳洲….

    引用來源

    延伸閱讀:

    讓學習贏在起跑點,來看學霸分享 iPad 搭 Apple Pencil 的高效率讀書筆記法

    老 Go 變出新把戲?Gogoro VIVA MIX 想變成你的形狀(試騎心得)

    您也許會喜歡:

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

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

    【其他文章推薦】

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

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

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

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

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

    ※回頭車貨運收費標準

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

  • 小米10T Lite 5G 在台推出:高通 Snapdragon 750G 5G 處理器、120Hz 更新率螢幕、64MP 四鏡頭主相機,萬元有找輕鬆入手

    小米10T Lite 5G 在台推出:高通 Snapdragon 750G 5G 處理器、120Hz 更新率螢幕、64MP 四鏡頭主相機,萬元有找輕鬆入手

    目前在台灣手機市場的5G 手機選擇從入門、中階到旗艦級都有,今(26)日稍早小米台灣也舉行「小米5G手機超值選」直播,帶大家看看在不同預算、使用需求考量下,消費者該選擇哪款5G 手機。另外,小米台灣也在今日直播迎來一款全新登台的5G 手機「小米10T Lite」,搭載高通Snapdragon 750 5G 行動平台、120Hz 更新率螢幕、6400 萬像素四鏡頭主相機,價格也特別訂在萬元有找相當超值。

    小米10T Lite 5G 在台推出:高通Snapdragon 750G 5G 處理器、120Hz 更新率螢幕、64MP 四鏡頭主相機,萬元有找輕鬆入手

    日前我們才報導過「小米10T Lite」這款中階5G手機將在台灣推出新機的消息,今(26)日也稍早小米台灣也已直播方式揭曉這款新機的上市資訊。
    小米10T Lite搭載6.67吋20:9顯示比例、 FHD+(2400×1080)解析度的居中挖孔全螢幕,機身前後採用康寧第五代大猩猩玻璃保護:

    顯示方面,小米10T Lite除了支持1500:1對比度、 NTSC 84%色域、 120Hz螢幕更新率、 240Hz觸控採樣率,螢幕也通過TÜVRheinland德國萊茵低藍光認證。
    另外,小米10T Lite前後配備智慧環境光感應器,能提供最舒適的視覺體驗。螢幕採用陽光螢幕3.0 ,在陽光直射下自動增強對比度以確保畫面清晰可見:

    相機方面,小米10T Lite 搭載6400 萬像素四鏡頭主相機,採用6400 萬像素F/1.89 光圈主鏡頭(SONY IMX682 感光元件)、800 萬像素120° 超廣角鏡頭、200 萬像素4cm 微距鏡頭以及200 萬像素景深鏡頭的搭配。

    前鏡頭則配備1600 萬像素前置自拍相機:

    小米10T Lite 相機應用功能也相當豐富,像是也支持前後鏡頭同時錄影:

    內建訂十連拍功能,最高可一分鐘600 張照片並轉換成影片:

    另外也內建長曝光模式,讓拍攝初學者也能拍出專業感的照片:

    延時攝影也支持自拍影片功能:

    性能方面,小米10T Lite 搭載Qualcomm Snapdragon 750G 5G 處理器, 6GB RAM 和128GB ROM :

    小米10T Lite 內建4820mAh 大電量電池,支持33W 快速充電能在59 分鐘充電至100% :

    配色方面,小米10 Lite在台共推出玫瑰金、珍珠灰以及海洋藍三種配色選擇。
    玫瑰金:

    珍珠灰:

    海洋藍:

    銷售資訊

    最後就是大家最關心的售價囉!小米10T Lite 在台推出6GB+128GB 單一規格,售價為NT$9,999 ,將於3 月2 日起於小米專賣店、遠傳電信、PChome24h購物小米旗艦館、法雅客網路商店、friDay購物與燦坤3C陸續開賣。

    即日起至3 月7 日止,購買任一款小米5G手機並出示指定截圖,即贈Redmi行動電源

    為慶祝小米10T Lite 5G 的上市,進一步完整小米在台灣市場5G手機的佈局,凡於活動期間於全台小米專賣及燦坤3C實體門市購買任一小米5G手機(包含小米10T Pro、小米10T 5G、小米10T Lite 5G及Redmi Note 9T 5G),出示小米台灣官方Facebook粉絲頁上2月26日直播截圖,即可獲贈Redmi行動電源10000 標準版白色乙個(價值NT$345)。

    小米5G手機超值選

    【其他文章推薦】

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

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

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

    ※回頭車貨運收費標準

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

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

  • Google 居然放任 Wear OS 語音助理功能壞了數個月… 外媒細數種種「不妙」徵兆

    Google 居然放任 Wear OS 語音助理功能壞了數個月… 外媒細數種種「不妙」徵兆

    還記得Wear OS,那個從Android Wear變身而來的Google穿戴系統嗎?其實,小編以往是真的超級喜歡這個系統的產品的,特別是騎車停紅綠燈時超方便觀看通知的手勢操作,還能支援iOS也是十分不錯的特色。不過…繼續閱讀Google居然放任Wear OS語音助理功能壞了數個月…外媒細數種種「不妙」徵兆報導內文。

    ▲圖片來源:Google

    Google 居然放任Wear OS 語音助理功能壞了數個月….

    還有在使用Google的智慧穿戴產品?那你最近是否很想因為「Hey, Google」語音助理功能無法使用的問題,而真的想敲敲Google肩膀問聲「嘿!它到底怎麼了?」其實這個已經持續了數個月的故障狀況,也有很多人發現。只不過,到了被媒體揭露之後才開始聽到Google著手進行處理的動作。對於這樣的狀況,外媒感到有些「不妙」。

    根據回報資料,這個在Wear OS上無法正常聲控啟動Google語音助理的問題,前前後後已經有不少人在官方頁面回報。然而真正炸裂的契機,反而是數個月後的近日,此事在Reddit被大幅度討論並引起外媒注意才得到了官方的正式回覆。更是因為裡面根本沒有提到任何預計時間讓用戶無法接受。

    畢竟別的App或功能也就算了,這可是Google招牌的功能啊。外媒ArsTechnica更根據種種症狀,認為最近才收購Fitbit的Google也許真的無心經營Wear OS生態系。

    像是Play Music與Hangouts在Google智慧錶系統中都沒有替代的方案,新的Google Chat則是尚未支援Wear OS。更諷刺的是,Apple Watch上反而找得到YouTube Music…再加上這次躺了幾個月叫不醒的Google Assistant的狀況,該網站認為這都是Wear OS平台死透的跡象。

    是說,Android Wear也曾是小編非常喜歡搭配手機使用的穿戴裝置產品。如果問我對於Google穿戴系統的不好印象,主要還是在於系統更新沒幾次就不再支援舊硬體的狀況。變成如果想持續更新用到Android Wear / Wear OS的新功能的話,就得要時常購買新手錶。以上,再加上能支援Android的穿戴裝置也算百花齊放各有各的特色拿不出太多差異性,也許Wear OS才會走到目前這步吧…

    【其他文章推薦】

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

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

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

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

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

    ※回頭車貨運收費標準

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

  • 從消息中間件看分佈式系統的多種套路

    從消息中間件看分佈式系統的多種套路

         

     

     

     

      

      消息中間件作為分佈式系統的重要成員,各大公司及開源均有許多解決方案。目前主流的開源解決方案包括RabbitMQ、RocketMQ、Kafka、ActiveMQ等。消息這個東西說簡單也簡單,說難也難。簡單之處在於好用方便,接入簡單使用簡單,異步操作能夠解耦系統間的依賴,同時失敗后也能夠追溯重試。難的地方在於,設計一套可以支撐業務的消息機制,並提供高可用架構,解決消息存儲、消息重試、消息隊列的負載均衡等一系列問題。然而難也不代表沒有方法或者“套路”,熟悉一下原理與實現,多看幾個框架的源碼后多總結勢必能找出一些共性。

      消息框架大同小異,熟練掌握其原理、工作機制是必要的。就拿用的比較多的RocketMQ為引,來說說消息引擎的設計與實現。阿里的消息引擎經過了從Notify到Napoli、再到MetaQ三代的發展,現在已經非常成熟,在不同部門的代碼中現在沒準都還可以從代碼里看到這一系列演進過程。當前的Apache RocketMQ 就是阿里將MetaQ項目捐贈給了Apache基金會,而內部還是沿用MetaQ的名稱。

          首先詮釋幾個消息相關的基本概念。

    • 每個消息隊列都必須建立一個Topic。
    • 消息可以分組,每個消息隊列都至少需要一個生產者Producer和一個消費者Consumer。生產者生產發送消息,消費者接收消費消息。
    • 每個消費者和生產者都會分批提個ID。

     

    RocketMQ 系統架構

     

        

     

      接下來再來看看RocketMQ的架構,如圖所示,簡要描述一下幾種角色及作用。 

    • NameServer
      • NameServer是消息Topic的註冊中心,用於發現和管理消息生產者、消費者、及路由關係。
    • Broker
      • 消息存儲與轉發的中轉站,使用隊列機制管理數據存儲。Broker中會存儲多份消息數據進行容錯,以Master/Slave的架構保證系統的高可用,Broker中可以部署單個或多個Master。單個Master的場景,Master掛掉后,Producer新產生的消息無法被消費,但已經發送到Broker的消息,由於Slave節點的存在,還能繼續被Consumer所消費;如果部署多個Master則系統能能正常運轉。
      • 另外,Broker中的Master和Slave不是像Zookeeper集群中用選舉機制進行確定,而是固定的配置,這也是在高可用場景需要部署多個Master的原因。
      • 生產者將消息發送到Broker中后,Broker會將消息寫到本地的CommitLog文件中,保存消息。
    • Producer
      • 生產者會和NameServer集群中某一節點建立長鏈接,定時從NamerServeri獲取Topic路由信息,並且和Broker建立心跳。
    • Consumer
      • 消費者需要給生產者一個明確的消費成功的回應,MetaQ才會認為消費成功,否則失敗。失敗后,RocketMQ會將消息重新發回Broker,在指定的延遲時間內進行重試,當重試達到一定的次數后(默認16次),MetaQ則認為此消息不能被消費,消息會被投遞到死信隊列。

     

      這個架構看其實是否很熟悉?好像接觸過的一些分佈式系統的架構和這個長的都比較像是吧,甚至只要裏面框圖的角色稍微換換就能變成另外一個框架的介紹,比如Dubbo/Redis…。

    並且在RocketMQ架構設計中,要解決的問題與其他分佈式框架也可以觸類旁通。Master/Slave機制,天然的讀寫分離方式都是分佈式高可用系統的典型解決方案。

    負載均衡

      負載均衡是消息框架需要解決的又一個重要問題。當系統中生產者生產了大量消息,而消費者有多個或多台機器時,就需要平衡負載,讓消息均分地被消費者進行消費。目前RocketMQ中使用了多種負載均衡算法。主要有以下幾種,靜態配置由於過於簡單,直接為消費者配置需要消費的隊列,因此直接忽略。

    1. 求平均數法
    2. 環形隊列法
    3. 一致Hash算法
    4. Machine Room算法
    5. 靜態配置

      來看一下源碼,RocketMQ內部對以上負載均衡算法均有實現,並定義了一個接口 AllocateMessageQueueStrategy,採用策略模式,每種負載均衡算法都依靠實現這個接口實現,在運行中,會獲取這個接口的實例,從而動態判斷到底採用的是哪種負載均衡算法。

     1 public interface AllocateMessageQueueStrategy {
     2 
     3     /**
     4      * Allocating by consumer id
     5      *
     6      * @param consumerGroup current consumer group
     7      * @param currentCID current consumer id
     8      * @param mqAll message queue set in current topic
     9      * @param cidAll consumer set in current consumer group
    10      * @return The allocate result of given strategy
    11      */
    12     List<MessageQueue> allocate(
    13         final String consumerGroup,
    14         final String currentCID,
    15         final List<MessageQueue> mqAll,
    16         final List<String> cidAll
    17     );
    18 
    19     /**
    20      * Algorithm name
    21      *
    22      * @return The strategy name
    23      */
    24     String getName();
    25 }

     

     

    1. 求平均數法

      顧名思義,就是根據消息隊列的數量和消費者的數量,求出單個消費者上應該負擔的平均消費隊列數,然後根據消費者的ID,按照取模的方式將消息隊列分配到指定的consumer上。具體代碼可以去Github上找,截取核心算法代碼如下, mqAll就是消息隊列的結構,是一個MessageQueue的List,cidAll是消費者ID的列表,也是一個List。考慮mqAll和cidAll固定時以及變化時,當前消費者節點會從隊列中獲取到哪個隊列中的消息,比如當 averageSize 大於1時,這時每個消費者上的消息隊列就不止一個,而分配在每個消費者的上的隊列的ID是連續的。

     

     1     int index = cidAll.indexOf(currentCID);
     2         int mod = mqAll.size() % cidAll.size();
     3         int averageSize =
     4             mqAll.size() <= cidAll.size() ? 1 : (mod > 0 && index < mod ? mqAll.size() / cidAll.size()
     5                 + 1 : mqAll.size() / cidAll.size());
     6         int startIndex = (mod > 0 && index < mod) ? index * averageSize : index * averageSize + mod;
     7         int range = Math.min(averageSize, mqAll.size() - startIndex);
     8         for (int i = 0; i < range; i++) {
     9             result.add(mqAll.get((startIndex + i) % mqAll.size()));
    10         }
    11         return result;

     

    2. 環形平均法

      這種算法更為簡單。首先獲取當前消費者在整個列表中的下標index,直接用求余方法得到當前消費者應該處理的消息隊列。注意mqAll的size和cidAll的size可以是任意的。

    • 當ciAll.size() == mqAll.size() 時,該算法就是類似hashtable的求余分桶。
    • 當ciAll.size() > mqAll.size() 時,那麼多出的消費者上並不能獲取到消費的隊列,只有部分消費者能夠獲取到消息隊列並執行,相當於在消費者資源充足的情況下,由於隊列數少,所以使用其中一部分消費者就能滿足需求,不用額外的開銷。
    • 當ciAll.size() < mqAll.size() 時,這樣每個消費者上需要負載的隊列數就超過了1個,並且區別於直接求平均的方式,分配在每個消費者上的消費隊列不是連續的,而是有一定步長的間隔。
    1         int index = cidAll.indexOf(currentCID);
    2         for (int i = index; i < mqAll.size(); i++) {
    3             if (i % cidAll.size() == index) {
    4                 result.add(mqAll.get(i));
    5             }
    6         }
    7         return result;

     

    3. 一致Hash算法

      循環所有需要消費的隊列,根據隊列toString后的hash值計算出處理當前隊列的最近節點並分配給該節點。routeNode 中方法稍微複雜一些,有時間建議細看,這裏就只說功能。

     1      Collection<ClientNode> cidNodes = new ArrayList<ClientNode>();
     2         for (String cid : cidAll) {
     3             cidNodes.add(new ClientNode(cid));
     4         }
     5 
     6         final ConsistentHashRouter<ClientNode> router; //for building hash ring
     7         if (customHashFunction != null) {
     8             router = new ConsistentHashRouter<ClientNode>(cidNodes, virtualNodeCnt, customHashFunction);
     9         } else {
    10             router = new ConsistentHashRouter<ClientNode>(cidNodes, virtualNodeCnt);
    11         }
    12 
    13         List<MessageQueue> results = new ArrayList<MessageQueue>();
    14         for (MessageQueue mq : mqAll) {
    15             ClientNode clientNode = router.routeNode(mq.toString());
    16             if (clientNode != null && currentCID.equals(clientNode.getKey())) {
    17                 results.add(mq);
    18             }
    19         }
    20 
    21         return results;

     

     

    4. Machine Room算法

      基於機房的Hash算法。這個命名看起來很詐唬,其實和上面的普通求余算法是一樣的,只不過多了個配置和過濾,為了把這個說清楚就把源碼貼全一點。可以看到在這個算法的實現類中多了一個成員 consumeridcs,這個就是consumer id的一個集合,按照一定的約定,預先給broker命名,例如us@metaq4,然後給不同集群配置不同的consumeridcs,從而實現不同機房處理不同消息隊列的能力。

     1 /*
     2  * Licensed to the Apache Software Foundation (ASF) under one or more
     3  * contributor license agreements.  See the NOTICE file distributed with
     4  * this work for additional information regarding copyright ownership.
     5  * The ASF licenses this file to You under the Apache License, Version 2.0
     6  * (the "License"); you may not use this file except in compliance with
     7  * the License.  You may obtain a copy of the License at
     8  *
     9  *     http://www.apache.org/licenses/LICENSE-2.0
    10  *
    11  * Unless required by applicable law or agreed to in writing, software
    12  * distributed under the License is distributed on an "AS IS" BASIS,
    13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  * See the License for the specific language governing permissions and
    15  * limitations under the License.
    16  */
    17 package com.aliyun.openservices.shade.com.alibaba.rocketmq.client.consumer.rebalance;
    18 
    19 import java.util.ArrayList;
    20 import java.util.List;
    21 import java.util.Set;
    22 import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.consumer.AllocateMessageQueueStrategy;
    23 import com.aliyun.openservices.shade.com.alibaba.rocketmq.common.message.MessageQueue;
    24 
    25 /**
    26  * Computer room Hashing queue algorithm, such as Alipay logic room
    27  */
    28 public class AllocateMessageQueueByMachineRoom implements AllocateMessageQueueStrategy {
    29     private Set<String> consumeridcs;
    30 
    31     @Override
    32     public List<MessageQueue> allocate(String consumerGroup, String currentCID, List<MessageQueue> mqAll,
    33         List<String> cidAll) {
    34         List<MessageQueue> result = new ArrayList<MessageQueue>();
    35         int currentIndex = cidAll.indexOf(currentCID);
    36         if (currentIndex < 0) {
    37             return result;
    38         }
    39         List<MessageQueue> premqAll = new ArrayList<MessageQueue>();
    40         for (MessageQueue mq : mqAll) {
    41             String[] temp = mq.getBrokerName().split("@");
    42             if (temp.length == 2 && consumeridcs.contains(temp[0])) {
    43                 premqAll.add(mq);
    44             }
    45         }
    46 
    47         int mod = premqAll.size() / cidAll.size();
    48         int rem = premqAll.size() % cidAll.size();
    49         int startIndex = mod * currentIndex;
    50         int endIndex = startIndex + mod;
    51         for (int i = startIndex; i < endIndex; i++) {
    52             result.add(mqAll.get(i));
    53         }
    54         if (rem > currentIndex) {
    55             result.add(premqAll.get(currentIndex + mod * cidAll.size()));
    56         }
    57         return result;
    58     }
    59 
    60     @Override
    61     public String getName() {
    62         return "MACHINE_ROOM";
    63     }
    64 
    65     public Set<String> getConsumeridcs() {
    66         return consumeridcs;
    67     }
    68 
    69     public void setConsumeridcs(Set<String> consumeridcs) {
    70         this.consumeridcs = consumeridcs;
    71     }
    72 }

     

      由於近些年阿裏海外業務的擴展和投入,RocketMQ 等中間件對常見的海外業務場景的支持也更加健全。典型的場景包括跨單元消費以及消息路由。跨單元消費是比較好實現的,就是在consumer中增加一個配置,指定接收消息的來源單元,RocketMQ內部會完成客戶端從指定單元拉取消息的工作。而全球消息路由則是需要一些公共資源,消息的發送方只能將消息發送到一個指定單元/機房,然後將消息路由到另外指定的單元,consumer部署在指定單元。區別在於一個配置在客戶端,一個配置在服務端。

     

     

    總結

    從RocketMQ的設計、原理以及用過的個人用過的其他分佈式框架上看,典型的分佈式系統在設計中無外乎要解決的就是以下幾點,RocketMQ全都用上了。

    • 服務的註冊和發現。一般會有一個統一的註冊中心進行管理維護。
    • 服務的提供方和使用方間的通信,可以是異步也可以是同步,例如dubbo服務同步服務,而消息類型就是異步通信。
    • HA——高可用架構。八字決 ———— “主從同步,讀寫分離”。 要再加一句的話可以是“異地多活”。
    • 負載均衡。典型的負載均衡算法在文章內容裏面已經列出好幾種了,常用的基本也就這些。

    當然消息框架設計中用到的套路遠不止這些,包括如何保證消息消費的順序性、消費者和服務端通信、以及消息持久化等問題也是難點和重點,同樣,分佈式緩存系統也需要解決這些問題,先寫到這裏,要完全理解並自己設計一個這樣的框架難度還是相當大的。

     

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

    【其他文章推薦】

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

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

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

    ※回頭車貨運收費標準

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

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

  • 【Spring註解驅動開發】組件註冊-@ComponentScan-自動掃描組件&指定掃描規則

    寫在前面

    在實際項目中,我們更多的是使用Spring的包掃描功能對項目中的包進行掃描,凡是在指定的包或子包中的類上標註了@Repository、@Service、@Controller、@Component註解的類都會被掃描到,並將這個類注入到Spring容器中。Spring包掃描功能可以使用XML文件進行配置,也可以直接使用@ComponentScan註解進行設置,使用@ComponentScan註解進行設置比使用XML文件配置要簡單的多。

    項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

    使用XML文件配置包掃描

    我們可以在Spring的XML配置文件中配置包的掃描,在配置包掃描時,需要在Spring的XML文件中的beans節點中引入context標籤,如下所示。

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context
                               http://www.springframework.org/context/spring-context.xsd ">
    

    接下來,我們就可以在XML文件中定義要掃描的包了,如下所示。

    <context:component-scan base-package="io.mykit.spring"/>
    

    整個beans.xml文件如下所示。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context.xsd">
    
        <context:component-scan base-package="io.mykit.spring"/>
    
        <bean id = "person" class="io.mykit.spring.bean.Person">
            <property name="name" value="binghe"></property>
            <property name="age" value="18"></property>
        </bean>
    </beans>
    

    此時,只要在io.mykit.spring包下,或者io.mykit.spring的子包下標註了@Repository、@Service、@Controller、@Component註解的類都會被掃描到,並自動注入到Spring容器中。

    此時,我們分別創建PersonDao、PersonService、和PersonController類,並在這三個類中分別添加@Repository、@Service、@Controller註解,如下所示。

    • PersonDao
    package io.mykit.spring.plugins.register.dao;
    
    import org.springframework.stereotype.Repository;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 測試的dao
     */
    @Repository
    public class PersonDao {
    }
    
    • PersonService
    package io.mykit.spring.plugins.register.service;
    
    import org.springframework.stereotype.Service;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 測試的Service
     */
    @Service
    public class PersonService {
    }
    
    • PersonController
    package io.mykit.spring.plugins.register.controller;
    
    import org.springframework.stereotype.Controller;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 測試的controller
     */
    @Controller
    public class PersonController {
    }
    

    接下來,我們在SpringBeanTest類中新建一個測試方法testComponentScanByXml()進行測試,如下所示。

    @Test
    public void testComponentScanByXml(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        String[] names = context.getBeanDefinitionNames();
        Arrays.stream(names).forEach(System.out::println);
    }
    

    運行測試用例,輸出的結果信息如下所示。

    personConfig
    personController
    personDao
    personService
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    person
    

    可以看到,除了輸出我們自己創建的bean名稱之外,也輸出了Spring內部使用的一些重要的bean名稱。

    接下來,我們使用註解來完成這些功能。

    使用註解配置包掃描

    使用@ComponentScan註解之前我們先將beans.xml文件中的下述配置註釋。

    <context:component-scan base-package="io.mykit.spring"></context:component-scan>
    

    註釋后如下所示。

    <!--<context:component-scan base-package="io.mykit.spring"></context:component-scan>-->
    

    使用@ComponentScan註解配置包掃描就非常Easy了!在我們的PersonConfig類上添加@ComponentScan註解,並將掃描的包指定為io.mykit.spring即可,整個的PersonConfig類如下所示。

    package io.mykit.spring.plugins.register.config;
    
    import io.mykit.spring.bean.Person;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 以註解的形式來配置Person
     */
    @Configuration
    @ComponentScan(value = "io.mykit.spring")
    public class PersonConfig {
    
        @Bean("person")
        public Person person01(){
            return new Person("binghe001", 18);
        }
    }
    

    沒錯,就是這麼簡單,只需要在類上添加@ComponentScan(value = “io.mykit.spring”)註解即可。

    接下來,我們在SpringBeanTest類中新增testComponentScanByAnnotation()方法,如下所示。

    @Test
    public void testComponentScanByAnnotation(){
        ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig.class);
        String[] names = context.getBeanDefinitionNames();
        Arrays.stream(names).forEach(System.out::println);
    }
    

    運行testComponentScanByAnnotation()方法輸出的結果信息如下所示。

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    personConfig
    personController
    personDao
    personService
    person
    

    可以看到使用@ComponentScan註解同樣輸出了bean的名稱。

    既然使用XML文件和註解的方式都能夠將相應的類注入到Spring容器當中,那我們是使用XML文件還是使用註解呢?我更傾向於使用註解,如果你確實喜歡使用XML文件進行配置,也可以,哈哈,個人喜好嘛!好了,我們繼續。

    關於@ComponentScan註解

    我們點開ComponentScan註解類,如下所示。

    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Repeatable;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.beans.factory.support.BeanNameGenerator;
    import org.springframework.core.annotation.AliasFor;
    import org.springframework.core.type.filter.TypeFilter;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Repeatable(ComponentScans.class)
    public @interface ComponentScan {
    
    	@AliasFor("basePackages")
    	String[] value() default {};
    
    	@AliasFor("value")
    	String[] basePackages() default {};
    
    	Class<?>[] basePackageClasses() default {};
    
    	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    
    	Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
    
    	ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
    
    	String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
    
    	boolean useDefaultFilters() default true;
    
    	Filter[] includeFilters() default {};
    
    	Filter[] excludeFilters() default {};
    
    	boolean lazyInit() default false;
    
    	@Retention(RetentionPolicy.RUNTIME)
    	@Target({})
    	@interface Filter {
    		FilterType type() default FilterType.ANNOTATION;
            
    		@AliasFor("classes")
    		Class<?>[] value() default {};
            
    		@AliasFor("value")
    		Class<?>[] classes() default {};
            
    		String[] pattern() default {};
    	}
    }
    

    這裏,我們着重來看ComponentScan類的兩個方法,如下所示。

    Filter[] includeFilters() default {};
    Filter[] excludeFilters() default {};
    

    includeFilters()方法表示Spring掃描的時候,只包含哪些註解,而excludeFilters()方法表示不包含哪些註解。兩個方法的返回值都是Filter[]數組,在ComponentScan註解類的內部存在Filter註解類,大家可以看下上面的代碼。

    1.掃描時排除註解標註的類

    例如,我們現在排除@Controller、@Service和@Repository註解,我們可以在PersonConfig類上通過@ComponentScan註解的excludeFilters()實現。例如,我們在PersonConfig類上添加了如下的註解。

    @ComponentScan(value = "io.mykit.spring", excludeFilters = {
            @Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class, Repository.class})
    })
    

    這樣,我們就使得Spring在掃描包的時候排除了使用@Controller、@Service和@Repository註解標註的類。運行SpringBeanTest類中的testComponentScanByAnnotation()方法,輸出的結果信息如下所示。

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    personConfig
    person
    

    可以看到,輸出的結果信息中不再輸出personController、personService和personDao說明Spring在進行包掃描時,忽略了@Controller、@Service和@Repository註解標註的類。

    2.掃描時只包含註解標註的類

    我們也可以使用ComponentScan註解類的includeFilters()來指定Spring在進行包掃描時,只包含哪些註解標註的類。

    這裏需要注意的是,當我們使用includeFilters()來指定只包含哪些註解標註的類時,需要禁用默認的過濾規則。

    例如,我們需要Spring在掃描時,只包含@Controller註解標註的類,可以在PersonConfig類上添加@ComponentScan註解,設置只包含@Controller註解標註的類,並禁用默認的過濾規則,如下所示。

    @ComponentScan(value = "io.mykit.spring", includeFilters = {
            @Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
    }, useDefaultFilters = false)
    

    此時,我們再次運行SpringBeanTest類的testComponentScanByAnnotation()方法,輸出的結果信息如下所示。

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    personConfig
    personController
    person
    

    可以看到,在輸出的結果中,只包含了@Controller註解標註的組件名稱,並沒有輸出@Service和@Repository註解標註的組件名稱。

    注意:在使用includeFilters()來指定只包含哪些註解標註的類時,結果信息中會一同輸出Spring內部的組件名稱。

    3.重複註解

    不知道小夥伴們有沒有注意到ComponentScan註解類上有一個如下所示的註解。

    @Repeatable(ComponentScans.class)
    

    我們先來看看@ComponentScans註解是個啥,如下所示。

    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    public @interface ComponentScans {
    	ComponentScan[] value();
    }
    

    可以看到,在ComponentScans註解類中只聲明了一個返回ComponentScan[]數組的value(),說到這裏,大家是不是就明白了,沒錯,這在Java8中是一個重複註解。

    對於Java8不熟悉的小夥伴,可以到【Java8新特性】專欄查看關於Java8新特性的文章。專欄地址小夥伴們可以猛戳下面的鏈接地址進行查看:

    https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&__biz=Mzg3MzE1NTIzNA==&scene=1&album_id=1325066823947321344#wechat_redirect

    在Java8中表示@ComponentScan註解是一個重複註解,可以在一個類上重複使用這個註解,如下所示。

    @Configuration
    @ComponentScan(value = "io.mykit.spring", includeFilters = {
            @Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
    }, useDefaultFilters = false)
    @ComponentScan(value = "io.mykit.spring", includeFilters = {
            @Filter(type = FilterType.ANNOTATION, classes = {Service.class})
    }, useDefaultFilters = false)
    public class PersonConfig {
    
        @Bean("person")
        public Person person01(){
            return new Person("binghe001", 18);
        }
    }
    

    運行SpringBeanTest類的testComponentScanByAnnotation()方法,輸出的結果信息如下所示。

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    personConfig
    personController
    personService
    person
    

    可以看到,同時輸出了@Controller註解和@Service註解標註的組件名稱。

    如果使用的是Java8之前的版本,我們就不能直接在類上寫多個@ComponentScan註解了。此時,我們可以在PersonConfig類上使用@ComponentScans註解,如下所示。

    @ComponentScans(value = {
            @ComponentScan(value = "io.mykit.spring", includeFilters = {
                    @Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
            }, useDefaultFilters = false),
            @ComponentScan(value = "io.mykit.spring", includeFilters = {
                    @Filter(type = FilterType.ANNOTATION, classes = {Service.class})
            }, useDefaultFilters = false)
    })
    

    再次運行SpringBeanTest類的testComponentScanByAnnotation()方法,輸出的結果信息如下所示。

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    personConfig
    personController
    personService
    person
    

    與使用多個@ComponentScan註解輸出的結果信息相同。

    總結:我們可以使用@ComponentScan註解來指定Spring掃描哪些包,可以使用excludeFilters()指定掃描時排除哪些組件,也可以使用includeFilters()指定掃描時只包含哪些組件。當使用includeFilters()指定只包含哪些組件時,需要禁用默認的過濾規則

    好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!

    項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

    寫在最後

    如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回復“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。

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

    【其他文章推薦】

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

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

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

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

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

    ※回頭車貨運收費標準

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

  • 這輛SUV說起操控,它敢認第二沒有人認第一!

    這輛SUV說起操控,它敢認第二沒有人認第一!

    5s的百公里加速時間。而且這隻是之前發布的Mini cooper JCW版本功率,countryman可能有着更讓人驚訝的動力表現。除了發動機不同的以外,懸架以及剎車都有着很大的改善,有着更加的性能表現。一旦駕駛起來,你就會發現它們之間有着很大的不同。

    前言

    對於SUV來說,天生的高重心以及高質量,使得它們在操控方面一直是比較弱的。不過有一個品牌卻是把運動做得淋漓盡致,最近它的又一諜照被外國所捕捉到,並且給我們帶來了不少的信息,相信這會是SUV界的最有駕駛樂趣的一員。

    (上圖為2017款Mini Countryman S)

    它就是寶馬旗下的MINI Countryman,大家可以將它理解為Mini cooper的加高五門版本。雖然加高加長了,但是它保留着Mini幾乎卡丁車的那種樂趣。

    最近被曝光的是JCW版本的Mini Country Man,這是在普通版本上性能更進一部的版本。而它在最近的曝光中,最先是在紐林伯格,也就是眾多跑車測試性能的紐北。有着這般的背景,它的性能不容小覷。

    單純在遮蓋的外觀來看,JCW版本Country Man在包圍上會有着較大的不同,取消了霧燈換來更大的散熱面積。

    (上圖為2017款Mini Countryman S發動機)

    動力方面將使用的是2.0T渦輪增壓發動機,最大功率162千瓦,最大扭矩320牛米,可以讓它有着6.5s的百公里加速時間。而且這隻是之前發布的Mini cooper JCW版本功率,countryman可能有着更讓人驚訝的動力表現。

    除了發動機不同的以外,懸架以及剎車都有着很大的改善,有着更加的性能表現。一旦駕駛起來,你就會發現它們之間有着很大的不同。

    內飾方面相信和新款的countryman幾乎一模一樣。僅僅在一些如方向盤、儀錶、座椅等細節處有着一點差異。

    在國內,countryman JCW的最大競爭對手就是奔馳的GLA45 ,售價為57.80。但鑒於這是Mini品牌的產品,所以價格極有可能會在40萬左右。

    編者總結:

    這應該是市場上最具駕駛樂趣的SUV,在操控以及動力上和競爭對手相比有着較大的優勢,在外觀內飾上也有着非常出色表現,非常的個性和獨特。不過這註定是一部分人的玩物,並且是不使用的玩物,畢竟它在後排空間上始終是處於弱勢。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

    ※回頭車貨運收費標準

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

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

  • 聽說是15萬最帥的選擇!教授詳解思域和CX-4

    聽說是15萬最帥的選擇!教授詳解思域和CX-4

    主觀加速感受還行,渦輪遲滯現象輕微,在國內限速的範圍內,動力輸出杠杠的夠用。地板油時會一臉懵逼地問你:“主人,要加速。”,遲滯一會兒才開始賣力加速,激烈駕駛時稱不上得心應手。CX-4: 動力輸出持續、線性,給你一種恰到好處的加速感,容易掌控,會給你持續有力、綿密的推背感。

    十代思域憑藉其強悍的動力、驚艷的外觀和強大的綜合產品力在緊湊型車市場里呼風喚雨,儼然就是不少年輕人、中年消費群體心目中的神車。加價提車、排期候車自不用說,競品車型紛紛降價促銷、改款換代提高競爭力來應對思域。

    友商們不可能眼睜睜看着本田獨攬着龐大的新消費群體,時隔兩個月,倔強的馬自達不甘示弱,推出了為年輕人而打造的轎跑SUV——CX-4,概念車般的外觀設計以及大膽的流體線條,以錯位的車型與思域短兵相接。

    下面為手拿15萬預算,躊躇于思域與CX-4之間的年輕人提供點選車建議。

    車身尺寸

    前臉設計

    思域:集未來感、科技感於一身,“X”樣式的前臉設計,粗碩的鍍鉻飾條延伸至兩邊眼角,十分提神。

    CX-4:繼續沿用家族式的魂動設計,流星眼LED大燈,低俯的車頭給人以時刻準備衝刺的感覺,魂動紅配色(2000元)更是彰顯個性的一抹艷麗。

    車身腰線

    思域:溜背的造型是外觀設計最大的亮點,尾部過渡自然,甚顯修長。

    CX-4: 最小離地間隙196mm, 有轎車低矮扁平的發動機蓋高度,亦有SUV高人一等的的氣勢,車身比例恰到好處,越看越有韻味,令人愛不釋手。

    車尾設計

    思域:大膽的迴旋鏢狀尾燈設計,尾標與時俱進地改為220Turbo,緊隨德系車以扭矩數值作為尾標銘牌的潮流。十分可惜的是雙邊共兩出的排氣管採用隱藏式設計,這是刻意為年輕人提供改裝餘地嗎?改改改!

    CX-4:貫穿兩邊車尾燈的鍍鉻飾條承托着銀色車標,恰到好處的美。2.0L車型同樣採用了雙邊兩出的排氣布局,相比其他車型要厚道。

    內飾設計

    思域:整體風格簡潔,符合大眾審美。用料看起來不錯,但實際體現一般,滿滿的塑料感,很多摸得到的地方例如擋把的邊上、車窗按鈕、內部門拉手、駕駛座大腿右側頂到的地方、A柱和B柱都是硬塑料。慶幸的是,金屬拉絲面板的運用彰顯出科技感。

    CX-4:強調簡約、清新的內飾風格,相比過去長安昂克賽拉和CX-5的整體視覺好的不是一星半點。運用了最新的懸浮式中控屏,採用不少金屬拉絲面板進行點綴,觀感和質感都得到提升。

    空間

    思域:貫徹本田MM Concept理念:乘員空間最大化、机械空間最小化理念。空間表現比大多數競品車型要優秀,尤其橫向空間,絲毫沒有緊湊型車壓迫的感覺。溜背造型並未對頭部空間造成太大影響,淺色頂棚加上較薄的前排座椅,不會覺得壓抑。

    CX-4:漂亮的流體車身線條,必然要犧牲乘坐空間,頭部空間方面受影響更大,工程師只能將座位高度盡可能調低,實際體驗只能算差強人意。後排中央地板隆起較高,且窗口面積較少、採光不理想,給乘坐人員壓迫的感覺。

    實用配置

    發動機

    思域:實測百公里提速時間讓人瞠目結舌——7.3秒,同價位車型中難覓對手。主觀加速感受還行,渦輪遲滯現象輕微,在國內限速的範圍內,動力輸出杠杠的夠用。地板油時會一臉懵逼地問你:“主人,要加速?”,遲滯一會兒才開始賣力加速,激烈駕駛時稱不上得心應手。

    CX-4: 動力輸出持續、線性,給你一種恰到好處的加速感,容易掌控,會給你持續有力、綿密的推背感。值得一提的是,馬自達工程師稱將CX-4的油門遲滯時間調校為0.3秒(與人體肌肉慣性相關),油門反應跟駕駛者預期處於一個基本同步的狀態。實測百公里提速時間9.8秒,對動力要求較高的選2.5L版本。

    變速箱

    思域:動力迅而不猛,因為駕駛者與發動機之間隔着台溫文爾雅的變速箱,CVT在行駛過程中存在感低,但不拖泥帶水,能化解生硬的頓挫,淡化了加速時的衝擊感。會通過轉速切換去模擬一些細小的換擋頓挫,轉速變化的換擋控制與駕駛者的意圖、預期判斷基本一致、同步,整體表現不錯。

    CX-4: 這台創馳藍天6AT變速箱在車速超過8km/h后其離合器完全鎖止,防止動力輸出在液力變矩器上出現過多損耗,提高傳動效率和優化燃油經濟性。很明白駕駛員意圖,執行力非常到位,一路上用細微的油門變化去挑逗它,都能很好滿足,彷彿時刻在等待你的命令。

    底盤

    思域:前麥弗遜式獨立懸架、后多連桿獨立懸架。路面小顛簸過濾得不錯,但保留了一定的路感信息傳遞給駕駛者,中後段對車身的支撐到位,富有運動感的懸架。

    CX-4:前麥弗遜式獨立懸架、后多連桿獨立懸架。懸架支撐性出色,過彎、掉頭時的車身姿態控制到位,保留了清晰的路感,行駛表現接近“彎道王”昂克賽拉,比絕大多數SUV的行駛質感要好。

    操控

    思域:路感被過濾得所剩無幾,懸架行程較長,有一定曠量,過彎、掉頭姿態傾側明顯,屬於舒適家用車的調校範疇。所幸轉彎半徑小,掉頭方便。轉向低速沉穩,但速度上來后變輕。

    CX-4:定位轎跑SUV不單是在外觀做文章,CX-4在操控方面亦盡量嚮往轎車方面靠攏。電動助力,指向精準,車頭指向靈活,車尾循跡性好,和不錯的駕駛樂趣,開起來不像是一台SUV。

    總結:目前年輕消費群體對思域、CX-4兩款車青睞有加,前瞻性的外觀設計、優秀的動力表現和出眾的操控都是它們的亮點,預算有限的建議選思域1.5T自動尊貴版,而預算充足不妨體驗下CX-4 2.5L 自動四驅藍天激情版,動力相比2.0L得到提升的同時還配備了四驅系統,適應更多路況的行駛。 它們就像是未來汽車走向的先行者,期待有更多的後起之秀面世,讓年輕消費者有更多的選擇。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

    ※回頭車貨運收費標準

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

  • WebService之Spring+CXF整合示例

    WebService之Spring+CXF整合示例

    一、Spring+CXF整合示例

    WebService是一種跨編程語言、跨操作系統平台的遠程調用技術,它是指一個應用程序向外界暴露一個能通過Web調用的API接口,我們把調用這個WebService的應用程序稱作客戶端,把提供這個WebService的應用程序稱作服務端。

    環境

    win10+Spring5.1+cxf3.3.2

    下載

    • 官網下載:https://archive.apache.org/dist/cxf/
    • 百度網盤:
      鏈接:https://pan.baidu.com/s/1nsUweTFG_6CcZKaVBCQ7uQ
      提取碼:4qp7

    服務端

    • 新建web項目
    • 放入依賴
      apache-cxf-3.3.2\lib中的jar包全部copy至項目WEB-INF\lib目錄下(偷個懶,這些jar包中包含了Spring所需的jar包)
    • web.xml中添加webService的配置攔截
    <!--webService  -->
    <servlet>
        <servlet-name>CXFService</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>CXFService</servlet-name>
        <url-pattern>/webservice/*</url-pattern>
    </servlet-mapping>
    
    • webservice服務接口
      在項目src目錄下新建pms.inface.WebServiceInterface
    package pms.inface;
    
    import javax.jws.WebMethod;
    import javax.jws.WebParam;
    import javax.jws.WebResult;
    import javax.jws.WebService;
    
    @WebService(targetNamespace = "http://spring.webservice.server", name = "WebServiceInterface")
    public interface WebServiceInterface {
    
    	@WebMethod
        @WebResult(name = "result", targetNamespace = "http://spring.webservice.server")
    	public String sayBye(@WebParam(name = "word", targetNamespace = "http://spring.webservice.server") String word);
    
    }
    
    
    • 接口實現類
      在項目src目錄下新建pms.impl.WebServiceImpl
    package pms.impl;
    
    import javax.jws.WebService;
    
    import pms.inface.WebServiceInterface;
    
    @WebService
    public class WebServiceImpl implements WebServiceInterface{
    
    	@Override
    	public String sayBye(String word) {
    		return word + "當和這個真實的世界迎面撞上時,你是否找到辦法和自己身上的慾望講和,又該如何理解這個鋪面而來的人生?";
    	}
    
    }
    
    
    • webservice配置文件
      WEB-INF目錄下新建webservice配置文件cxf-webService.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:jaxws="http://cxf.apache.org/jaxws"
    	xmlns:cxf="http://cxf.apache.org/core"
    	xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
           http://cxf.apache.org/jaxws
           http://cxf.apache.org/schemas/jaxws.xsd
           http://cxf.apache.org/core
    	   http://cxf.apache.org/schemas/core.xsd
    	   http://cxf.apache.org/transports/http/configuration
    	   http://cxf.apache.org/schemas/configuration/http-conf.xsd
    	   ">
    	   
    	<import resource="classpath:META-INF/cxf/cxf.xml" />
    
    	<!-- 使用jaxws:server標籤發布WebService服務 ,設置address為訪問地址, 和web.xml文件中配置的CXF配合為一個完整的路徑 -->
    	<!-- serviceClass為實現類的接口 serviceBean引用配置好的WebService實現類 -->
    	<jaxws:server address="/webServiceInterface"
    		serviceClass="pms.inface.WebServiceInterface">
    		<jaxws:serviceBean>
    			<ref bean="WebServiceImpl" />
    		</jaxws:serviceBean>
    	</jaxws:server>
    	
    	<!-- 為所有的WS設置超時時間 ,此時為默認值 連接時間30s,等待回復時間為60s-->	
    	<http-conf:conduit name="*.http-conduit">
    		<http-conf:client ConnectionTimeout="60000" ReceiveTimeout="120000"/>
    	</http-conf:conduit>
    
    </beans>
    
    • spring配置文件
      WEB-INF目錄下新建spring配置文件applicationContext.xml
    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
       <bean id="WebServiceImpl" class="pms.impl.WebServiceImpl"></bean>
    	
    	<import resource="cxf-webService.xml" />
    
    </beans>
    

          在web.xml中配置applicationContext.xml

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
    		    /WEB-INF/applicationContext.xml
    		</param-value>
      </context-param>
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
    • 將項目放至tomcat中啟動
      啟動后訪問地址:localhost:PORT/項目名/webservice/webServiceInterface?wsdl,如下圖所示,webservice接口發布成功

    二、SoapUI測試

    SoapUI是一個開源測試工具,通過soap/http來檢查、調用、實現Web Service的功能/負載/符合性測試。

    下載

    • 百度網盤
      鏈接:https://pan.baidu.com/s/1N2RTqhvrkuzx7YJvmDeY7Q
      提取碼:e1w3

    測試

    • 打開SoapUI,新建一個SOAP項目,將剛才的發布地址copyInitial WSDL欄,點擊OK按鈕
    • 發起接口請求

    三、客戶端

    使用wsdl2java工具生成webservice客戶端代碼

    • 該工具在剛才下載的apache-cxf-3.3.2\bin目錄下
    • 配置環境變量
      設置CXF_HOME,並添加%CXF_HOME %/binpath環境變量。
    • CMD命令行輸入wsdl2java -help,有正常提示說明環境已經正確配置
    • wsdl2java.bat用法:
    wsdl2java –p 包名 –d 存放目錄 -all wsdl地址
    
    -p 指定wsdl的命名空間,也就是要生成代碼的包名
    
    -d 指令要生成代碼所在目錄
    
    -client 生成客戶端測試web service的代碼
    
    -server 生成服務器啟動web service代碼
    
    -impl 生成web service的實現代碼,我們在方式一用的就是這個
    
    -ant 生成build.xml文件
    
    -all 生成所有開始端點代碼
    
    • 生成客戶端代碼
    wsdl2java -p pms.inface -d ./ -all http://localhost:8080/spring_webservice_server/webservice/webServiceInterface?wsdl
    

    客戶端調用

    • 新建web項目
    • 放入依賴
      apache-cxf-3.3.2\lib中的jar包全部copy至項目WEB-INF\lib目錄下
    • wsdl2java生成的代碼放至src.pms.inface目錄下
    調用方法一:
    • 新建webServiceClientMain測試
    package pms;
    
    import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
    import pms.inface.WebServiceInterface;
    
    public class webServiceClientMain {
    	public static void main(String[] args) {
    		JaxWsProxyFactoryBean svr = new JaxWsProxyFactoryBean();
    		svr.setServiceClass(WebServiceInterface.class);
    		svr.setAddress("http://localhost:8080/spring_webservice_server/webservice/webServiceInterface?wsdl");
    		WebServiceInterface webServiceInterface = (WebServiceInterface) svr.create();
    
    		System.out.println(webServiceInterface.sayBye("honey,"));
    	}
    }
    
    • 運行webServiceClientMain
    調用方法二:
    • 在src目錄下新建applicationContext.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:jaxws="http://cxf.apache.org/jaxws"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
    		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    		http://www.springframework.org/schema/context
    		http://www.springframework.org/schema/context/spring-context-3.0.xsd
    		http://cxf.apache.org/jaxws
    		http://cxf.apache.org/schemas/jaxws.xsd">
    
    	<jaxws:client id="webServiceInterface"
    		serviceClass="pms.inface.WebServiceInterface"
    		address="http://localhost:8080/spring_webservice_server/webservice/webServiceInterface?wsdl" >
    	</jaxws:client>	
    </beans>
    
    • 新建webServiceClientTest測試
    package pms;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import pms.inface.WebServiceInterface;
    
    public class webServiceClientTest {
    
    	public static void main(String[] args) {
    		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    		WebServiceInterface webServiceInterface = context.getBean(WebServiceInterface.class);
    		String result = webServiceInterface.sayBye("honey,");
    		System.out.println(result);
    	}
    	
    }
    
    • 運行webServiceClientTest

    四、服務端攔截器

    • 需求場景:服務提供方安全驗證,也就是webservice自定義請求頭的實現,服務接口在身份認證過程中的密碼字段滿足SM3(哈希函數算法標準)的加密要求
    • SM3加密所需jar包:commons-lang3-3.9.jarbcprov-jdk15on-1.60.jar,這兩個jar包在剛才下載的apache-cxf-3.3.2\lib下就有
    • 請求頭格式
    <security>
    	<username></username>
    	<password></password>
    </auth>
    
    • src.pms.interceptor下新建WebServiceInInterceptor攔截器攔截請求,解析頭部
    package pms.interceptor;
    
    import java.util.List;
    import javax.servlet.http.HttpServletRequest;
    import javax.xml.namespace.QName;
    import org.apache.cxf.binding.soap.SoapMessage;
    import org.apache.cxf.headers.Header;
    import org.apache.cxf.interceptor.Fault;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    import org.apache.cxf.transport.http.AbstractHTTPDestination;
    import org.w3c.dom.Element;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    import pms.support.Sm3Utils;
    import pms.support.StringUtils;
    
    /**
     * WebService的輸入攔截器
     * @author coisini
     * @date May 2020, 13
     *
     */
    public class WebServiceInInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
    	
        private static final String USERNAME = "admin";
        private static final String PASSWORD = "P@ssw0rd";
        
        /**
         * 允許訪問的IP
         */
        private static final String ALLOWIP = "127.0.0.1;XXX.XXX.XXX.XXX";
    
    	public WebServiceInInterceptor() {
    		/*
    		 * 攔截器鏈有多個階段,每個階段都有多個攔截器,攔截器在攔截器鏈的哪個階段起作用,可以在攔截器的構造函數中聲明
    		 * RECEIVE 接收階段,傳輸層處理
    		 * (PRE/USER/POST)_STREAM 流處理/轉換階段
    		 * READ SOAPHeader讀取 
    		 * (PRE/USER/POST)_PROTOCOL 協議處理階段,例如JAX-WS的Handler處理 
    		 * UNMARSHAL SOAP請求解碼階段 
    		 * (PRE/USER/POST)_LOGICAL SOAP請求解碼處理階段 
    		 * PRE_INVOKE 調用業務處理之前進入該階段 
    		 * INVOKE 調用業務階段 
    		 * POST_INVOKE 提交業務處理結果,並觸發輸入連接器
    		 */
    		super(Phase.PRE_INVOKE);
    	}
    
    	/**
    	  * 客戶端傳來的 soap 消息先進入攔截器這裏進行處理,客戶端的賬目與密碼消息放在 soap 的消息頭<security></security>中,
    	  * 類似如下:
         * <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
         * <soap:Header><security><username>admin</username><password>P@ssw0rd</password></security></soap:Header>
         * <soap:Body></soap:Body></soap:Envelope>
         * 現在只需要解析其中的 <head></head>標籤,如果解析驗證成功,則放行,否則這裏直接拋出異常,
         * 服務端不會再往後運行,客戶端也會跟着拋出異常,得不到正確結果
         *
         * @param message
         * @throws Fault
         */
    	@Override
        public void handleMessage(SoapMessage message) throws Fault {
    		System.out.println("PRE_INVOKE");
    		
    		HttpServletRequest request = (HttpServletRequest)message.get(AbstractHTTPDestination.HTTP_REQUEST);
    	    String ipAddr=request.getRemoteAddr();
    	    System.out.println("客戶端訪問IP----"+ipAddr);
    	    
    	    if(!ALLOWIP.contains(ipAddr)) {
    			throw new Fault(new IllegalArgumentException("非法IP地址"), new QName("0009"));
    		}
    		
    		/**
    		 * org.apache.cxf.headers.Header
             * QName :xml 限定名稱,客戶端設置頭信息時,必須與服務器保持一致,否則這裏返回的 header 為null,則永遠通不過的
             */
    		Header authHeader = null;
    		//獲取驗證頭
    		List<Header> headers = message.getHeaders();
    		for(Header h:headers){
    			if(h.getName().toString().contains("security")){
    				authHeader=h;
    				break;
    			}
    		}
    		System.out.println("authHeader");
    		System.out.println(authHeader);
    		
    		if(authHeader !=null) {
    			Element auth = (Element) authHeader.getObject();
    			NodeList childNodes = auth.getChildNodes();
    			String username = null,password = null;
    			for(int i = 0, len = childNodes.getLength(); i < len; i++){
    					Node item = childNodes.item(i);
    					if(item.getNodeName().contains("username")){
    						username = item.getTextContent();
    						System.out.println(username);
    					}
    					if(item.getNodeName().contains("password")){
    						password = item.getTextContent();
    						System.out.println(password);
    					}
    			}
    			
    			if(StringUtils.isBlank(username) || StringUtils.isBlank(password)) { 
    		    	throw new Fault(new IllegalArgumentException("用戶名或密碼不能為空"), new QName("0001")); 
    		    }
    			
    			if(!Sm3Utils.verify(USERNAME, username) || !Sm3Utils.verify(PASSWORD,password)) { 
    		    	throw new Fault(new IllegalArgumentException("用戶名或密碼錯誤"), new QName("0008")); 
    		    }
    		  
    		    if (Sm3Utils.verify(USERNAME, username) && Sm3Utils.verify(PASSWORD,password)) { 
    		    	System.out.println("webService 服務端自定義攔截器驗證通過...."); 
    		    	return;//放行
    		    } 
    		}else {
    			throw new Fault(new IllegalArgumentException("請求頭security不合法"), new QName("0010"));
    		}
    	}
    
    	// 出現錯誤輸出錯誤信息和棧信息
    	public void handleFault(SoapMessage message) {
    		Exception exeption = message.getContent(Exception.class);
    		System.out.println(exeption.getMessage());
    	}
    	
    }
    
    • src.pms.support下新建Sm3Utils加密類
    package pms.support;
    
    import java.io.UnsupportedEncodingException;
    import java.security.Security;
    import java.util.Arrays;
    import org.bouncycastle.crypto.digests.SM3Digest;
    import org.bouncycastle.crypto.macs.HMac;
    import org.bouncycastle.crypto.params.KeyParameter;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
    
    /**
     * SM3加密
     * @author coisini
     * @date May 2020, 13
     */
    public class Sm3Utils {
    	 private static final String ENCODING = "UTF-8";
         static {
             Security.addProvider(new BouncyCastleProvider());
         }
    	    
        /**
         * sm3算法加密
         * @explain
         * @param paramStr
         * 待加密字符串
         * @return 返回加密后,固定長度=32的16進制字符串
         */
        public static String encrypt(String paramStr){
            // 將返回的hash值轉換成16進制字符串
            String resultHexString = "";
            try {
                // 將字符串轉換成byte數組
                byte[] srcData = paramStr.getBytes(ENCODING);
                // 調用hash()
                byte[] resultHash = hash(srcData);
                // 將返回的hash值轉換成16進制字符串
                resultHexString = ByteUtils.toHexString(resultHash);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return resultHexString;
        }
        
        /**
         * 返回長度=32的byte數組
         * @explain 生成對應的hash值
         * @param srcData
         * @return
         */
        public static byte[] hash(byte[] srcData) {
            SM3Digest digest = new SM3Digest();
            digest.update(srcData, 0, srcData.length);
            byte[] hash = new byte[digest.getDigestSize()];
            digest.doFinal(hash, 0);
            return hash;
        }
        
        /**
         * 通過密鑰進行加密
         * @explain 指定密鑰進行加密
         * @param key
         *            密鑰
         * @param srcData
         *            被加密的byte數組
         * @return
         */
        public static byte[] hmac(byte[] key, byte[] srcData) {
            KeyParameter keyParameter = new KeyParameter(key);
            SM3Digest digest = new SM3Digest();
            HMac mac = new HMac(digest);
            mac.init(keyParameter);
            mac.update(srcData, 0, srcData.length);
            byte[] result = new byte[mac.getMacSize()];
            mac.doFinal(result, 0);
            return result;
        }
        
        /**
         * 判斷源數據與加密數據是否一致
         * @explain 通過驗證原數組和生成的hash數組是否為同一數組,驗證2者是否為同一數據
         * @param srcStr
         *            原字符串
         * @param sm3HexString
         *            16進制字符串
         * @return 校驗結果
         */
        public static boolean verify(String srcStr, String sm3HexString) {
            boolean flag = false;
            try {
                byte[] srcData = srcStr.getBytes(ENCODING);
                byte[] sm3Hash = ByteUtils.fromHexString(sm3HexString);
                byte[] newHash = hash(srcData);
                if (Arrays.equals(newHash, sm3Hash))
                    flag = true;
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return flag;
        }
        
        public static void main(String[] args) {
            // 測試二:account
            String account = "admin";
            String passoword = "P@ssw0rd";
            String hex = Sm3Utils.encrypt(account);
            System.out.println(hex);//dc1fd00e3eeeb940ff46f457bf97d66ba7fcc36e0b20802383de142860e76ae6
            System.out.println(Sm3Utils.encrypt(passoword));//c2de40449a2019db9936381fa9810c22c8548a8635ed2b7fb3c7ec362e37429d
            //驗證加密后的16進制字符串與加密前的字符串是否相同
            boolean flag =  Sm3Utils.verify(account, hex);
            System.out.println(flag);// true
        }
    }
    
    • StringUtils工具類
    package pms.support;
    
    /**
     * 字符串工具類
     * @author coisini
     * @date Nov 27, 2019
     */
    public class StringUtils {
    
    	/**
    	 * 判空操作
    	 * @param value
    	 * @return
    	 */
    	public static boolean isBlank(String value) {
    		return value == null || "".equals(value) || "null".equals(value) || "undefined".equals(value);
    	}
    
    }
    
    • cxf-webService.xml添加攔截器配置
    <!-- 在此處引用攔截器 -->
    <bean id="InInterceptor"
    	class="pms.interceptor.WebServiceInInterceptor" >
    </bean>
    
    <cxf:bus>
    	<cxf:inInterceptors>
    		<ref bean="InInterceptor" />
    	</cxf:inInterceptors>
    </cxf:bus> 
    
    • SoapUI調用
    • java調用

      服務端攔截器到此結束,由上圖可以看出攔截器配置生效

    五、客戶端攔截器

    • src.pms.support下新建AddHeaderInterceptor攔截器攔截請求,添加自定義認證頭部
    package pms.support;
    
    import java.util.List;
    import javax.xml.namespace.QName;
    import org.apache.cxf.binding.soap.SoapHeader;
    import org.apache.cxf.binding.soap.SoapMessage;
    import org.apache.cxf.headers.Header;
    import org.apache.cxf.helpers.DOMUtils;
    import org.apache.cxf.interceptor.Fault;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    
    public class AddHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage>{ 
        
        private String userName; 
        private String password; 
           
        public AddHeaderInterceptor(String userName, String password) { 
            super(Phase.PREPARE_SEND); 
            this.userName = userName; 
            this.password = password;  
        } 
       
        @Override 
        public void handleMessage(SoapMessage msg) throws Fault { 
        	   System.out.println("攔截...");
            
               /**
                * 生成的XML文檔
                * <authHeader>
                *      <userName>admin</userName>
                *      <password>P@ssw0rd</password>
                * </authHeader>
                */ 
            
            	// SoapHeader部分待添加的節點
         		QName qName = new QName("security");
         		Document doc = DOMUtils.createDocument();
    
         		Element pwdEl = doc.createElement("password");
         		pwdEl.setTextContent(password);
         		Element userEl = doc.createElement("username");
         		userEl.setTextContent(userName);
         		Element root = doc.createElement("security");
         		root.appendChild(userEl);
         		root.appendChild(pwdEl);
         		// 創建SoapHeader內容
         		SoapHeader header = new SoapHeader(qName, root);
         		// 添加SoapHeader內容
         		List<Header> headers = msg.getHeaders();
         		headers.add(header); 
        } 
    }
    
    • java調用,修改webServiceClientMain調用代碼如下
    public class webServiceClientMain {
    	public static void main(String[] args) {
    		JaxWsProxyFactoryBean svr = new JaxWsProxyFactoryBean();
    		svr.setServiceClass(WebServiceInterface.class);
    		svr.setAddress("http://localhost:8081/spring_webservice_server/webservice/webServiceInterface?wsdl");
    		WebServiceInterface webServiceInterface = (WebServiceInterface) svr.create();
    		
    		// jaxws API 轉到 cxf API 添加日誌攔截器
    		org.apache.cxf.endpoint.Client client = org.apache.cxf.frontend.ClientProxy
    				.getClient(webServiceInterface);
    		org.apache.cxf.endpoint.Endpoint cxfEndpoint = client.getEndpoint();
    		//添加自定義的攔截器
    		cxfEndpoint.getOutInterceptors().add(new AddHeaderInterceptor("dc1fd00e3eeeb940ff46f457bf97d66ba7fcc36e0b20802383de142860e76ae6", "c2de40449a2019db9936381fa9810c22c8548a8635ed2b7fb3c7ec362e37429d"));
    		
    		System.out.println(webServiceInterface.sayBye("honey,"));
    	}
    }
    

    • SoapUI調用

    六、代碼示例

    服務端:https://github.com/Maggieq8324/spring_webservice_server.git
    客戶端:https://github.com/Maggieq8324/spring_webservice_client.git

    .end

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

    【其他文章推薦】

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

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

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

    ※回頭車貨運收費標準

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

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