月份: 2020 年 9 月

  • 挪威大力提倡電動汽車 9月Tesla銷量奪冠

    美國電動汽車廠商特斯拉(Tesla),在挪威推出電動轎車Model S才2個月,需求即湧現,甚至9月榮登挪威最熱賣車款,市場認為這跟挪威政府大力補貼電動車有莫大關係。

    特斯拉9月在挪威賣出616輛Model S,在挪威9月汽車總銷售裡占5.1%。至於日產電動車Leaf,1 ~9月為挪威包括汽油等各車種裡,暢銷排名第5的車款,在挪威整體車市裡占約3%。外界估計目前在挪威路上行駛的電動車達14,500輛。根據美國電力驅動運輸協會(EDTA)資料,美國去年僅賣出13,427輛電動車。

    挪威大力提倡電動車,成為其他推廣電動車國家的一個典範。挪威首都奧斯陸市長史坦(Fabian Stang),最近宣布奧斯陸為「電動車之都」。他說,挪威對電動車立場,是對環保所做更大承諾的一部份。挪威政府估計,該國為全球人均電動車比例最高的國家。挪威全國人口約500萬人。

    外界認為挪威有規模7,500億美元的主權財富基金做後盾,才能提供許多補貼來大力提倡電動車,使許多國家望塵莫及。其主權基金是以石油收益來建立,外界估計是全球規模最大的主權基金。

    挪威給電動車豁免領牌費和25%增值稅,如此每輛電動車的車主立即省下數萬美元;豁免通行費,並可在奧斯陸的高速公路上使用公車專用道,能在最繁忙時段避開塞車之苦。挪威汽油價格為歐洲最高,每公升汽油15.16挪威克朗(約74.6台幣),讓電動車有很大吸引力。

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

    【其他文章推薦】

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

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

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

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

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

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

  • 截至9月底 日產Leaf全球累計銷量83,000台

    日本汽車大廠日產汽車(Nissan)昨(8)日發布新聞稿稱,旗下全球首款量產型電動車「Leaf」於全球各個市場的銷售氣勢持續高漲,截至2013年9月底為止,Leaf全球累計銷售量達83,000台,9月Leaf全球銷售量更高達4,700台、創史上最高單月銷售紀錄。另外,截至9月底為止,Leaf於日本市場的累計銷售量達30,000台。

    日產表示,Leaf於2010年10月搶先於日本/北美市場開賣,且銷售量逐年增長,其中2012年度Leaf於日本市場的銷售量就達11,600台、且預估今年度(2013年度)日本市場銷售量可望更優於2012年度水準。

    為了加快電動車的普及速度,日產還計劃於2013年度下半年內(2013年10月-2014年3月)追加於日本國內的日產銷售通路增設700座快速充電器。目前日本全國的快速充電器數量總計約1,900座,其中約800座設置在日產的銷售通路。

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

    【其他文章推薦】

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

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

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

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

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

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

  • 預購突破8000臺 BMW考慮擴大電動i3生產規模

    預購突破8000臺 BMW考慮擴大電動i3生產規模

    據彭博今(16)日報導,德國豪華車廠寶馬汽車公司(BMW)CFO Friedrich Eichiner表示,因初期需求超乎該公司原先預期,故考慮擴大電動車的生產規模。Friedrich Eichiner表示,i3雖尚未在歐洲市場開賣,但目前接獲的預購訂單已突破8,000台,且預估此種需求可望持續下去,故有必要立即進行進一步的投資。

    據悉,i3將在11月16日於德國開賣,售價為3萬4,950歐元(約460萬日圓),之後並將在2014年1-6月期間於美國、日本、中國大陸進行販售。BMW預估2014年i3銷售量可達1萬台以上。

    近來電動汽車需求出現明顯增長,除BMW之外,日產(Nissan)電動車「Leaf」也交出亮眼成績單。

    據日經新聞報導,日產汽車首席運營官(COO)志賀俊之10月11日於東京都內舉行的日本證券分析師大會上表示,日產電動車「Leaf」今年度(2013年度)全球銷售量預估將能達到5萬台以上的水準,將較2012年度的3萬台呈現大幅度的增長(將大增7成)。

    根據日產汽車10月8日發布的資料顯示,截至2013年9月底為止,Leaf全球累計銷售量達83,000台,而2013年9月份Leaf全球銷售量更高達4,700台、創史上最高單月銷售紀錄。

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

    【其他文章推薦】

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

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

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

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

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

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

  • 調查顯示電動車市占率有望在2020年超過10%

    據悉,2013年中國節能與新能源汽車產業發展高峰論壇於上周在北京舉行,中國汽車工程學會副秘書長張寧在壇於上表示,針對486位汽車工程師進行的《電動汽車技術進步和產業化前景調查問卷》顯示,電動汽車有望在2020年-2025年之間實現商業化。2020年,電動車在世界乘用車市場中的佔有率將達到10%-15%。

    根據調查數據,目前影響電動汽車發展的主要瓶頸還是電池技術、成本和續駛裡程。數據還顯示,到2020年,燃油汽車仍擁有15%-25%的發展潛力。因此,從中國的現階段來講,應當不要排斥某種技術路線,還是應當用多種方式推動節能減排,包括電動汽車及燃料汽車。

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

    【其他文章推薦】

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

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

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

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

    ※超省錢租車方案

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

  • 萬向電動汽車獲得中國工信部資質認證

    萬向電動汽車獲得中國工信部資質認證

    中國工信部今(22)日發布《車輛生產企業及產品公告》(第254批),公示了三家新增車輛生產企業的名單,公示時間為22日-28日。其中萬向電動汽車有限公司名列其中。

    此前,萬向在電動汽車相關領域投入重資,雖已經初步形成車載電子系統、動力電池和電動汽車領等較完整的產業鏈,但在國內市場上,作為從事電動汽車相關業務的公司,仍缺少工信部認證的資質。

    2013年1月,萬向成功收購美國A123,完善了電池產業鏈條,借全球知名企業品牌優勢,擴大了其在美國市場的銷售份額。

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

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

  • 中國國務院批復新一輪新能源汽車推廣方案

    中國科技部部長萬鋼上周在2013中國汽車產業發展(泰達)國際論壇上透露,國務院已正式批復新一輪的新能源汽車示范推廣方案,四部委正在制定實施細則,並將盡快正式啟動。「直接補貼到企業」將是新一輪補貼政策最大的變化。

    新一輪補貼政策的內容主要有:以試點城市為核心,設立試點區域,擴大輻射范圍,加速區域電動汽車推廣;改善原有財政資金補貼方式,加快資金補貼落實力度;混合動力客車將向全國推廣;對充電站的建設進行財政支持。

    另外,發改委、工信部、商務部等多部委對特斯拉總部進行了訪問考察。此次訪問或對特斯拉順利進入中國市場有促進作用。

    中國國內電動汽車也在快速發展。以杭州為例,在國內率先啟用了可出租的純電動微公交,每小時20元(人民幣),一次充滿電可跑80-100公裏,每次充電大約耗時30分鐘。杭州市未來設想,每5-10公裏有1個立體車庫站點,建成綠色的微公交網點係統。

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

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

  • Tesla執行長稱燃料電池不適合推廣 純電動車才有未來

    據外媒本週二報導,美國電動跑車製造商特斯拉(Tesla)執行長Elon Musk本週在慕尼黑特斯拉展示中心表示,很多人都說純電動車根本沒有未來,但他認為,氫燃料電池基本上只是一種行銷的伎倆,氫是一種危險性頗高的氣體,比較適合用來推動火箭。

    Elon Musk在演說中提到,旗下價格較低的大眾車種(可能會被命名為「Model E」)預料將在12-15個月內開發完成、2016年開賣,而休旅車「Model X」則會在明(2014)年問世。

    此前,豐田汽車(Toyota) 董事長內山田武(Takeshi Uchiyamada)曾與9月30日在華盛頓特區經濟俱樂部(Economic Club of Washington, D.C.)發表演說後表示,Toyota之所以並未推出任何一款重量級純電動車,是因為該公司不認為這種產品會有市場。

    目前全球燃料電池車的研發以日系車廠為軸心形成三大陣營,其中Toyota已表明計劃於2015年開賣燃料電池車。據日經新聞報導,預估2025年日本國內的燃料電池車普及數量將達200萬台。

    另據日經新聞報導,日本政府也計劃於2015年度結束前,在國內整備100座氫燃料充填據點「氫氣站」,其他日本企業也紛紛著手進行相關技術的研發。為促進燃料電池車的普及,千代田化工建設(CHIYODA)計畫投下約300億日圓,於2015年在川崎市興建全球首座大規模氫燃料供應基地。

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

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

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

  • 在運行時生成C# .NET類

    在運行時生成C# .NET類

    ​本文譯自​:​Generating C# .NET Classes at Runtime
    作者:WedPort

    在我的C#職業生涯中,有幾次我不得不在運行時生成新的類型。希望把它寫下來能幫助有相同應用需求的人。這也意味着我以後不必在查找相同問題的StackOverflow文章了。我最初是在.NET 4.6.2中這樣做的,但我已經更新到為.NET Core 3.0提供了示例。所有代碼都可以在我的GitHub上面找到。
    GitHub:https://github.com/cheungt6/public/tree/master/ReflectionEmitClassGeneration

    為什麼我需要在運行時生成類?

    在運行時生產新類型的需求通常是由於運行時才知道類屬性,滿足性能要求以及需要在新類型中添加功能。當你嘗試這樣做的時候,你應該考慮的第一件事是:這是否真的是一個明智的解決方案。在深入思考之前,還有很多其他事情可以嘗試,問你自己這樣的問題:

    1. 我可以使用普通的類嗎
    2. 我可以使用Dictionary、Tuple或者對象數組(Array)?
    3. 我是否可以使用擴展對象
    4. 我確定我不能使用一個普通的類嗎?

    如果你認為這仍然是必要的,請繼續閱讀下面的內容。

    示例用例

    作為一名開發人員,我將大量數據綁定到各種WPF Grids中。大多數時候屬性是固定的,我可以使用預定義的類。有時候,我不得不動態的構建網格,並且能夠在應用程序運行時更改數據。採取以下显示ID和一些財務數據的類(FTSE和CAC是指數,其屬性代表指數價格):

    public class PriceHolderViewModel : ViewModelBase
    {
        public long Id { get; set; }
        public decimal FTSE100 { get; set; }
        public decimal CAC40 { get; set; }
    }
    

    如果我們僅對其中的屬性感興趣,該類定義的非常棒。但是,如果要使用更多屬性擴展此類,則需要在代碼中添加它,重新編譯並在新版本中進行部署。

    相反的,我們可以做的是跟蹤對象所需的屬性,並在運行時構建類。這將允許我們在需要是不斷的添加和刪除屬性,並使用反射來更新它們的值。

    // Keep track of my properties
    var _properties = new Dictionary<string, Type>(new[]{
       new KeyValuePair<string, Type>( "FTSE100", typeof(Decimal) ),
       new KeyValuePair<string, Type>( "CAC40", typeof(Decimal) ) });
    

    創建你的類型

    下面的示例向您展示了如何在運行時構建新類型。你需要使用**System.Reflection.Emit**庫來構造一個新的動態程序集,您的類將在其中創建,然後是模塊和類型。與舊的** .NET Framework**框架不同,在舊的版本中,你需要在當前程序的AppDomain中創建程序集 ,而在** .NET Core** 中,AppDomain不再可用。你將看到我使用GUID創建了一個新類型名稱,以便於跟蹤類型的版本。在以前,你不能創建具有相同名稱的兩個類型,但是現在似乎不是這樣了。

    public Type GeneratedType { private set; get; }
    
    private void Initialise()
    {
        var newTypeName = Guid.NewGuid().ToString();
        var assemblyName = new AssemblyName(newTypeName);
        var dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
        var dynamicModule = dynamicAssembly.DefineDynamicModule("Main");
        var dynamicType = dynamicModule.DefineType(newTypeName,
                TypeAttributes.Public |
                TypeAttributes.Class |
                TypeAttributes.AutoClass |
                TypeAttributes.AnsiClass |
                TypeAttributes.BeforeFieldInit |
                TypeAttributes.AutoLayout,
                typeof(T));     // This is the type of class to derive from. Use null if there isn't one
        dynamicType.DefineDefaultConstructor(MethodAttributes.Public |
                                            MethodAttributes.SpecialName |
                                            MethodAttributes.RTSpecialName);
        foreach (var property in Properties)
            AddProperty(dynamicType, property.Key, property.Value);
    
        GeneratedType = dynamicType.CreateType();
    }
    

    在定義類型時,你可以提供一種類型,從中派生新的類型。如果你的基類具有要包含在新類型中的某些功能或屬性,這將非常有用。之前,我曾使用它在運行時擴展ViewModelSerializable類型。

    在你創建了TypeBuilder后,你可以使用下面提供的代碼開始添加屬性。它創建了支持字段和所需的中間語言,以便通過GetterSetter訪問它們。為每個屬性完成此操作后,可以使用CreateType()創建類型的實例。

    private static void AddProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
    {
        var fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
        var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
        
        var getMethod = typeBuilder.DefineMethod("get_" + propertyName,
            MethodAttributes.Public |
            MethodAttributes.SpecialName |
            MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
        var getMethodIL = getMethod.GetILGenerator();
        getMethodIL.Emit(OpCodes.Ldarg_0);
        getMethodIL.Emit(OpCodes.Ldfld, fieldBuilder);
        getMethodIL.Emit(OpCodes.Ret);
    
        var setMethod = typeBuilder.DefineMethod("set_" + propertyName,
              MethodAttributes.Public |
              MethodAttributes.SpecialName |
              MethodAttributes.HideBySig,
              null, new[] { propertyType });
        var setMethodIL = setMethod.GetILGenerator();
        Label modifyProperty = setMethodIL.DefineLabel();
        Label exitSet = setMethodIL.DefineLabel();
    
        setMethodIL.MarkLabel(modifyProperty);
        setMethodIL.Emit(OpCodes.Ldarg_0);
        setMethodIL.Emit(OpCodes.Ldarg_1);
        setMethodIL.Emit(OpCodes.Stfld, fieldBuilder);
        setMethodIL.Emit(OpCodes.Nop);
        setMethodIL.MarkLabel(exitSet);
        setMethodIL.Emit(OpCodes.Ret);
    
        propertyBuilder.SetGetMethod(getMethod);
        propertyBuilder.SetSetMethod(setMethod);
    }
    

    有了類型后,就很容易通過使用Activator.CreateInstance()來創建它的實例。但是,你希望能夠更改已創建的屬性的值,為了做到這一點,你可以再次使用反射來獲取propertyInfos並提取Set方法。一旦有了這些屬性,電影它們類設置屬性值就相對簡單了。

    foreach (var property in Properties)
    {
        var propertyInfo = GeneratedType.GetProperty(property.Key);
        var setMethod = propertyInfo.GetSetMethod();
        setMethod.Invoke(objectInstance, new[] { propertyValue });
    }
    

    現在,您可以在運行時使用自定義屬性來創建自己的類型,並具有更新其值的功能,一切就緒。 我發現的唯一障礙是創建一個可以存儲新類型實例的列表。 WPF中的DataGrid傾向於只讀取List的常規參數類型的屬性。 這意味着即使您使用新屬性擴展了基類,使用AutoGenerateProperties也只能看到基類中的屬性。 解決方案是使用生成的類型顯式創建一個新的List。 我在下面提供了如何執行此操作的示例:

    var listGenericType = typeof(List<>);
    var list = listGenericType.MakeGenericType(GeneratedType);
    var constructor = list.GetConstructor(new Type[] { });
    var newList = (IList)constructor.Invoke(new object[] { });
    foreach (var value in values)
        newList.Add(value);
    

    結論

    我已經在GitHub中創建了一個示例應用程序。它包含一個UI來幫助您調試和理解運行時新類型的創建,以及如何更新值。如果您有任何問題或意見,請隨時與我們聯繫。

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

    【其他文章推薦】

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

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

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

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

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

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

  • 一個線上問題的思考:Eureka註冊中心集群如何實現客戶端請求負載及故障轉移?

    一個線上問題的思考:Eureka註冊中心集群如何實現客戶端請求負載及故障轉移?

    前言

    先拋一個問題給我聰明的讀者,如果你們使用微服務SpringCloud-Netflix進行業務開發,那麼線上註冊中心肯定也是用了集群部署,問題來了:

    你了解Eureka註冊中心集群如何實現客戶端請求負載及故障轉移嗎?

    可以先思考一分鐘,我希望你能夠帶着問題來閱讀此篇文章,也希望你看完文章後會有所收穫!

    背景

    前段時間線上Sentry平台報警,多個業務服務在和註冊中心交互時,例如續約註冊表增量拉取等都報了Request execution failed with message : Connection refused 的警告:

    緊接着又看到 Request execution succeeded on retry #2 的日誌。

    看到這裏,表明我們的服務在嘗試兩次重連后和註冊中心交互正常了。

    一切都顯得那麼有驚無險,這裏報Connection refused 是註冊中心網絡抖動導致的,接着觸發了我們服務的重連,重連成功后一切又恢復正常。

    這次的報警雖然沒有對我們線上業務造成影響,並且也在第一時間恢復了正常,但作為一個愛思考的小火雞,我很好奇這背後的一系列邏輯:Eureka註冊中心集群如何實現客戶端請求負載及故障轉移?

    註冊中心集群負載測試

    線上註冊中心是由三台機器組成的集群,都是4c8g的配置,業務端配置註冊中心地址如下(這裏的peer來代替具體的ip地址):

    eureka.client.serviceUrl.defaultZone=http://peer1:8080/eureka/,http://peer2:8080/eureka/,http://peer3:8080/eureka/
    

    我們可以寫了一個Demo進行測試:

    註冊中心集群負載測試

    1、本地通過修改EurekaServer服務的端口號來模擬註冊中心集群部署,分別以87618762兩個端口進行啟動
    2、啟動客戶端SeviceA,配置註冊中心地址為:http://localhost:8761/eureka,http://localhost:8762/eureka

    3、啟動SeviceA時在發送註冊請求的地方打斷點:AbstractJerseyEurekaHttpClient.register(),如下圖所示:

    這裏看到請求註冊中心時,連接的是8761這個端口的服務。

    4、更改ServiceA中註冊中心的配置:http://localhost:8762/eureka,http://localhost:8761/eureka
    5、重新啟動SeviceA然後查看端口,如下圖所示:

    此時看到請求註冊中心是,連接的是8762這個端口的服務。

    註冊中心故障轉移測試

    以兩個端口分別啟動EurekaServer服務,再啟動一個客戶端ServiceA。啟動成功后,關閉一個8761端口對應的服務,查看此時客戶端是否會自動遷移請求到8762端口對應的服務:

    1、以87618762兩個端口號啟動EurekaServer
    2、啟動ServiceA,配置註冊中心地址為:http://localhost:8761/eureka,http://localhost:8762/eureka
    3、啟動成功后,關閉8761端口的EurekaServer
    4、在EurekaClient發送心跳請求的地方打上斷點:AbstractJerseyEurekaHttpClient.sendHeartBeat()
    5、查看斷點處數據,第一次請求的EurekaServer8761端口的服務,因為該服務已經關閉,所以返回的responsenull

    6、第二次會重新請求8762端口的服務,返回的response為狀態為200,故障轉移成功,如下圖:

    思考

    通過這兩個測試Demo,我以為EurekaClient每次都會取defaultZone配置的第一個host作為請求EurekaServer的請求的地址,如果該節點故障時,會自動切換配置中的下一個EurekaServer進行重新請求。

    那麼疑問來了,EurekaClient每次請求真的是以配置的defaultZone配置的第一個服務節點作為請求的嗎?這似乎也太弱了!!?

    EurekaServer集群不就成了偽集群!!?除了客戶端配置的第一個節點,其它註冊中心的節點都只能作為備份和故障轉移來使用!!?

    真相是這樣嗎?NO!我們眼見也不一定為實,源碼面前毫無秘密!

    翠花,上乾貨!

    客戶端請求負載原理

    原理圖解

    還是先上結論,負載原理如圖所示:

    這裡會以EurekaClient端的IP作為隨機的種子,然後隨機打亂serverList,例如我們在商品服務(192.168.10.56)中配置的註冊中心集群地址為:peer1,peer2,peer3,打亂后的地址可能變成peer3,peer2,peer1

    用戶服務(192.168.22.31)中配置的註冊中心集群地址為:peer1,peer2,peer3,打亂后的地址可能變成peer2,peer1,peer3

    EurekaClient每次請求serverList中的第一個服務,從而達到負載的目的。

    代碼實現

    我們直接看最底層負載代碼的實現,具體代碼在
    com.netflix.discovery.shared.resolver.ResolverUtils.randomize() 中:

    這裏面random 是通過我們EurekaClient端的ipv4做為隨機的種子,生成一個重新排序的serverList,也就是對應代碼中的randomList,所以每個EurekaClient獲取到的serverList順序可能不同,在使用過程中,取列表的第一個元素作為serverhost,從而達到負載的目的。

    思考

    原來代碼是通過EurekaClientIP進行負載的,所以剛才通過DEMO程序結果就能解釋的通了,因為我們做實驗都是用的同一個IP,所以每次都是會訪問同一個Server節點。

    既然說到了負載,這裏肯定會有另一個疑問:

    通過IP進行的負載均衡,每次請求都會均勻分散到每一個Server節點嗎?

    比如第一次訪問Peer1,第二次訪問Peer2,第三次訪問Peer3,第四次繼續訪問Peer1等,循環往複……

    我們可以繼續做個試驗,假如我們有10000個EurekaClient節點,3個EurekaServer節點。

    Client節點的IP區間為:192.168.0.0 ~ 192.168.255.255,這裏面共覆蓋6w多個ip段,測試代碼如下:

    /**
     * 模擬註冊中心集群負載,驗證負載散列算法
     *
     *  @author 一枝花算不算浪漫
     *  @date 2020/6/21 23:36
     */
    public class EurekaClusterLoadBalanceTest {
    
        public static void main(String[] args) {
            testEurekaClusterBalance();
        }
    
        /**
         * 模擬ip段測試註冊中心負載集群
         */
        private static void testEurekaClusterBalance() {
            int ipLoopSize = 65000;
            String ipFormat = "192.168.%s.%s";
            TreeMap<String, Integer> ipMap = Maps.newTreeMap();
            int netIndex = 0;
            int lastIndex = 0;
            for (int i = 0; i < ipLoopSize; i++) {
                if (lastIndex == 256) {
                    netIndex += 1;
                    lastIndex = 0;
                }
    
                String ip = String.format(ipFormat, netIndex, lastIndex);
                randomize(ip, ipMap);
                System.out.println("IP: " + ip);
                lastIndex += 1;
            }
    
            printIpResult(ipMap, ipLoopSize);
        }
    
        /**
         * 模擬指定ip地址獲取對應註冊中心負載
         */
        private static void randomize(String eurekaClientIp, TreeMap<String, Integer> ipMap) {
            List<String> eurekaServerUrlList = Lists.newArrayList();
            eurekaServerUrlList.add("http://peer1:8080/eureka/");
            eurekaServerUrlList.add("http://peer2:8080/eureka/");
            eurekaServerUrlList.add("http://peer3:8080/eureka/");
    
            List<String> randomList = new ArrayList<>(eurekaServerUrlList);
            Random random = new Random(eurekaClientIp.hashCode());
            int last = randomList.size() - 1;
            for (int i = 0; i < last; i++) {
                int pos = random.nextInt(randomList.size() - i);
                if (pos != i) {
                    Collections.swap(randomList, i, pos);
                }
            }
    
            for (String eurekaHost : randomList) {
                int ipCount = ipMap.get(eurekaHost) == null ? 0 : ipMap.get(eurekaHost);
                ipMap.put(eurekaHost, ipCount + 1);
                break;
            }
        }
    
        private static void printIpResult(TreeMap<String, Integer> ipMap, int totalCount) {
            for (Map.Entry<String, Integer> entry : ipMap.entrySet()) {
                Integer count = entry.getValue();
                BigDecimal rate = new BigDecimal(count).divide(new BigDecimal(totalCount), 2, BigDecimal.ROUND_HALF_UP);
                System.out.println(entry.getKey() + ":" + count + ":" + rate.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP) + "%");
            }
        }
    }
    

    負載測試結果如下:

    可以看到第二個機器會有50%的請求,最後一台機器只有17%的請求,負載的情況並不是很均勻,我認為通過IP負載並不是一個好的方案。

    還記得我們之前講過Ribbon默認的輪詢算法RoundRobinRule,【一起學源碼-微服務】Ribbon 源碼四:進一步探究Ribbon的IRule和IPing 。

    這種算法就是一個很好的散列算法,可以保證每次請求都很均勻,原理如下圖:

    故障轉移原理

    原理圖解

    還是先上結論,如下圖:

    我們的serverList按照client端的ip進行重排序后,每次都會請求第一個元素作為和Server端交互的host,如果請求失敗,會嘗試請求serverList列表中的第二個元素繼續請求,這次請求成功后,會將此次請求的host放到全局的一個變量中保存起來,下次client端再次請求 就會直接使用這個host

    這裏最多會重試請求兩次。

    代碼實現

    直接看底層交互的代碼,位置在
    com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient.execute() 中:

    我們來分析下這個代碼:

    1. 第101行,獲取client上次成功server端的host,如果有值則直接使用這個host
    2. 第105行,getHostCandidates()是獲取client端配置的serverList數據,且通過ip進行重排序的列表
    3. 第114行,candidateHosts.get(endpointIdx++),初始endpointIdx=0,獲取列表中第1個元素作為host請求
    4. 第120行,獲取返回的response結果,如果返回的狀態碼是200,則將此次請求的host設置到全局的delegate變量中
    5. 第133行,執行到這裏說明第120行執行的response返回的狀態碼不是200,也就是執行失敗,將全局變量delegate中的數據清空
    6. 再次循環第一步,此時endpointIdx=1,獲取列表中的第二個元素作為host請求
    7. 依次執行,第100行的循環條件numberOfRetries=3,最多重試2次就會跳出循環

    我們還可以第123和129行,這也正是我們業務拋出來的日誌信息,所有的一切都對應上了。

    總結

    感謝你看到這裏,相信你已經清楚了開頭提問的問題。

    上面已經分析完了Eureka集群下Client端請求時負載均衡的選擇以及集群故障時自動重試請求的實現原理。

    如果還有不懂的問題,可以添加我的微信或者給我公眾號留言,我會單獨和你討論交流。

    本文首發自:一枝花算不算浪漫 公眾號,如若轉載請在文章開頭標明出處,如需開白可直接公眾號回復即可。

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

    【其他文章推薦】

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

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

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

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

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

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

  • 說說TCP的三次握手和四次揮手

    說說TCP的三次握手和四次揮手

    一、傳輸控制協議TCP簡介

    1.1 簡介

    TCP(Transmission Control Protocol) 傳輸控制協議,是一種 面向連接的、可靠的、基於字節流的傳輸層 通信協議。

    TCP是一種面向連接(連接導向)的、可靠的基於字節流的傳輸層通信協議。TCP將用戶數據打包成報文段,它發送后啟動一個定時器,另一端收到的數據進行確認、對失序的數據重新排序、丟棄重複數據。

    TCP把連接作為最基本的對象,每一條TCP連接都有兩個端點,這種端點我們叫作套接字(socket),將端口號拼接到IP地址即構成了套接字,例如 192.1.1.6:50030

    1.2 特點

    • 面向連接的、可靠的、基於字節流的 傳輸層 通信協議
    • 將應用層的數據流分割成文段併發送給目標節點的TCP層
    • 數據包都有序號,對方收到則發送ACK確認,未收到則重傳
    • 使用校驗和來檢驗數據在傳輸過程中是否有誤

    二、TCP報文頭

    1、源端口(Source Port)/ 目的端口(Destination Port):他們各佔2個字節,標示該段報文來自哪裡(源端口)以及要傳給哪個上層協議或應用程序(目的端口)。進行tcp通信時,一般client是通過系統自動選擇的臨時端口號,而服務器一般是使用知名服務端口號或者自己指定的端口號(比如DNS協議對應端口53,HTTP協議對應80)

    2、序號(Sequence Number):佔據四個字節,TCP是面向字節流的,TCP連接中傳送的字節流中的每個字節都按順序編號,例如如一段報文的序號字段值是107,而攜帶的數據共有100個字段,如果有下一個報文過來,那麼序號就從207(100+107)開始,整個要傳送的字節流的起始序號必須要在連接建立時設置。首部中的序號字段值指的是本報文段所發送的數據的第一個字節的序號

    3、確認序號(Acknowledgment Number):4個字節,是期望收到對方下一個報文段的第一個數據字節的序號,若確認號=N,則表明:到序號N-1為止的所有數據都已正確收到,例如:B收到A發送過來的報文,其序列號字段是301,而數據長度是200字節,這表明了B正確的收到了A到序號500(301+200-1)為止的數據,因此B希望收到A的下一個數據序號是501,於是B在發送給A的確認報文段中,會把ACK確認號設置為501

    4、數據偏移(Offset):4個字節。指出TCP報文段的數據起始處距離報文段的起始處有多遠,這個字段實際上是指出TCP報文段的首部長度。由於首部中還有長度不確定的選項字段,因此數據偏移字段是必要的。單位是32位字,也就是4字節,4位二進制最大表示15,所以數據偏移也就是TCP首部最大60字節

    5、保留(Reserved):6個字節。保留域

    6、TCP Flags:控制位,由八個標誌位組成,每個標誌位表示控制的功能,我們主要來介紹TCP Flags中常用的六個,

    • URG(緊急指針標誌):當URG=1時,表明緊急指針字段有效。它告訴系統此報文段中有緊急數據,應儘快傳送(相當於高優先級的數據),而不要按原來的排隊順序來傳送。例如,已經發送了很長的一個程序在主機上運行。但後來發現了一些問題,需要取消該程序的運行。因此用戶從鍵盤發出中斷命令。如果不使用緊急數據,那麼這兩個字符將存儲在接收TCP的緩存末尾。只有在所有的數據被處理完畢后這兩個字符才被交付接收方的應用進程。這樣做就浪費了許多時間

    • ACK(確認序號標誌):當ACK=1時確認號字段有效。當ACK=0時,確認號無效。TCP規定,在連接建立后所有的傳送的報文段都必須把ACK置1

    • PSH(push標誌):當兩個應用進程進行交互式的通信時,有時在一端的應用進程希望在鍵入一個命令后立即就能收到對方的響應。在這種情況下,TCP就可以使用推送操作。這時,發送方TCP把PSH置1,並立即創建一個報文段發送出去。接收方TCP收到PSH=1的報文段,就儘快地交付接收應用進程,而不再等到整個緩存都填滿了後向上交付

    • RST(重置連接標誌):TCP連接中出現嚴重差錯(如由於主機崩潰或其他原因),必須釋放連接,然後再重新建立運輸連接,可以用來拒絕一個非法的報文段或拒絕打開一個連接

    • SYN(同步序號,用於建立連接過程):在連接建立時用來同步序號。當SYN=1而ACK=0時,表明這是一個連接請求報文段。對方若同意建立連接,則應在相應的報文段中使用SYN=1和ACK=1。因此,SYN置為1就表示這是一個連接請求或連接接受保溫。

    • FIN(finish標誌,用於釋放連接):當FIN=1時,表明此報文段的發送方的數據已發送完畢,並要求釋放運輸連接

    7、窗口(Window)是TCP流量控制的一個手段。這裏說的窗口,指的是接收通告窗口(Receiver Window,RWND)。它告訴對方本端的TCP接收緩衝區還能容納多少字節的數據,這樣就可以控制發送數據的速度

    8、檢驗和(Checksum):檢驗範圍包括首部和數據兩部分,由發送端填充,接收端對TCP報文段執行CRC算法以檢驗TCP報文段在傳輸過程中是否損壞。這也是TCP可靠傳輸的一個重要保障

    9、緊急指針(Urgent Pointer):緊急指針僅在URG=1時才有意義,它指出本報文段中的緊急數據的字節數(緊急數據結束后就是普通數據)。因此,緊急指針指出了緊急數據的末尾在報文段中的位置。當所有緊急數據都處理完時,TCP就告訴應用程序恢復到正常操作。值得注意的是,即使窗口為零時也可發送緊急數據。

    10、TCP可選項(TCP Options):長度可變,最長可達40字節。當沒有使用“選項”時,TCP的首部長度是20字節。

    三、TCP的三次握手

    所謂三次握手(Three-Way Handshake)即建立TCP連接,就是指建立一個TCP連接時,需要客戶端和服務端總共發送3個包以確認連接的建立。在socket編程中,這一過程由客戶端執行connect來觸發,整個流程如下圖所示:

    在TCP/IP協議中,TCP協議提供可靠的連接服務,採用三次握手建立一個連接。

    第一次握手: 建立連接時,客戶端發送SYN包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認,SYN:同步序列編號(Synchronize Sequence Numbers)。

    第二次握手: 服務器收到 SYN 包,必須確認客戶的 SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;

    第三次握手: 客戶端收到服務器的SYN + ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手。

    3.1 為什麼需要三次握手才能建立連接

    • 為了初始化Sequence Number 的初始值,實現可靠數據傳輸, TCP 協議的通信雙方, 都必須維護一個序列號, 以標識發送出去的數據包中, 哪些是已經被對方收到的。 三次握手的過程即是通信雙方相互告知序列號起始值, 並確認對方已經收到了序列號起始值的必經步驟
    • 如果只是兩次握手, 至多只有連接發起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認

    3.2 首次握手的隱患——SYN超時

    一、問題起因分析:
    1. 服務器收到客戶端的SYN,回復SYN和ACK的時候未收到ACK確認
    2. 服務器不斷重試直至超時,Linux默認等待63秒才斷開連接;(重複5次【不包括第一次】,從1秒開始,每次重試都翻倍:1+2+4+8+16+32=63秒)
    二、針對SYN Flood的防護措施:
    1. SYN隊列滿后,通過tcp_syncookies參數會發SYN cookie【源端口+目標端口+時間戳組成】
    2. 若為正常連接則Client會回發SYN Cookie,直接建立連接;

    3.3 保活機制:

    當我們建立連接后,Client出現故障怎麼辦?

    1. 向對方發送保活探測報文,如果未收到響應則繼續發送;
    2. 嘗試次數達到保活探測數仍未收到相應則中斷連接;

    四、TCP的四次揮手

    所謂四次揮手(Four-Way Wavehand)即終止TCP連接,就是指斷開一個TCP連接時,需要客戶端和服務端總共發送4個包以確認連接的斷開。在socket編程中,這一過程由客戶端或服務端任一方執行close來觸發,整個流程如下圖所示:

    由於TCP連接時全雙工的,因此,每個方向都必須要單獨進行關閉,這一原則是當一方完成數據發送任務后,發送一個FIN來終止這一方向的連接,收到一個FIN只是意味着這一方向上沒有數據流動了,即不會再收到數據了,但是在這個TCP連接上仍然能夠發送數據,直到這一方向也發送了FIN。首先進行關閉的一方將執行主動關閉,而另一方則執行被動關閉。

    • 第一次揮手: Client發送一個FIN,用來關閉Client到Server的數據傳送,Client進入FIN_WAIT_1狀態
    • 第二次揮手: Server收到FIN后,發送一個ACK給Client,確認序號為收到序號+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態
    • 第三次揮手: Server發送一個FIN,用來關閉Server到Client的數據傳送,Server進入LAST_ACK狀態
    • 第四次揮手: Client收到FIN后,Client進入TIME_WAIT狀態,接着發送一個ACK給Server,確認序號為收到序號+1,Server進入CLOSED狀態,完成四次揮手
    一、為什麼會有TIME_WAIT狀態

    客戶端連接在收到服務器的結束報文段之後,不會直接進入CLOSED狀態,而是轉移到TIME_WAIT狀態。在這個狀態,客戶端連接要等待一段長為2MSL,即兩倍的報文段最大生存時間,才能完全關閉,其原因主要有兩點:

    • 確保有足夠的時間放對方收到ACK包
    • 避免新舊連接混淆
    二、為什麼需要四次握手才能斷開連接

    因為TCP連接是全雙工的網絡協議,允許同時通信的雙方同時進行數據的收發,同樣也允許收發兩個方向的連接被獨立關閉,以避免client數據發送完畢,向server發送FIN關閉連接,而server還有發送到client的數據沒有發送完畢的情況。所以關閉TCP連接需要進行四次握手,每次關閉一個方向上的連接需要FIN和ACK兩次握手,發送發和接收方都需要FIN報文和ACK報文

    三、服務器出現大量CLOSE_WAIT狀態的原因

    是由於對方關閉socket連接,我方忙於讀或寫,沒有及時關閉連接

    當客戶端因為某種原因先於服務端發出了FIN信號,就會導致服務端被動關閉,若服務端不主動關閉socket發FIN給Client,此時服務端Socket會處於CLOSE_WAIT狀態(而不是LAST_ACK狀態)。通常來說,一個CLOSE_WAIT會維持至少2個小時的時間(系統默認超時時間的是7200秒,也就是2小時)。如果服務端程序因某個原因導致系統造成一堆CLOSE_WAIT消耗資源,那麼通常是等不到釋放那一刻,系統就已崩潰

    解決:
    1、檢查代碼,特別是釋放資源的代碼
    2、檢查配置,特別是處理請求的線程配置

    Linux的檢查代碼:netstat -n | awk '/^tcp/{++S[$NF]}END{for(a in S) print a,S[a]}'

    五、總結

    到這裏TCP的三次握手四次揮手就講完了,好久都沒有寫技術文章了,寫了一下,感覺還挺好的,上面是博主的認識,有寫的不好的地方,大家可以在評論區討論或者提問,博主看到了會第一時間回復大家,最近也準備開始面試了,先好好準備一下,希望今年可以找到心滿意足的工作,也希望今年面試的小夥伴們都有一個好的office,大家一起加油,我是牧小農,我喂自己帶鹽,大家加油。

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

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

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

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

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

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

    ※回頭車貨運收費標準