標籤: 網頁設計公司

  • 售價降低還換裝1.5T 這款合資轎車不輸思域

    售價降低還換裝1.5T 這款合資轎車不輸思域

    輪轂方面使用的是美規思域Si上的18英寸旋風式輪轂,樣式非常漂亮。內飾:基本沒有變化內飾方面也僅僅進行了一些小改動,基本保持着舊款的設計。但是增加了配色,導航也終於被加入到車機裏面。依舊使用着数字显示式儀錶盤,這是一種用過都說好的設計,数字显示的時速一目瞭然,而且較高的位置真有點抬頭显示的感覺。

    前言

    本田思域可謂是現今緊湊型轎車的當紅辣子雞,即使需要加價也難以阻擋人們對它的喜愛之情,其中最吸引他們的就是思域上那款動力油耗表現出色的1.5T發動機以及極高的配置。而最近本田為我們帶來2017款本田傑德,這輛大兩廂汽車更換了十代思域上的1.5T發動機,功率雖然有所降低,但是配置更為豐富,那麼它具體又有着怎樣的升級呢?又是否值得購買呢?

    傑德定位非常獨特,你可以將它當做一輛旅行車,也可以將其當做是MpV。而本田定位它是一輛緊湊型汽車,在筆者的眼中它是一輛大兩廂車,首先來自老款思域的平台讓它有着轎車般的駕駛感受,其次6座的布局實在和7座MpV有差異。這次2017款傑德的定價為12.99-17.99萬,購買門檻比起之前更低了。

    外觀:更換了LED光源以及新增配色,更為精神

    2017款傑德增加了新翠綠、波爾多紅等車身顏色,更為個性以及好看。在前臉方面進氣格柵增加了少許鍍鉻裝飾,看着更為時尚,而這種家族史設計使得傑德前臉更像是雅閣。

    而集成了LED日間行車燈的LED大燈非常醒目,亮度相當高,不過這是1.5T中高配車型的專利,1.8L以及1.5T最低配都與這LED大燈無緣。

    尾燈也同樣使用了LED燈源,辨識率更高,關鍵的一點是視覺效果更佳。除此以外,傑德使用了雙邊共雙出的排氣管設計,看着有一點性能味。

    離地間隙提升了10mm,舊款托底的毛病減弱不少,但是有一點需要注意,傑德是一輛兩廂車,對於兩廂車而言這個離地間隙已經是很大的了。輪轂方面使用的是美規思域Si上的18英寸旋風式輪轂,樣式非常漂亮。

    內飾:基本沒有變化

    內飾方面也僅僅進行了一些小改動,基本保持着舊款的設計。但是增加了配色,導航也終於被加入到車機裏面。

    依舊使用着数字显示式儀錶盤,這是一種用過都說好的設計,数字显示的時速一目瞭然,而且較高的位置真有點抬頭显示的感覺。

    配置:提升不少,但尚未完美

    這次配置上的提升是比較明顯的,讓傑德競爭力一下子提升不少,增加了LED大燈、日間行車燈、LED尾燈、18英寸輪轂、併線輔助系統、車道偏離系統、主動剎車、自適應巡航系統等,而且1.5T車型全系標配了全景天窗。

    傑德似乎是想要向思域看齊,但傑德卻沒有思域上的自動駐車以及电子手剎,稍顯遺憾,而且作為最低配車型並沒有配備ESp車身穩定系統。總的而言,傑德在配置上以及性價比上提升不少。

    動力:最大的亮點

    一直以來,傑德都是被當做是老款思域的重造產物,使用了同樣的平台,同樣的動力總成。底盤表現非常出色,但1.8L自然吸氣發動機卻難以挑起重擔,油耗以及耐用度優秀,卻給人白開水的感覺。這次加入的1.5T發動機,就像是辣椒一樣一下子把這款大兩廂車變得勁爆刺激味蕾。最大功率115千瓦,最大扭矩203牛米,比起思域上略有下降,依然是優秀水平,可以理解為思域上的1.5T低功率版本。

    和這款1.5T渦輪增壓發動機相配合的依然是CVT變速箱,和思域一樣,根據思域上平均8.5L的實測油耗,我們有理由相信使用同款發動機的傑德油耗也能同樣優秀。

    這一次2017款本田傑德的上市把之前的一些小問題幾乎都解決了,更低的價格、換上了更強悍的動力總成、加入了導航、更豐富的舒適性配置以及安全配置,這樣看來傑德是越趨完美。加上同價位並沒有相似的競爭對手,傑德可能會成為東本的第二個思域。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

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

  • 為何有點小錢的人都不願買國產車?

    為何有點小錢的人都不願買國產車?

    廢話這麼多,歸根結底說白了就是一個字 – 錢。當你的財力和社會地位提升起來后,相信你的內心自然會有答案。

    2016年汽車圈發生了很多事,其中令叫獸印象最深的是自主品牌的叫好聲四起,銷量上也取得了空前的突破。不過當叫獸冷靜下來理智的想一想,當下自主品牌真的是形勢一片大好,即將崛起了嗎?

    廢話這麼多,歸根結底說白了就是一個字 – 錢。當你的財力和社會地位提升起來后,相信你的內心自然會有答案。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

  • 好開、省油還有科技感,教授把這輛車推薦給挑剔的朋友們

    好開、省油還有科技感,教授把這輛車推薦給挑剔的朋友們

    聽了的介紹,三位老同學都下定決心要買17款新蒙迪歐了,我想既然他們都將是17款新蒙迪歐的車主了,所以約了他們提車三個月之後回來探討一些用車的經驗。三個月後,和三位老同學再次相聚,討論一下蒙迪歐的用車心得。智行第一位同學,也就是那位駕齡不長的女司機,她說讓她印象最深刻的還是17款新蒙迪歐的LKA車道保持輔助系統,一旦車輛在無意識情況下偏離車道,系統將通過震動方向盤和儀錶盤信息提醒,並輔助她重回當前行駛車道,這讓她在高速公路上行駛時避免了偏離車道所造成的危險。

    身邊的朋友知道喜歡玩車

    於是經常能收到他們的諮詢

    什麼十萬買啥車、二十萬買啥車

    諸如此類的問題

    其實都可以輕鬆應對的

    然而就像廣東話說的

    “上得山多終遇虎”

    也會有天被問題難住

    三個月前的同學聚會

    有三位老同學說他們感情好

    想買同一輛車,都要二十萬出頭而且夠大氣的

    首先女同學是一名女司機

    作為職業女性的她,要經常跑高速

    而且她還是新手

    所以她要我推薦一輛好開、安全的車給她

    第二位同學則是環保主義者

    什麼都不管,就要油耗低

    (大哥,外觀大氣的車好難找油耗低的吧)

    第三位同學是一名技術宅

    典型的IT男

    需要一輛有技術含量的車子

    聽了他們的要求后,第一時間是吐槽根本沒有這樣的車。但兢兢業業鍥而不舍的我在網上做了大量的搜索之後,發現有一輛車非常適合這三位老同學,那就是長安福特的17款新蒙迪歐。

    17款新蒙迪歐指導價17.98萬起,首先在價格上是符合要求的,然後在外觀上,馬丁式的家族前臉、LED大燈、2850mm的長軸距、以及肌肉感十足的車身線條,時尚個性又不失沉穩的外觀能讓不同年齡層的人接受。

    內飾方面,採用了對稱式的布局,整體的內飾簡潔明了,多媒體系統也已經升級到第三代的SYNC操作系統,操作更加流暢,功能也更加完善。除此以外,旋鈕式換擋的設計也讓車內的檔次感提升不少。

    行駛方面,拿2.0T車型來說,最大馬力達到245ps,搭配調校成熟的6AT手自一體變速箱,動力表現充沛且平順,絲毫不用擔心有小馬拉大車的感覺;底盤方面採用了多連桿獨立后懸架,整體偏向於舒適的調校,對細碎顛簸的過濾十分徹底。可以看出,實際的行駛表現也很符合蒙迪歐的定位。

    聽了的介紹,三位老同學都下定決心要買17款新蒙迪歐了,我想既然他們都將是17款新蒙迪歐的車主了,所以約了他們提車三個月之後回來探討一些用車的經驗。

    三個月後,和三位老同學再次相聚,討論一下蒙迪歐的用車心得。

    智行

    第一位同學,也就是那位駕齡不長的女司機,她說讓她印象最深刻的還是17款新蒙迪歐的LKA車道保持輔助系統,一旦車輛在無意識情況下偏離車道,系統將通過震動方向盤和儀錶盤信息提醒,並輔助她重回當前行駛車道,這讓她在高速公路上行駛時避免了偏離車道所造成的危險。

    除此以外,對於這位新手司機來說,帶行人識別功能的智能感應制動保護系統、以及只需控制油門和剎車,即可輕鬆把車停在理想位置的主動泊車輔助系統和ACC全速智能自適應巡航控制系統都讓她駕駛蒙迪歐的過程更加輕鬆。另外,車廂內的10個安全氣囊的環繞式防護也讓她駕駛的過程中更加安心。

    智擎

    至於那位作為環保主義者的老同學,關注油耗的他對17款新蒙迪歐的EcoBoost雙渦流渦輪增壓直噴發動機贊不絕口,因為先進的雙渦流渦輪增壓技術能有效緩解低速時的遲滯性,使峰值扭矩提早爆發,帶來更佳的燃油經濟性。

    而為了達到更好的節能環保效果,他選擇了Hybrid車型,17款新蒙迪歐的混合動力版本採用阿特金森循環發動機配合電動機,並搭載eCVT電控無級變速器,讓燃油與電力良好融合,高效環保,帶來煥然一新的駕駛體驗與樂趣。

    智聯

    最後一位老同學就是做IT男的技術宅,他最滿意的就是17款新蒙迪歐搭載的福特派TM互聯技術,只需要在手機中下載福特派TM應用,就可以遠程控制車輛並且通過手機來查看燃油剩餘量等車輛概況,十分方面。

    在多媒體功能方面,車主可以通過藍牙或者USB數據線將手機和車輛相連,來拓展多媒體的功能,同時,17款新蒙迪歐的多媒體系統支持智能聲控以及智能中文手寫輸入,支持多字連寫,讓人機之間的交互變得更加簡單。

    總結

    其實早就知道17款新蒙迪歐的三大亮點能夠讓我的這三位老同學滿足,“智行”讓駕駛更加輕鬆而且安全、“智擎”在提供充沛動力的同時又保證了良好的燃油經濟性、“智聯”則結合了時下流行的智能手機,既提供了用車的便利,又豐富了多媒體功能。三管齊下,相信不只是我的這三位老同學,廣大的消費者都會被17款新蒙迪歐吸引到。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

  • 一網打盡枚舉操作 .net core

    本文介紹如何使用枚舉以及,如何將枚舉類型更好的應用於項目中,看完本文可以有序的將項目中的枚舉更容易的使用到每個角落。

    1,分析枚舉

     /// <summary>
        /// 性別
        /// </summary>
        public enum Gender
        {
            /// <summary>
            ////// </summary>
            
            Man = 0,
            /// <summary>
            ////// </summary>
            
            Women = 1
        }

     

    如1所示,這是一個非常普通的枚舉類,在項目中使用的話,一般都會將它作為某實體的一個屬性,這個時候問題就來了,在頁面裡邊我們是需要拿到與之相關的描述信息和對應的值作為一個下拉框或者checkbox讓用戶去選擇,同時也不可以將Disable和enable作為給用戶最終展示的信息,需要去手動去拼,於是有了如下的方式

    2,枚舉類信息完善,增加描述信息

     /// <summary>
        /// 性別
        /// </summary>
        public enum Gender
        {
            /// <summary>
            ////// </summary>
            [Description("")]
            Man = 0,
            /// <summary>
            ////// </summary>
            [Description("女")]
            Women = 1
        }

    再給枚舉增加一個擴展方法

     /// <summary>
            /// 獲取到對應枚舉的描述-沒有描述信息,返回枚舉值
            /// </summary>
            /// <param name="enum"></param>
            /// <returns></returns>
            public static string GetDescription(this Enum @enum)
            {
                Type type = @enum.GetType();
                string name = Enum.GetName(type, @enum);
                if (name == null)
                {
                    return null;
                }
                FieldInfo field = type.GetField(name);
                if (!(Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute attribute))
                {
                    return name;
                }
                return attribute?.Description;
            }

     好像到這一步的時候問題可以得到解決,通過getdescroption()這類的方法可以去獲取到與枚舉相應的描述信息用於展示,但是這也僅限於在mvc模式下,通過viewbag將枚舉的類中的每一項都加到枚舉集合中返回給頁面,在頁面裡邊遍歷,如果枚舉類型很多,那麼這類型的重複邏輯就會很多,非常的心煩。

    如果可以將這些操作做一個封裝,用一個接口可以返回所有枚舉類型的相關信息,就好了,於是有了如下的做法

    1 創建一個描述枚舉的類 

     public class EnumModel
        {
            /// <summary>
            ///枚舉原始值
            /// </summary>
            public ValueType Source { get; set; }
            /// <summary>
            /// 描述信息
            /// </summary>
            public string Description { get; set; }
            /// <summary>
            /// value
            /// </summary>
            public int Value { get; set; }
        }

    2,寫一個方法通過傳遞一個枚舉類類型去得到List<EnumModel>

    public static List<EnumModel> GetEnumListModels<T>()
            {
                var model = default(T);
                FieldInfo[] fieldinfo = typeof(T).GetFields();
                List<EnumModel> result = new List<EnumModel>();
                foreach (FieldInfo field in fieldinfo)
                {
                    EnumModel enumModel = new EnumModel();
                    if (!(Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute attribute))
                    {
                        enumModel.Description = field.GetValue(model).ToString();
                    }
                    else
                    {
                        enumModel.Description = attribute.Description;
                    }
                    enumModel.Value = field.GetValue(model).GetHashCode();
                    enumModel.Source = field.GetValue(model) as ValueType;
                    if (field.GetValue(model).ToString() != "0")
                    {
                        result.Add(enumModel);
                    }
                    
                }
                return result;
            }

    3,寫一個接口,輸入枚舉的類的名稱,調用2中的方法,得到具體的返回信息

    [Route("[controller]/{name}")]
            public IActionResult GetEnumList(string name)
            {
                Assembly assembly = Assembly.Load("Ftw");
                Type t = assembly.GetType(string.Concat("Ftw.Enum.", name), throwOnError: false, ignoreCase: true);
                if (t == null)
                {
                    throw new ServiceException(string.Concat("請確保枚舉[", name, "]在 Ftw.Enum 中定義"));
                }
                Type enumhelp = typeof(EnumHelper);
                var obj = enumhelp.GetMethod("GetEnumListModels").MakeGenericMethod(t);
                return Json(obj.Invoke(t, null));
            }
    

      解釋一下:Ftw是類庫的名稱,Enum是Ftw類庫下的一個文件夾,所有的枚舉類都在Enum下邊放着,EnumHelper是 2 中方法【GetEnumListModels】所在的類,通過反射程序集得到枚舉類型,通過反射程序集將類型傳入GetEnumListModels作為 T 最後執行方法的到list.

    比如  Gender的調用,假如 GetEnumList所在的controller是EnumController,那麼調用就是通過  Enum/Gender ,對於.net core, mvc .net core api這類項目這種方式是非常有幫助的。

     

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

    【其他文章推薦】

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

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

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

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

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

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

  • 一個生物專業學生的內心獨白:我為什麼能去互聯網大廠?

    這回,考慮到近期關注了許多新朋友,並且大多都是學生黨,可能對我還不是特別熟悉。因此我決定重新把我從非科班如何通過自學(狗屎運)進入大廠的經歷分享出來,希望能夠給予一些將要面臨秋招,或者將要準備進入互聯網行業的同學一丟丟的幫助。

    早期關注我的讀者可能都約莫着記得,我是在華科讀的本碩,專業是生物醫學工程。雖說專業的的確確是帶了生物二字,但是我老實交代,實際上並不是純粹的生物技術方向上的專業。簡單科普一下:

    生物醫學工程是屬於交叉程度非常高,主要面向生物醫學領域的工程學科。我們在學校的主要課程包含硬件設計、軟件設計、生化基礎以及儀器科學等。

    所以呢,原諒本文的些許標題黨。但是說實話,由於學科高度交叉,我們實際上所習得的技能就是一團漿糊。在計算機科學方面的系統知識積累,與純生物專業相比,也不過是五十步笑百步。

    一 啟智階段

    雖說大學期間學的都不是計算機,但是我對計算機的興趣萌芽卻是十分的早。那估計都得有二十年前了,嗯誇張了點。不過應該是在小學時候了。

    我是在老家鄉下上的小學,你們可能想象不到,那個時候的鄉下小學竟然都有電腦課。不過那時候不叫電腦課,叫做“微機課”。

    微機課是幹嘛的呢?就是一群小朋友排排隊,拖鞋進到一個乾淨的房間里,然後幾個人圍着一台那種屁股又大又方的電腦,玩紙牌。沒錯,就是windows上流行多年的紙牌遊戲。

    怎麼說呢。我玩紙牌賊溜。 大概天生對這一類東西的接受能力比較強,所以上手很快,並且除了紙牌,還有空當接龍,掃雷,3D彈球。這麼說可能有點暴露年齡了。

    最早windows系統普及的這一批遊戲真的讓我對“微機”這麼個東西產生了懵懂的概念和持續的嚮往。

    二 本科階段

    之後的成長道路中,一直對計算機耍的挺溜。但是有點尷尬的是,沒人領進門。所以一直都是在娛樂,並沒有真正意義上接觸到計算機內部或者代碼編程的世界。

    初識代碼

    所以在上大學之前,基本上對編程一無所知,但是卻有一種強烈的對計算機的熱愛。不過那時對於計算機的理解僅限於【裝系統】,【裝軟件】和【拆洗主機風扇】。不要問我為什麼沒有去學計算機,要怪就只能怪考試時坐在我旁邊的那位,大概天生就得了瘋狂抖腿病的二貨。氣!

    一不小心,還進了生科院。但是緣分這種東西,真的妙不可言。這一切的開始源於一場面試。

    所在的大學是一所以工科著稱的高校,其中創業氣氛十分濃厚。學校因此有許多小有名氣的科創團隊,基本上是由老師主導,各專業學生組成的小團體。可不要小看這些小團隊,世界級程序設計大賽的獎牌獲得者經常就出於此類團隊。剛上大學的我們單純稚嫩,自然會被被這些團隊的大幅宣傳報和滿目的獎牌稱號所吸引膜拜。我也不例外。

    當時我便懷着澎湃的心情申請了一家曾多次在微軟創新杯奪得金獎的團隊。一個從鄉下來的小伙,第一次參加面試,第一次單獨和碼農小姐姐夜晚座談,第一次參加所謂的通宵測試。

    也就是在那一個晚上,開啟了我新世界的大門。

    那天晚上給我一群編程小白的任務,是模仿百度首頁,實現整個網頁的設計、布局和基本鏈接。提前給出的提示是w3c的教程網址。

    從來沒有接觸過編程的我在此之前,連編程的流程都不清楚,更不必說編譯環境、編譯語言甚至是源代碼閱讀(當然這個任務也涉及不到這些)。

    但是也就是那一個晚上,讓我真正意義上的接觸了敲代碼這個事兒。

    沒錯,我的編程起點是HTML+CSS。可惜的是,我並沒有通過那個團隊最終的面試。不過從那以後,我就開始了網站開發的自學之路。

    個人自學的堅持很大程度上基於興趣,源於在室友面前一頓裝逼后的成就感。但這就是一個生科院的學生在課後的最大樂趣所在。

    小試牛刀

    在接觸到網站開發之後,從最初的HTML+CSS到後來的HTML5+CSS3+JS+ASP,從靜態頁面的布局到動態網頁的請求。

    雖然感覺技術的成長也就是從博客的複製粘貼走向了文檔的複製粘貼。但是終於迎來了小試牛刀的機會。

    學校某大型學校組織需要做一個展示網站,朋友拉上了我和幾個人承擔了這個事情。這算是第一次真正意義上的項目開發,不過整個網站的功能不多,主要還是展示為主,後台也直接通過學校網絡平台整合就行了。所以整體下來做的事情並不多。

    迷茫困惑

    一直以來,都是自己通過博客自學,東拼西湊的建立起知識架構。但是其實技術基礎十分不牢固,不懂計算機基礎、計算機網絡,更別說編譯原理、操作系統等基本的知識。因此在很長一段時間內,覺得好像也沒有會什麼,一直對自己的能力保持懷疑。

    在這段時間內,有過跑去搞嵌入式,硬件開發,甚至是產品設計、界面設計,視頻製作。但都不一而終。

    一段迷茫,一段頹廢。一不小心就成為校園裡的學長,於是開始緊緊茫茫的尋找實驗室。

    新的方向

    好在我們專業與生科院其它專業相比,還是帶有工科氣質的。醫學影像學本身就是這個專業最為重要的一個方向。因此懷着對編程的興趣,加入了一個專門做醫學影像的實驗室。

    進入實驗室,不再是之前的漫無目的,隨處拾荒,但卻也並不會接觸到前沿新興的技術。畢竟在實驗室里,工程技術更多的只是工具。

    不過值得一提的是,在實驗室里,我接觸到了算法思想。醫學圖像本來就是圖像處理領域的一大分支,因此圖像處理算法的了解同樣也是至關重要。

    從這個時候開始,我已經不再搞網站開發那一套東西,專心使用C++寫我的算法。那時候深度學習還沒那麼熱門,大多數的圖像處理方式還是基於傳統的算法進行。雖然算法能力薄弱,但是倒也是我所求。

    同時,為了獲得數據,還接觸到Linux系統以及一些腳本,在做研究的過程中也大大增加自己的技術面。

    與此同時,為了鞏固自己的一些技術基礎,也為了督促自己的學習,我報考了計算機等級考試和國家軟考。可能對於計算機專業的學生來說,沒有什麼含金量,輕而易舉。但是對於一個非科班的學生而言,這樣的考試可以很大程度上幫助自己去重新組織零碎的知識。畢竟不是所有人都有足夠的精力去跟着計算機系的人上課。

    本科階段的經歷讓我知道自己與計算機系的差距,雖然接觸了許多東西,但是也都不夠精,甚至沒有底氣去獨立承擔一個小的開發任務。

    因此最終選擇了讀研。希望讓自己的技術都夠在某一個點上精進,能夠獨立的做出點東西來。當然最終的結果可能並不是自己當初所想,但是說到底還是得感謝在研究生期間所做出的努力。

    三 碩士階段

    我研究生的生活是從大四就開始的。因為是本校保研生,所以在大三下確定好實驗室后,基本就直接開始搬磚的苦逼生活了。當然舍友和同學在為畢業而慶祝歡快的時候,我就開始在實驗室早出晚歸。

    讀研的開始

    進入實驗室之後,剛開始一段時間也是沒有人帶。這能靠自己去多多實驗室專業方向的文章,然後學一點基本技能。更重要的其實是為了在實驗室混個臉熟。

    實驗室的研究方向主要是核醫學成像系統,整個系統的搭建涉及到核科學研究、物理數學模型的建立、电子信息的數據獲取、自動化控制設計、机械結構設計、軟件和算法處理以及醫學實驗驗證等一系列的過程。

    但對我們個人來說,主要是做某一個方向即可。當時我還是自認有一些編程基礎,於是計劃去做軟件開發或者數據處理的方向。但是最初期是沒有人帶的,所以就花三天時間自學了python3,把廖雪峰的python教程從頭到尾走了一遍。

    可惜的是,學完之後也並沒有派上用場。

    之後實驗室新項目確立,我被導師安排到了一個新項目組裡。在項目組中,主要負責的是圖像重建的部分。圖像重建是醫學影像領域非常重要的一個研究方向,目的是將影像系統採集到的多維多尺度的信息根據成像原理,依照不同算法還原成二維或三維的斷層圖像。所以本質上來說,圖像重建的重點也是在於算法設計和優化。

    方向的多變

    開始所在的項目組內,主要的工作還是基於軟件編程和算法開發,涉及到的技術棧主要還是以C++為主,然後加上一些圖型庫,如QtopenGLVTK等等。不過都是處於調用函數調包的階段,也沒有很深入的研究算法原理和實現機制。實話說其實個人提升不大。

    過了一段時間后,實驗室師兄拉我去幫他做事。師兄本身能力非常出眾,科研水平不容置疑,所以當時就過去了。之後在他手下做的事就很多變了,不只是軟件開發,還涉及到数字電路編程,PCB制板,系統仿真等。

    那個時候還來實驗室沒多久,沒有仔細的想過自己之後的就業方向,只想着能夠在研究生期間多做些成果,多發文章。因此,跟着師兄搬磚的時候也沒有過多考慮對之後的求職是否有幫助,只是想把事情做好,做出有意義的成果。

    不過幸運的是,在大四暑假前,也就是研究生入學之前。根據師兄的指導,將一個軟件開發的工作給擴充,投了一篇領域內的頂會。雖然覺得所作的工作沒什麼特別的,但是最終文章被錄還是非常開心的。也正因為這個事,之後更想着能夠多發文章多出成果。同時,師兄也比較會熬雞湯,各種說辭讓我放佛感覺到即將成為科研巨人,登上學校官網,走上人生巔峰。

    綜合能力鍛煉

    在實驗室,除了完成自己的科研任務,還有很重要的一部分,就是寫本子。也就是申請項目基金等等文字工作。

    我估計有參与過的項目申請或結題工作不下十餘項,每次都會涉及到大量文字的整理、表達、排版等工作。一般的學生對於這樣的工作都是極度排斥的,覺得很煩又沒什麼用。當然我也是這樣覺得的。

    只不過在寫了大量的本子後會發現,其實這也是綜合能力的鍛煉。我們導師常說,博士生一定要會寫項目,碩士生的話盡量寫。說明他把這也是當作一種能力的培養。在師兄手下,我也經常性的寫本子,甚至於超過了敲的代碼。那時候也沒有很明確的技術培養規劃,所以倒也覺得還行。

    獨自前行

    只不過好景不長。帶我的師兄由於過於優秀,將要離開實驗室去其它高校就職。這對當時的我來說,處境有些許尷尬。因為我的研究方向是與師兄的工作有交叉的,同時一些設備和材料也需要師兄的支持。

    實驗室本身的氛圍是以博士帶頭,碩士輔助進行項目開展的,而我的研究方向是我自己獨立出來的一個題目。說白了就是實驗室就我一個人在搞這個,但是這個方向不是導師關注的方向。

    因此,在這之後,我又回歸到了一個人做事的階段。大概是研一下開始,我就只能自己一個人去想自己的研究課題,偶爾會跟老師討論,但是也沒有很確定的工作路線。具體的工作內容也只能靠自己去思索。

    在這段期間,我寫過verilog,調過FPGA,畫過板子,畫過工程圖,當然也寫過軟件做過算法。一個人做事的話,可以說是很自由,沒有老師管,也沒有報告的壓力。只是很多時候發現有一些想法沒辦法實現,也無法對前沿領域有很深的洞察。空有一腔發文章報效實驗室的熱血,卻發現像是站在水中的浮木上,搖搖欲墜。當然這其中,個人的問題佔有很大比重。

    摸魚的日子

    不敢說自己的有多麼自律,多麼出色。可能世上大多人都是芸芸眾生,我也不過一個普通人。在自己做研究的同時,每次到臨近頂會收集文章摘要之時,我都會很积極的熬個一兩個月,將自己的想法和結果寫成一到兩篇的會議文章。我自己也大概知道其實工作沒有太大的突破點,但也是希望能夠賭一把。

    只不過兩年來,一共整理出數篇會議摘要,都沒有被同意投稿。每一次還是會有些灰心,也有些不服。會時刻回想起導師的反饋,而後想到一堆反駁的理由。只不過都沒用。

    每一次之後,都會有一段時間的消沉。不太想去實驗室,不太想看論文,在實驗室可能也就是摸摸魚罷了。很多時候覺得鬥志滿滿,又會瞬間像泄了氣的皮球,沒有精力去完成任何事。

    職業方向

    到如今,算是大四的時光,在實驗室已經呆了快四年。平心而論,沒有做過什麼突出的成果,也沒有練就什麼出色的技術。研究生活高開低走,一度以為自己是什麼科研巨星,後來僅剩的一絲熱情也在導師的「也沒看出你有什麼天分」中黯然消逝。不過還好,在這之前,我找到了自己的職業方向。

    我本身就是一個比較後知后覺的人,一方面是懶,另一方面又對自己有一種迷之自信。我真正意義上開始準備校招還是在去年六月底七月初的時候,也就是研二下學期快結束的時候吧。我們實驗室的碩士生出去基本就是兩個方向,軟件或者硬件。搞電路的基本都選擇去做硬件開發,其它的大部分都會選擇做軟件。我也沒啥可想的,雖然做過硬件,但是技術水平根本不敢出手。所以求職的方向直接就定在了軟件開發相關。

    當時還不知道應聘互聯網還要刷題,還要複習,以為上去介紹下自己,講講自己做的跟互聯網沒有半毛錢關係的項目就可以顯得很有想法的樣子。後來真正開始準備的時候才發現,自己原來還差得有點遠。

    老實說在研究生期間,主要使用C++Matlab,主要的項目就是用Qt寫了一個客戶端,裏面有網絡通信的模塊可以對數據做一些處理,並且能夠显示圖譜。用Matlab主要就是做了一些比較基礎的圖像處理和機器學習的算法。光這項目再加上非科班帶「生物」二字的專業,的確讓不清楚的人會十動然拒。

    不過其實也沒得選擇。生醫專業對口的公司都是做醫療器械,比如聯影、邁瑞等。但是其實去這些公司也是做軟件開發工程師。相對來說,互聯網公司技術好,待遇好,發展好,自然就成為大部分的選擇去向。

    說是跨行,但是其實也沒有別的更好的選擇。我們老師常說希望我們學生以後能夠在我們這個領域發光發熱。這倒是真的,國家的發展還是離不開這些能夠在某個領域深耕的人,而不是為了一昧追求熱點和高薪而忘記了初心。

    四 求職經歷

    講講我的秋招經歷吧。我的秋招雖然準備的晚,但是其實還是挺順利的。從19年8月開始接到面試,2個月內已經拿到了15+的offer,基本平均薪資都在30w+。包括抖音美團華為小米等等。那段時間真的是狀態上來了,就是佛擋殺佛,神擋殺神。

    不過剛開始的時候,由於實驗室的原因,沒有辦法出去實習。甚至由於一些原因,一直拖到暑期前才開始準備複習。

    那時已經快七月初了。急匆匆的登上各種學習網站,發現這也太多要看了的吧,還得刷題。關鍵一看面經,這都啥呀。還要手撕代碼的嗎?

    這一看當時不要緊,關鍵晚上就焦慮的快睡不着了。每天都在想應該怎樣複習,怎麼寫簡歷,沒有項目該怎麼辦。

    剛開始的時候連簡歷都不敢投,因為老覺得簡歷一過去就會安排面試。後來發現這完全就是多慮了。大概從七月初我就開始投簡歷,因為七八月是一些公司提前批招聘的階段。

    許多非科班的學生,在投遞簡歷的時候才能發現自己的無助。我在簡歷投遞初期,基本沒有任何反饋。提前批階段,許多公司都會去爭奪更優秀的簡歷候選者,對於生物專業的學生真的沒有什麼優勢。

    但是沒有關係,既然選擇了這條路,那麼就要堅持下去。投一家無人應答,那麼就投十家,投五十家。我在整個秋招階段,總共投遞過近一百家公司。許多在提前批沒有給予反饋的公司,後期大部分都有電話聯繫重新開啟面試流程。所以,就算認為自己的簡歷再不夠出色,也要相信總會有瞎了眼的HR(誤)。

    我當時一直在堅持投簡歷,只要看到的招聘信息都會去投遞。還記得第一次做測評題的時候都非常緊張,以為這就是筆試題。非常認真的拿紙筆在計算,慌的不行。

    後來直到八月初才收到第一份面試邀請,多益網絡。當時約了面試之後簡直怕死了,雖然說複習了一個月大部分的 C++ 基礎知識都看得差不多了,但是肯定是不夠的。面試的時候面試小哥全程就低頭照着題庫念,也不看我。我這邊的音頻信號也不太行,他那邊說話都聽不清楚。兩個人就在無數次的重複和確認。關鍵是面試完之後我自我感覺還非常良好。

    最終結果還是掛了,說實話打擊挺大的,感覺因此對面試產生了恐懼。不過後來試着自己跟自己講解,慢慢的也習慣了面試的感覺。

    隨後在八月底的時候,終於收穫了第一家 ihandy 的 offer。並且在進行總管面的時候,跟面試官進行了深入的交談。面完之後讓我有一種都不好意思拒絕這家公司的念頭。這也是第一次感受到了面試官的認可。

    隨後,便一發不可收拾。這個時候,已經準備了大概兩個月,基本的技術知識我都看完了,劍指 offer 上的66題以及 leetcode 上基本的題也大概刷了兩次。同時獲得了一個 offer 之後,對自己的認可度非常高,使得面試的狀態非常好。

    後來給了面試機會的公司基本都拿到了 offer。像字節跳動、美團、虎牙等等公司,面完的感覺就是基本穩了。不過可惜的是阿里簡歷面后,內推人開始說通過了,後來不知道什麼情況流程就拖到結束了。

    騰訊也一直沒有撈過我面試。感覺如果在狀態頂峰的情況下能有面試機會的話,還是很有希望的。不過也說明簡歷依然不夠出色,非科班沒有實習經歷,項目也比較水,導致AT大廠連面試機會都沒給。這也是秋招比較遺憾的地方。

    另外,記住面試過程,跟投遞簡歷一樣,一定要多面多總結。

    如果你的表達能力不好,沒有別的好的辦法,只能多練。自己在面試前問自己問題,然後用自己的話陳述出來。甚至是錄音自己聽,感覺一下面試官聽到你的回答是做何感想。

    心態要好。面試官也是人,不可能所有人都能夠絕對公平的跟你面試,所以遇到人品不好的面試官,做好自己就行了。

    要善於總結。每一次的面試都可以做好記錄,錄音或者筆記都可以。面試完之後需要多回顧,發現自己的錯誤,感受面試官對你的引導,然後下次面試注意。我一般喜歡用印象筆記記錄東西,每一次的面試記錄我都記錄在印象筆記上。電腦手機都可以看,即使是出門現場面試也不怕。

    最後就是一定要堅持下去。金九銀十,金三銀四。把握好機會,要善於規劃自己的成功。

    五 複習準備

    想進互聯網的技術崗,基本都是要提前準備的。當然某些大佬及大大佬除外。無論是校招還是社招,都需要針對自己的求職崗位進行必要的理論知識複習、項目經歷反思和算法能力訓練。只不過校招會偏向於基礎和算法能力,社招可能都會重點考察。

    除項目經歷外,複習的階段主要分為語言基礎、數據結構和算法、計算機網絡、操作系統、數據庫以及算法刷題。

    語言基礎:

    以 C++ 為主。我不喜歡看又厚又重的語言書,因此複習全程是以博客、開發文檔和實踐相結合的方式進行技術點複習。C++ 的技術點相對於其它語言來說,不算多,也不算難。技術重點的篩選可以從面經中提取而來。當然每一屆都差不多,所以找找別人總結好的資料看就行。(想要我複習資料的,可關注公眾號後台回復秋招領取)

    數據結構和算法:

    這應該是編程的基礎,重要需要了解的數據結構不出10種。花點時間弄清楚它們的原理、結構和使用方法,常用的操作也需要掌握。最難不過紅黑樹。

    這裏的算法指的是常用的算法,比如排序、遍歷,與數據結構相結合的數據操作方式。需要保證手寫才行。

    計算機網絡:

    網絡部分的內容其實可以算是最重要的,無論是前端後端都需要掌握網絡通信過程中的操作和機制。技術點可參考網絡服務器的請求和響應過程。將其中所有涉及到的協議、機制了解清楚,就可以掌握大部分了。

    操作系統:

    如果有Linux使用經歷和腳本編程基礎在面試中會很加分。對於操作系統的理解建議按照Linuxwindos系統的區分進行。

    數據庫:

    SQL基本操作必須要掌握,還包括一些關係型數據庫的基本原理和機制,內容不多,多看看就可以掌握。

    Redis同樣也是加分項,有能力的可去研究下源碼。

    算法刷題

    這一部分不多說,普通人只能勤能補拙。無論是劍指offer66題還是leetcode都可以,劍指刷兩遍,leetcode兩百題,基本沒有問題了。刷題時不要死刷,可以根據類型刷,比如鏈表操作、二叉樹操作、動態規劃等。相同類型重複做,能夠更好的培養算法思想。

    六 感想體會

    說起求職的過程,其實真要我來評價的話,估計運氣是佔了一大部分,連我這樣都行,你們也可以的。不過最終能夠獲得一些互聯網公司認可的原因,我認為主要有以下幾點原因吧:

    1.本科階段接觸互聯網行業比較早,對於這個行業有自己的見解。

    2.本科階段有考過一些計算機水平的證書,大概系統的學了一下計算機相關的基礎知識。

    3.用C++比較多,對語言基礎的理解比較好。

    4.面試狀態比較好,比較會表達自己的想法。

    5.學習能力還行,能夠在面試官的引導下找到他想問的技術點。

    6.準備過程比較有規劃,能夠快速的掌握面試的重點。

    7.人長得老實,比較容易獲得信任感。(這個你們可能學不了^_^)

    8.比較幸運。

    前天剛碩士答辯完,這两天也把學位申請的各種材料提交上去了。一不小心,七年的大學校園時光真的要結束了,兜兜轉轉感覺好像依舊是一無是處,一事無成。但是依舊希望以後能夠:

    二龍騰飛、三羊開泰、四季平安、 五福臨門、六六大順、七星高照、八方來財、九九同心、十全十美、百事亨通、千事吉祥、萬事如意

    人生無常,活在當下,且行且珍惜!

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

    【其他文章推薦】

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

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

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

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

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

  • 【C#】AutoMapper 使用手冊

    目錄

    • 1 入門例子
    • 2 註冊
      • 2.1 Profile
    • 3 配置
      • 3.1 命名約定
      • 3.2 配置可見性
      • 3.3 全局屬性/字段過濾
      • 3.4 識別前綴和後綴
      • 3.5 替換字符
    • 4 調用構造函數
    • 5 數組和列表映射
      • 5.1 處理空集合
      • 5.2 集合中的多態
    • 6 方法到屬性映射
    • 7 自定義映射
    • 8 扁平化映射
      • 8.1 IncludeMembers
    • 9 嵌套映射

    本文基於 AutoMapper 9.0.0

    AutoMapper 是一個對象-對象映射器,可以將一個對象映射到另一個對象。

    官網地址:http://automapper.org/

    官方文檔:https://docs.automapper.org/en/latest/

    1 入門例子

    public class Foo
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    
    public class FooDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    
    public void Map()
    {
        var config = new MapperConfiguration(cfg => cfg.CreateMap<Foo, FooDto>());
    
        var mapper = config.CreateMapper();
    
        Foo foo = new Foo { ID = 1, Name = "Tom" };
    
        FooDto dto = mapper.Map<FooDto>(foo);
    }
    

    2 註冊

    在使用 Map 方法之前,首先要告訴 AutoMapper 什麼類可以映射到什麼類。

    var config = new MapperConfiguration(cfg => cfg.CreateMap<Foo, FooDto>());
    

    每個 AppDomain 只能進行一次配置。這意味着放置配置代碼的最佳位置是在應用程序啟動中,例如 ASP.NET 應用程序的 Global.asax 文件。

    從 9.0 開始 Mapper.Initialize 方法就不可用了。

    2.1 Profile

    Profile 是組織映射的另一種方式。新建一個類,繼承 Profile,並在構造函數中配置映射。

    public class EmployeeProfile : Profile
    {
        public EmployeeProfile()
        {
            CreateMap<Employee, EmployeeDto>();
        }
    }
    
    var config = new MapperConfiguration(cfg =>
    {
        cfg.AddProfile<EmployeeProfile>();
    });
    

    Profile 內部的配置僅適用於 Profile 內部的映射。應用於根配置的配置適用於所有創建的映射。

    AutoMapper 也可以在指定的程序集中掃描從 Profile 繼承的類,並將其添加到配置中。

    var config = new MapperConfiguration(cfg =>
    {
        // 掃描當前程序集
        cfg.AddMaps(System.AppDomain.CurrentDomain.GetAssemblies());
        
        // 也可以傳程序集名稱(dll 名稱)
        cfg.AddMaps("LibCoreTest");
    });
    

    3 配置

    3.1 命名約定

    默認情況下,AutoMapper 基於相同的字段名映射,並且是 不區分大小寫 的。

    但有時,我們需要處理一些特殊的情況。

    • SourceMemberNamingConvention 表示源類型命名規則
    • DestinationMemberNamingConvention 表示目標類型命名規則

    LowerUnderscoreNamingConventionPascalCaseNamingConvention 是 AutoMapper 提供的兩個命名規則。前者命名是小寫並包含下劃線,後者就是帕斯卡命名規則(每個單詞的首字母大寫)。

    我的理解,如果源類型和目標類型分別採用了 蛇形命名法駝峰命名法,那麼就需要指定命名規則,使其能正確映射。

    public class Foo
    {
        public int Id { get; set; }
    
        public string MyName { get; set; }
    }
    
    public class FooDto
    {
        public int ID { get; set; }
    
        public string My_Name { get; set; }
    }
    
    public void Map()
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Foo, FooDto>();
    
            cfg.SourceMemberNamingConvention = new PascalCaseNamingConvention();
            cfg.DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention();
        });
    
        var mapper = config.CreateMapper();
    
        Foo foo = new Foo { Id = 2, MyName = "Tom" };
    
        FooDto dto = mapper.Map<FooDto>(foo);
    }
    

    3.2 配置可見性

    默認情況下,AutoMapper 僅映射 public 成員,但其實它是可以映射到 private 屬性的。

    var config = new MapperConfiguration(cfg =>
    {
        cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.SetMethod.IsPrivate;
        cfg.CreateMap<Source, Destination>();
    });
    

    需要注意的是,這裏屬性必須添加 private set,省略 set 是不行的。

    3.3 全局屬性/字段過濾

    默認情況下,AutoMapper 嘗試映射每個公共屬性/字段。以下配置將忽略字段映射。

    var config = new MapperConfiguration(cfg =>
    {
    	cfg.ShouldMapField = fi => false;
    });
    

    3.4 識別前綴和後綴

    var config = new MapperConfiguration(cfg =>
    {
        cfg.RecognizePrefixes("My");
        cfg.RecognizePostfixes("My");
    }
    

    3.5 替換字符

    var config = new MapperConfiguration(cfg =>
    {
        cfg.ReplaceMemberName("Ä", "A");
    });
    

    這功能我們基本上用不上。

    4 調用構造函數

    有些類,屬性的 set 方法是私有的。

    public class Commodity
    {
        public string Name { get; set; }
    
        public int Price { get; set; }
    }
    
    public class CommodityDto
    {
        public string Name { get; }
    
        public int Price { get; }
    
        public CommodityDto(string name, int price)
        {
            Name = name;
            Price = price * 2;
        }
    }
    

    AutoMapper 會自動找到相應的構造函數調用。如果在構造函數中對參數做一些改變的話,其改變會反應在映射結果中。如上例,映射后 Price 會乘 2。

    禁用構造函數映射:

    var config = new MapperConfiguration(cfg => cfg.DisableConstructorMapping());
    

    禁用構造函數映射的話,目標類要有一個無參構造函數。

    5 數組和列表映射

    數組和列表的映射比較簡單,僅需配置元素類型,定義簡單類型如下:

    public class Source
    {
        public int Value { get; set; }
    }
    
    public class Destination
    {
        public int Value { get; set; }
    }
    

    映射:

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Source, Destination>();
    });
    IMapper mapper = config.CreateMapper();
    
    var sources = new[]
    {
        new Source { Value = 5 },
        new Source { Value = 6 },
        new Source { Value = 7 }
    };
    
    IEnumerable<Destination> ienumerableDest = mapper.Map<Source[], IEnumerable<Destination>>(sources);
    ICollection<Destination> icollectionDest = mapper.Map<Source[], ICollection<Destination>>(sources);
    IList<Destination> ilistDest = mapper.Map<Source[], IList<Destination>>(sources);
    List<Destination> listDest = mapper.Map<Source[], List<Destination>>(sources);
    Destination[] arrayDest = mapper.Map<Source[], Destination[]>(sources);
    

    具體來說,支持的源集合類型包括:

    • IEnumerable
    • IEnumerable
    • ICollection
    • ICollection
    • IList
    • IList
    • List
    • Arrays

    映射到現有集合時,將首先清除目標集合。如果這不是你想要的,請查看AutoMapper.Collection。

    5.1 處理空集合

    映射集合屬性時,如果源值為 null,則 AutoMapper 會將目標字段映射為空集合,而不是 null。這與 Entity Framework 和 Framework Design Guidelines 的行為一致,認為 C# 引用,數組,List,Collection,Dictionary 和 IEnumerables 永遠不應該為 null

    5.2 集合中的多態

    這個官方的文檔不是很好理解。我重新舉個例子。實體類如下:

    public class Employee
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    
    public class Employee2 : Employee
    {
        public string DeptName { get; set; }
    }
    
    public class EmployeeDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    
    public class EmployeeDto2 : EmployeeDto
    {
        public string DeptName { get; set; }
    }
    

    數組映射代碼如下:

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Employee, EmployeeDto>().Include<Employee2, EmployeeDto2>();
        cfg.CreateMap<Employee2, EmployeeDto2>();
    });
    IMapper mapper = config.CreateMapper();
    
    var employees = new[]
    {
        new Employee { ID = 1, Name = "Tom" },
        new Employee2 { ID = 2, Name = "Jerry", DeptName = "R & D" }
    };
    
    var dto = mapper.Map<Employee[], EmployeeDto[]>(employees);
    

    可以看到,映射后,dto 中兩個元素的類型,一個是 EmployeeDto,一個是 EmployeeDto2,即實現了父類映射到父類,子類映射到子類。

    如果去掉 Include 方法,則映射后 dto 中兩個元素的類型均為 EmployeeDto

    6 方法到屬性映射

    AutoMapper 不僅能實現屬性到屬性映射,還可以實現方法到屬性的映射,並且不需要任何配置,方法名可以和屬性名一致,也可以帶有 Get 前綴。

    例如下例的 Employee.GetFullName() 方法,可以映射到 EmployeeDto.FullName 屬性。

    public class Employee
    {
        public int ID { get; set; }
    
        public string FirstName { get; set; }
    
        public string LastName { get; set; }
    
        public string GetFullName()
        {
            return $"{FirstName} {LastName}";
        }
    }
    
    public class EmployeeDto
    {
        public int ID { get; set; }
    
        public string FirstName { get; set; }
    
        public string LastName { get; set; }
    
        public string FullName { get; set; }
    }
    

    7 自定義映射

    當源類型與目標類型名稱不一致時,或者需要對源數據做一些轉換時,可以用自定義映射。

    public class Employee
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public DateTime JoinTime { get; set; }
    }
    
    public class EmployeeDto
    {
        public int EmployeeID { get; set; }
    
        public string EmployeeName { get; set; }
    
        public int JoinYear { get; set; }
    }
    

    如上例,IDEmployeeID 屬性名不同,JoinTimeJoinYear 不僅屬性名不同,屬性類型也不同。

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Employee, EmployeeDto>()
            .ForMember("EmployeeID", opt => opt.MapFrom(src => src.ID))
            .ForMember(dest => dest.EmployeeName, opt => opt.MapFrom(src => src.Name))
            .ForMember(dest => dest.JoinYear, opt => opt.MapFrom(src => src.JoinTime.Year));
    });
    

    8 扁平化映射

    對象-對象映射的常見用法之一是將複雜的對象模型並將其展平為更簡單的模型。

    public class Employee
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public Department Department { get; set; }
    }
    
    public class Department
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    
    public class EmployeeDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public int DepartmentID { get; set; }
    
        public string DepartmentName { get; set; }
    }
    

    如果目標類型上的屬性,與源類型的屬性、方法都對應不上,則 AutoMapper 會將目標成員名按駝峰法拆解成單個單詞,再進行匹配。例如上例中,EmployeeDto.DepartmentID 就對應到了 Employee.Department.ID

    8.1 IncludeMembers

    如果屬性命名不符合上述的規則,而是像下面這樣:

    public class Employee
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public Department Department { get; set; }
    }
    
    public class Department
    {
        public int DepartmentID { get; set; }
    
        public string DepartmentName { get; set; }
    }
    
    public class EmployeeDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public int DepartmentID { get; set; }
    
        public string DepartmentName { get; set; }
    }
    

    Department 類中的屬性名,直接跟 EmployeeDto 類中的屬性名一致,則可以使用 IncludeMembers 方法指定。

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Employee, EmployeeDto>().IncludeMembers(e => e.Department);
        cfg.CreateMap<Department, EmployeeDto>();
    });
    

    9 嵌套映射

    有時,我們可能不需要展平。看如下例子:

    public class Employee
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public int Age { get; set; }
    
        public Department Department { get; set; }
    }
    
    public class Department
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public string Heads { get; set; }
    }
    
    public class EmployeeDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public DepartmentDto Department { get; set; }
    }
    
    public class DepartmentDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    

    我們要將 Employee 映射到 EmployeeDto,並且將 Department 映射到 DepartmentDto

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Employee, EmployeeDto>();
        cfg.CreateMap<Department, DepartmentDto>();
    });
    

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 環境篇:嘔心瀝血@CDH線上調優

    環境篇:嘔心瀝血@CDH線上調優

    環境篇:嘔心瀝血@CDH線上調優

    • 為什麼出這篇文章?

    近期有很多公司開始引入大數據,由於各方資源有限,並不能合理分配服務器資源,和服務器選型,小恭弘=叶 恭弘這裏將工作中的總結出來,給新入行的小夥伴帶個方向,不敢說一定對,但是本人親自測試,發現集群使用率穩定提高了3分之1,最高可達到2分之1,有不對的地方歡迎留言指出。

    注:可能有些服務沒有設計,使用到的小夥伴可以參照這種方式去規劃。

    0 資源:集群服務安排

    服務名稱 子服務 CM-64G ZK-Kafka(3台)-12G DataNode(3台)-64G NameNode1-64G NameNode2-64G Resourcemanager1-32G Resourcemanager2-32G hive-hbase-16G hive-hbase-16G
    MySQL MySQL
    CM Activity Monitor
    Alert Publisher
    Event Server
    Host Monitor
    Service Monitor




    HDFS NameNode
    DataNode
    Failover Controller
    JournalNode
    X

    X
    X

    X


    X

    X
    X
    X
    Yarn NodeManager
    Resourcemanager
    JobHisoryServer

    X
    X
    X

    X

    Zookeeper Zookeeper Server
    Kafka Kafka Broker
    Hive Hive Metastore Server
    HiveServer2
    Gateway(安裝對應應用服務器)
    X


    X

    X
    Hbase HMaster
    HRegionServer
    Thrift Server
    X


    X

    X
    Oozie Oozie Server
    Hue Hue Server
    Load Balancer
    X

    X
    Spark History Server
    Gateway(安裝對應應用服務器)

    X
    Flume Flume Agent (安裝對應應用服務器)
    Sqoop Sqoop(安裝對應應用服務器)

    1 優化:Cloudera Management

    1.1 Cloudera Management Service

    這些服務主要是提供監控功能,目前的調整主要集中在內存放,以便有足夠的資源 完成集群管理。

    服務 選項 配置值
    Activity Monitor Java Heap Size 2G
    Alert Publisher Java Heap Size 2G
    Event Server Java Heap Size 2G
    Host Monitor Java Heap Size 4G
    Service Monitor Java Heap Size 4G
    Reports Manager Java Heap Size 2G
    Navigator Metadata Server Java Heap Size 8G

    2 優化:Zookeeper

    服務 選項 配置值
    Zookeeper Java Heap Size (堆棧大小) 4G
    Zookeeper maxClientCnxns (最大客戶端連接數) 1024
    Zookeeper dataDir (數據文件目錄+數據持久化路徑) /hadoop/zookeeper (建議獨立目錄)
    Zookeeper dataLogDir (事務日誌目錄) /hadoop/zookeeper_log (建議獨立目錄)

    3 優化:HDFS

    3.1 磁盤測試

    3.1.1 讀測試

    hdparm 用於查看硬盤的相關信息或對硬盤進行測速、優化、修改硬盤相關參數設定

    #安裝hdparm
    yum install hdparm
    #獲取硬盤符
    fdisk -l
    #讀測試(讀取上一步獲取硬盤符)
    hdparm -t /dev/vda
    

    三次測試結果:

    Timing buffered disk reads: 500 MB in 0.84 seconds = 593.64 MB/sec

    Timing buffered disk reads: 500 MB in 0.93 seconds = 538.80 MB/sec

    Timing buffered disk reads: 500 MB in 0.74 seconds = 672.95 MB/sec

    說明:接近1s秒讀取了500MB磁盤,讀速度約 500 MB/秒

    3.1.2 寫測試

    dd 這裏使用 time + dd 簡單測試寫速度,不要求很精確

    查看內存緩存情況
    free -m
     
    清除緩存
    sync; echo 3 > /proc/sys/vm/drop_caches
     
    查block size
    blockdev --getbsz /dev/vda
     
    寫測試
    echo 3 > /proc/sys/vm/drop_caches; time dd if=/dev/zero of=/testdd bs=4k count=100000
    

    三次測試結果:

    記錄了100000+0 的讀入
    記錄了100000+0 的寫出

    409600000 bytes (410 MB) copied, 0.574066 s, 714 MB/s –410MB複製,用時0.57秒,評估714M/s

    409600000 bytes (410 MB) copied, 1.84421 s, 222 MB/s –410MB複製,用時1.84秒,評估222 M/s

    409600000 bytes (410 MB) copied, 1.06969 s, 383 MB/s –410MB複製,用時1.06秒,評估383M/s

    3.1.3 網絡帶寬

    iperf3測量一個網絡最大帶寬

    #安裝iperf3
    yum -y install iperf3
     
    #服務端
    iperf3 -s
     
    #客戶端
    iperf3 -c 上調命令執行的服務機器IP
    

    測試結果:

    [ ID]–>線程id Interva–>傳輸時間 Transfer–>接收數據大小 Bandwidth–>帶寬每秒大小 Retr 角色
    [ 4] 0.00-10.00 sec 17.0 GBytes 14.6 Gbits/sec 0 sender–>發送
    [ 4] 0.00-10.00 sec 17.0 GBytes 14.6 Gbits/sec receiver–>接收

    3.2 官方壓測

    3.2.1 用戶準備

    由於只能使用yarn配置了允許用戶,故這裏選擇hive用戶,如果su hive不能進入,則需要配置該步驟

    usermod -s /bin/bash  hive
    su hive
    

    3.2.2 HDFS 寫性能測試

    • 測試內容:HDFS集群寫入10個128M文件(-D指定文件存儲目錄)
    hadoop  jar /opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/jars/hadoop-mapreduce-client-jobclient-3.0.0-cdh6.2.0-tests.jar  TestDFSIO  -D test.build.data=/test/benchmark -write -nrFiles 10 -fileSize 128
    

    INFO fs.TestDFSIO: —– TestDFSIO —– : write
    INFO fs.TestDFSIO: Date & time: Thu Jun 11 10:30:36 CST 2020
    INFO fs.TestDFSIO: Number of files: 10 –十個文件
    INFO fs.TestDFSIO: Total MBytes processed: 1280 –總大小1280M
    INFO fs.TestDFSIO: Throughput mb/sec: 16.96 –吞吐量 每秒16.96M
    INFO fs.TestDFSIO: Average IO rate mb/sec: 17.89 –平均IO情況17.89M
    INFO fs.TestDFSIO: IO rate std deviation: 4.74 –IO速率標準偏差
    INFO fs.TestDFSIO: Test exec time sec: 46.33 –總運行時間

    3.2.3 HDFS 讀性能測試

    • 測試內容:HDFS集群讀取10個128M文件
    hadoop jar /opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/jars/hadoop-mapreduce-client-jobclient-3.0.0-cdh6.2.0-tests.jar TestDFSIO  -D test.build.data=/test/benchmark -read -nrFiles 10 -fileSize 128
    

    INFO fs.TestDFSIO: —– TestDFSIO —– : read
    INFO fs.TestDFSIO: Date & time: Thu Jun 11 10:41:19 CST 2020
    INFO fs.TestDFSIO: Number of files: 10 –文件數
    INFO fs.TestDFSIO: Total MBytes processed: 1280 –總大小
    INFO fs.TestDFSIO: Throughput mb/sec: 321.53 –吞吐量 每秒321.53M
    INFO fs.TestDFSIO: Average IO rate mb/sec: 385.43 –平均IO情況385.43M
    INFO fs.TestDFSIO: IO rate std deviation: 107.67 –IO速率標準偏差
    INFO fs.TestDFSIO: Test exec time sec: 20.81 –總運行時間

    3.2.4 刪除測試數據

    hadoop jar /opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/jars/hadoop-mapreduce-client-jobclient-3.0.0-cdh6.2.0-tests.jar TestDFSIO  -D test.build.data=/test/benchmark -clean
    

    3.3 參數調優

    服務 選項 配置值
    NameNode Java Heap Size (堆棧大小) 56G
    NameNode dfs.namenode.handler.count (詳見3.3.2) 80
    NameNode dfs.namenode.service.handler.count (詳見3.3.2) 80
    NameNode fs.permissions.umask-mode (使用默認值022) 027(使用默認值022)
    DataNode Java Heap Size (堆棧大小) 8G
    DataNode dfs.datanode.failed.volumes.tolerated (詳見3.3.3) 1
    DataNode dfs.datanode.balance.bandwidthPerSec (DataNode 平衡帶寬) 100M
    DataNode dfs.datanode.handler.count (服務器線程數) 64
    DataNode dfs.datanode.max.transfer.threads (最大傳輸線程數) 20480
    JournalNode Java Heap Size (堆棧大小) 1G

    3.3.1 數據塊優化

    dfs.blocksize = 128M

    • 文件以塊為單位進行切分存儲,塊通常設置的比較大(最小6M,默認128M),根據網絡帶寬計算最佳值。
    • 塊越大,尋址越快,讀取效率越高,但同時由於MapReduce任務也是以塊為最小單位來處理,所以太大的塊不利於於對數據的并行處理。
    • 一個文件至少佔用一個塊(如果一個1KB文件,佔用一個塊,但是佔用空間還是1KB)
    • 我們在讀取HDFS上文件的時候,NameNode會去尋找block地址,尋址時間為傳輸時間的1%時,則為最佳狀態。
      • 目前磁盤的傳輸速度普遍為100MB/S
      • 如果尋址時間約為10ms,則傳輸時間=10ms/0.01=1000ms=1s
      • 如果傳輸時間為1S,傳輸速度為100MB/S,那麼一秒鐘我們就可以向HDFS傳送100MB文件,設置塊大小128M比較合適。
      • 如果帶寬為200MB/S,那麼可以將block塊大小設置為256M比較合適。

    3.3.2 NameNode 的服務器線程的數量

    • dfs.namenode.handler.count=20*log2(Cluster Size),比如集群規模為16 ,8以2為底的對數是4,故此參數設置為80
    • dfs.namenode.service.handler.count=20*log2(Cluster Size),比如集群規模為16 ,8以2為底的對數是4,故此參數設置為80

    NameNode有一個工作線程池,用來處理不同DataNode的併發心跳以及客戶端併發的元數據操作。該值需要設置為集群大小的自然對數乘以20,。

    3.3.3 DataNode 停止提供服務前允許失敗的卷的數量

    DN多少塊盤損壞后停止服務,默認為0,即一旦任何磁盤故障DN即關閉。 對盤較多的集群(例如DN有超過2塊盤),磁盤故障是常態,通常可以將該值設置為1或2,避免頻繁有DN下線。

    4 優化:YARN + MapReduce

    服務 選項 配置值 參數說明
    ResourceManager Java Heap Size (堆棧大小) 4G
    ResourceManager yarn.scheduler.minimum-allocation-mb (最小容器內存) 2G 給應用程序 Container 分配的最小內存
    ResourceManager yarn.scheduler.increment-allocation-mb (容器內存增量) 512M 如果使用 Fair Scheduler,容器內存允許增量
    ResourceManager yarn.scheduler.maximum-allocation-mb (最大容器內存) 32G 給應用程序 Container 分配的最大內存
    ResourceManager yarn.scheduler.minimum-allocation-vcores (最小容器虛擬 CPU 內核數量) 1 每個 Container 申請的最小 CPU 核數
    ResourceManager yarn.scheduler.increment-allocation-vcores (容器虛擬 CPU 內核增量) 1 如果使用 Fair Scheduler,虛擬 CPU 內核允許增量
    ResourceManager yarn.scheduler.maximum-allocation-vcores (最大容器虛擬 CPU 內核數量) 16 每個 Container 申請的最大 CPU 核數
    ResourceManager yarn.resourcemanager.recovery.enabled true 啟用后,ResourceManager 中止時在群集上運行的任何應用程序將在 ResourceManager 下次啟動時恢復,備註:如果啟用 RM-HA,則始終啟用該配置。
    NodeManager Java Heap Size (堆棧大小) 4G
    NodeManager yarn.nodemanager.resource.memory-mb 40G 可分配給容器的物理內存數量,參照資源池內存90%左右
    NodeManager yarn.nodemanager.resource.cpu-vcores 32 可以為容器分配的虛擬 CPU 內核的數量,參照資源池內存90%左右
    ApplicationMaster yarn.app.mapreduce.am.command-opts 右紅 傳遞到 MapReduce ApplicationMaster 的 Java 命令行參數 “-Djava.net.preferIPv4Stack=true
    ApplicationMaster yarn.app.mapreduce.am.resource.mb (ApplicationMaster 內存) 4G
    JobHistory Java Heap Size (堆棧大小) 2G
    MapReduce mapreduce.map.memory.mb (Map 任務內存) 4G 一個MapTask可使用的資源上限。如果MapTask實際使用的資源量超過該值,則會被強制殺死。
    MapReduce mapreduce.reduce.memory.mb (Reduce 任務內存) 8G 一個 ReduceTask 可使用的資源上限。如果 ReduceTask 實際使用的資源量超過該值,則會被強制殺死
    MapReduce mapreduce.map.cpu.vcores 2 每個 MapTask 可使用的最多 cpu core 數目
    MapReduce mapreduce.reduce.cpu.vcores 4 每個 ReduceTask 可使用的最多 cpu core 數目
    MapReduce mapreduce.reduce.shuffle.parallelcopies 20 每個 Reduce 去 Map 中取數據的并行數。
    MapReduce mapreduce.task.io.sort.mb(Shuffle 的環形緩衝區大小) 512M 當排序文件時要使用的內存緩衝總量。注意:此內存由 JVM 堆棧大小產生(也就是:總用戶 JVM 堆棧 – 這些內存 = 總用戶可用堆棧空間)
    MapReduce mapreduce.map.sort.spill.percent 80% 環形緩衝區溢出的閾值
    MapReduce mapreduce.task.timeout 10分鐘 Task 超時時間,經常需要設置的一個參數,該參數表 達的意思為:如果一個 Task 在一定時間內沒有任何進 入,即不會讀取新的數據,也沒有輸出數據,則認為 該 Task 處於 Block 狀態,可能是卡住了,也許永遠會 卡住,為了防止因為用戶程序永遠 Block 住不退出, 則強制設置了一個該超時時間。如果你的程序對每條輸入數據的處理時間過長(比如會訪問數據庫,通過網絡拉取數據等),建議將該參數調大,該參數過小常出現的錯誤提示是 :AttemptID:attempt_12267239451721_123456_m_00 0335_0 Timed out after 600 secsContainer killed by the ApplicationMaster。

    5 優化:Impala

    服務 選項 配置值 參數說明
    Impala Daemon mem_limit (內存限制) 50G 由守護程序本身強制執行的 Impala Daemon 的內存限制。
    如果達到該限制,Impalad Daemon 上運行的查詢可能會被停止
    Impala Daemon Impala Daemon JVM Heap 512M 守護進程堆棧大小
    Impala Daemon scratch_dirs 節點上多塊獨立磁盤(目錄) Impala Daemon 將溢出信息等數據寫入磁盤以釋放內存所在的目錄。這可能是大量數據
    Impala Catalog Server Java Heap Size 8G 堆棧大小

    6 優化:Kafka

    6.1 官方壓測

    6.1.1 Kafka Producer 壓力測試

    • record-size 是一條信息有多大,單位是字節。
    • num-records 是總共發送多少條信息。
    • throughput 是每秒多少條信息,設成-1,表示不限流,可測出生產者最大吞吐量。
    bash /opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/lib/kafka//bin/kafka-producer-perf-test.sh --topic test --record-size 100 --num-records 100000 --throughput -1 --producer-props bootstrap.servers=cdh01.cm:9092,cdh02.cm:9092,cdh03.cm:9092
    

    100000 records sent, 225733.634312 records/sec (21.53 MB/sec),

    8.20 ms avg latency, 66.00 ms max latency,

    3 ms 50th, 28 ms 95th, 30 ms 99th, 30 ms 99.9th.

    參數解析:一共寫入 10w 條消息,吞吐量為 21.53 MB/sec,每次寫入的平均延遲

    為 8.20 毫秒,最大的延遲為 66.00 毫秒。

    6.1.2 Kafka Consumer 壓力測試

    • zookeeper 指定 zookeeper 的鏈接信息
    • topic 指定 topic 的名稱
    • fetch-size 指定每次 fetch 的數據的大小
    • messages 總共要消費的消息個數
    bash /opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/lib/kafka//bin/kafka-consumer-perf-test.sh --broker-list cdh01.cm:9092,cdh02.cm:9092,cdh03.cm:9092 --topic test --fetch-size 10000 --messages 10000000 --threads 1
    

    start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec, rebalance.time.ms, fetch.time.ms, fetch.MB.sec, fetch.nMsg.sec

    2020-06-11 17:53:48:179, 2020-06-11 17:54:04:525, 57.2205, 3.5006, 600000, 36706.2278, 3051, 13295, 4.3039, 45129.7480

    start.time:2020-06-11 17:53:48:179 開始時間

    end.time:2020-06-11 17:54:04:525 結束時間(用時16秒)

    data.consumed.in.MB:57.2205 消費57M數據

    MB.sec:3.5006 3.5M/S

    data.consumed.in.nMsg:600000 消費60萬消息

    nMsg.sec:36706.2278 36706條消息/S

    rebalance.time.ms:3051 平衡時間3S

    fetch.time.ms:13295 抓取時間13S

    fetch.MB.sec:4.3039 一秒抓取4.3M

    fetch.nMsg.sec:45129.7480 一秒抓取45129條消息

    開始測試時間,測試結束數據,共消費數據57.2205MB,吞吐量 3.5M/S,共消費600000條,平均每秒消費36706.2278條。

    6.1.3 Kafka 機器數量計算

    Kafka 機器數量(經驗公式)= 2 X(峰值生產速度 X 副本數 /100)+ 1

    先拿到峰值生產速度,再根據設定的副本數,就能預估出需要部署 Kafka 的數量。

    比如我們的峰值生產速度是 50M/s。副本數為 2。

    Kafka 機器數量 = 2 X( 50 X 2 / 100 )+ 1 = 3 台

    6.2 參數調優

    服務 選項 配置值 參數說明
    Kafka Broker Java Heap Size of Broker 2G Broker堆棧大小
    Kafka Broker Data Directories 多塊獨立磁盤
    Kafka 服務 Maximum Message Size 10M 服務器可以接收的消息的最大大小。此屬性必須與使用者使用的最大提取大小同步。否則,不守規矩的生產者可能會發布太大而無法消費的消息
    Kafka 服務 Replica Maximum Fetch Size 20M 副本發送給leader的獲取請求中每個分區要獲取的最大字節數。此值應大於message.max.bytes。
    Kafka 服務 Number of Replica Fetchers 6 用於複製來自領導者的消息的線程數。增大此值將增加跟隨者代理中I / O并行度。

    7 優化:HBase

    服務 選項 配置值 參數說明
    HBase Java Heap Size 18G 客戶端 Java 堆大小(字節)主要作用來緩存Table數據,但是flush時會GC,不要太大,根據集群資源,一般分配整個Hbase集群內存的70%,16->48G就可以了
    HBase hbase.client.write.buffer 512M 寫入緩衝區大小,調高該值,可以減少RPC調用次數,單數會消耗更多內存,較大緩衝區需要客戶端和服務器中有較大內存,因為服務器將實例化已通過的寫入緩衝區並進行處理,這會降低遠程過程調用 (RPC) 的數量。
    HBase Master Java Heap Size 8G HBase Master 的 Java 堆棧大小
    HBase Master hbase.master.handler.count 300 HBase Master 中啟動的 RPC 服務器實例數量。
    HBase RegionServer Java Heap Size 31G HBase RegionServer 的 Java 堆棧大小
    HBase RegionServer hbase.regionserver.handler.count 100 RegionServer 中啟動的 RPC 服務器實例數量,根據集群情況,可以適當增加該值,主要決定是客戶端的請求數
    HBase RegionServer hbase.regionserver.metahandler.count 60 用於處理 RegionServer 中的優先級請求的處理程序的數量
    HBase RegionServer zookeeper.session.timeout 180000ms ZooKeeper 會話延遲(以毫秒為單位)。HBase 將此作為建議的最長會話時間傳遞給 ZooKeeper 仲裁
    HBase RegionServer hbase.hregion.memstore.flush.size 1G 如 memstore 大小超過此值,Memstore 將刷新到磁盤。通過運行由 hbase.server.thread.wakefrequency 指定的頻率的線程檢查此值。
    HBase RegionServer hbase.hregion.majorcompaction 0 合併周期,在合格節點下,Region下所有的HFile會進行合併,非常消耗資源,在空閑時手動觸發
    HBase RegionServer hbase.hregion.majorcompaction.jitter 0 抖動比率,根據上面的合併周期,有一個抖動比率,也不靠譜,還是手動好
    HBase RegionServer hbase.hstore.compactionThreshold 6 如在任意一個 HStore 中有超過此數量的 HStoreFiles,則將運行壓縮以將所有 HStoreFiles 文件作為一個 HStoreFile 重新寫入。(每次 memstore 刷新寫入一個 HStoreFile)您可通過指定更大數量延長壓縮,但壓縮將運行更長時間。在壓縮期間,更新無法刷新到磁盤。長時間壓縮需要足夠的內存,以在壓縮的持續時間內記錄所有更新。如太大,壓縮期間客戶端會超時。
    HBase RegionServer hbase.client.scanner.caching 1000 內存未提供數據的情況下掃描儀下次調用時所提取的行數。較高緩存值需啟用較快速度的掃描儀,但這需要更多的內存且當緩存為空時某些下一次調用會運行較長時間
    HBase RegionServer hbase.hregion.max.filesize 50G HStoreFile 最大大小。如果列組的任意一個 HStoreFile 超過此值,則託管 HRegion 將分割成兩個
    HBase Master hbase.master.logcleaner.plugins 日誌清除器插件 org.apache.hadoop.hbase.master.cleaner.TimeToLiveLogCleaner
    HBase hbase.replication false 禁用複製
    HBase hbase.master.logcleaner.ttl 10min 保留 HLogs 的最長時間,加上如上兩條解決oldWALs增長問題

    8 優化:Hive

    服務 選項 配置值 參數說明
    HiveServer2 Java Heap Size 4G
    Hive MetaStore Java Heap Size 8G
    Hive Gateway Java Heap Size 2G
    Hive hive.execution.engine Spark 執行引擎切換
    Hive hive.fetch.task.conversion more Fetch抓取修改為more,可以使全局查找,字段查找,limit查找等都不走計算引擎,而是直接讀取表對應儲存目錄下的文件,大大普通查詢速度
    Hive hive.exec.mode.local.auto(hive-site.xml 服務高級配置,客戶端高級配置) true 開啟本地模式,在單台機器上處理所有的任務,對於小的數據集,執行時間可以明顯被縮短
    Hive hive.exec.mode.local.auto.inputbytes.max(hive-site.xml 服務高級配置,客戶端高級配置) 50000000 文件不超過50M
    Hive hive.exec.mode.local.auto.input.files.max(hive-site.xml 服務高級配置,客戶端高級配置) 10 個數不超過10個
    Hive hive.auto.convert.join 開啟 在join問題上,讓小表放在左邊 去左鏈接(left join)大表,這樣可以有效的減少內存溢出錯誤發生的幾率
    Hive hive.mapjoin.smalltable.filesize(hive-site.xml 服務高級配置,客戶端高級配置) 50000000 50M以下認為是小表
    Hive hive.map.aggr 開啟 默認情況下map階段同一個key發送給一個reduce,當一個key數據過大時就發生數據傾斜。
    Hive hive.groupby.mapaggr.checkinterval(hive-site.xml 服務高級配置,客戶端高級配置) 200000 在map端進行聚合操作的條目數目
    Hive hive.groupby.skewindata(hive-site.xml 服務高級配置,客戶端高級配置) true 有數據傾斜時進行負載均衡,生成的查詢計劃會有兩個MR Job,第一個MR Job會將key加隨機數均勻的分佈到Reduce中,做部分聚合操作(預處理),第二個MR Job在根據預處理結果還原原始key,按照Group By Key分佈到Reduce中進行聚合運算,完成最終操作
    Hive hive.exec.parallel(hive-site.xml 服務高級配置,客戶端高級配置) true 開啟并行計算
    Hive hive.exec.parallel.thread.number(hive-site.xml 服務高級配置,客戶端高級配置) 16G 同一個sql允許的最大并行度,針對集群資源適當增加

    9 優化:Oozie、Hue

    服務 選項 配置值 參數說明
    Oozie Java Heap Size 1G 堆棧大小
    Hue Java Heap Size 4G 堆棧大小

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

    【其他文章推薦】

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

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

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

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

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

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

  • Android官方新推的DI庫 Hilt

    Android官方新推的DI庫 Hilt

    Hilt是Google Android官方新推薦的依賴注入工具.
    已加入到官方文檔: Dependency injection with Hilt. 目前是alpha release階段.

    Hilt是在Dagger之上, Hilt單詞的意思是: 刀把, 柄.
    代碼庫還是這個google/dagger.

    Hilt的出現, 讓我想起了曾經曇花一現的dagger.android, 不知道hilt能不能經得住時間的考驗.

    本文介紹Hilt的基本使用. 熟悉dagger的朋友可能會發現很容易.

    Hilt是花里胡哨的小打小鬧? 還是下一個主流工具? 讓我們拭目以待.

    Codelab練習例子

    • Codelab: Using Hilt in your Android app.

    我的Fork: https://github.com/mengdd/android-hilt.

    這個項目最開始是一個用ServiceLocator手動注入的例子, 功能是記錄用戶點擊button操作, 显示log. 有兩個Fragment和Activity.

    通過Codelab中一系列步驟的改造, 最終改成用Hilt做依賴注入.

    本文舉例就用其中的代碼片段了.

    原repo還貼心地附上了改造前後的對比diff: Full codelab comparison.

    Hilt依賴添加

    project root: build.gradle:

    buildscript {
        ext.hilt_version = '2.28-alpha'
        ...
        dependencies {
            ...
            classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
        }
    }
    

    需要Android 4.0以上.

    app/build.gradle:

    apply plugin: 'kotlin-kapt'
    apply plugin: 'dagger.hilt.android.plugin'
    
    android {
        ...
    }
    
    dependencies {
        // Hilt dependencies
        implementation "com.google.dagger:hilt-android:$hilt_version"
        kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
    
        // Hilt testing dependency
        androidTestImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
        // Make Hilt generate code in the androidTest folder
        kaptAndroidTest "com.google.dagger:hilt-android-compiler:$hilt_version"
    }
    

    後面兩個是測試的依賴.

    概念, 用詞解釋

    • Container, 對應Component.
    • Bindings, 對應依賴.
    The information Hilt has about how to provide instances of different types are also called bindings.
    
    • Component hierarchy是指依賴在當前component中可以用, 也可以在它包含的child component中用. 舉例: application容器中的依賴activity可以用, 但是反過來不行.

    Hilt基本使用

    • @HiltAndroidApp: 標記在Application類上, 觸發代碼生成, application container是整個app的parent container.
    @HiltAndroidApp
    class LogApplication : Application()
    
    • @AndroidEntryPoint: 標記在Activity和Fragment上. 創建了一個和當前Activity/fragment生命周期相關的container. 目前支持的類型是: Activity, Fragment, View, Service, BroadcastReceiver.
    @AndroidEntryPoint
    class ExampleActivity : AppCompatActivity() { ... }
    

    每次Fragment(或Activity等)創建都會有它對應的container實例.

    • 字段注入: 用@Inject標記字段, 注意字段不能是private的.
    @AndroidEntryPoint
    class LogsFragment : Fragment() {
    
        @Inject
        lateinit var dateFormatter: DateFormatter
        ...
    }
    


    Component lifetimes可以看到每種類型的component創建和注入的時間.

    Activity是onCreate(), Fragment是onAttach().

    對Fragment來說, 在onAttach()的時候完成了對象的注入, 之後訪問對象都沒有問題.

    • 提供依賴: 用@Inject標記構造器.
    class DateFormatter @Inject constructor() {
        ...
    }
    

    它的依賴可以在構造參數中標明.

    Hilt的Scope支持

    默認所有的依賴都是沒有scope的, 每次注入依賴都創建新的實例.
    Hilt自動在對應的生命周期創建/銷毀對象: Component lifetimes.

    也可以把依賴scope到某個component, 這樣在這個component內, 依賴就是單例.

    • scope: @Singleton: application container的scope, 說明是application範圍內的單例. @ActivityScoped對應activity component.

    所有可用的scope: Component scopes.

    module

    module的使用基本和dagger一樣, 用來提供一些無法用構造@Inject的依賴, 比如接口, 第三方庫類型, Builder模式構造的對象等.

    • @Module: 標記這是一個module. 在Kotlin代碼中, module可以是一個object.
    • @Provides: 標記方法, 提供返回值類型的依賴.
    • @Binds: 標記抽象方法, 返回接口類型, 實現是方法的唯一參數.

    Hilt多了一個註解:

    • @InstallIn: 指明module對應哪個container, 也即Component.
      Generated components for Android classes
    @InstallIn(ApplicationComponent::class)
    @Module
    object DatabaseModule {
    
        @Provides
        @Singleton
        fun provideDatabase(@ApplicationContext appContext: Context): AppDatabase {
            return Room.databaseBuilder(
                appContext,
                AppDatabase::class.java,
                "logging.db"
            ).build()
        }
    
        @Provides
        fun provideLogDao(database: AppDatabase): LogDao {
            return database.logDao()
        }
    }
    

    module的拆分

    module的名字最好能傳達它提供了什麼類型的依賴, 所以多種依賴拆分多個modules寫比較好.

    @InstallIn(ActivityComponent::class)
    @Module
    abstract class NavigationModule {
        @Binds
        abstract fun bindNavigator(impl: AppNavigatorImpl): AppNavigator
    }
    

    @Binds@Provides不能放在同一個module里.
    因為@Binds需要module是一個abstract class, 而@Provides需要module是一個object.
    放一起會報錯: error: A @Module may not contain both non-static and abstract binding methods.

    默認依賴

    有一些默認依賴是已經有的, 比如:

    • @ApplicationContext.
    • @ActivityContext.

    可以直接作為@Provides方法或@Inject構造的參數.

    默認依賴是和component對應的.

    Component default bindings

    Qualifier

    要提供同一個接口的不同實現, 可以用不同的註解來標記. (dagger之前用的是@Named).

    A qualifier is an annotation used to identify a binding.

    舉例: LoggerDataSource接口提供了內存和數據庫兩種實現.

    定義兩個註解:

    @Qualifier
    annotation class InMemoryLogger
    
    @Qualifier
    annotation class DatabaseLogger
    

    module中提供的時候用來標記相應的依賴:

    @InstallIn(ApplicationComponent::class)
    @Module
    abstract class LoggingDatabaseModule {
    
        @DatabaseLogger
        @Singleton
        @Binds
        abstract fun bindDatabaseLogger(impl: LoggerLocalDataSource): LoggerDataSource
    }
    
    @InstallIn(ActivityComponent::class)
    @Module
    abstract class LoggingInMemoryModule {
    
        @InMemoryLogger
        @ActivityScoped
        @Binds
        abstract fun bindInMemoryLogger(impl: LoggerInMemoryDataSource): LoggerDataSource
    }
    

    這裏用了兩個module因為它們對應兩個不同的component, 一個是application一個是activity, 依賴也是相應的scope.

    注入的時候也對應加上:

    @InMemoryLogger
    @Inject
    lateinit var logger: LoggerDataSource
    

    @EntryPoint

    Hilt支持最常用的Android組件, 對於默認不支持的類型, 如果還想做字段注入, 需要用@EntryPoint.

    注意這裏只是限制了字段注入的情況, 自定義類型一般用構造注入比較方便(如果能用的話).

    @EntryPoint的意思就是一個邊界點, 這裏可以通往Hilt的世界, 得到容器提供的依賴對象們.

    Codelab中的例子是一個ContentProvider.

    關鍵的部分是這段代碼:

    @InstallIn(ApplicationComponent::class)
    @EntryPoint
    interface LogsContentProviderEntryPoint {
        fun logDao(): LogDao
    }
    
    private fun getLogDao(appContext: Context): LogDao {
        val hiltEntryPoint = EntryPointAccessors.fromApplication(
            appContext,
            LogsContentProviderEntryPoint::class.java
        )
        return hiltEntryPoint.logDao()
    }
    

    Hilt and Dagger

    當初的dagger.android已被放棄, 它是為了簡化dagger在Android上的使用而單獨推出的. (根據Activity和Fragment的生命周期, 開發者不用手動調用inject方法, 但是確實最開始的setup code比較多.)

    Hilt相對於Dagger來說, 簡化了幾個點:

    • 不用自己創建Component.
    • 不用手動調用inject()方法來完成字段注入.
    • 不用自己在Application中保存component.
    • 提供了一些Scope, 不用自己定義和寫.
    • 提供了一些默認依賴, 比如Context.

    總體來說Hilt就是針對Android做的定製, 讓依賴注入框架用起來更方便. 畢竟dagger是一個java注入庫, 它的應用範圍不限於Android.

    因為Hilt和Dagger可以共存, 可以逐步遷移. 既然官方推薦了, 可以在項目內小範圍地先試試.

    最後推薦這個cheat sheet

    Reference

    • Dependency Injection on Android with Hilt
    • Dependency injection with Hilt
    • Dagger Hilt

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

    【其他文章推薦】

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

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

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

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

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

  • .net core3.1 abp動態菜單和動態權限(思路) (二)

    .net core3.1 abp動態菜單和動態權限(思路) (二)

    ps:本文需要先把abp的源碼下載一份來下,跟着一起找實現,更容易懂

    在abp中,對於權限和菜單使用靜態來管理,菜單的加載是在登陸頁面的地方(具體是怎麼知道的,瀏覽器按F12,然後去sources中去找)

    這個/AbpScripts/GetScripts是獲取需要初始化的script,源自AbpScriptsController,GetScripts方法包括

    頁面加載時的鏈接是:http://localhost:62114/AbpScripts/GetScripts?v=637274153555501055

    _multiTenancyScriptManager //當前租戶初始化 對應報文的 abp.multiTenancy

    _sessionScriptManager //當前session初始化 對應報文的 abp.session
    _localizationScriptManager  //本地化的初始化 對應報文的 abp.localization
    _featuresScriptManager  //對應報文的 abp.features
    _authorizationScriptManager  //權限初始化  對應報文的 abp.auth
    _navigationScriptManager  //導航菜單初始化  對應報文的 abp.nav
    _settingScriptManager  //設置初始化  對應報文的 abp.setting
    _timingScriptManager  //對應報文的 abp.clock
    _customConfigScriptManager  //對應報文的 abp.custom

     

     

     

     

     好了,現在基本算是找到菜單和權限js獲取的地方了,一般系統裏面,權限是依賴於菜單和菜單按鈕的,所以我們先不管權限,先把菜單做成動態加載的

    從await _navigationScriptManager.GetScriptAsync()開始,一路F12,大概流程是

    (接口)INavigationScriptManager=>(接口實現)NavigationScriptManager=>(方法)GetScriptAsync=>(調用)await _userNavigationManager.GetMenusAsync=>
    (接口)IUserNavigationManager=>(接口實現)UserNavigationManager=>(方法)GetMenuAsync=>(調用)navigationManager.Menus=>
    (接口)INavigationManager=>(接口實現)NavigationManager=>(非靜態構造函數為Menus屬性賦值)NavigationManager

     到這裏之後基本就到底了,我們看看NavigationManager的內容

        internal class NavigationManager : INavigationManager, ISingletonDependency
        {
            public IDictionary<string, MenuDefinition> Menus { get; private set; }  //屬性
    
            public MenuDefinition MainMenu //屬性
            {
                get { return Menus["MainMenu"]; }
            }
    
            private readonly IIocResolver _iocResolver;  
            private readonly INavigationConfiguration _configuration;
    
            public NavigationManager(IIocResolver iocResolver, INavigationConfiguration configuration) //非靜態構造函數
            {
                _iocResolver = iocResolver;
                _configuration = configuration;
    
                Menus = new Dictionary<string, MenuDefinition>
                        {
                            {"MainMenu", new MenuDefinition("MainMenu", new LocalizableString("MainMenu", AbpConsts.LocalizationSourceName))}
                        };
            }
    
            public void Initialize()  //初始化方法
            {
                var context = new NavigationProviderContext(this);
    
                foreach (var providerType in _configuration.Providers)
                {
                    using (var provider = _iocResolver.ResolveAsDisposable<NavigationProvider>(providerType))
                    {
                        provider.Object.SetNavigation(context);  //中式英語翻譯一下,應該是設置導航
                    }
                }
            }
        }

    這個類裏面就只有屬性、需要注入的接口聲明、非靜態構造函數、初始化方法,我們到這裏需要關注的是Menus這個屬性,這個屬性似乎將會包含我們需要生成的菜單內容

    Menus = new Dictionary<string, MenuDefinition>
                        {
                            {"MainMenu", new MenuDefinition("MainMenu", new LocalizableString("MainMenu", AbpConsts.LocalizationSourceName))}
                        };

    這裡是對Menus的賦值,實例化了一個Dictionary,前面的不用看,主要是看標紅的這句話,從new LocalizableString(“MainMenu”, AbpConsts.LocalizationSourceName)裏面獲取到值

    好了現在基本找到地方了,我們不知道LocalizableString是什麼意思,但是我們可以百度一波

    ILocalizableString/LocalizableString:封裝需要被本地化的string的信息,並提供Localize方法(調用ILocalizationManager的GetString方法)返回本地化的string. SourceName指定其從那個本地化資源讀取本地化文本。

      LocalizableString(“Questions”, “”) 如果本地找不到資源,會報300

    大概的意思是通過new LocalizableString,我們可以在本地化來源為AbpConsts.LocalizationSourceName的string裏面尋找到Key為MainMenu的value(理解不對請噴)

     

    現在需要去找到那個地方對MainMenu進行了本地化操作,一般來說這個事情都是在程序加載的時候進行的,先對MainMenu進行讀取,保存到本地,然後在_navigationScriptManager讀取,傳輸給前台

    似乎不好找了,但是我們發現有一個類型MenuDefinition,F12一下,可以發現寶藏

    namespace Abp.Application.Navigation
    {
        /// <summary>
        /// Represents a navigation menu for an application.  //表示應用程序的導航菜單
    /// </summary>
        public class MenuDefinition : IHasMenuItemDefinitions
        {
            /// <summary>
            /// Unique name of the menu in the application. Required.  //應用程序中菜單的唯一名稱。 必須
            /// </summary>
            public string Name { get; private set; }
    
            /// <summary>
            /// Display name of the menu. Required.  //菜單显示名稱 必須
    /// </summary>
            public ILocalizableString DisplayName { get; set; }
    
            /// <summary>
            /// Can be used to store a custom object related to this menu. Optional.  //可用於存儲與此菜單相關的自定義對象
    /// </summary>
            public object CustomData { get; set; }
    
            /// <summary>
            /// Menu items (first level).   //菜單項(第一級)
    /// </summary>
            public List<MenuItemDefinition> Items { get; set; }
    
            /// <summary>
            /// Creates a new <see cref="MenuDefinition"/> object.
            /// </summary>
            /// <param name="name">Unique name of the menu</param>
            /// <param name="displayName">Display name of the menu</param>
            /// <param name="customData">Can be used to store a custom object related to this menu.</param>
            public MenuDefinition(string name, ILocalizableString displayName, object customData = null)
            {
                if (string.IsNullOrEmpty(name))
                {
                    throw new ArgumentNullException("name", "Menu name can not be empty or null.");
                }
    
                if (displayName == null)
                {
                    throw new ArgumentNullException("displayName", "Display name of the menu can not be null.");
                }
    
                Name = name;
                DisplayName = displayName;
                CustomData = customData;
    
                Items = new List<MenuItemDefinition>();
            }
    
            /// <summary>
            /// Adds a <see cref="MenuItemDefinition"/> to <see cref="Items"/>.
            /// </summary>
            /// <param name="menuItem"><see cref="MenuItemDefinition"/> to be added</param>
            /// <returns>This <see cref="MenuDefinition"/> object</returns>
            public MenuDefinition AddItem(MenuItemDefinition menuItem)
            {
                Items.Add(menuItem);
                return this;
            }
    
            /// <summary>
            /// Remove menu item with given name
            /// </summary>
            /// <param name="name"></param>
            public void RemoveItem(string name)
            {
                Items.RemoveAll(m => m.Name == name);
            }
        }
    }

    找到了菜單的類型了,那麼我們去找保存的地方就好找了,我們其實可以根據AddItem這個方法去找,去查看哪個地方引用了

    AddItem方法添加的是MenuItemDefinition類型的變量,那我們現在退出abp源碼,去我們的AbpLearn項目中去全局搜索一下

     

     

    看來是同一個AbpLearnNavigationProvider類裏面,雙擊過去看一下

     

        /// <summary>
        /// This class defines menus for the application.
        /// </summary>
        public class AbpLearnNavigationProvider : NavigationProvider
        {
            public override void SetNavigation(INavigationProviderContext context)
            {
                context.Manager.MainMenu
                    .AddItem(
                        new MenuItemDefinition(
                            PageNames.Home,
                            L("HomePage"),
                            url: "",
                            icon: "fas fa-home",
                            requiresAuthentication: true
                        )
                    ).AddItem(
                        new MenuItemDefinition(
                            PageNames.Tenants,
                            L("Tenants"),
                            url: "Tenants",
                            icon: "fas fa-building",
                            permissionDependency: new SimplePermissionDependency(PermissionNames.Pages_Tenants)
                        )
                    ).AddItem(
                        new MenuItemDefinition(
                            PageNames.Users,
                            L("Users"),
                            url: "Users",
                            icon: "fas fa-users",
                            permissionDependency: new SimplePermissionDependency(PermissionNames.Pages_Users)
                        )
                    ).AddItem(
                        new MenuItemDefinition(
                            PageNames.Roles,
                            L("Roles"),
                            url: "Roles",
                            icon: "fas fa-theater-masks",
                            permissionDependency: new SimplePermissionDependency(PermissionNames.Pages_Roles)
                                )
                    )
                    .AddItem(
                        new MenuItemDefinition(
                            PageNames.About,
                            L("About"),
                            url: "About",
                            icon: "fas fa-info-circle"
                        )
                    ).AddItem( // Menu items below is just for demonstration!
                        new MenuItemDefinition(
                            "MultiLevelMenu",
                            L("MultiLevelMenu"),
                            icon: "fas fa-circle"
                        ).AddItem(
                            new MenuItemDefinition(
                                "AspNetBoilerplate",
                                new FixedLocalizableString("ASP.NET Boilerplate"),
                                icon: "far fa-circle"
                            ).AddItem(
                                new MenuItemDefinition(
                                    "AspNetBoilerplateHome",
                                    new FixedLocalizableString("Home"),
                                    url: "https://aspnetboilerplate.com?ref=abptmpl",
                                    icon: "far fa-dot-circle"
                                )
                            ).AddItem(
                                new MenuItemDefinition(
                                    "AspNetBoilerplateTemplates",
                                    new FixedLocalizableString("Templates"),
                                    url: "https://aspnetboilerplate.com/Templates?ref=abptmpl",
                                    icon: "far fa-dot-circle"
                                )
                            ).AddItem(
                                new MenuItemDefinition(
                                    "AspNetBoilerplateSamples",
                                    new FixedLocalizableString("Samples"),
                                    url: "https://aspnetboilerplate.com/Samples?ref=abptmpl",
                                    icon: "far fa-dot-circle"
                                )
                            ).AddItem(
                                new MenuItemDefinition(
                                    "AspNetBoilerplateDocuments",
                                    new FixedLocalizableString("Documents"),
                                    url: "https://aspnetboilerplate.com/Pages/Documents?ref=abptmpl",
                                    icon: "far fa-dot-circle"
                                )
                            )
                        ).AddItem(
                            new MenuItemDefinition(
                                "AspNetZero",
                                new FixedLocalizableString("ASP.NET Zero"),
                                icon: "far fa-circle"
                            ).AddItem(
                                new MenuItemDefinition(
                                    "AspNetZeroHome",
                                    new FixedLocalizableString("Home"),
                                    url: "https://aspnetzero.com?ref=abptmpl",
                                    icon: "far fa-dot-circle"
                                )
                            ).AddItem(
                                new MenuItemDefinition(
                                    "AspNetZeroFeatures",
                                    new FixedLocalizableString("Features"),
                                    url: "https://aspnetzero.com/Features?ref=abptmpl",
                                    icon: "far fa-dot-circle"
                                )
                            ).AddItem(
                                new MenuItemDefinition(
                                    "AspNetZeroPricing",
                                    new FixedLocalizableString("Pricing"),
                                    url: "https://aspnetzero.com/Pricing?ref=abptmpl#pricing",
                                    icon: "far fa-dot-circle"
                                )
                            ).AddItem(
                                new MenuItemDefinition(
                                    "AspNetZeroFaq",
                                    new FixedLocalizableString("Faq"),
                                    url: "https://aspnetzero.com/Faq?ref=abptmpl",
                                    icon: "far fa-dot-circle"
                                )
                            ).AddItem(
                                new MenuItemDefinition(
                                    "AspNetZeroDocuments",
                                    new FixedLocalizableString("Documents"),
                                    url: "https://aspnetzero.com/Documents?ref=abptmpl",
                                    icon: "far fa-dot-circle"
                                )
                            )
                        )
                    );
            }
    
            private static ILocalizableString L(string name)
            {
                return new LocalizableString(name, AbpLearnConsts.LocalizationSourceName);
            }
        }

    好了,現在我們找到菜單定義的地方了,那麼我們如何去做動態菜單哪?

     

    首先我們想一下需要什麼樣的動態菜單?

    1.從數據庫加載,不從數據庫加載怎麼叫動態

    2.可以根據不同Host(管理者)和Tenant(租戶)加載不同的菜單,不可能管理者和租戶看到的菜單全是一個樣子的吧!

    3.可以根據不同的角色或者用戶加載不同的菜單(這個就牽扯到權限了,比如誰可以看到什麼,不可以看到什麼)

    4.權限、按鈕最好和菜單相綁定,這樣便於控制

    ……

     

    根據以上幾點,我們可以確定

    1.必須要在用戶登錄之後加載出來的菜單才能符合條件

    2.菜單需要建一個表(因為abp默認沒有單獨的菜單表),來進行存放

    3.字段需要包含:菜單名,菜單與權限對應的名稱(用於動態權限),菜單對應的Url,Icon,級聯父Id,是否啟用,排序,租戶Id

    4.需要對菜單進行編輯時,因為牽扯到多租戶,我們需要對多租戶定義一個標準的菜單,在添加租戶時,自動將標準菜單複製保存一份到新租戶中,所以我們需要對於菜單的進行區分,一般來說Host對應的數據行TenantId(int)都為null,我們可以將標準菜單的TenantId標為-1,已經分配保存的菜單TenantId為當前租戶Id,這樣便於區分和查詢

     

    好了,讓我們開始寫動態菜單吧

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

    【其他文章推薦】

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

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

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

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

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

  • 剛果法院首例 12年殺害500多頭大象 盜獵者將蹲30年苦牢

    剛果法院首例 12年殺害500多頭大象 盜獵者將蹲30年苦牢

    環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:ENS

    剛果共和國刑事法院以殺害國家公園巡守員未遂、販賣盜獵來的象牙、持有武器等多項罪名,判處知名盜獵者和象牙走私者吉拉德(Mobanza Mobembo Gérard)30年徒刑,且必須支付3800萬中非法郎(約台幣199萬元)給受傷的巡守員。

    這是剛果共和國的野生動植走私者首次在刑事法院被定罪。

    吉拉德因殺害國家公園巡守員未遂、販賣盜獵來的象牙、持有武器等多項罪名,遭判處30年徒刑。照片來源:WCS
    新聞稿

    吉拉德帶領集團 在國家公園殺死500多頭大象

    根據調查,人稱蓋瓦尼歐(Guyvanho)的吉拉德帶領一個約25人的盜獵集團。自2008年至今,他們在諾娃貝爾多基國家公園(Nouabalé-Ndoki National Park)一帶殺死了500多頭大象。

    該公園成立於1993年,位於剛果北部省份,周圍是非洲森林象和稀有大型猿類的棲地,包括西部低地大猩猩和東部黑猩猩。

    剛果民主共和國籍的蓋瓦尼歐於2008年前後來到剛果共和國北部。2018年初,他和其他幾名集團成員殺死了11頭大象,被諾娃貝爾多基國家公園巡守員逮個正著,雙方發生駁火。起初蓋瓦尼奧趁亂逃脫,但同夥三名成員被捕。他們的供詞成為對蓋瓦尼歐發出逮捕令的根據。

    除了被殺害的大象數量眾多外,這次事件還顯示該犯罪集團以暴力對抗公權力的傾向,而且公園內外四周的大象盜獵集團行徑越來越囂張。

    2018年5月蓋瓦尼歐被捕,被押在歐耶索省城監獄中等待審判。然而,在2018年6月2日,審判開始前12天,蓋瓦尼歐竟然逃獄了。儘管如此,他的審判仍持續進行,並因缺席被判處5年徒刑加500萬中非法郎(約台幣26萬元)的罰款。

    除了被殺害的大象數量眾多外,這次事件還顯示該犯罪集團以暴力對抗公權力的傾向。照片來源:WCS
    臉書

    蓋瓦尼奧於2019年7月20日再度落網

    法院再次發出逮捕令。蓋瓦尼歐仍然是公園野生動物犯罪部門的通緝要犯,所有曾出沒過的地點都受到監視。這段期間,他曾被目擊繼續參加盜獵活動,每次都與國家公園的巡守員發生槍戰。

    2019年5月31日,一名巡守員返回諾娃貝爾多基國家公園總部途中剛好碰上一群剛收工的盜獵者,蓋瓦尼奧赫然在其中。盜獵者對巡守員開槍,導致兩名巡守員受傷,其中一名重傷,幸好被受過急救訓練的同僚救回。

    蓋瓦尼奧這次又逃走了,但似乎向人吹噓事發經過而走漏了風聲,當局因此再次掌握他新的藏身之處。透過監視行動,蓋瓦尼奧終於在2019年7月20日落網。

    蓋瓦尼奧被送回歐耶索省服刑,並再次嘗試越獄,這次沒有成功。很顯然,蓋瓦尼奧在歐耶索省有強大的後援,極有可能再次越獄。國家公園管理部門於2019年8月成功獲得批准,得以將蓋瓦尼奧和三名同夥轉移到布拉柴維爾監獄。

    學者:此次判決是剛果在野生動植物保護上的重要里程碑

    諾娃貝爾多基國家公園由總部位於紐約的野生動物保護協會(Wildlife Conservation Society, WCS)和剛果共和國政府透過諾娃貝爾多基基金會合作管理,已經長達25年。

    WCS中非地區主任斯托克斯(Emma Stokes)博士說,這名盜獵犯能被繩之以法,靠的是諾娃貝爾多基國家公園野生動物犯罪部門和反盜獵部門三年來的努力,以及和森林經濟部、警方和地方檢察官等的多個有關當局合作的成果。

    「此次判決是剛果共和國刑事法庭在野生動植物保護上一個重要的里程碑。以前所有環境犯罪都是在民事法庭審理的,最高刑期僅五年。今日的判決顯示,野生動植物犯罪不會再被容忍,將從重量刑。」斯托克斯說。

    Congo Imprisons Elephant Poacher for 30 Years BRAZZAVILLE, Republic of Congo, August 22, 2020 (ENS)

     A criminal court in the Republic of Congo has sentenced a notorious poacher and ivory trafficker, Mobanza Mobembo Gérard, alias Guyvanho, to 30 years in prison for the attempted murder of park rangers, trafficking of ivory from poached elephants, possession of military weapons, and other charges. He is also required to pay damages of 38 million Central African Francs (US$68,000) to the injured rangers.

    The 30-year sentence marks the first-ever conviction in the criminal courts of a wildlife trafficker in the Republic of Congo.

    Investigations revealed that Guyvanho led a group of approximately 25 poachers who, based on the number of hunts reported, could have killed upwards of 500 elephants in the area of Nouabalé-Ndoki National Park since 2008.

    Established in 1993, in the northern provinces of Congo, the park is inhabited by forest elephants and rare great apes, including western lowland gorillas and the eastern subspecies of chimpanzees.

    In early 2018, Guyvanho – a citizen of the Democratic Republic of Congo who had arrived in the northern Republic of Congo around 2008 – and several other members of his team were caught in an operation led by Nouabalé-Ndoki National Park rangers, after they allegedly killed 11 elephants. A firefight ensued and Guyvanho was initially able to escape, but three members of his team were arrested. Their statements provided sufficient grounds for an arrest warrant to be issued against him.

    In addition to the number of elephants killed, this incident demonstrated the willingness of this group to respond with violence when challenged. This was indicative of a trend of increasing violence of elephant poaching gangs in and around the park.

    In May 2018, Guyvanho was arrested and remanded in prison in the provincial town of Ouesso to await trial. However, on June 2, 2018, 12 days before his trial was to take place, Guyvanho escaped from the Ouesso prison. Still, his trial went ahead, and he was sentenced in absentia to five years in prison with a five million Central African Franc (US$9,000) fine.

    A further arrest warrant was issued. He remained a priority target of the park’s Wildlife Crime Unit – and locations known to be used by Guyvanho were monitored, but no arrest was made.

    During this time, he was cited as a participant in a number of subsequent hunts, each of which featured exchanges of gunfire with park rangers.

    On May 31, 2019, a ranger patrol returning to Nouabalé-Ndoki National Park HQ happened across a group of poachers – including Guyvanho – returning from a hunt. The patrol was fired upon by the poachers, and two patrol members were wounded, one of them seriously. His life was saved by fellow rangers with medical training.

    Guyvanho was again able to escape but was reported to have bragged about the incident. This information was passed to the authorities along with a new location for Guyvanho’s home. A surveillance operation was launched to confirm the information from the Wildlife Crime Unit, and, based on this information, Guyvanho was arrested by the Ouesso Police on July 20, 2019.

    Guyvanho was returned to prison in Ouesso to serve his sentence but following a further escape attempt, this time unsuccessful, it became clear that Guyvanho had a sufficiently strong support network in Ouesso that another escape attempt was highly likely. A transfer to Brazzaville prison was requested by the park authorities and approved, and in August 2019 Guyvanho and three associates were successfully moved.

    All the convicts will be transferred back to Brazzaville this week to serve their sentences.

    The Nouabalé-Ndoki National Park is governed by a 25-year public-private partnership between the New York City-based Wildlife Conservation Society, WCS, and the Government of the Republic of Congo through the Nouabalé-Ndoki Foundation.

    Dr. Emma Stokes, WCS regional director for Central Africa, says many people cooperated to bring this poacher to justice. “The sentencing is the culmination of more than three years of work by the Nouabalé-Ndoki National Park’s Wildlife Crime Unit and Anti-Poaching department. It is also the result of fruitful cooperation with multiple Congolese authorities, including the Ministry of Forest Economy, the Police, and District Prosecutors.

    Dr. Stokes listed some of those supporting WCS involvement in this case. “WCS commends our government partners in the Republic of Congo and thanks our donors for their ongoing support in this case, including The Wildcat Foundation, Save the Elephants’ and Wildlife Conservation Network’s Elephant Crisis Fund, the Sangha Trinational Trust Fund, U.S. State Department’s Bureau for International Narcotics and Law Enforcement Affairs, the European Union, and the United States Agency for International Development’s Central Africa Regional Program for the Environment (USAID-CARPE).”

    “This unprecedented conviction in the criminal court is a major milestone in the protection of wildlife in the Republic of Congo. Previously, all environmental crimes were tried in the civil courts where the maximum penalty under the wildlife law was five years. Today’s sentencing sends an extremely strong message that wildlife crime will not be tolerated and will be prosecuted at the highest levels,” Stokes said.

    “We are confident that today’s sentence will serve as a deterrent to would-be criminals that you will serve hard time if you break our wildlife laws and put park rangers and Congo’s national security in danger,” she said.

    After the sentencing, an official from the Sangha District Court declared, “This verdict confirms the fact that under the pretext of being poaching gangs, it is actually well-organized criminal gangs operating in our forests.”

    ※ 全文及圖片詳見:ENS

    盜獵
    象牙
    大象盜獵
    國際新聞
    剛果
    生物多樣性

    作者

    姜唯

    如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。

    林大利

    於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。

    延伸閱讀

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

    【其他文章推薦】

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

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

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

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

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

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