標籤: 台北網站設計

  • 50行Python代碼實現視頻中物體顏色識別和跟蹤(必須以紅色為例)

    50行Python代碼實現視頻中物體顏色識別和跟蹤(必須以紅色為例)

    目前計算機視覺(CV)與自然語言處理(NLP)及語音識別並列為人工智能三大熱點方向,而計算機視覺中的對象檢測(objectdetection)應用非常廣泛,比如自動駕駛、視頻監控、工業質檢、醫療診斷等場景。

    目標檢測的根本任務就是將圖片或者視頻中感興趣的目標提取出來,目標的識別可以基於顏色、紋理、形狀。其中顏色屬性運用十分廣泛,也比較容易實現。下面就向大家分享一個我做的小實驗———通過OpenCV的Python接口來實現從視頻中進行顏色識別和跟蹤。

    下面就是我們完整的代碼實現(已調試運行):

    import numpy as np
    import cv2
    font = cv2.FONT_HERSHEY_SIMPLEX
    lower_green = np.array([35, 110, 106])  # 綠色範圍低閾值
    upper_green = np.array([77, 255, 255])  # 綠色範圍高閾值
    lower_red = np.array([0, 127, 128])  # 紅色範圍低閾值
    upper_red = np.array([10, 255, 255])  # 紅色範圍高閾值
    #需要更多顏色,可以去百度一下HSV閾值!
    # cap = cv2.VideoCapture('1.mp4')  # 打開視頻文件
    cap = cv2.VideoCapture(0)#打開USB攝像頭
    if (cap.isOpened()):  # 視頻打開成功
        flag = 1
    else:
        flag = 0
    num = 0
    if (flag):
        while (True):
            ret, frame = cap.read()  # 讀取一幀
           
            if ret == False:  # 讀取幀失敗
                break
            hsv_img = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
            mask_green = cv2.inRange(hsv_img, lower_green, upper_green)  # 根據顏色範圍刪選
            mask_red = cv2.inRange(hsv_img, lower_red, upper_red) 
     # 根據顏色範圍刪選
            mask_green = cv2.medianBlur(mask_green, 7)  # 中值濾波
            mask_red = cv2.medianBlur(mask_red, 7)  # 中值濾波
            mask = cv2.bitwise_or(mask_green, mask_red)
            mask_green, contours, hierarchy = cv2.findContours(mask_green, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
            mask_red, contours2, hierarchy2 = cv2.findContours(mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    
            for cnt in contours:
                (x, y, w, h) = cv2.boundingRect(cnt)
                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 255), 2)
                cv2.putText(frame, "Green", (x, y - 5), font, 0.7, (0, 255, 0), 2)
    
            for cnt2 in contours2:
                (x2, y2, w2, h2) = cv2.boundingRect(cnt2)
                cv2.rectangle(frame, (x2, y2), (x2 + w2, y2 + h2), (0, 255, 255), 2)
                cv2.putText(frame, "Red", (x2, y2 - 5), font, 0.7, (0, 0, 255), 2)
            num = num + 1
            cv2.imshow("dection", frame)
            cv2.imwrite("imgs/%d.jpg"%num, frame)
            if cv2.waitKey(20) & 0xFF == 27:
                break
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    如圖所示,我們將會檢測到紅色區域

    最終的效果圖:

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

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    台灣海運大陸貨務運送流程

    兩岸物流進出口一站式服務

  • Appium+python自動化(四十一)-Appium自動化測試框架綜合實踐 – 即將落下帷幕(超詳解)

    Appium+python自動化(四十一)-Appium自動化測試框架綜合實踐 – 即將落下帷幕(超詳解)

    1.簡介

      今天我們緊接着上一篇繼續分享Appium自動化測試框架綜合實踐 – 代碼實現。到今天為止,大功即將告成;框架所需要的代碼實現都基本完成。

    2.data數據封裝

    2.1使用背景

    在實際項目過程中,我們的數據可能是存儲在一個數據文件中,如txt,excel、csv文件類型。我們可以封裝一些方法來讀取文件中的數據來實現數據驅動。

    2.2案例

    將測試賬號存儲在account.csv文件,內容如下:

    account.csv

    hg2018

    hg2018

    hg2019

    zxw2019

    666

    222

    參考代碼

    2.3enumerate()簡介

    enumerate()是python的內置函數

    • enumerate在字典上是枚舉、列舉的意思
    • 對於一個可迭代的(iterable)/可遍歷的對象(如列表、字符串),enumerate將其組成一個索引序列,利用它可以同時獲得索引和值
    • enumerate多用於在for循環中得到計數。

    2.4enumerate()使用

    如果對一個列表,既要遍歷索引又要遍曆元素時,首先可以這樣寫:

    參考代碼
    list = ["", "", "一個", "測試","數據"]
    
    for i in range(len(list)):
    
        print(i,list[i])

    上述方法有些累贅,利用enumerate()會更加直接和優美:

    參考代碼
    list1 = ["", "", "一個", "測試","數據"]
    
    for index, item in enumerate(list1):
    
            print(index,item)

    3.數據讀取方法封裝

      數據讀取方法也屬於公共方法,這裏我們首先實現一下,然後將其封裝到裡邊即可。

    3.1數據讀取方法實現的參考代碼

    import csv
    
    
         def get_csv_data(csv_file,line):
    
            with open(csv_file, 'r', encoding='utf-8-sig') as file:
    
                reader=csv.reader(file)
    
                for index, row in enumerate(reader,1):
    
                    if index == line:
    
                        return row
    
     
    
        csv_file='../data/account.csv'
    
        data=get_csv_data(csv_file,3)
    
        print(data)

    3.2封裝

    將其封裝在公共方法中,在其他地方用到的時候,直接導入調用即可。

    4.utf-8與utf-8-sig兩種編碼格式的區別

    UTF-8以字節為編碼單元,它的字節順序在所有系統中都是一樣的,沒有字節序的問題,也因此它實際上並不需要BOM(“ByteOrder Mark”)。但是UTF-8 with BOM即utf-8-sig需要提供BOM。

    5.config文件配置

    各種配置文件都放在這個目錄下。

    5.1日誌文件配置 

    主要是一些日誌信息的配置。

    log.config

     參考代碼
    [loggers]
    keys=root,infoLogger
    
    [logger_root]
    level=DEBUG
    handlers=consoleHandler,fileHandler
    
    [logger_infoLogger]
    handlers=consoleHandler,fileHandler
    qualname=infoLogger
    propagate=0
    
    [handlers]
    keys=consoleHandler,fileHandler
    
    [handler_consoleHandler]
    class=StreamHandler
    level=INFO
    formatter=form02
    args=(sys.stdout,)
    
    [handler_fileHandler]
    class=FileHandler
    level=INFO
    formatter=form01
    args=('../logs/runlog.log', 'a')
    
    [formatters]
    keys=form01,form02
    
    [formatter_form01]
    format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
    
    [formatter_form02]
    format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

    6.測試用例封裝

    這裏宏哥舉例給小夥伴們演示:封裝註冊和登錄兩個測試用例。

    6.1測試用例執行開始結束操作封裝

    測試用例執行開始和結束的封裝,其他模塊用到直接導入,調用即可。

    myunit.py

    參考代碼
    # coding=utf-8
    # 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行
    
    # 2.註釋:包括記錄創建時間,創建人,項目名稱。
    '''
    Created on 2019-11-20
    @author: 北京-宏哥   QQ交流群:707699217
    Project:Appium自動化測試框架綜合實踐 - 代碼實現
    '''
    # 3.導入模塊
    import unittest
    from kyb_testProject.common.desired_caps import appium_desired
    import logging
    from time import sleep
    
    class StartEnd(unittest.TestCase):
        def setUp(self):
            logging.info('=====setUp====')
            self.driver=appium_desired()
    
        def tearDown(self):
            logging.info('====tearDown====')
            sleep(5)
            self.driver.close_app()

    6.2註冊用例

    開始註冊用例代碼邏輯的實現。

    test_register.py

    參考代碼
    # coding=utf-8
    # 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行
    
    # 2.註釋:包括記錄創建時間,創建人,項目名稱。
    '''
    Created on 2019-11-20
    @author: 北京-宏哥   QQ交流群:707699217
    Project:Appium自動化測試框架綜合實踐 - 代碼實現
    '''
    # 3.導入模塊
    from kyb_testProject.common.myunit import StartEnd
    from kyb_testProject.businessView.registerView import RegisterView
    import logging,random,unittest
    
    class RegisterTest(StartEnd):
        def test_user_register(self):
            logging.info('======test_user_register======')
            r=RegisterView(self.driver)
    
            username = 'bjhg2019' + 'fly' + str(random.randint(1000, 9000))
            password = 'bjhg2020' + str(random.randint(1000, 9000))
            email = 'bjhg' + str(random.randint(1000, 9000)) + '@163.com'
    
            self.assertTrue(r.register_action(username,password,email))
    
    if __name__ == '__main__':
        unittest.main()

    6.3登錄用例

    開始登錄用例代碼邏輯的實現。

    test_login.py

    參考代碼
    # coding=utf-8
    # 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行
    
    # 2.註釋:包括記錄創建時間,創建人,項目名稱。
    '''
    Created on 2019-11-13
    @author: 北京-宏哥   QQ交流群:707699217
    Project:Appium自動化測試框架綜合實踐 - 代碼實現
    '''
    # 3.導入模塊
    from kyb_testProject.common.myunit import StartEnd
    from kyb_testProject.businessView.loginView import LoginView
    import unittest
    import logging
    
    class TestLogin(StartEnd):
        csv_file='../data/account.csv'
    
        @unittest.skip('test_login_zxw2018')
        def test_login_zxw2018(self):
            logging.info('======test_login_zxw2018=====')
            l=LoginView(self.driver)
            data=l.get_csv_data(self.csv_file,2)
    
            l.login_action(data[0],data[1])
            self.assertTrue(l.check_loginStatus())
    
        # @unittest.skip('skip test_login_zxw2017')
        def test_login_zxw2017(self):
            logging.info('======test_login_zxw2017=====')
            l=LoginView(self.driver)
            data = l.get_csv_data(self.csv_file, 1)
    
            l.login_action(data[0], data[1])
            self.assertTrue(l.check_loginStatus())
    
        @unittest.skip('test_login_error')
        def test_login_error(self):
            logging.info('======test_login_error=====')
            l = LoginView(self.driver)
            data = l.get_csv_data(self.csv_file, 3)
    
            l.login_action(data[0], data[1])
            self.assertTrue(l.check_loginStatus(),msg='login fail!')
    
    if __name__ == '__main__':
        unittest.main()

    7.小結

    到此,Appium自動化測試框架就差下一篇就全部完成了,聰明的你都懂了嗎???嘿嘿!慢慢地來吧。

    下節預告

    下一篇,講解執行測試用例,生成測試報告,以及自動化平台,請關注宏哥,敬請期待!!!

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

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    台灣海運大陸貨務運送流程

    兩岸物流進出口一站式服務

  • Thrift總結(四)Thrift實現雙向通信

    Thrift總結(四)Thrift實現雙向通信

    前面介紹過 Thrift 安裝和使用,介紹了Thrift服務的發布和客戶端調用,可以查看我之前的文章:

    但是,之前介紹的都是單向的客戶端發送消息,服務端接收消息。而客戶端卻得不到服務器的響應。

    那如果我們要實現雙向通信(即:客戶端發送請求,服務端處理返回,服務端發送消息,客戶端處理返回)的功能,該怎麼實現呢?

     

    其實在不涉及語言平台的制約,WebService或是webapi 就可以實現這種客戶端發起請求,服務端的處理的單向流程。

    然而,實際場景中,可能我們的某些業務需求,更需要服務端能夠響應請求並處理數據。下面我通過一個demo案例,介紹下Thrift 是如何實現雙向通信的。

     

    一、安裝Thrift

    這裏不再贅述,戳這裏查看我上篇文章的介紹:

     

    二、編寫Thrift IDL文件 

    編寫thrift腳本,命名為student.thrift  如下:

    service HelloWorldService{
        void SayHello(1:string msg);
    }

    生成service 的方法,之前的文章有介紹,這裏就不介紹了。

     

    三、編寫服務端代碼

    創建HelloThrift.Server 服務端工程,添加HelloWorldBidirectionServer類,HelloWorldBidirectionServer 實現了Iface接口用於接收客戶端消息,並有一個客戶端傳輸層對象集合用於記錄所有已連接的客戶端。

     public class HelloWorldBidirectionServer : HelloWorldBidirectionService.Iface
        {
            public void Run(int port)
            {
                try
                {
                    TServerTransport transport = new TServerSocket(port);
    
                    TTransportFactory transportFac = new TTransportFactory();
    
                    TProtocolFactory inputProtocolFactory = new TBinaryProtocol.Factory();
                    TThreadPoolServer server = new TThreadPoolServer(getProcessorFactory(), transport, transportFac, inputProtocolFactory);
    
                    server.Serve();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
    
            public static List<TTransport> TransportCollection = new List<TTransport>();
    
            public void SayHello(string msg)
            {
                Console.WriteLine(string.Format("{0:yyyy/MM/dd hh:mm:ss} 服務端接收到消息: {1}", DateTime.Now, msg));
            }
    
            public void SayToClient(string msg)
            {
                try
                {
                    foreach (TTransport trans in TransportCollection)
                    {
                        TBinaryProtocol protocol = new TBinaryProtocol(trans);
                        HelloWorldBidirectionService.Client client = new HelloWorldBidirectionService.Client(protocol);
                        //Thread.Sleep(1000);
                        client.SayHello(msg);
                        //Console.WriteLine("發給了客戶端喲");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
    
            public TProcessorFactory getProcessorFactory()
            {
                return new HelloWorldBidirectionProcessor();
            }
        }
    
        public class HelloWorldBidirectionProcessor : TProcessorFactory
        {
            public TProcessor GetProcessor(TTransport trans, TServer server = null)
            {
                if (trans.IsOpen)
                {
                    HelloWorldBidirectionServer.TransportCollection.Add(trans);
                    Console.WriteLine("客戶端連上。");
                }
    
                HelloWorldBidirectionServer srv = new HelloWorldBidirectionServer();
                return new global::HelloWorldBidirectionService.Processor(srv);
            }
        }

     

    四、編寫客戶端代碼

    首先創建HelloThrift.Client客戶端項目,添加接收服務端消息的類HelloWorldBidirectionClient,裏面只有一個實現Iface接口的方法:

      public class HelloWorldBidirectionClient
        {
            static HelloWorldBidirectionService.Client client = null;
            public void ConnectAndListern(int port, string ip = "127.0.0.1")
            {
                //Tsocket: TCP/IP Socket接口
                TSocket tSocket = new TSocket(ip, port);
                //消息結構協議
                TProtocol protocol = new TBinaryProtocol(tSocket);
                try
                {
                    if (client == null)
                    {
                        client = new global::HelloWorldBidirectionService.Client(protocol);
                        tSocket.Open();//建立連接
                        StartListern(tSocket);//啟動監聽線程
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
    
            public void Say(string msg)
            {
                if (client != null)
                    client.SayHello(msg);
            }
    
            void StartListern(TSocket tSocket)
            {
                Thread t = new Thread(new ParameterizedThreadStart(Run));
                t.Start(tSocket);
            }
    
            public void Run(object tSocket)
            {
                HelloWorldBidirectionService.Processor process = new HelloWorldBidirectionService.Processor(new HelloWorldBidirectionFace());
    
                try
                {
                    while (process.Process(new TBinaryProtocol((TSocket)tSocket), new TBinaryProtocol((TSocket)tSocket)))
                    {
                        Console.WriteLine("消息接收完成,等下一波,阻塞中......");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("連接斷開..." + ex.Message);
                }
            }
    
        }
        class HelloWorldBidirectionFace : HelloWorldBidirectionService.Iface
        {
            public void SayHello(string msg)
            {
                Console.WriteLine(string.Format("{0:yyyy/MM/dd hh:mm:ss} 收到服務端響應消息 {1}", DateTime.Now, msg));
    
            }
        }

     實現客戶端,ConnectAndListern方法可以與服務端建立連接,並開啟客戶端端口監聽來自服務端的信息。Say方法可將消息發送至服務端。

     

    五、測試

     測試效果如下:

     

     

     

    六、最後

      1. 關於使用Thrift 構建我們自己的rpc 的方法,這裏基本講完了。其他的方法本文就不再演示了,調用起來都是一樣。  

      2. 後續會簡單討論一下Thrift 框架的通信原理。

      3. 源代碼下載,

     

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

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”

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

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

    小三通海運與一般國際貿易有何不同?

    小三通快遞通關作業有哪些?

  • 北極白鯨驚奇現身泰晤士河 再不回家專家憂安危

    摘錄自2018年9月26日中央社報導

    據英國各大媒體報導,一隻迷途的白鯨現身英國泰晤士河,第一次被目擊是25日在肯特郡(Kent)格雷夫森德(Gravesend)的泰晤士河段,當時白鯨正在駁船附近覓食,被暱稱為「班尼」(Benny),距離其原棲息地北極圈水域達數千公里。26日白鯨再次現身同個地點,引發是否迷航及可能遇險的擔憂。

    鯨豚保育協會(WDC)海洋哺乳類動物科學家羅特(Rob Lott)表示,這隻白鯨正受到監控,以防牠擱淺。「但白鯨停留在泰晤士河口的時間愈長,就愈令人擔心。」

    海洋生物保育慈善團體ORCA的保育學家巴比(Lucy Babey)表示:「這是白鯨出現在英國的最南端紀錄。」

    英國海洋生物救援組織表示,英國最後一次發現白鯨蹤跡是3年前在北英格蘭的諾森伯蘭(Northumberland)海岸,以及北愛爾蘭,但極為罕見。

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

    【其他文章推薦】

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    台灣海運大陸貨務運送流程

    兩岸物流進出口一站式服務

  • 中國新能源汽車零部件發展規劃成果集中亮相北京10月車展

    中國新能源汽車零部件發展規劃成果集中亮相北京10月車展

    2016年10月13—16日,以“選擇·行動——未來從現在開始”為主題的2016(第四屆)節能與新能源汽車產業發展規 劃成果展覽會、中國國際汽車新能源及技術應用展覽會將在北京國家會議中心舉辦。展會由中國國家工業和資訊化部支援,科學技術部、中國國際貿易促進委員會批准,是貫徹落實國務院《節能與 新能源汽車產業發展規劃(2012—2020年)》,推動中國節能與新能源汽車產業發展的重要舉措之一。本屆展會將全方面覆蓋節能環保汽車、純電動 汽車、混合動力汽車、插電式混合動力和燃料電池汽車,以及電池、電機、電控等關鍵零部件和充電設施等產品,眾多國際知名汽車企業、零部件和充電設施供應 商將攜旗下新品參展。

    汽車產業是國民經濟的重要支柱產業,也是實現新一輪技術革命和產業變革的重要載體。中國汽車產業快速發展,產銷增速連續七年居世界第一位,新能源汽車產業初具規模,15年中國已成為全球最大的新能源汽車生產以及銷售市場.

    但同時,據中國國家863“節能與新能源汽車”重大項目監理諮詢專家組王秉剛調研,中國目前具有生產傳統汽車ABS系統能力的工廠主要有亞太、元豐與伯特利等幾家,他們是未來最有希望成為中國自主電動汽車制動系統的生產企業,與國外同類企業相比,他們規模都不大,在同類產品的市場佔有率不足5%,在年產量達2000多萬輛的中國汽車產業裡,如此重要的底盤部件,自主企業生產的比例低到差不多可以忽略不計的程度。中國汽車產業空心化可見一斑!這裡舉的僅是制動系統的例子,其它零部件也存在類似情況,就連備受關注的動力電池也未必樂觀,已經有國外大電池企業,在部分地方政府優惠政策的支援下在國內建廠。

    中國新能源汽車零部件企業正加強關鍵核心技術研發,推進技術創新,竭盡全力避免傳統汽車產業存在的核心技術缺失、產業空心化現象,在新能源汽車行業重蹈覆轍。據車展合作單位盛大超越展覽公司嶽巍介紹,“新能源汽車零部件主要包括電機、驅動控制系統和電源系統,目前中國在新能源汽車零部件上的技術創新有序推進,整體研發水平不斷提升,中國企業的電機及驅動控制系統在新能源客車(如宇通、安凱、中通等)及乘用車(比亞迪、北汽、江淮等)上已得到廣泛應用,這些成果大部分將在北京10月新能源汽車展上展出,中國的電機及驅動控制系統參展企業包括:德沃仕、英威騰、中冶南方、超同步、合普動力、合康動力、福工動力、八達等。這些企業有的具有國外及科研院所的專業背景,有的是從電梯控制、機床控制發展而來。比如合普動力是低速電動車電機驅動控制的領軍企業,隨著高速新能源汽車市場的不斷成長,已開發了電動客車及乘用車電機驅動控制產品”。

    盛大超越展覽公司電池及BMS展區經理耿忱也對中國企業抱有積極信心,他說,”北京10月新能源汽車成果展上的中國電池企業非常給力,沃特瑪、寧德時代、中航鋰電、力神電池、比克電池、萬向、山東威能、神工光電、內蒙古稀奧科、實聯長宜等知名電池企業展示了車用動力電池技術及市場應用的發展成果,部分電池展商還帶來了實際應用整車配合展示”。他還表示,“環宇賽爾、科隆集團、均勝普瑞等一批新的中國電池及BMS展商將有望加盟2016北京10月新能源汽車成果展,給主機廠提供更加前沿和多元化的產品解決方案“。

    展會相關

    2016中國國際汽車新能源及技術應用展覽會
    節能與新能源汽車產業發展規劃成果展覽會
    行業地位:中國最大高速新能源汽車展
    展覽規模:30,000平方米(2015年)
    觀眾數量:60,000人次(2015年)
    展覽週期:每年一屆,2013年首屆
    連絡人:岳巍 先生
    手機:+86 135 5286 5285
    展會網址:
     

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

    【其他文章推薦】

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    台灣海運大陸貨務運送流程

    兩岸物流進出口一站式服務

  • 前端每周學習分享–第12期

    1.VuePress

    大家看過不少Vue.js及其子項目的文檔,一定發現了它們風格完全一致,界面清爽,讀起來很舒服,它們都使用了vuepress。

    VuePress是尤大為了支持 Vue 及其子項目的文檔需求而寫的一個靜態網站生成工具,廣泛用於編寫技術文檔 ,可以部署在github上做個人博客。

    原理:

    在構建過程中,會創建應用程序的服務器渲染版本,通過訪問每個路由,來渲染相應的 HTML。

    其中, 每個 markdown 文件都使用 編譯為 HTML,然後作為 Vue 組件的模板進行處理。這允許你直接在 markdown 文件中使用 Vue,在需要嵌入動態內容時,這種使用方式非常有用。

    十分實用的特性:

    • md文件可內嵌vue代碼
    • 可自定義主題
    • 利用service worker做離線緩存
    • 多語言支持
    • 基於git的最近更新

    官方文檔:

    快速搭建:

    2.WebWorker

    web worker是運行在後台的jacvascript,利用類似線程的消息傳遞實現并行,獨立於其他腳本,不會影響頁面的性能。

    web worker能夠長時間運行,有理想的啟動性能以及理想的內存消耗。

    worker 創建后,它可以向它的創建者指定的事件監聽函數傳遞消息,這樣該worker生成的所有任務都會接收到這些消息。

    webworker有專用線程dedicated worker(單窗口專用),sharedWorker(可多窗口共享),以及後來的service worker(目前瀏覽器支持程度還不高)。

    2.1.dedicated worker

    使用方法:

    worker線程里監聽onmessage,

    頁面線程里創建worker對象:const myworker = new Worker("worker.js")

    發送消息:postMessage(msg)

    接受消息:onmessage = function(e){const msg = e.data}

    msg的數據格式自行定義。

    終止worker:myworker.terminate()

    例如下面的示例,worker會接收頁面上輸入的兩個数字,計算出乘積后返回結果。

    worker.js

    onmessage = function(e) {
      console.log('Worker: Message received from main script');
      let result = e.data[0] * e.data[1];
      if (isNaN(result)) {
        postMessage('Please write two numbers');
      } else {
        let workerResult = 'Result: ' + result;
        console.log('Worker: Posting message back to main script');
        postMessage(workerResult);
      }
    }

    index.html里

    const first = document.querySelector('#number1');
    const second = document.querySelector('#number2');
    const result = document.querySelector('.result');
    if (window.Worker) {
        const myWorker = new Worker("worker.js");
    
        first.onchange = function() {
          myWorker.postMessage([first.value, second.value]);
          console.log('Message posted to worker');
        }
    
        second.onchange = function() {
          myWorker.postMessage([first.value, second.value]);
          console.log('Message posted to worker');
        }
    
        myWorker.onmessage = function(e) {
            result.textContent = e.data;
            console.log('Message received from worker');
        }
    } else {
        console.log('Your browser doesn\'t support web workers.')
    }

    2.2.shared worker

    共享進程可以連接到多個不同的頁面,這些頁面必須屬於相同的域(相同的協議,主機以及端口)

    在火狐中,共享進程不能在私有與公共文檔間進行共享。

    SharedWorker.port返回一個MessagePort對象,用來進行通信和對共享進程進行控制。

    創建共享進程對象:const myWorker = new SharedWorker("worker.js");

    獲取端口:

    發送消息:myWorker.port.postMessage(msg)

    接收消息:myWorker.port.onmessage = function(e) {const msg = e.data}

    worker線程獲取端口:onconnect = function(e) {const port = e.ports[0]}

    啟動端口:port.start()

    2.3.service worker

    Service Worker 可以理解為一個介於客戶端和服務器之間的一個代理服務器 ,常用於做離線資源緩存

    出於對安全問題的考慮,Service Worker 只能被使用在 https 或者本地的 localhost 環境下。

    暫時沒有仔細學這塊,可以閱讀。

    參考文章:

    3.代碼相關

    3.1.元素內文本垂直居中

    已知元素高度的話,可以設置line-height:元素高度.

    如果元素高度未知,就不能使用line-height了。

    有人會想使用line-height:100%,會發現這是不行的,這個百分比是相對當前字體尺寸,而不是元素高度。

    我使用了flex布局實現

        display: flex;
        align-items:center;
        justify-content:center;

    還可以設置padding來使文本看起來垂直居中

    padding: 50px 20px;

    3.2.微信小程序自定義placeholder的隱藏時機

    在一個searchBar組件中,有一個自定的placeholder如下:

    <!-- <view
    ​        wx:if="{{!inputValue.length}}"
    ​        class="placeholder" >
    ​        {{placeholder}}
    ​     </view> -->

    原生的placeholder不是在觸發bindinput時隱藏,而是在輸入鍵盤按鈕點擊時。使用inputValue.length來判斷显示自定義的placeholder會在某些輸入法中導致拼音預覽和自定義placeholder重疊(因為拼音显示的時候value值還沒變)

    最後選擇棄用這個自定義placeholder,使用input組件的placeholder屬性,並使用placeholder-class來設置它的樣式。

    3.3.關於微信小程序原生組件的坑

    原生組件有camera、canvas、input (僅在focus時表現為原生組件) 、live-player、live-pusher、map、textarea、video、cover-view、cover-image。

    所以當你用canvas畫圖表、使用地圖、播放視頻甚至做文本輸入時,都是可能遇到相關坑點的。

    1. 關於原生組件、組件之間的層級關係、

    ​ 原生組件的層級始終高於普通組件,不論普通組件的z-index設置了多少。

    ​ 后插入的原生組件可以覆蓋之前的原生組件。

    ​ 原生組件之間的相對層級關係可以通過z-index來調整。

    ​ 原生組件會遮擋vconsole彈出的調試面板。

    ​ cover-view和cover-image可以覆蓋在部分原生組件上。

    1. cover-view的使用

    ​ cover-view在做地圖、畫布、視頻上的彈出層時是會用到的,但它有很多使用限制。

    ​ cover-view只能內嵌cover-view、cover-image、button,其他元素在真機上就會被cover-view給覆蓋住,如果想內嵌radio、picker等就只能自己用這3個可內嵌的元素來實現。

    ​ cover-view不支持iconfont,也不支持單邊border、background-imageshadowoverflow: visible等。

    1. input的使用

    ​ input在不聚焦時是佔位元素,會被原生組件遮擋,聚焦時才使用原生組件渲染。這就會出現input設置了更高的z-index,不聚焦時仍會被其他原生組件遮住。

    ​ 要解決這個問題,可以使用textarea來代替input。

    ​ 我的一個解決方案是,加一個標誌位來記錄input是否聚焦,當不聚焦時,显示一個承載value值的cover-view(它需要綁定一個觸發聚焦的點擊事件),聚焦時,就显示input組件。

    3.4.多個標籤頁之間的通信方案

    1. 使用websocket

    2. 使用localstorage或者cookie

    3. 使用sharedworker

    我遇到的問題是需要在新窗口打開當前網站的新窗口時,能繼承上一個窗口的vuex的狀態樹里的某些數據。這不需要和服務器打交道,最好就在本地。

    最後使用localstorage來做,在跳轉新窗口前更新localstorage,在新窗口根組件掛載時取出數據。

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

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    台灣海運大陸貨務運送流程

    兩岸物流進出口一站式服務

  • 菲亞特董事長公開闢謠 廣汽入主消息不實

    據《歐洲汽車新聞》報導,菲亞特克萊斯勒公司(FCA)董事長約翰•埃爾坎(John Elkann)明確指出,未與中國廣汽集團簽訂股權出售協定。

    2015年FCA與廣汽延續廣汽菲亞特的合作關係,成立了廣汽菲克合資公司,投產Jeep自由光和自由俠。近日,《義大利日報》發佈了一篇爆料新聞,除在生產方面的合作外,廣汽集團還有意入主FCA,展開資本層面的聯姻。

    埃爾坎同時也是菲亞特控股集團EXOR的董事長,25日召開了Exor股東大會。針對《義大利日報》的爆料,埃爾坎當日公開闢謠,公司與廣汽集團未簽訂股權出售協定。

    埃爾坎也被詢問標緻雪鐵龍集團(PSA集團)是否可成為FCA的良好合作夥伴,該董事長則指出其公司正在尋求的推動轉型的合作夥伴,PSA並不在公司的考慮範圍內。

    據法國《回聲報》的報導,法國政府正在考慮出售其持有的PSA 14%的部分或全部股權。

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

    【其他文章推薦】

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    台灣海運大陸貨務運送流程

    兩岸物流進出口一站式服務

  • 大眾計畫為高端車型開發第二個電動汽車平臺

    據外媒報導稱,大眾正在考慮開發第二個電動汽車平臺,用於更大、更高端的車型。

    大眾的第一個電動汽車平臺名叫MEB,曾在CES上展示 過,BUDD-e概念車就是用該平臺開發的。該平臺專門針對小型汽車、輕型商務用車,充電一次行駛距離大約155英里至310英里(250-500公 裡)。目前大眾正在調查MEB平臺的彈性,看它是否可以用在高端車型上,比如Phaeton(輝騰),未來大眾可能會為高端車型專門開發一個新平臺。

    按照大眾的計畫,2019年之前MEB平臺準備就緒,因為該平臺主要針對的是電動汽車,擴展性更強。也就是說汽車製造商調整軸距、軌距、座位位置更加容易一些,它們可以將平臺用在更多的車型上。落地式電池尤其引人注目,因為工程師可以根據車型改變電池的大小。

    大眾集團電子研發主管沃克馬•坦尼伯格(Volkmar Tanneberger)稱,‘巧克力電池’(chocolate battery)製造更容易,可以進行大規模工業化生產。MEB能夠用在所有車型上,包括超小型Polo和中型汽車帕薩特(Passat),未來MEB有可能會成為大眾所有產品線的基石。如果MEB架構無法用在更大型的汽車上,比如輝騰,開發第二個電動汽車平臺就會變得有意義。保時捷Mission E和奧迪e-tron Quattro將會擁有自己的平臺,因為早在MEB架構公佈之前,它們已經設計成型。

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

    【其他文章推薦】

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    台灣海運大陸貨務運送流程

    兩岸物流進出口一站式服務

  • 利用SSH隧道技術穿越內網訪問遠程設備

    利用SSH隧道技術穿越內網訪問遠程設備

    本文為作者原創,轉載請註明出處:

    通常,我們用於調試的計算機無法遠程訪問位於局域網中的待調試設備。通過 ssh 的端口轉發(又稱 ssh 隧道)技術,可以實現這種遠程調試功能。

    下文中,sshc 指 ssh 客戶端,sshd 指 ssh 服務器。

    1. ssh 端口轉發模式簡介

    ssh 客戶端運行於本地機器,它的作用是:登錄到目標機器並在目標機器上執行命令。它可以建立一個安全通道,為不安全網絡上兩個不受信任的主機提供安全的加密通信。X11 連接、任意 TCP 端口和 UNIX 域套接字也可以通過 ssh 安全通道進行轉發。

    ssh 連接並登錄到指定的主機名(用戶名可選)。如果指定了命令,命令將在遠程主機上執行,而不是在本機 shell 里執行。

    1.1 ssh 常用選項簡介

    ssh 端口轉發相關的常用選項如下:

    -C

    請求壓縮所有數據(包括 stdin、stdout、stderr 和用於轉發的 X11、TCP 和 UNIX 域連接的數據)。壓縮算法與 gzip 使用的算法相同,壓縮級別由 ssh 協議版本 1 的 CompressionLevel 選項控制。在調製解調器線路和其他慢速連接上採用壓縮是可取的,但它會減慢快速網絡上的速度。

    -f

    請求 ssh 在執行命令之前轉到後台。如果用戶希望 ssh 在後台運行,但 ssh 需要用戶提供密碼或口令,使用 -f 選項就很有用,在用戶輸入密碼之後,ssh 就會轉入後台運行。這個選項隱含了 -n 選項的功能(-n 選項將 stdin 重定向到 /dev/null,從而避免後台進程讀 stdin)。在遠程站點上啟動 X11 程序的推薦方法是使用 “ssh -f host xterm” 。

    如果 ExitOnForwardFailure 配置選項設置的是 “yes”,則使用 -f 選項啟動的 ssh 客戶端會等所有的遠程端口轉發建立成功后才將自己轉到後台運行。

    -n

    將 stdin 重定向到 /dev/null (實際上是為了防止後台進程從stdin讀取數據)。當 ssh 在後台運行時必須使用此選項。

    一個常見的技巧是使用它在目標機器上運行 X11 程序。例如,ssh -n shadow.cs.hut.fi emacs & 將在 shadows.cs.hut.fi 上啟動 emacs 程序。X11 的連接將通過加密通道自動轉發。ssh 程序將在後台運行。(如果 ssh 需要請求密碼或口令,則此操作無效;參見-f選項。)

    -N

    不執行遠程命令。此選項用於只需要端口轉發功能時。

    -g

    允許遠程主機連接到本地轉發端口。如果用於多路復用連接,則必須在主進程上指定此選項。

    -t

    強制分配一個偽終端。在目標機上執行任意的基於屏幕的程序時(例如,實現菜單服務),分配偽終端很有用。使用多個 -t 選項則會強制分配終端,即使 ssh 沒有本地終端。

    -T

    禁止分配偽終端。

    -L [bind_address:]port:host:hostport
    -L [bind_address:]port:remote_socket
    -L local_socket:host:hostport
    -L local_socket:remote_socket

    數據從本機轉發到遠程。本機上指定 TCP 端口或 UNIX 套接字的連接將被轉發到目標機上指定端口或套接字。

    上述參數中,bind_address 指本地地址;port 指本地端口;local_socket 指本地 UNIX 套接字;host 指遠程主機地址;hostport 指遠程端口;remote_socket 指遠程 UNIX 套接字。

    本地(ssh 客戶端)與遠程(ssh 服務端)建立一條連接,此連接的形式有四種:

    本地 [bind_address:]port    <====>   遠程 host:hostport  
    本地 [bind_address:]port    <====>   遠程 remote_socket  
    本地 local_socket           <====>   遠程 host:hostport  
    本地 local_socket           <====>   遠程 remote_socket  

    位於本機的 ssh 客戶端會分配一個套接字來監聽本地 TCP 端口(port),此套接字可綁定本機地址(bind_address, 可選,本機不同網卡具有不同的 IP 地址)或本地 UNIX 套接字(local_socket)。每當一個連接建立於本地端口或本地套接字時,此連接就會通過安全通道進行轉發。

    也可在配置文件中設置端口轉發功能。只有超級用戶可以轉發特權端口。

    默認情況下,本地端口是根據 GatewayPorts 設置選項綁定的。但是,使用顯式的bind_address 可將連接綁定到指定地址。bind_address 值是 “localhost”時,表示僅監聽本機內部數據[TODO: 待驗證],值為空或“*”時,表示監聽本機所有網卡的監聽端口。

    注意:localhost 是個域名,不是地址,它可以被配置為任意的 IP 地址,不過通常情況下都指向 127.0.0.1(ipv4)和 。127.0.0.1 這個地址通常分配給 loopback 接口。loopback 是一個特殊的網絡接口(可理解成虛擬網卡),用於本機中各個應用之間的網絡交互。

    GatewayPorts 說明 (查閱 man sshd_config):指定是否允許遠程主機(ssh客戶端)連接到本機(ssh服務端)轉發端口。默認情況下,sshd(8)將遠程端口轉發綁定到環回地址,這將阻止其他遠程主機連接到本機轉發端口。GatewayPorts 也可設置為將將遠程端口轉發綁定到非環回地址,從而允許其他遠程主機連接到本機。GatewayPorts 值“no”,表示強制遠程端口轉發僅對本機可用;值“yes”,表示強制遠程端口轉發綁定到通配符地址;值“clientspecified”,表示允許客戶端選擇轉發綁定到的地址。默認是“no”。

    -R [bind_address:]port:host:hostport
    -R [bind_address:]port:local_socket
    -R remote_socket:host:hostport
    -R remote_socket:local_socket

    此選項在本地機上執行,目標機上指定 TCP 端口或 UNIX 套接字的連接將被轉發到本機上指定端口或套接字。

    上述參數中,bind_address 指遠程地址;port 指遠程端口;remote_socket 指遠程 UNIX 套接字;host 指本地地址;hostport 指本地端口;local_socket 指本地 UNIX 套接字。

    工作原理:位於遠程的 ssh 服務端會分配一個套接字來監聽 TCP 端口或 UNIX 套接字。當目標機(服務端)上有新的連接建立時,此連接會通過安全通道進行轉發,本地機執行當前命令的進程收到此轉發的連接后,會在本機內部新建一條 ssh 連接,連接到當前選項中指定的端口或套接字。參 2.3 節分析。

    也可在配置文件中設置端口轉發功能。只有超級用戶可以轉發特權端口。

    默認情況下,目標機(服務端)上的 TCP 監聽套接字只綁定迴環接口。也可將目標機上的監聽套接字綁定指定的 bind_address 地址。bind_address 值為空或 “*” 時,表示目標機上的監聽套接字會監聽目標機上的所有網絡接口。僅當目標機上 GatewayPorts 設置選項使能時,通過此選項為目標機指定 bind_address 才能綁定成功(參考 sshd_config(5))。

    如果 port 參數是 ‘0’,目標機(服務端)可在運行時動態分配監聽端口並通知本地機(客戶端),如果同時指定了 “-O forward” 選項,則動態分配的監聽端口會被打印在標準輸出上。

    -D [bind_address:]port

    指定本地“動態”應用程序級端口轉發。它的工作方式是分配一個套接字來監聽本地端口(可選綁定指定的 bind_address)。每當連接到此端口時,連接都通過安全通道進行轉發,然後使用應用程序協議確定將遠程計算機連接到何處。目前支持 SOCKS4 和 SOCKS5 協議,ssh 將充當 SOCKS 服務器。只有 root 用戶可以轉發特權端口。還可以在配置文件中指定動態端口轉發。

    IPv6 地址可以通過將地址括在方括號中來指定。只有超級用戶可以轉發特權端口。默認情況下,本地端口是根據 GatewayPorts 設置選項進行綁定的。但是,可以使用顯式的 bind_address 將連接綁定到特定的地址。bind_address 值為 “localhost” 時表示監聽端口僅綁定為本地使用,而空地址或 “*” 表示監聽所有網絡接口的此端口。

    1.2 ssh 端口轉發模式

    ssh 的端口轉發有三種模式:

    • 本地:ssh -C -f -N -g -L local_listen_port:remote_host:remote_port agent_user@agent_host

      將本地機監聽端口 local_listen_port 上的數據轉發到遠程端口 remote_host:remote_port

    • 遠程:ssh -C -f -N -g -R agent_listen_port:local_host:local_port agent_user@agent_host

      將代理機監聽端口 agent_listen_port 上的數據轉發到本地端口 local_host:local_port

    • 動態:ssh -C -f -N -g -D listen_port agent_user@agent_host

    2. 利用 ssh 隧道建立遠程調試環境

    組網環境下設備角色如下:

    代理機:把一個具有公網 IP 的中間服務器用作 ssh 代理,將這台代理機稱作代理 A(Agent)。

    目標機:把待調試的目標機器稱作目標機 T(Target)。目標機通常是待調試的設備,處於局域網內,外網無法直接訪問內網中的設備。

    本地機:把調試用的本地計算機稱作本地機 L(Local)。本地機通常也位於局域網內。

    L 和 T 無法互相訪問,但 L 和 T 都能訪問 A。我們將 T 通過 ssh 連接到A,將 L 也通過 ssh 連接到A,A 用於轉發數據,這樣就能使用本地計算機 L 來訪問遠端設備 R。

    2.1 目標機 T (sshc)

    2.1.1 shell 中 T 連接 A

    目標機 T 上的 sshc 連接代理機 A 上的 sshd:

    ssh -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126

    這條命令的作用:
    1. 建立一條 ssh 連接,T 上的 ssh 客戶端連接到 A 上的 ssh 服務器,A 的 IP 是 120.198.45.126,端口號是 10022,賬號是10022;
    2. 如果有其他 ssh 客戶端連接到了 A 的 10022 端口上,則 A 會將這條連接轉發到 T,T 在內部建立新的連接,連接到本機 22 端口。

    這條命令在 T 上執行。在 T 連接 A 這條命令里,T 是本地主機(local),A 是遠程主機(remote)。

    解釋一下此命令各選項:

    • -T 不分配偽終端;
    • -f 使 ssh 進程在用戶輸入密碼之後轉入後台運行;
    • -N 不執行遠程指令,即遠程主機(代理機A)不需執行指令,只作端口轉發;
    • -g 允許遠程主機(代理機A)連接到本地轉發端口;
    • -R 將遠程主機(代理機A)指定端口上的連接轉發到本機端口;
    • frank@120.198.45.126
      表示使用遠程主機 120.198.45.126 上的用戶 frank 來連接遠程主機;
    • :10022:127.0.0.1:22
      表示本機迴環接口(127.0.0.1,也可使用本機其他網絡接口的地址,比如以太網 IP 或 WiFi IP)的 22 端口連接到遠程主機的 10022 接口,因遠程主機 10022 綁定的地址為空,所以遠程主機會監聽其所有網絡接口的 10022 端口。

    在目標機 shell 中查看連接是否建立:

    root@localhost:~# ps | grep ssh
    22850 root      2492 S    ssh -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
    22894 root      3500 S    grep ssh

    在目標機 shell 中關閉 ssh 連接:

    kill -9 $(pidof ssh)

    此時在目標機 T 和代理機 A 中查看 ssh 連接信息,兩端都可以看到 ssh 連接不存在了。

    2.1.2 C 代碼中 T 連接 A 的處理

    C 代碼中主要還是調用 2.1.1 節中的命令。但是由 C 代碼編譯生成的進程無法在命令行和用戶進行交互,因此要避免交互問題。

    1. 避免首次連接時的 y/n(或yes/no) 詢問

    如果是首次登錄代理機 A,本機(目標機 T)沒有 A 的信息,需用用戶手動輸入 y 之後才能繼續。打印如下:

    root@localhost:~# ssh -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
    
    Host '120.198.45.126' is not in the trusted hosts file.
    (ssh-rsa fingerprint md5 86:09:0c:1b:fd:0b:02:8c:29:62:7f:ff:70:1b:64:f5)
    Do you want to continue connecting? (y/n) 

    如果 T 上有 A 的信息,可通過執行刪除操作:rm ~/.ssh/known_hosts 再進行上述測試。

    如果是在 C 代碼中執行登錄命令,進程在後台自動運行,是無法和用戶進行交互的。為了避免交互動作,應該禁止 ssh 發出 y/n 的詢問。

    如果 ssh 客戶端是 dropbear ssh,則添加 -y 參數,如下:

    ssh -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126

    如果 ssh 客戶端是 openssh,則添加 -o StrictHostKeyChecking=no 選項,如下:

    ssh -o "StrictHostKeyChecking no" -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126

    2. 避免輸入登錄密碼

    避免由用戶手動輸入登錄密碼有如下方法:

    1) 用 ssh-copy-id 把本地主機的公鑰複製到遠程主機的authorized_keys文件上,登錄不需要輸入密碼。
    2) 用 expect 調用 shell 腳本,向 shell 腳本發送密碼。這種方式是模擬鍵盤輸入。
    3) 如果是 openssh,則用 sshpass 向 ssh 命令行傳遞密碼。如果是 dropbear,則通過 DROPBEAR_PASSWORD 環境變量向 ssh 命令行傳遞密碼。

    我們採用第 3 種方法。

    假如代理機 A 上用戶 frank 密碼是 123456,則最終 C 代碼里應執行的指令如下:

    # openssh
    sshpass -p '123456' ssh -o "StrictHostKeyChecking no" -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
    
    # dropbear
    DROPBEAR_PASSWORD='123456' ssh -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126

    dropbear 無法接收 DROPBEAR_PASSWORD 變量傳遞密碼的處理方法:

    dropbear 包含 ssh 客戶端和 ssh 服務器,體積小巧,常用於嵌入式設備。dropbear ssh 無法接收 sshpass 傳入的密碼信息。但 dropbear ssh 可以通過環境變量 DROPBEAR_PASSWORD 傳入密碼信息。openwrt 從某一版開始,通過打補丁的方式禁用了 DROPBEAR_PASSWORD 選項,我們可以找到對應的補丁,開啟 DROPBEAR_PASSWORD 選項,再重新編譯生成 dropbear。如下:

    修改 dropbear patch 文件(如下路徑位於 openwrt 源碼根目錄):

    vim package/network/services/dropbear/patches/120-openwrt_options.patch

    將如下幾行刪除:

    @@ -226,7 +226,7 @@ much traffic. */
      * note that it will be provided for all "hidden" client-interactive
      * style prompts - if you want something more sophisticated, use 
      * SSH_ASKPASS instead. Comment out this var to remove this functionality.*/
    -#define DROPBEAR_PASSWORD_ENV "DROPBEAR_PASSWORD"
    +/*#define DROPBEAR_PASSWORD_ENV "DROPBEAR_PASSWORD"*/
     
     /* Define this (as well as ENABLE_CLI_PASSWORD_AUTH) to allow the use of
      * a helper program for the ssh client. The helper program should be

    重新編譯生成 dropbear,並替換設備里已安裝的 dropbear。

    #define DEFAULT_SSH_AGENT_HOST      "120.198.45.126"
    #define DEFAULT_SSH_AGENT_PORT      "10022"
    #define DEFAULT_SSH_AGENT_USER      "ssha_debug"
    #define DEFAULT_SSH_AGENT_PASSWD    "220011ssha"
    int login_to_ssh_agent(const char *host, const char *port, const char *user, const char *passwd)
    {
        // openssh client:
        // sshpass -p '123456' ssh -o "StrictHostKeyChecking no" -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
    
        // dropbear ssh clent:
        // DROPBEAR_PASSWORD='123456' ssh -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
    
        char cmd[256];
        snprintf(cmd, sizeof(cmd), "DROPBEAR_PASSWORD='%s' ssh -y -T -f -N -g -R :%s:127.0.0.1:22 %s@%s", 
                 (passwd != NULL) ? passwd : DEFAULT_SSH_AGENT_PASSWD, 
                 (port != NULL) ? port : DEFAULT_SSH_AGENT_PORT,
                 (user != NULL) ? user : DEFAULT_SSH_AGENT_USER,
                 (host != NULL) ? host : DEFAULT_SSH_AGENT_HOST);
        printf("login to ssh agent: \n%s\n", cmd);
        system(cmd);
    
        return 0;
    }

    2.2 代理機 A (sshd)

    在 /etc/ssh/sshd_config 中添加如下幾行后重啟 ssh 服務:

    GatewayPorts yes
    UseDNS no
    GSSAPIAuthentication no

    目標機 T 發起連接后,在代理機 A 上查詢目標機 T 是否連接成功:

    sudo netstat -anp | grep 10022

    打印形如:

    tcp        0      0 0.0.0.0:10022           0.0.0.0:*               LISTEN      8264/sshd: frank
    tcp6       0      0 :::10022                :::*                    LISTEN      8264/sshd: frank

    上述打印中,8264 就是和目標機 T 保持連接的 sshd 進程號,如需關閉當前連接重新建立一個新的連接,則先在代理機 A 上執行:

    kill -9 8264

    然後再執行 2.1 節的指令,就會建立一次新的代理連接。

    為了安全,我們可以專門新建一個用戶,僅用於 ssh 端口轉發功能,不能在 shell 中使用此用戶登錄。如下創建一個 ssha_debug 的用戶,無 shell 登錄權限。然後為此用戶創建密碼。注意系統中 nologin 文件的位置,不同系統可能路徑不同。

    sudo useradd ssha_debug -M -s /usr/sbin/nologin
    sudo passwd ssha_debug

    2.3 本地機 L (sshc)

    2.3.1 本地機 L 登錄目標機 T

    有三種方式:

    1. 在本地機 L 上通過 ssh 登錄代理機 A,在 A 的 shell 中再登錄目標機 T

    代理服務器的公網 ip 是 120.198.45.126,內網 ip 是 192.168.1.102。

    1) 先使用 ssh(SecureCRT 或 OpenSSH 命令行) 登錄上代理服務器的 shell。如果調試機在內網,既可登錄代理機的外網 ip,也可登錄其內網 ip。

    2) 在代理機的 shell 中執行如下命令登錄遠程設備:

    ssh -p 10022 root@127.0.0.1 -vvv

    注意,此命令中用戶 root 及其密碼是遠程設備上的賬戶。

    如果提示 Host key 認證失敗之類的信息,請按提示執行如下命令:

    ssh-keygen -f "/home/frank/.ssh/known_hosts" -R [127.0.0.1]:10022

    也可直接刪除當前用戶目錄下的 .ssh/known_hosts 文件。
    然後重新執行登錄設備操作。

    建議優先使用此方法。

    2. 在本地機 L 上使用 ssh 命令登錄目標機 T

    Win 10 系統默認安裝有 OpenSSH 客戶端。可以在調試機 Windows 命令行中執行:

    ssh -p 10022 root@120.198.45.126 -vvv

    對於本地計算機來說,待調試的設備 ip 地址不可見。本機登錄到代理機 120.198.45.126 的轉發端口 10022,通過代理機轉發功能,本地機能成功登錄到遠程設備上。注意,此命令中用戶 root 及其密碼是設備上的賬戶,不是 SSH 代理服務器上的賬戶。

    如果出現認證失敗之類的信息。可刪除 C:/Users/當前用戶/.ssh/known_hosts 文件,然後再試。

    3. 在本地機 L 上使用 SecureCRT 工具登錄目標機 T

    也可以直接使用 SecureCRT 軟件,設置好代理機的 ip(120.198.45.126) 和端口號(10022),填上設備的登錄用戶和登錄密碼。

    不建議使用此方法。因為連接過程太長或連接失敗的話,無法看到錯誤提示信息。

    2.3.2 查看代理機 A 打印信息

    在 L 執行登錄 T 之前查看打印信息:

    frank@SERVER:~$ sudo netstat -anp  | grep 10022
    tcp        0      0 0.0.0.0:10022           0.0.0.0:*               LISTEN      106438/sshd: frank
    tcp6       0      0 :::10022                :::*                    LISTEN      106438/sshd: frank

    在 L 執行登錄 T 之後查看打印信息:

    frank@SERVER:~$ sudo netstat -anp  | grep 10022
    tcp        0      0 0.0.0.0:10022           0.0.0.0:*               LISTEN      106438/sshd: frank
    tcp        0      0 192.168.1.102:10022     120.229.163.51:27027    ESTABLISHED 106438/sshd: frank
    tcp6       0      0 :::10022                :::*                    LISTEN      106438/sshd: frank

    可以看到,上述第二行是 L 執行登錄命令后新出現的打印信息。表示新建立了一條 L 到 A 的 ssh 連接。

    L 端的外網地址 120.229.163.51:27027 連接到 A 上的 192.168.1.102:10022,L 通常位於局域網內、具有一個內網地址,120.229.163.51 可能是 L 連接的路由器的公網 IP。

    這條連接建立后,A 將這條連接轉發到 R。

    2.3.3 查看目標機 T 打印信息

    在 L 執行登錄 T 之前查看打印信息:

    root@localhost:~# netstat -anp | grep 22
    tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      917/sshd
    tcp        0      0 192.168.202.140:47989   120.198.45.126:22       ESTABLISHED 9452/ssh
    tcp        0      0 192.168.202.140:22      192.168.202.100:64737   ESTABLISHED 2041/sshd
    tcp        0      0 :::22                   :::*                    LISTEN      917/sshd

    在 L 執行登錄 T 之後查看打印信息:

    root@localhost:~# netstat -anp | grep 22
    tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      917/sshd
    tcp        0      0 192.168.202.140:47989   120.198.45.126:22       ESTABLISHED 9452/ssh
    tcp        0      0 192.168.202.140:51732   192.168.202.140:22      ESTABLISHED 9452/ssh
    tcp        0      0 192.168.202.140:22      192.168.202.140:51732   ESTABLISHED 9579/sshd
    tcp        0      0 :::22                   :::*                    LISTEN      917/sshd

    可以看到,上述第 3 行和第 4 行是登錄之後新增加的打印信息。

    第 2 行,表示 T 上的 ssh 客戶端連接到了 A 上的 ssh 服務端,進程號是 9452。第 3 行,表示進程 9452 收到了 A 轉發來的 ssh 連接后,在本機內部建立新的 ssh 連接,使用 51732 端口號作為 ssh 客戶端,連接到本機 22 端口,22 端口是 sshd 端口。第 4 行,表示本機新啟動一個 sshd 進程,來接收 sshc 的連接。

    這樣,L 到 T 的 ssh 通路徹底打通了。A 將來自 L 的連接轉發到 R,R 在內部啟動了 sshd 來處理來自 L 的請求,通過 A 的代理作用,實現了 L 上的 sshc 和 T 上的 sshd 的交互。

    3. 典型使用場景步驟總結

    上文已涵蓋詳細使用方法,但篇幅太長。此處簡單總結使用步驟如下:

    3.1 在代理機 A 上執行

    使用 SecureCRT 登錄代理機 A。代理機外網 ip 120.198.45.126,內網 ip 192.168.1.102,端口 22。如果本地機與代理機在同一個局域網裡,使用代理機的內網 ip 登錄即可。

    在代理機 shell 中查看是否有未關閉的 ssh 隧道:

    sudo netstat -anp | grep 10022

    若打印形如:

    tcp        0      0 0.0.0.0:10022           0.0.0.0:*               LISTEN      8264/sshd: frank
    tcp6       0      0 :::10022                :::*                    LISTEN      8264/sshd: frank

    則表示有未關閉的 ssh 隧道連接。執行如下命令可關閉連接。

    kill -9 8264

    3.2 在目標機 T 上執行

    使用遠程應用程序接口或者在遠程設備 T 上做一些特殊操作,觸發 T 執行如下兩條指令之一:

    # openssh
    sshpass -p '123456' ssh -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
    
    # dropbear
    DROPBEAR_PASSWORD='123456' ssh -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126

    3.3 在本地機 L 上執行

    在本地機 L 上執行如下指令,登錄遠程目標機 T:

    ssh -vvv -p 10022 root@120.198.45.126

    另外一種變通的方式是,在本地機先通過 ssh 登錄上代理機 A 的 shell。然後在 A 的 shell 中執行如下指令:

    ssh -vvv -p 10022 root@127.0.0.1

    4. 注意事項

    1. 確保代理機 A 所在的網絡防火牆不屏蔽 10022 端口
    2. 確保代理機 A 上 /etc/ssh/sshd_config 配置文件設置正確
    3. 關閉 ssh 隧道既可在代理機 A 上進行(關閉相應的 sshd 進程),也可在目標機 T 上進行(關閉相應的 ssh 進程)
    4. 每次只能訪問一台目標機。如果想同時訪問多台,可以代理機上設置多個轉發端口,每條連接使用一個端口進行轉發
    5. 為保證安全,打開 ssh 隧道時盡量使用無登錄權限的用戶,並且此用戶的密碼建議經常更新

    5. 參考資料

    [1] 阮一峰,
    [2]
    [3]

    6. 修改記錄

    2019-11-20 V1.0 初稿

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

    【其他文章推薦】

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    台灣海運大陸貨務運送流程

    兩岸物流進出口一站式服務

  • 技術人如何利用 github+Jekyll ,搭建一個獨立免費的技術博客

    技術人如何利用 github+Jekyll ,搭建一個獨立免費的技術博客

    上次有人留言說,技術博客是程序員的標配,但據我所知絕大部分技術同學到現在仍然沒有自己的技術博客。原因有很多,有的是懶的寫,有的是怕寫不好,還有的是一直想憋個大招,幻想做到完美再發出來,結果一直胎死腹中。但其實更多程序員是不知道如何去搭建一個博客,其實如今搭建一個個人技術博客非常簡單,其中最簡單搭建方式莫屬使用 GitHub Pages + Jekyll 了,我的博客就是使用這種技術。

    GitHub Pages

    Github Pages 是面向用戶、組織和項目開放的公共靜態頁面搭建託管服務,站點可以被免費託管在 Github 上,你可以選擇使用 Github Pages 默認提供的域名 github.io 或者自定義域名來發布站點。Github Pages 支持 自動利用 Jekyll 生成站點,也同樣支持純 HTML 文檔,將你的 Jekyll 站點託管在 Github Pages 上是一個不錯的選擇。

    使用 Github Pages 搭建博客有以下幾個優點:

    • 完全免費,其中服務器、流量、域名什麼的都管,完全零費用搭建一個技術博客
    • 寫博客就是提交代碼,讓寫作和編程的體驗保持一致
    • 支持綁定自己的域名
    • 提供流行的網頁主題模板

    缺點也是有的:

    • 不支持動態內容,博客必須都是靜態網頁,一般會使用 Jekyll 來構建內容。
    • 博客不能被百度索引,因 Github 和百度有過節,所以 Github 就把百度給屏蔽了。
    • 倉庫空間不大於1G
    • 每個月的流量不超過100G
    • 每小時更新不超過 10 次

    Github Pages 使用 Jekyll 來構建內容,那麼 Jekyll 是什麼呢?

    Jekyll 介紹

    Jekyll 是一個簡單的博客形態的靜態站點生產機器。它有一個模版目錄,其中包含原始文本格式的文檔,通過一個轉換器(如 Markdown)和我們的 Liquid 渲染器轉化成一個完整的可發布的靜態網站,你可以發布在任何你喜愛的服務器上。Jekyll 也可以運行在 GitHub Page 上,也就是說,你可以使用 GitHub 的服務來搭建你的項目頁面、博客或者網站,而且是完全免費的。

    但如果我們只是在 GitHub 上面使用的話,到不需要知道 Jekyll 的語法,一般 Github 會自動將我們寫的 Markdown 文件轉換成靜態頁面。使用 Jekyll 需要使用 Markdown 語法來寫你的文章,不過 Markdown 語法非常簡單,做為程序員來講基本上兩三天就掌握了,大家也可以參考這篇文章:。

    給大家分享一些 Jekyll 主題,這個網站下有很多 主題,大家可以根據自己的愛好去選擇博客主題。

    我的個人博客

    我的博客經過了三個階段,第一個階段,完全依託於使用 GitHub Pages 來構建;第二個階段,將博客託管於國外的一個服務商;第三個階段,服務器遷移回到國內、域名備案。之前也寫過幾篇關於技術博客的文章,如下:

    使用 Github Pages + Jekyll 構建一個技術博客很簡單,基本上步驟就是網上找一個自己喜歡的主題,直接 Fork 到自己的 Github ,然後在刪掉原博客中的內容,在上傳自己的文章即可,以我自己的博客為例。

    我的博客最初使用的是,但這個主題已經盡兩年多都沒有更新了。因此後期我在這個主題的基礎上做了一些改動,其中有依賴組件的更新,結合個人情況對個別頁面進行了改版,就成為了現在的樣子:

    使用這個主題的原因是,我比較喜歡簡潔大氣的風格,並且此博客主題對代碼展示支持良好。

    快速構建一個博客

    以我的博客為例,介紹如何最快搭建一個博客。這也是我博客經歷的第一個階段。

    1、首先打開地址,點擊 Fork 按鈕將代碼複製一份到自己的倉庫。

    過上一分鐘,你的 github 倉庫發現一個 ityouknow.github.io 項目。

    2、刪除 CNAME 文件

    刪除項目中的 CNAME 文件,CNAME 是定製域名的時候使用的內容,如果不使用定製域名會存在衝突。

    3、設置 GitHub Pages

    點擊 Settings 按鈕打開設置頁面,頁面往下拉到 GitHub Pages 相關設置,在 Source 下面的複選框中選擇 master branch ,然後點擊旁邊的 Save 按鈕保存設置。

    4、重命名項目

    點擊 Settings 按鈕打開設置頁面,重命名項目名稱為:github_username.github.io。

    github_username 是你的 github 登錄用戶名

    5、重命名之後,再次回到 Settings > GitHub Pages 頁面

    會發現存在這樣一個地址:

    這個時候,你訪問此地址已經可以看到博客的首頁,但是點擊文章的時鏈接跳轉地址不對,這是因為少配置了一個文件。

    6、配置 _config.yml

    打開項目目錄下的 _config.yml 文件,修改以下配置:

    repository: github_username/github_username.github.io
    github_url: https://github.com/github_username
    url: https://github_username.github.io

    這時候在訪問地址: https://github_username.github.io,就會發現博客就已經構建完成了。剩下的事情就是去項目的 _posts 目錄下刪除掉我的文章,然後按照 Jekyll 的語法就寫自己的文章就好了。

    github_username 為你的 github id。

    自定義域名

    雖然通過地址https://github_username.github.io可以正常訪問博客,但是技術小夥伴們肯定有人想使用自己的域名訪問博客,這樣的需求 GitHub Pages 也是支持的。

    首先需要設置域名解析,將域名的地址指向自己的 github 博客地址。這裏以萬網的域名配置為例,選擇需要設置的域名點擊解析,在域名解析頁面添加以下兩條記錄

    紅框內,需要填寫自己github_username值。

    然後重新打開項目的 Settings > GitHub Pages 頁面,Custom domain 下的輸入框輸入剛才設置的域名:xxx.com,點擊保存即可。

    重新配置 _config.yml

    打開項目目錄下的 _config.yml 文件,修改以下配置:

    url: http://www.xxx.com

    等待一分鐘之後,瀏覽器訪問地址:www.xxx.com 即可訪問博客。

    自定義 DIY 博客

    一般同學到上面這一步也就完成了,基本滿足了 80% 技術同學的需求。但還是有一些同學們有更高的追求,比如說使用 Github Pages 雖然簡單方便,但是不能被百度檢索白白流失了大量的流量,還有一個原因有些時候,博客網絡訪問穩定性不是很高。

    當時我在國外有幾個虛擬機,本來用作它用,後來在上面安裝了一個 Nginx 作為靜態頁面的服務器。首先我在本機(win10)安裝了 Jekyll 環境,將 Github 上的博客代碼下載下來之後,在本機編譯成靜態的 Html ,然後手動上傳到服務的 Nginx 目錄下;然後將域名指向虛擬機。

    非常不建議大家實踐以上這段內容,win10 上面安裝 Jekyll 環境是一段慘痛的經歷。

    就這樣很麻煩的步驟我用了幾個月後,實在是受不了了,一方面因為服務器在國外,有時候仍然不穩定(可能因為服務器安裝了代理),另一方面我需要使用一些功能,使用這些功能的前提是網站需要備案,那段時間騰訊雲在做活動,就把博客又從國外搬了回來,順便重新優化了一下流程。

    仍然把博客託管在 Github 上面,每次提交完代碼后,在騰訊雲上面執行一個腳本,這個腳本會自動從 Github 拉取最新更新的文件,並自動生產靜態的 Html 文件推送到 Nginx 目錄,域名重新指向這台服務器。可以在 Github 上面設置一些鈎子,當提交代碼的時候自動觸髮腳本,也可以定時觸髮腳本來發布文章。

    腳本內容如下:

    cd /usr/local/ityouknow.github.io
    git pull http://github.com/ityouknow/ityouknow.github.io.git
    jekyll build --destination=/usr/share/nginx/html

    執行此腳本的前提是安裝好 git\jekyll 環境,這個網上有很多案例,這裏就不再多描述了。
    關於 Jekyll 環境搭建和使用可以參考這裏:

    自動化部署

    這两天看到,我也按照他的步驟實踐了一番,很好用,所以把自動化部署這段寫補上。

    配置 Webhook

    在開發過程中的 Webhook,是一種通過通常的 callback,去增加或者改變 Web page或者 Web app 行為的方法。這些 Callback 可以由第三方用戶和開發者維持當前,修改,管理,而這些使用者與網站或者應用的原始開發沒有關聯。Webhook 這個詞是由 Jeff Lindsay 在 2007 年在計算機科學 hook 項目第一次提出的。

    用大白話講就是,代碼倉庫在收到代碼提交的時候,會自動觸發一個 url 類型的通知,你可以根據這個通知去做一些事情,比如提交了代碼就自動去部署項目。

    我們的自動部署博客也是利用了這個機制,Github 自帶了 Webhook 功能,我們直接配置即可使用。

    在 Github 倉庫的項目界面,比如本博客項目 https://github.com/ityouknow/ityouknow.github.io,點擊 Setting->Webhooks->Add Webhook,在添加 Webhooks 的配置信息,我的配置信息如下:

    Payload URL: http://www.ityouknow.com/deploy
    Content type: application/json
    Secret: 123456

    如下圖:

    服務器接受推送

    我們需要在博客的服務器上面建立一個服務,來接收 Github 提交代碼后的推送,從而來觸發部署的腳本。 Github 上有一個開源項目可以做這個事情 。

    這個開源項目目的很單純,就是負責接收 Github 推送過來的通知,然後執行部署腳本,不過他是使用 NodeJs 來開發的,所以我們先需要在 Centos 上面按照 Node 環境。

    centos7 安裝 Node 環境

    首先添加源

    sudo rpm -ivh https://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-7-11.noarch.rpm
    
    //yum安裝node.js
    yum install -y nodejs

    然後在安裝 github-webhook-handler

    npm install -g github-webhook-handler     #安裝 github-webhook-handler
    #如果沒有安裝成功,可以選擇法2來安裝
    npm install -g cnpm --registry=http://r.cnpmjs.org
    cnpm install -g github-webhook-handler

    安裝成功之後,我們需要添加一個腳本。進入到安裝目錄下:

    cd  /usr/lib/node_modules/github-webhook-handler

    新建 deploy.js

    vi deploy.js

    腳本內容如下:

    var http = require('http')
    var createHandler = require('github-webhook-handler')
    var handler = createHandler({ path: '/deploy', secret: 'ityouknow' }) //監聽請求路徑,和Github 配置的密碼
     
    function run_cmd(cmd, args, callback) {
      var spawn = require('child_process').spawn;
      var child = spawn(cmd, args);
      var resp = "";
     
      child.stdout.on('data', function(buffer) { resp += buffer.toString(); });
      child.stdout.on('end', function() { callback (resp) });
    }
     
    http.createServer(function (req, res) {
      handler(req, res, function (err) {
        res.statusCode = 404
        res.end('no such location')
      })
    }).listen(3006)//監聽的端口
     
    handler.on('error', function (err) {
      console.error('Error:', err.message)
    })
     
    handler.on('push', function (event) {
      console.log('Received a push event for %s to %s',
        event.payload.repository.name,
        event.payload.ref);
      run_cmd('sh', ['/usr/local/depoly.sh'], function(text){ console.log(text) });//成功后,執行的腳本。
    })

    腳本的作業就是啟動一個監聽端口來接收請求,接收到請求后執行部署腳本,腳本內容的關鍵點已經標註上註釋。

    部署博客的腳本如下:depoly.sh

    echo `date`
    cd /usr/local/ityouknow.github.io
    echo start pull from github 
    git pull http://github.com/ityouknow/ityouknow.github.io.git
    echo start build..
    jekyll build --destination=/usr/share/nginx/html

    就是拉取代碼,進行部署而已。

    這個腳本的啟動需要藉助 Node 中的一個管理 forever 。forever 可以看做是一個 nodejs 的守護進程,能夠啟動,停止,重啟我們的 app 應用。

    不過我們的先安裝 forever,然後需要使用 forever 來啟動 deploy.js 的服務,執行命令如下:

    npm install forever -g   #安裝
    $ forever start deploy.js          #啟動
    $ forever stop deploy.js           #關閉
    $ forever start -l forever.log -o out.log -e err.log deploy.js   #輸出日誌和錯誤
    /root/node-v8.12.0-linux-x64/lib/node_modules/forever/bin/forever start -l forever.log -o out.log -e err.log deploy.js
    
    如果報錯:
    /root/node-v8.12.0-linux-x64/lib/node_modules/forever/bin/forever start -a -l forever.log -o out.log -e err.log deploy.js

    同時一般情況下,我們不會對外保留很多端口,所以需要通過博客的地址來轉發,需要在 Nginx 上面添加一個轉發配置,用來監聽的 /deploy 請求轉發到 nodejs 服務上,配置代碼如下:

    location = /deploy {
         proxy_pass http://127.0.0.1:3006/deploy;
    }

    這樣我們整個自動化部署就完了,每次提交代碼時,Github 會發送 Webhook 給地址http://www.ityouknow.com/deploy,Nginx 將 /deploy 地址轉發給 Nodejs 端口為 3306 的服務,最後通過 github-webhook-handler 來執行部署腳本,已到達自動部署的目的。

    以後只需要我們提交代碼到 Github ,就會自動觸發博客的自動化部署。

    可能會出現的問題

    有一些小夥伴反饋在克隆博客的時候出現了一些問題,在這裏集中回復一下。

    1、克隆博客后格式丟失

    這是很多讀者反饋的第一個問題,因為我的博客 css 和 圖片是放到另外一個域名下的:www.itmind.net ,因此這塊大家克隆過去需要改成本地的。

    主要涉及的文件 ityouknow.github.io\_includes 目錄下 head.html 和 footer.html 兩個文件夾,將文件中的 http://www.ityouknow.com/xxx/xxx 改為相對路徑/xxx/xxx即可。

    2、留言功能丟失

    這裏就需要大家修改一下 _config.yml 中 gitalk 的配置信息。具體如何操作大家可以參考這篇文章 。註冊完之後,需要在 _config.yml 配置以下信息:

    gitalk:
        owner: ityouknow
        repo: blog-comments
        clientID: 61bfc53d957e74e78f8f
        clientSecret: 31c61e66cdcc9ada8db2267ee779d0bdafac434c

    將這裏改成你註冊好的信息

    3、博客

    博客現在還缺檢索功能,下一頁和上一頁功能、系列文章優化查看的功能,大家克隆後有完善功能的,也請幫忙留意,共同把這個博客完善的更好。

    最後,大家可以在這篇文章下留下你的個人博客地址,方便同行們觀賞、交流、學習。

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

    【其他文章推薦】

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    台灣海運大陸貨務運送流程

    兩岸物流進出口一站式服務