機器學習實記(一)簡介機器學習

一.寫在前面

  本系列中的內容來自對李宏毅老師的機器學習教程的理解和一些個人理解的筆記,主要用於之後方便查看複習。李宏毅老師的機器學習教程地址:https://www.bilibili.com/video/av35932863?from=search&seid=3902369523652897681。

二.機器學習概念

2.1人工智能、機器學習、深度學習這幾個概念的關係

  要想理解清楚機器學習的基本概念,首先要明白機器學習在人工智能領域中所處的位置,再簡單明了些首先要明白人工智能、機器學習、深度學習這幾個概念的關係。三者關係如下圖2-1

                      圖2-1 人工智能、機器學習、深度學習三者關係

從圖可以直觀的看出,人工智能的範圍>機器學習的範圍>深度學習的範圍,而平時常說的神經網絡的這些有關概念則是屬於深度學習下的分支。再具體而言這三者的關係,其實人工智能是作為我們最終所要達到的目標,所謂的人工智能概念我覺的用之前某科學家提過的一種說法簡單理解一下就行,即當用一塊黑幕將計算機遮蓋起來,黑幕外的人無法區分所交互的對象是人還是計算機就算是達到了人工智能標準,再簡單點說就是使計算機做到過去只有人才能做到的事,其他更為深入的解釋歡迎查看百度詞條https://baike.baidu.com/item/人工智能/9180?fr=aladdin。而機器學習則是實現人工智能這一目標的手段,這說明還有其他手段有興趣的同學可以自行去了解一下,不過個人認為其餘的手段不是現在的主流研究方向吧。而深度學習則是作為機器學習下的一個重要分支。

2.2 理解機器學習

  所謂機器學習概念,簡單的理解就是教會計算機學習,舉個具體的例子,當你給它看完貓的某一張圖片后,你告訴機器這是貓,當你給它看完一張狗的圖片后,你告訴它這是狗,不斷重複這一過程讓機器學習大量不同的貓狗照片,學習結束后給予機器一張全新的貓或狗的照片,機器能夠成功的識別出貓和狗。再進一步抽象的理解,我們可以發現這一過程十分類似於數學上求解函數表達式,即這個過程其實是在尋找一個函數f,當輸入某張貓的照片函數f的輸出為貓,當輸入某張狗的照片函數f的輸出為狗。再進一步具體而言整個機器學習框架可以理解為如圖2-2

                                                                            圖2-2 機器學習框架描述

整個機器學習框架的步驟大體上分為三步:第一步定義一個函數池,其中有大量的備選函數f1、f2、f3…..fn;第二步對各個函數進行評價,這裏的話其實我們可以將每個函數理解為一個模型,所謂的評價的標準可以理解為對每個輸入照片各個模型所能識別出來的精確度,優秀的模型識別照片的準確度更高;第三部選出最優的函數fbest,即選出最優的模型。

2.3 理解機器學習的學習圖

  機器學習的學習圖如圖2-3,這張圖看起來很複雜,其實結構很清晰,主要分為三中顏色藍、橙、綠三種顏色,分別代表運用情景,所要解決問題的目標,以及解決問題用到的方法。

                           圖2-3 機器學習學習圖

  首先是藍色部分,即運用情景從圖中可以看出有supervise learning、semi-supervise learning、transer learning、unsupervised learning、reinforcement learning,看到這麼多運用情景不經想問一個問題,為什麼會有這麼多運用場景的劃分?原因其實很簡單,從前面的對機器學習的介紹中我們可以發現,機器學習的過程中是需要大量的帶標籤的數據,所謂帶標籤的數據就是指給出了輸入數據的正確結果,比如說輸入一張貓的圖片,這個貓就是這張圖片的標籤,但其實當數據量巨大的時候,給每個數據標上標籤是需要消耗大量時間的,所以由輸入的數據是否帶有標籤就產生了不同的運用場景。

  supervise learning 即監督學習,訓練所使用的數據均帶標籤,即告訴機器輸入數據的正確答案。semi-supervise learning 即半監督學習,訓練中的數據有一部分帶標籤,其餘數據不帶標籤交給機器自己學習。transer learning 即遷移學習 訓練數據中有一部分帶標籤,剩下的數據來自其他模型帶標籤或不帶標籤的數據,舉個例子我們做貓和狗的分類,我們有少量的已經有標籤的貓和狗的數據,還有大量的可能是帶標籤或不帶標籤的其他動物的數據,將這些數據也交由機器學習。unsupervised learning 即無監督學習,訓練中的數據均不帶標籤。reinforcement learning 即強化學習,這種學習方式與前面的學習方式均有所不同,前面的學習方式中均為告訴機器輸入數據的正確答案或者直接將數據交由機器,在強化學習中數據均不帶標籤,它是通評價告訴機器這次學習的結果,舉個例子阿爾法狗,當它完成一次棋局對戰最終取得勝利,則給與機器較高的評價,讓機器自身進行調整,雖然機器本身並不知道自己到底哪下的好,但知道這麼下贏了。

  其次是橙色部分,即所要解決問題的目標,分為regression和classification以及structured learning。regression即所要解決的問題的解是個數值,比方說預測某日pm2.5的值;classification即所要解決的問題為分類問題,包括二分類即回答是或者否,比方說判斷郵件是否為垃圾郵件,多分類對輸入的數據進行多個類別分類,比方說判斷輸入的文章屬於哪個板塊是娛樂版塊還是體育板塊還是金融板塊等等;structured learning即所要解決的問題是結構化的,比方說輸入一段語言判斷語言的內容。

  最後是綠色部分,這一塊的話還記的我在2.2中說過的函數池嗎,所謂的函數其實就是模型,我們可以使用不同的模型來解決不同的問題,這些模型有linear model即線性模型、non-linear model 即非線性模型,非線性模型中就包括之前提到的深度學習還有一些非線性模型。

三.寫在最後

  這一部分主要是對機器學習要有個整體的大概理解,包括理解機器學習的整體框架的大致模樣,尤其是要明白框架中所說的函數其實就是模型的概念,再由就是要對為何要這樣劃分學習圖有自己的理解,至於是否要記住學習圖的內容倒是其次。

【精選推薦文章】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

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

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

【工具篇】在.Net中實現HTML生成圖片或PDF的幾種方式

前段時間由於項目上的需求,要在.Net平台下實現把HTML內容生成圖片或PDF文件的功能,特意在網上研究了幾種方案,這裏記錄一下以備日後再次使用。當時想着找一種開發部署都比較清爽並且運行穩定的方案,但實際上兩者同時滿足基本不可能,只能做一個自己覺得合適的取捨,下面從兩個維度(清爽指數和功能指數)逐一對比。


1.   WebBrowser

這種方案在開發時不依賴任務外部程序集和nuget包,部署時也不需要安裝額外的工具和服務,可以說是非常清爽了。它藉助了WinForm下的WebBrowser控件實現HTML內容渲染,並把渲染結果繪製在Bitmap中,進而保存成圖片或PDF文件。這種方案簡單粗暴,是C#中最基礎的實現方式,也是網上搜索結果最多的一種,下面看它的核心代碼(從網上拼湊得來):

 1     class WebBrowserPage2Image
 2     {
 3         Bitmap m_Bitmap;
 4 
 5         string m_Url;               
 6 
 7         public void Convert(string pageUrl, string fileName)
 8         {
 9             m_Url = pageUrl;
10             Thread m_thread = new Thread(new ThreadStart(HtmlDrawToBitmap));
11             m_thread.SetApartmentState(ApartmentState.STA);
12             m_thread.IsBackground = true;
13             m_thread.Start();
14             m_thread.Join();
15             MemoryStream stream = new MemoryStream();
16             m_Bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
17             byte[] buff = stream.ToArray();
18             FileStream fs = new FileStream(fileName, FileMode.Create);
19             stream.WriteTo(fs);
20             stream.Dispose();
21             stream.Close();
22             fs.Close();
23         }
24 
25         private void HtmlDrawToBitmap()
26         {
27             WebBrowser browser = new WebBrowser();
28             browser.ScrollBarsEnabled = false;
29             browser.Navigate(m_Url);
30             browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(delegate (object sender, WebBrowserDocumentCompletedEventArgs bdce)
31             {
32                 if (browser.ReadyState == WebBrowserReadyState.Complete)
33                 {
34                     //myWebBrowser.Document.Body.Style = "zoom:180%";
35                     Rectangle r = browser.Document.Body.ScrollRectangle;
36                     browser.Height = r.Height;
37                     browser.Width = r.Width;
38                     m_Bitmap = new Bitmap(browser.Width, browser.Height);
39                     browser.BringToFront();
40                     browser.DrawToBitmap(m_Bitmap, new Rectangle() { Width = browser.Width, Height = browser.Height });
41                 }
42             });
43             while (browser.ReadyState != WebBrowserReadyState.Complete)
44             {
45                 Application.DoEvents();
46             }
47             browser.Dispose();
48         }
49     }

View Code

雖然開發起來非常簡潔,但是問題也很明顯。WebBrowser是Winform下的一個組件,在非Winform項目中運行會出現不可知的異常,即使在Winform項目中,數據量比較大的時候依然會出現卡死的情況。我做過500次循環的測試,在執行到100多次的時候程序出現假死不動也無異常拋出。除此之外,生成的圖片失真也比較嚴重,特殊字體和部分CSS樣式無法渲染。總的來說,基本無法達到生成環境需求。

清爽指數:★★★★★    功能指數:★


2.         Wkhtmltox

這也是網上廣泛流傳的一個方案,wkhtmltox是一套開源的命令行工具,提供了圖片和PDF的轉換能力,它採用C++編寫,使用Webkit作為渲染引擎,開源地址是https://github.com/wkhtmltopdf/wkhtmltopdf。使用方法就是在命令行工具中執行命令,例如:

wkhtmltopdf --grayscale  https://www.baidu.com  baidu.pdf

如果要在.Net項目中使用的話,核心問題就是用程序喚起命令行,同時指定參數執行即可,類似於下面的代碼:

       System.Diagnostics.ProcessStartInfo Info = new System.Diagnostics.ProcessStartInfo();
       Info.FileName = @"D:\dev\wkhtmltox\bin\wkhtmltopdf.exe";
       Info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
       Info.CreateNoWindow = true;
       Info.Arguments = @"-q --orientation Landscape https://www.baidu.com D:\\baidu.pdf";
       System.Diagnostics.Process proc = System.Diagnostics.Process.Start(Info);
       proc.WaitForExit();
       proc.Close();

更多強大的功能例如加水印、分頁、改樣式等可以參考這篇文章:https://www.cnblogs.com/82xb/p/7837597.html

詳細的參數說明可以查看文檔:https://wkhtmltopdf.org/usage/wkhtmltopdf.txt

GitHub上有很多針對各個開發語言的封裝,使用起來比較方便,唯一不爽的是部署項目前要先安裝好這個工具。

清爽指數:★★★★    功能指數:★★★★


3.         PuppeteerSharp

這個就更厲害了,說到這個就不得不先介紹下Puppeteer,因為PuppeteerSharp正是從Puppeteer衍生而來。

Puppeteer是由谷歌開源的一個Node項目,它提供了和Chrome DevTools的通信能力,基本上我們能在Chrome實現的操作通過它的API都可以實現,強大到讓你不敢相信。主要的應用有:

  • 生成頁面快照(圖片、PDF)
  • 爬蟲,網站內容抓取
  • 自動化測試(模擬鍵盤鼠標輸入,表單提交,UI測試等)
  • 網站性能分析(追蹤,時間線捕獲等)

開源地址是https://github.com/GoogleChrome/puppeteer

在Node項目中使用Puppeteer非常簡單,先安裝npm包:

npm i puppeteer

安裝過程可能會有點慢,因為在安裝的時候會下載一個最近版本的Chromium(Mac下大概170M,Linux下大概282M,Windows下大概280M)。當然,如果你本地已經有一個Chromium,可以設置npm的全局配置PUPPETEER_SKIP_CHROMIUM_DOWNLOAD 跳過下載,然後在程序中手動指定Chromium的位置。

生成圖片和PDF文件例子:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://www.baidu.com');
  await page.screenshot({path: 'baidu.png'});
  await page.pdf({path: 'baidu.pdf', format: 'A4'});
  await browser.close();
})(); 

Puppeteer默認使用無界面模式(headless:true),如果想看到完整的瀏覽器界面,可以通過下面的設置開啟:

  const browser = await puppeteer.launch({headless: false});

Puppeteer提供了豐富的選擇器接口,可以輕鬆實現模擬輸入和鼠標點擊,例如:

  await page.type('#index-kw', 'cnblogs');
  await page.click('#index-bn');

      還支持指定使用設備:

  const devices = require('puppeteer/DeviceDescriptors');
  await page.emulate(devices['iPhone 8']);

      詳細的API文檔可以參考:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md

Puppeteer確實非常強大,但由於它是一個Node包無法直接在C#項目中使用,那怎麼辦呢?好在有國外的大神把Puppeteer移植到了.Net平台,也就是PuppeteerSharp。

注意:PuppeteerSharp是基於NetStandard 2.0開發的,所以項目的平台最低版本要是.NET Framework 4.6.1和.NET Core 2.0。

首先通過nuget安裝:

PM > Install-Package PuppeteerSharp

導入命名空間:

  using PuppeteerSharp;

下面是我在ASP.NET Core 2.1下封裝的測試方法:

        [HttpPost, Route("page2img")]
        public async Task<string> PageToImage(string url, int? width, int? height)
        {
            await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
            var browser = await Puppeteer.LaunchAsync(new LaunchOptions
            {
                Headless = true,
                //ExecutablePath="",
                Args = new string[] { "--no-sandbox" }
            });
            var page = await browser.NewPageAsync();
            bool fullPage = true;
            if (width.HasValue && height.HasValue)
            {
                await page.SetViewportAsync(new ViewPortOptions
                {
                    Width = width.Value,
                    Height = height.Value
                });
                fullPage = false;
            }
            await page.GoToAsync(System.Web.HttpUtility.UrlDecode(url));
            string fileName = $"/Files/{Guid.NewGuid().ToString()}.png";
            await page.ScreenshotAsync($"{AppDomain.CurrentDomain.BaseDirectory}{fileName}", new ScreenshotOptions { FullPage = fullPage });
            return $"{Request.Host.ToString()}{fileName}";
        }

上面方法的第一行:

  await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);

程序會判斷本地環境有沒有可用的Chromium,如果沒有的話會自動下載一個默認版本的Chromium,這個過程可能會有點長,下載成功後會在項目根目錄多一個這樣的文件夾:

和前面說的一樣,如果本地已經下載過Chromium,可以通過LaunchOptionsExecutablePath字段指定一個目錄。目前PuppeteerSharp在網上的資料還不是很多,但是得益於它與Puppeteer高度完整和相似的API,Puppeteer的文檔對它基本都能適用。

總體來說,這個工具功能強大並且比較穩定(我在Windows和Linux下都測試通過),是一個不錯的選擇,但是由於它必須依賴於Chromium來運行,打包部署並不是很方便,我建議把它作為一個獨立的web服務。

清爽指數:★★★    功能指數:★★★★★


4.         IronPdf

    除了一些開源的項目和工具能提供HTML轉圖片或PDF的功能,很多商業軟件公司也提供了這樣的產品,IronPdf算是裏面比較有代表性的一個。和其他收費軟件不同的是,IronPdf有一個對開發者免費試用的license:

    IronPdf的主要特性包括:

  • 任何類型的HTML文件、代碼片段、URL生成PDF
  • PDF編輯
  • 圖片與PDF互轉
  • 支持HTML5和CSS3,支持響應式布局,支持JS腳本,豐富的配置選項
  • 支持C#、VB、Webform、ASP.NET MVC、.NET CORE

    我們可以在官網下載DLL文件直接引用到項目,也可以通過nuget來安裝:

PM > Install-Package IronPdf

    導入命名空間:

  using IronPdf;

    一個最簡單的例子:

// Create a PDF from any existing web page
var Renderer = new IronPdf.HtmlToPdf();
Renderer.PrintOptions.EnableJavaScript = true;
Renderer.PrintOptions.PaperOrientation = IronPdf.PdfPrintOptions.PdfPaperOrientation.Landscape;
var PDF = Renderer.RenderUrlAsPdf("https://www.baidu.com");
PDF.SaveAs("baidu.pdf");

// This neat trick opens our PDF file so we can see the result
System.Diagnostics.Process.Start("baidu.pdf");

    添加水印:

  pdf.WatermarkAllPages("<h2 style='color:red'>SAMPLE</h2>", PdfDocument.WaterMarkLocation.MiddleCenter, 50, -45, "https://www.baidu.com");

    用圖片生成PDF文檔:

// Select one or more images.  This example selects all JPEG images in a specific folder.
var ImageFiles = Directory.EnumerateFiles(@"C:\project\assets").Where(f => f.EndsWith(".jpg") || f.EndsWith(".jpeg"));

// Convert the images to a PDF and save it.
ImageToPdfConverter.ImageToPdf(ImageFiles).SaveAs(@"C:\project\composite.pdf");

    更多高級功能和配置可以參考官網例子:https://ironpdf.com/examples/image-to-pdf/

 清爽指數:★★★★    功能指數:★★★★

    

寫在最後

    以上幾種方式,都是我在本次實踐中總結出來的,可能不是很全面,歡迎大家不吝補充。

    遺憾的是,最終項目沒有用上面的任何一種方式,而是抓取到HTML內容後用正則解析,然後用Bitmap一點一點重新畫圖生成圖片文件保存。因為我要截取的頁面內容很少,就是一個簡單的电子處方箋,需求上也沒有要求必須完全和原網頁100%一致,繪圖也算是一個不錯的方案,但是缺點是一旦HTML結構或樣式發生變化,那這套東西就失效了,好在這個不會輕易變更,也算是一個折中方案。

 

 

 

【精選推薦文章】

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

想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

帶您來看台北網站建置台北網頁設計,各種案例分享

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

docker (2)—存儲、網絡(利用docker容器上線靜態網站)

一、docker底層依賴的核心技術

1、命名空間 (Namespaces)

2、控制組 (Control Groups)

3、聯合文件系統 (Union File System)

4Linux 虛擬網絡支持:本地和容器內創建虛擬接口

(1) 命名空間(Namespaces):

實現了容器間資源的隔離,每個容器擁有自己獨立的命名空間 , 運行其中的應用就像是運行在獨立的操作系統中一樣 , 我們都可以看到文件系統,網卡等資源保證了容器之間互不影響,namesaces管理進程號 , 每個進程命名空間有一套自己的進程號管理方法 ,

程命名空間是一個父子關係的結構 , 子空間中的進程對於父空間是可見的。

(2) 控制組 (Control Groups) :

  控制組 (Control groups)–CGroups Linux 內核的一個特性 ,主要用來對共享資源進行隔離、限制、審計等 。cgroups 允許對於進程或進程組公平( 不公平 ) 的分配 CPU 時間、內存分配和 I/O 帶寬。

容器通過 cgroups 來得到所能夠管理資源的分配和使用。因此容器所獲得資源僅為所有系統資源的一個部分

1、資源限制 : 內存子系統為進程組設置內存使用上限,內存達到上限后再申請內存,就會發出 Out of Memory

2、 優先級 : 通過優先級讓一些組得到更多 CPU 等資源

3、 資源審計 : 用來統計系統上實際把多少資源用到適合的目的上 , 可以使用 cpuacct 子系統記錄某個進程組使用的 CPU 時間

4、 隔離 : 為組隔離名字空間 , 這樣一個組不會看到其他組的進程 .網絡連接和文件系統

5、 控制 : 掛起 . 恢復和啟動等操作

(3)聯合文件系統 (Union File System) :

  docker 中使用AUFS(another Union File System v2 版本以後的Advanced multi-layered Unification File System) 控製為每一個成員目錄設定只讀 / 讀寫 / 寫出權限 , 同時 AUFS 有一個類似分層的概念 , 對只讀權限的分支可以邏輯上進行增量的修改.

二、docker的存儲

docker兩種存儲資源類型:

1Data Volume (數據卷)

2Data Volume Dontainers — 數據卷容器

(1) Data Volume (數據卷):

Data Volume 本質上是 Docker Host 文件系統中的目錄或文件,使用類似與 Linux 下對目錄或者文件進行 mount 操作。數據卷可以在容器之間共享和重用,對數據卷的更改會立馬生效,對數據卷的更新不會影響鏡像,卷會一直存在,直到沒有容器使用。

Data Volume(數據卷)的特點:

1Data Volume 是目錄或文件,而非沒有格式化的磁盤(塊設備)。

2、容器可以讀寫 volume 中的數據。

3volume 數據可以被永久的保存,即使使用它的容器已經銷毀。

 Data Volume 的使用:

1、在宿主機根目錄下創建一個目錄(數據卷)

 

2、啟動一個容器並將數據卷掛載到容器的目錄下

 

3、驗證  ( 持久化的需要映射目錄)

 

#

 

(2)Data Volume Dontainers — 數據卷容器

數據卷容器就是一個普通的容器,只不過是專門用它提供數據卷供其他容器掛載使用

Data Volume Dontainers使用:

1、創建一個名為 dbdata 的數據卷,並在其中創建一個數據卷掛載到 /dbdata

docker run -dti -v /dbdata --name dbser centos:latest

2、再啟動兩個容器,並使用數據卷容器

docker run -dti --volumes-from dbser --name db1 centos:latest

#

3、驗證

 

#

容器 db1 db2 同時掛載了同一個數據卷到本地相同 /dbdata目錄。三個容器任何一個目錄下的寫入,都可以時時同步到另外兩個

三、docker 三種網絡

  docker 網絡從覆蓋範圍可分為單個 host 上的容器網絡和跨多個 host 的網絡,docker 目前提供了映射容器端口到宿主主機和容器互聯機制來為容器提供網絡服務,在啟動容器的時候,如果不指定參數,在容器外部是沒有辦法通過網絡來訪問容器內部的網絡應用和服務的

docker 安裝時會自動在host上創建三個網絡

docker   network  ls   (查看docker  網絡)

(1) docker–none網絡

none 網絡就是什麼都沒有的網絡。掛在這個網絡下的容器除了 lo,沒有其他 任何網卡。容器創建時,可以通過 –network=none 指定使用 none 網絡

 

none網絡的應用

封閉的網絡意味着隔離,一些對安全性要求高並且不需要聯網的應用可以使用 none 網絡。

(2)docker–host網絡

連接到 host 網絡的容器,共享 docker host 的網絡棧,容器的網絡配置與host 完全一樣。可以通過 –network=host 指定使用 host 網絡

 

host 網絡的應用

  直接使用 Docker host 的網絡最大的好處就是性能,如果容器對網絡傳輸效率有較高要求,就可以選擇 host 網絡。當然不便之處就是犧牲一些靈活性,比如要考慮端口衝突問題,Docker host上已經使用的端口就不能再用了。

Docker host 的另一個用途是讓容器可以直接配置 host 網路。比如某些跨host 的網絡解決方案,其本身也是以容器方式運行的,這些方案需要對網絡進行配置,比如管理 iptables

(3) docker–bridge 網絡

docker 安裝時會創建一個 命名為 docker0 linux bridge。如果不指定–network,創建的容器默認都會掛到 docker0

 

#

 

eth0@if29      veth04c5851 是一對 veth pair

#

  veth pair 是一種成對出現的特殊網絡設備,可以把它們想象成由一根虛擬網線連接起來的一對網卡,網卡的一頭(eth0@if29)在容器中,另一頭( veth04c5851)掛在網橋 docker0 上,其效果就是將 eth0@if29也掛在了docker0 上。

# 查看網絡配置信息 ( 設置容器ip 網段、網關)

docker network inspect bridge

 

#

注:容器創建時,docker 會自動從 172.17.0.0/16 中分配一個 IP,這裏 16 位的掩碼保證有足夠多的 IP 可以供容器。

四、創建 user-defined網絡 (自定義網絡)

通過 bridge 驅動創建類似前面默認的 bridge 網絡

1、利用bridge驅動創建名為my-net2網橋(docker會自動分配網段)

docker network create --driver bridge my-net2

# 查看網絡配置信息

 

# 查看網橋

 

2、利用bridge驅動創建名為my-net3網橋(user-defined (自定義)網段及網關)

docker network create --driver bridge --subnet 172.33.1.0/24 --gateway 172.33.1.1 my-net3

# 查看網絡配置信息

 

# 查看網橋

 

3、啟動容器使用新建的my-net3網絡

docker run -it  --network=my-net3  busybox:latest

4、啟動容器使用my-net3網絡並指定ip(只有使用 –subnet 創建的網絡才能指定靜態 IP,如果是docker自動分配的網段不可以指定ip

docker run -it --network=my-net3  --ip 172.33.1.100  busybox:latest

 

5、讓已啟動不同vlanbusybox容器,可以連接到my-net2(其實在busybox中新建了my-net2的網卡)(添加網卡。訪問不同的網段)

 

# #docker network connect my-net3  08493ae30117   ( 連接)

6、使用–name指定啟動容器名字,可以使用docker自帶DNS通信,但只能工作在user-defined 網絡,默認的 bridge 網絡是無法使用 DNS 的。

#docker run -it --network=my-net3 --name=bbox1 busybox:latest

#docker run -it --network=my-net3 --name=bbox2 busybox:latest

7、容器之間的網絡互聯

&1、創建一個 db 容器

docker run -dti --name db centos:latest

&2、創建一個 web 容器,並使其連接到 容器db

docker run -dti --name web --link db:dblink centos:latest /bin/bash

–link db:dblink 實際是連接對端的名字和這個鏈接的名字,也就是和 db 容器建立一個叫做 dblink 的鏈接

 

# 測試  

 

注:此鏈接通信是單向的

8、容器端口映射

在啟動容器的時候,如果不指定參數,在容器外部是沒有辦法通過網絡來訪問容器內部的網絡應用和服務的,當容器需要通信時,我們可以使用 -P (大) &&-p (小)來指定端口映射

(1)   -P Docker 會隨機映射一個 49000 49900 的端口到容器內部開放的網絡端口

(2)   -p :則可以指定要映射的端口,並且在一個指定的端口上只可以綁定一個容器。

支持的格式

 IP HostPort ContainerPort

 IP : : ContainerPort

 IP HostPort

&1、 查看映射

docker port   CONTAINER ID/NAMES

&2、映射所有接口地址,此時綁定本地所有接口上的 5200 到容器的 5200 接口,訪問任何一個本地接口的 5000 ,都會直接訪問到容器內部

docker run -dti -p 5200:5200 centos:latest  /bin/bash

&3、多次使用可以實現多個接口的映射

docker run -dti -p 5400:5400  -p 5300:5300 centos:latest  /bin/bash

&4、映射到指定地址的指定接口

此時會綁定本地 192.168.226.147 接口上的 5100 到容器的 5100 接口

docker run -dti -p 192.168.226.147:5100:5100 centos:latest /bin/bash

 

&5、映射到指定地址的任意接口

此時會綁定本地 192.168.226.147 接口上的任意一個接口到容器的 5500 接口

docker run -dti -p 192.168.226.147::5500 centos:latest /bin/bash

實驗、通過端口映射實現訪問本地的 IP:PORT 可以訪問到容器內的 web

1、將容器80端口映射到主機8090端口

docker run -itd -p 192.168.226.147:8090:80 --name http-test httpd:latest

2、查看剛運行docker容器

docker  ps

 

3、進入 容器

 

4、容器內部編輯網頁文件 index.html

 

5、到宿主機上打開瀏覽器輸入 IP:PORT 訪問驗證

http://192.168.226.147:8090/

 

6、宿主機上傳靜態網站測試文件

 

7、解壓

 

8、把解壓的目錄上傳至容器下的網站根目錄

docker cp jd 67b3daf15a40:/usr/local/apache2/htdocs

9、進入容器,刪除原來的index.html 文件

 

10、展開目錄

 

11web 訪問

http://192.168.226.147:8090/

 

 

【精選推薦文章】

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

想要讓你的商品在網路上成為最夯、最多人討論的話題?

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

不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師"嚨底家"!!

【劍指Offer】調整數組順序使奇數位於偶數前面

題目描述

輸入一個整數數組,實現一個函數來調整該數組中数字的順序,使得所有的奇數位於數組的前半部分,所有的偶數位於數組的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。

解法1

最直接的思路是再構建一個新數組,先遍歷一遍原數組,把其中的奇數依次添加到新數組中,再遍歷一遍原數組把其中的偶數依次添加到新數組中,時間複雜度為O(2n)。實現代碼如下

實現代碼

public int[] reOrderArray2(int[] array)
{
    int[] arr = new int[array.Length];
    int index = 0;
    for (int i = 0; i < array.Length; i++)
    {
        // 奇數
        if ((array[i] % 2) != 0)
        {
            arr[index] = array[i];
            index++;
        }
    }
    for (int i = 0; i < array.Length; i++)
    {
        // 偶數
        if ((array[i] % 2) == 0)
        {
            arr[index] = array[i];
            index++;
        }
    }
    return arr;
}

解法2

C#的數組是不支持動態添加元素的,我們可以使用泛型List,來實現在指定位置插入元素。基本思路是遍歷原數組,依次將元素插入到List中,如果是偶數元素,默認插入到List的末尾。如果是奇數元素,則插入到所有的偶數元素之前(已插入的所有奇數元素之後),因此需要記錄最後插入的奇數元素的索引。實現代碼如下,算法的時間複雜度是O(n)

實現代碼

public int[] reOrderArray(int[] array)
{
    List<int> list = new List<int>();
    // 最後插入奇數元素的索引
    int index = 0;
    foreach (int i in array)
    {
        if ((i % 2) == 0)
            list.Add(i);
        else
        {
            list.Insert(index, i);
            index++;
        }
    }
    return list.ToArray();
}

解法3

上面的兩種解法都用到臨時數組或List,空間複雜度是O(n),某些情況下可能希望空間複雜度越低越好。下面這種解法雖然時間複雜度提高了,但降低了空間複雜度,不再需要額外的空間。基本思路是遍歷原數組,如果遇到了奇數元素,就將該元素向前移動,該元素前面的偶數元素都依次向後移動。
舉個例子:比如數組{1, 2, 4, 3, 5}
遍曆數組,得到第一個元素是奇數1,其前面沒有元素所以不做移動
第二個,第三個是偶數,不做處理。
第四個元素是奇數3,所以將3往前移動,3前面的偶數元素{2, 4}都向後移動。移動后的數組為{1, 3, 2, 4, 5}
接着第五個元素是奇數5,所以將5往前移動,5前面的偶數元素{2, 4}都向後移動。移動后的數組為{1, 3, 5, 2, 4}
可以這樣理解,每發現一個奇數時,就將這個奇數移動到了它最終應該在的位置上。

實現代碼

public int[] reOrderArray(int[] array)
{
    for(int i = 0; i < array.Length; i++)
    {
        if((array[i] % 2) != 0)
        {
            int temp = array[i];
            int j = i - 1;
            for(; j >= 0; j--)
            {
                if ((array[j] % 2) != 0)
                    break;
                array[j + 1] = array[j]; 
            }
            array[j + 1] = temp;
        }
    }
    return array;
}

【精選推薦文章】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

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

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

多線程與高併發(一)多線程入門

一、基礎概念

多線程的學習從一些概念開始,進程和線程,併發與并行,同步與異步,高併發。

1.1 進程與線程

幾乎所有的操作系統都支持同時運行期多個任務,所有運行中的任務通常就是一個進程,進程是處於運行過程中的程序,進程是操作系統進行資源分配和調度的一個獨立單位。

進程有三個如下特徵:

  • 獨立性:進程是系統中獨立存在的實體,它可以擁有自己獨立的資源,每一個進程都擁有自己私有的地址空間。在沒有經過進程本身允許的情況下,一個用戶進程不可以直接訪問其他進程的地址空間。

  • 動態性:進程與程序的區別在於,程序只是一個靜態的指令集合,而進程是一個正在系統中活動的指令集合。在進程中加入了時間的概念,進程具有自己的生命周期和各種不同的狀態,這些概念在程序中部是不具備的。

  • 併發性:多個進程可以在單個處理器上併發執行,多個進程之間不會互相影響。

線程是進程的組成部分,一個進程可以擁有多個線程,而線程必須有一個父進程,線程可以有自己的堆棧、自己的程序計數器和自己的局部變量,但不擁有系統資源。比如使用QQ時,我們可以同事傳文件,發送圖片,聊天,這就是多個線程在進行。

線程可以完成一定的任務,線程能夠獨立運行的,它不知道有其他線程的存在,線程的執行是搶佔式的,當前線程隨時可能被掛起。

總之:一個程序運行后至少有一個進程,一個進程里可以有多個線程,但至少要有一個線程。

1.2 併發和并行

併發和并行是比較容易混淆的概念,他們都表示兩個或者多個任務一起執行,但併發側重多個任務交替執行,同一時刻只能有一條指令執行,但多個進程指令被快速輪換執行,使得在宏觀上具有多個進程同時執行的效果。而并行確實真正的同時執行,有多條指令在多個處理器上同時執行,并行的前提條件就是多核CPU。

1.3 同步和異步

同步和異步通常用來形容一次方法調用。同步方法調用一旦開始,調用者必須等到方法調用返回后,才能繼續後續的行為。異步方法調用更像一個消息傳遞,一旦開始,方法調用就會立即返回,調用者可以繼續後續的操作。

1.4 高併發

高併發一般是指在短時間內遇到大量操作請求,非常具有代表性的場景是秒殺活動與搶票,高併發是互聯網分佈式系統架構設計中必須考慮的因素之一,高併發相關常用的一些指標有響應時間(Response Time),吞吐量(Throughput),每秒查詢率QPS(Query Per Second),併發用戶數等。

多線程在這裏只是在同/異步角度上解決高併發問題的其中的一個方法手段,是在同一時刻利用計算機閑置資源的一種方式

1.5 多線程的好處

線程在程序中是獨立的、併發的執行流,擁有獨立的內存單元,多個線程共享父進程里的全部資源,線程共享的環境有進程的代碼段,進程的公有數據等,利用這些共享數據,線程很容易實現相互之間的通信,可以提高程序的運行效率。

多線程的好處主要有:

  • 進程之間不能共享內存,但線程之間共享內存非常容易。

  • 系統創建進程時需要給進程重新分配系統資源,但創建線程代價小得多,所以使用多線程實現多任務併發比多進程效率高

  • Java語言內置了多線程功能支持。

二、使用多線程

上面講了多線程的一些概念,都有些抽象,下面將學習如何使用多線程,創建多線程的方式有三種。

2.1 繼承Thread類創建

繼承Thread創建並啟動多線程有三個步驟:

  1. 定義類並繼承Thread,重寫run()方法,run()方法中為需要多線程執行的任務。

  2. 創建該類的實例,即創建了線程對象。

  3. 調用實例的start()方法啟動線程。

public class FirstThread extends Thread {

    private int i=0;
    public void run() {
        for (; i < 100; i++) {
            //獲取當前線程名稱
            System.out.println(this.getName() + " " + i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            //Thread的靜態方法currentThread,獲取當前線程
            System.out.println(Thread.currentThread().getName());
            if (i == 20) {
                //創建線程並啟動
                new FirstThread().start();
                new FirstThread().start();
            }

        }
    }
}

運行結果可以看到兩個線程的i並不是連續的,說明他們並不共享數據。

2.2 實現Runnable接口

實現Runnable接口創建並啟動多線程也有以下步驟:

  1. 定義類並繼承Runnable接口,重寫run()方法,run()方法中為需要多線程執行的任務。

  2. 創建該類的實例,並以此實例作為target為參數來創建Thread對象,這個Thread對象才是真正的多線程對象。

public class SecondThread implements Runnable {
    private int i = 0;
    
    @Override
    public void run() {
        for (; i < 100; i++) {
            //此時想要獲取到多線程對象,只能使用Thread.currentThread()方法
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            //Thread的靜態方法currentThread,獲取當前線程
            System.out.println(Thread.currentThread().getName());
            if (i == 20) {
                //創建線程並啟動
                SecondThread secondThread=new SecondThread();
                new Thread(secondThread,"線程一").start();
                new Thread(secondThread,"線程二").start();
            }

        }
    }
}

2.3 使用Callable和Future

Callable是Runnable的增加版,主要是接口中的call()方法可以有返回值,並且可以申明拋出異常,使用Callable創建的步驟如下:

  1. 定義類並繼承Callable接口,重寫call()方法,run()方法中為需要多線程執行的任務。

  2. 創建類實例,使用FutureTask來包裝對象實例,

  3. 使用FutureTask對象作為Thread的target來創建多線程,並啟動線程。

  4. 調用FutureTask對象的get()方法來獲取子線程結束后的返回值。

public class ThirdThread {

    public static void main(String[] args) {
        //使用lambda表達式
        FutureTask<Integer> task = new FutureTask<>((Callable<Integer>) () -> {
            int i = 0;
            for (; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "的循環變量i的值:" + i);
            }
            return i;
        });
        for (int i = 0; i < 100; i++) {
            //Thread的靜態方法currentThread,獲取當前線程
            System.out.println(Thread.currentThread().getName());
            if (i == 20) {
                //創建線程並啟動
                new Thread(task, "有返回值的線程").start();
            }
        }
        try {
            System.out.println("線程的返回值:" + task.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

這裏使用了lambda表達式,不使用表達式的方式也很簡單,可以去源碼中查看。Callable與Runnable方式基本相同,只不過增加了返回值且可允許聲明拋出異常。

使用三種方式都可以創建線程,且方式也相對簡單,大體分為實現接口和實現Thread類兩種,這兩種都各有優缺點。

繼承接口實現:

  • 優點:除了繼承接口之外,還可以繼承其他類。這種方式多個線程共享一個target對象,可以處理用於共同資源的情況。
  • 缺點:編程稍微複雜一些,並且沒有直接獲取當前線程對象的方式,必須使用Thread.currentThread()方式。

基礎Thread類:

  • 優點:編程簡單

  • 缺點:不能繼承其他類

三、多線程的生命周期

線程狀態是線程中非常重要的一個概念,然而我看過很多資料,線程的狀態理解有很多種方式,很多人將其分為五個基本狀態:新建、就緒、運行、阻塞、死亡,但在狀態枚舉中並不是這五個狀態,我不知道是什麼原因(有大神可以解答更好),只能按照枚舉中的狀態根據自己的理解。

  1. 初始(NEW):新創建了一個線程對象,但還沒有調用start()方法,而且就算調用了改方法也不代表狀態立即改變。

  2. 運行(RUNNABLE):在運行的狀態肯定就處於RUNNABLE狀態。

  3. 阻塞(BLOCKED):表示線程阻塞,或者說線程已經被掛起了。

  4. 等待(WAITING):進入該狀態的線程需要等待其他線程做出一些特定動作(通知或中斷)。

  5. 超時等待(TIMED_WAITING):該狀態不同於WAITING,它可以在指定的時間后自行返回。

  6. 終止(TERMINATED):表示該線程已經執行完畢。

狀態流程圖如下:

理解:初始狀態很好理解,這個時候其實還不能被稱為一個線程,因為他還沒被啟動,當調用start()方法后,線程正式啟動,但是也不代表立即就改變了狀態。

運行狀態中其實包含兩種狀態,運行中(RUNING)就緒(READY)

就緒狀態表示你有資格運行,只要CPU還未調度到你,就處於就緒狀態,有幾個狀態會是線程狀態編程就緒狀態

  • 調用線程的start()方法。

  • 當前線程sleep()方法結束,其他線程join()結束,等待用戶輸入完畢,某個線程拿到對象鎖。

  • 當前線程時間片用完了,調用當前線程的yield()方法。

  • 鎖池裡的線程拿到對象鎖后。

運行中(RUNING)狀態比較好理解,線程調度程序選擇了當前線程作。

阻塞狀態是線程阻塞在進入synchronized關鍵字修飾的方法或代碼塊(獲取鎖)時的狀態。

等待狀態是指線程沒有被CPU分配執行時間,需要等待,這種等待是需要被显示的喚醒,否則會無限等待下去。

超時等待狀態是這現在沒有被CPU分配執行時間,需要等待,不過這種等待不需要被显示的喚醒,會設置一定的時間后zi懂喚醒。

死亡狀態也很好理解,說明線程方法被執行完成,或者出錯了,線程一旦進入這個狀態就代表徹底的結束

 

【精選推薦文章】

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

想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

帶您來看台北網站建置台北網頁設計,各種案例分享

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

Git的使用 — 用git玩翻github,結尾有驚喜!有驚喜!有驚喜!林妙妙看了說:牛呲呼啦帶閃電 (三)(超詳解)

簡介

      上一篇主要講解的是Git安裝及配置,這一篇就詳細的從無到有的來用Git玩翻github。

一、什麼是Github

Github是全球最大的社交編程及代碼託管網站(https://github.com/)。

Github可以託管各種git庫,並提供一個web界面(用戶名.github.io/倉庫名)

二、Github和Git是什麼關係 

Git是版本控制軟件

Github是項目代碼託管的平台,藉助git來管理項目代碼

1、 使用Github

目的:藉助github託管項目代碼

2、基本概念

a、倉庫(Repository)

 倉庫的意思,即你的項目,你想在 GitHub 上開源一個項目,那就必須要新建一個 Repository ,如果你開源的項目多了,你就擁有了多個 Repositories 。

 倉庫用來存放項目代碼,每個項目對應一個倉庫,多個開源項目則有多個倉庫。

b、收藏(Star)

倉庫主頁star按鈕,意思為收藏項目的人數,收藏項目,方便下次查看,在 GitHub 上如果你有一個項目獲得100個star都算很不容易了!

【如何收藏】

 操作:打開對應項目主頁,點擊右上角  star 按鈕即可收藏

 情景:張三無意訪問到李四的開源項目感覺不錯並進行收藏

 

【如何查看自己得收藏】

 

c、複製項目(Fork)派生

這個不好翻譯,如果實在要翻譯我把他翻譯成分叉,什麼意思呢?你開源了一個項目,別人想在你這個項目的基礎上做些改進,然後應用到自己的項目中,這個時候他就可以 Fork 你的項目(打開項目主頁點擊右上角的fork按鈕即可),然後他的 GitHub 主頁上就多了一個項目,只不過這個項目是基於你的項目基礎(本質上是在原有項目的基礎上新建了一個分支),他就可以隨心所欲的去改進,但是絲毫不會影響原有項目的代碼與結構。

注意:該fork的項目時獨立存在的

比如:張三fork了李四的項目,相當於張三複制了李四的項目,所以自己也單獨有了一個一樣名稱的倉庫(注:該倉庫會聲明來自於李四,但是獨立存在)

d、發起請求(Pull Request)

發起請求,這個其實是基於 Fork 的,還是上面那個例子,如果別人在你基礎上做了改進,後來覺得改進的很不錯,應該要把這些改進讓更多的人收益,於是就想把自己的改進合併到原有項目里,這個時候他就可以發起一個 Pull Request(簡稱PR) ,原有項目創建人,也就是你,就可以收到這個請求,這個時候你會仔細review他的代碼,並且測試覺得OK了,就會接受他的PR,這個時候他做的改進原有項目就會擁有了。

e、關注(Watch)

這個也好理解就是觀察,如果你 Watch 了某個項目,那麼以後只要這個項目有任何更新,你都會第一時間收到關於這個項目的通知提醒。

f、問題(Issue)

發現代碼BUG,但是目前沒有成型代碼,需要討論時用; 問題的意思,舉個例子,就是你開源了一個項目,別人發現你的項目中有bug,或者哪些地方做的不夠好,他就可以給你提個 Issue ,即問題,提的問題多了,也就是 Issues ,然後你看到了這些問題就可以去逐個修復,修復ok了就可以一個個的 Close 掉。

g、Github主頁

賬號創建成功或點擊網址導航欄github圖標都可進入github主頁:該頁左側主要显示用戶動態以及關注用戶或關注倉庫的動態;右側显示所有的git庫

h、倉庫主頁

倉庫主頁主要显示項目的信息,如:項目代碼,版本,收藏/關注/fork情況等

i、個人主頁

個人信息:頭像,個人簡介,關注我的人,我關注的人,我關注的git庫,我的開源項目,我貢獻的開源項目等信息

3、註冊github賬號

官方網址:https://github.com

注意:

a、因為github在國外服務器所以訪問較慢或者無法訪問,需要FQ(***)

b、私有倉庫只能自己或者指定的朋友才有權限操作(私有倉庫是收費的)

c、新註冊的用戶必須驗證郵箱后才可以創建git庫倉庫

4、創建倉庫/創建新項目

說明:一個git庫(倉庫)對應一個開源項目。通過git管理git庫

a、創建倉庫

1)點擊【Start a project】創建一個倉庫

2)問題:點擊【Start a project】創建一個倉庫,后出現該頁面

2)原因:未驗證郵箱,點擊下圖框框中的鏈接進行驗證

 

3)點擊【resend】發送郵件驗證郵箱

 

 4)點擊【verify email address】驗證郵箱

   說明:驗證成功後會自動跳轉github主頁,重新點擊【Start a project】即可創建倉庫

 

5) 驗證郵箱后,點擊【Start a project】進入下圖界面

b、倉庫主頁說明

 

 

 注意:qq郵箱需要設置白名單才可以收到郵件

設置QQ郵箱白名單

1、打開QQ郵箱、點擊【設置】

2、點擊【反垃圾】

3、點擊【設置域名白名單】

4、在新頁面的input框中輸入【github.com】添加即可

5、倉庫管理

a、新建文件:倉庫主頁,點擊【create new file】創建倉庫文件

 

 

 

   

b、編輯文件:倉庫主頁,點擊【需要修改的文件】進入文件詳情頁

 

    

c、刪除文件

 

d、被刪除文件如何查看信息

答案:點擊commits按鈕查看

e、上傳文件

 

f、搜索倉庫文件:快捷鍵(t)

6、下載/檢出項目

7、Github Issues

作用:發現代碼BUG,但是目前沒有成型代碼,需要討論時用;或者使用開源項目出現問題時使用

情景:張三發現李四開源git庫,則發提交了一個issue;李四隔天登錄在github主頁看到通知並和張三交流,最後關閉issue

三、基本概念(實戰操作)

1、Github主頁

 

2、個人主頁

 

四、開源項目貢獻流程

1、新建Issue

提交使用問題或者建議或者想法

2、Pull Request

步驟:

a、 fork項目

b、 修改自己倉庫的項目代碼

c、 新建 pull request

d、 等待作者操作審核

五、下面就是驚喜:Github  Pages搭建網站

1、個人站點

訪問:https://用戶名.github.io

搭建步驟:

a、創建個人站點-》新建倉庫(倉庫名必須是【用戶名.github.io】)

 

b、在倉庫下新建index.html的文件即可

注意:a、Github Page僅支持靜態頁面

   b、倉庫裏面只能是.html文件

   c、個人主頁也可以設置主題

2、Project Pages 項目站點

訪問:https://用戶名.github.io/倉庫名

原理:gh-pages 用於構建和發布

搭建步驟

a、進入項目主頁,點擊settings

b、在settings頁面,點擊【Choose a theme 】來自動生成主題頁面

c、新建站點基礎信息設置

d、選擇主題

e、發布網頁(publish page)

六、小結

Clone和Fork的區別:

fork(派生):將別人的倉庫複製一份到自己的倉庫。
clone(克隆):將倉庫克隆到自己本地電腦中。

Fork的主要應用場景:
1.在A的倉庫中fork項目B (此時我們自己的github就有一個一模一樣的倉庫B,但是URL不同)
2.將我們修改的代碼push到自己github中的倉庫B中
3.pull request ,主人就會收到請求,並決定要不要接受你的代碼

【精選推薦文章】

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

想要讓你的商品在網路上成為最夯、最多人討論的話題?

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

不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師"嚨底家"!!

kubeadm1.14.1 安裝Metrics Server

Metrics API

介紹Metrics-Server之前,必須要提一下Metrics API的概念

Metrics API相比於之前的監控採集方式(hepaster)是一種新的思路,官方希望核心指標的監控應該是穩定的,版本可控的,且可以直接被用戶訪問(例如通過使用 kubectl top 命令),或由集群中的控制器使用(如HPA),和其他的Kubernetes APIs一樣。

官方廢棄heapster項目,就是為了將核心資源監控作為一等公民對待,即像pod、service那樣直接通過api-server或者client直接訪問,不再是安裝一個hepater來匯聚且由heapster單獨管理。

假設每個pod和node我們收集10個指標,從k8s的1.6開始,支持5000節點,每個節點30個pod,假設採集粒度為1分鐘一次,則:

10 x 5000 x 30 / 60 = 25000 平均每分鐘2萬多個採集指標

因為k8s的api-server將所有的數據持久化到了etcd中,顯然k8s本身不能處理這種頻率的採集,而且這種監控數據變化快且都是臨時數據,因此需要有一個組件單獨處理他們,k8s版本只存放部分在內存中,於是metric-server的概念誕生了。

其實hepaster已經有暴露了api,但是用戶和Kubernetes的其他組件必須通過master proxy的方式才能訪問到,且heapster的接口不像api-server一樣,有完整的鑒權以及client集成。這個api現在還在alpha階段(18年8月),希望能到GA階段。類api-server風格的寫法:generic apiserver

有了Metrics Server組件,也採集到了該有的數據,也暴露了api,但因為api要統一,如何將請求到api-server的/apis/metrics請求轉發給Metrics Server呢,解決方案就是:kube-aggregator,在k8s的1.7中已經完成,之前Metrics Server一直沒有面世,就是耽誤在了kube-aggregator這一步。

kube-aggregator(聚合api)主要提供:

  • Provide an API for registering API servers.

  • Summarize discovery information from all the servers.

  • Proxy client requests to individual servers.

詳細設計文檔:參考鏈接

metric api的使用:

  • Metrics API 只可以查詢當前的度量數據,並不保存歷史數據

  • Metrics API URI 為 /apis/metrics.k8s.io/,在 k8s.io/metrics 維護

  • 必須部署 metrics-server 才能使用該 API,metrics-server 通過調用 Kubelet Summary API 獲取數據

如:

http://127.0.0.1:8001/apis/metrics.k8s.io/v1beta1/nodes
​
http://127.0.0.1:8001/apis/metrics.k8s.io/v1beta1/nodes/<node-name>
​
http://127.0.0.1:8001/apis/metrics.k8s.io/v1beta1/namespace/<namespace-name>/pods/<pod-name>

Metrics-Server

Metrics server定時從Kubelet的Summary API(類似/ap1/v1/nodes/nodename/stats/summary)採集指標信息,這些聚合過的數據將存儲在內存中,且以metric-api的形式暴露出去。

Metrics server復用了api-server的庫來實現自己的功能,比如鑒權、版本等,為了實現將數據存放在內存中嗎,去掉了默認的etcd存儲,引入了內存存儲(即實現Storage interface)。因為存放在內存中,因此監控數據是沒有持久化的,可以通過第三方存儲來拓展,這個和heapster是一致的。

 

Metrics server出現后,新的​Kubernetes 監控架構將變成上圖的樣子

  • 核心流程(黑色部分):這是 Kubernetes正常工作所需要的核心度量,從 Kubelet、cAdvisor 等獲取度量數據,再由metrics-server提供給 Dashboard、HPA 控制器等使用。

  • 監控流程(藍色部分):基於核心度量構建的監控流程,比如 Prometheus 可以從 metrics-server 獲取核心度量,從其他數據源(如 Node Exporter 等)獲取非核心度量,再基於它們構建監控告警系統。

官方地址:https://github.com/kubernetes-incubator/metrics-server

部署

mkdir metrics;cd metics
git clone https://github.com/kubernetes-incubator/metrics-server.git
cd metrics-server/deploy/1.8+/

 修改metrics-server-deployment.yaml,紅色command部分。

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: metrics-server
  namespace: kube-system
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: metrics-server
  namespace: kube-system
  labels:
    k8s-app: metrics-server
spec:
  selector:
    matchLabels:
      k8s-app: metrics-server
  template:
    metadata:
      name: metrics-server
      labels:
        k8s-app: metrics-server
    spec:
      serviceAccountName: metrics-server
      volumes:
      # mount in tmp so we can safely use from-scratch images and/or read-only containers
      - name: tmp-dir
        emptyDir: {}
      containers:
      - name: metrics-server
        image: k8s.gcr.io/metrics-server-amd64:v0.3.3
        command:
        - /metrics-server
        - --metric-resolution=30s
        - --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
        - --kubelet-insecure-tls
        imagePullPolicy: Always
        volumeMounts:
        - name: tmp-dir
          mountPath: /tmp

 創建

[root@cn-hongkong 1.8+]# kubectl apply -f .
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader unchanged
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator unchanged
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader unchanged
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io unchanged
serviceaccount/metrics-server unchanged
deployment.extensions/metrics-server configured

 等待一會就可以看下集群的資源使用情況了!

 

【精選推薦文章】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

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

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

微信小程序入口場景的問題整理與相關解決方案

前言

最近一段時間都在做小程序。

雖然是第二次開發小程序,但是上次做小程序已經是一年前的事了,所以最終還是被坑得死去活來。

這次是從零開始開發一個小程序,其實除了一些莫名其妙的兼容性問題,大多數坑點都是在微信小程序的各個入口場景處。

所以這裏整理一下微信小程序的各個入口場景,以及從這些入口場景進入小程序會面臨的問題以及解決方案。

這裏只列出常用的幾種場景:

  • [簡單場景]啟動小程序並進入
  • [簡單場景]退出重進(啟動小程序后,退出小程序,再次進入小程序)
  • [簡單場景]退出重進首頁(啟動小程序后,退出小程序,通過掃二維碼再次進入小程序)
  • [複雜場景]啟動並進入指定頁面(從小程序的分享卡片或者微信發送的通知消息進入小程序)
  • [複雜場景]退出重進指定頁面(啟動小程序后,退出小程序,從小程序的分享卡片或者微信發送的通知消息進入小程序)

啟動小程序並進入

微信小程序的入口場景光微信提供的場景值就有幾十種,但是絕大多數都可以劃分為啟動小程序並進入。

這是最常用的一種進入小程序的方式,比如通過搜索進入或者點擊最近使用小程序的方式進入,都算是這種類型。

這一場景下,首先我們需要明白髮生了什麼:

下載小程序 => 啟動小程序 onLaunch事件觸發 => 加載首頁 onLoad事件觸發 => 首頁 onShow事件

然後在這個場景下,需要注意以下幾個問題:

  1. 這個場景下一般會涉及到登錄。
    所謂登錄,不一定是要在這個階段做,但是登錄信息的判斷這個階段是一定要做的。
    通常前端肯定是要將登錄的這些信息存儲在小程序的storage里,然後在onLaunch事件中判斷是否登錄,沒登錄就跳轉到登錄頁面,登錄了就跳轉到首頁。
    這裏的登錄判斷一定要放在onLaunch,而不要放在首頁的onLoad裏面,因為小程序啟動一定會進入onLaunch,而不一定會進入首頁的onLoad。
  2. 而登錄頁面在設計的時候最好要加上一個url參數,傳入登錄成功后跳轉到的頁面地址,而不是登錄之後始終跳轉到首頁,後面會講為什麼需要這麼做。
  3. onLaunch階段是否有發出請求,並在請求完成後進行了頁面跳轉,或者請求完成設置storage,並在onLoad頁面中使用?
    這種情況的出現,會導致在請求時間過長時,首頁的onLoad已經執行了,此時就會出現BUG。
    對於這個問題,有的人會用定時器去判斷是否完成這個操作,但是我的建議是盡量避免在onLaunch中進行這些操作。
    如果一定要有,那麼最好的方式就是做一個加載頁面去承載這些功能。
  4. 首頁數據的初始化,一般是放在onLoad中執行。當然總是有些特殊的需求是要放在onShow裏面的。
    關於onLoad和onShow,最常見的處理區別就在跳轉頁面時。
    當載入首頁時,先觸發onLoad,再觸發onShow。
    此時通過wx.navigateTo 的方式跳轉到頁面A,這個時候首頁並沒有被關閉,那麼從頁面A再返回首頁時,onLoad就不會觸發,但onShow會觸發。
    通常在加載數據時,一般會用到onLoad。
    但是如果說頁面A更新了數據,然後返回首頁時,首頁的相關數據也需要更新。
    那麼初始化數據就不能放在onLoad里,而需要放在onShow里。
    (當然還有一種方式是通過getCurrentPages的方式在頁面A中調用首頁的方法。但是這裏極不推薦這種方式,屬於某個頁面的事情一定要給這個頁面。最好不要將頁面間的職責通過這種方式打亂,容易引起代碼混亂,不易維護。)

退出重進(啟動小程序后,退出小程序,再次進入小程序)

這種場景實際上是對第一種場景的擴展。

而所謂的退出小程序不管你是點右上角的退出按鈕還是Home鍵直接切出都算是這類退出。

但是退出后再立即進入小程序的時候,依然會進入你退出小程序時所在的頁面,而不會觸發onLaunch,也不會觸發這個頁面的onLoad,不過onShow是肯定會觸發的。

這一場景下,首先我們需要明白髮生了什麼:

再次進入小程序 => 進入退出小程序時所在頁面 觸發onShow

在這個場景下,只需要注意onShow中是否有不可重複執行的操作。

例如onShow中會獲取用戶喜歡吃的食物,加載到頁面的列表中,在這種場景下,如果不清空之前的列表或者加個判斷的話,就會出現重複數據。

退出重進首頁(啟動小程序后,退出小程序,通過掃二維碼再次進入小程序)

這種場景實際上是對第二種場景的擴展。

我們通常給二維碼配置的是一個無參數的小程序首頁地址,當我們退出小程序,通過掃二維碼再次進入小程序時會進入首頁。

這一場景下,首先我們需要明白髮生了什麼:

再次進入小程序 => 進入退出小程序時所在頁面A 不觸發onShow => 觸發頁面A onHide => 觸發頁面A onUnload=> 進入首頁 onLoad => 首頁onShow

在這個場景下,除了需要注意第二種場景存在的問題,還需要注意頁面A的onHide事件中是否會觸發奇怪的操作,例如頁面跳轉。

啟動並進入指定頁面(從小程序的分享卡片或者微信發送的通知消息進入小程序)

這塊場景常見於邀請他人進入小程序,需要注意的是他們往往被賦予了更多的業務功能,也就往往增大了小程序的實現難度。

這一場景下,首先我們需要明白髮生了什麼:

下載小程序 => 啟動小程序 onLaunch事件觸發 => 加載指定頁面 onLoad事件觸發 =>指定頁面  onShow事件

這裏就可以看出,並不是進入小程序就一定會進入首頁的onLoad。

所以這就是為什麼之前強調不要將登錄判斷放在首頁的onLoad中,而一定要放在onLaunch里。

但是這裏又和掃二維碼不同,掃二維碼的鏈接一般都是指定的首頁。

而這裏通常跳轉到的是非首頁的頁面,而且可能還多了複雜的業務功能。

我們在需求分析和設計階段應該更多地考慮到這裏可能會引發的複雜問題,而盡量將此處的業務邏輯簡化,或者加大估時。

接下來,我們將根據業務從簡單到複雜,慢慢講解這個場景下可能存在的問題。

最簡單的邀請函(進入小程序首頁)

和第一種場景差不多,這裏略過

進階邀請函(進入小程序指定頁面,帶參數,需要根據參數初始化頁面)

這種情況下,需要考慮以下幾個問題:

  1. 首先在onLaunch階段會判斷是否登錄,沒登錄那麼就需要跳轉到登錄頁面,登錄頁面登錄之後,肯定要跳轉到這個頁面,而不是首頁。
    所以之前說過登錄頁面設計的時候需要傳入一個url參數,來明確登錄成功后跳轉到哪個頁面。
  2. 這種跳轉到指定頁面的情況通常都需要一個回到首頁的按鈕。
    就比如邀請某人查看一篇文章,點擊邀請卡片後會進入小程序內的文章詳情。
    一般在小程序內通常是通過點擊文章列表跳轉到文章詳情,那麼這個時候可以逐級返回到首頁。
    但是在點擊邀請函進入的情況是沒有返回功能的,此時如果沒有回到首頁功能,那麼用戶可能就永遠沒法回到首頁。
    (其實是可以的,但是小程序的的這個功能藏得比較深,不要指望所有用戶都那麼熱愛摸索)
  3. 這裏一定要特別注意第一種場景的第三個應該注意的問題,對於第一種場景而言那個問題因為啟動次數很多容易出現,但是在當前的場景下可能很容易被忽略掉。

涉及身份的邀請函(進入小程序指定頁面,帶參數,需要根據參數切換身份,更可能涉及到登錄)

為了更好地說明這種情況,我們來列舉一個場景。

如果有一個打車軟件,進入這個軟件後有兩種身份,一種是乘客,一種是司機。

用戶是司機,那麼看到的是頁面A或者選定了TabA,如果是乘客,那麼看到的是頁面B或者選定了TabB。

而且還有一個需求,用戶上次登陸時什麼身份,這次登陸也是什麼身份。

考慮到換手機的場景,那麼這個信息肯定是存儲在服務端的,所以進入小程序的時候會去請求服務端進行判斷。

現在我用司機的身份發了個單,微信給了個通知消息,我沒點開。然後切換到乘客的身份了,再去點擊通知消息,那麼我會以司機的身份去打開這個消息。

這個場景其實在業務上來看是很合理的,但是對於我們的程序實現來看,複雜度一下子就上來了。

  1. 首先我們確定一下這個請求身份信息的請求在哪個階段發出?
    onLaunch?
    那麼是不是需要在onLoad階段去獲取這個身份的信息然後給出不同的頁面?
    這樣一下子就會出現進階邀請函的第三個問題,而且還不僅僅是這一個問題,之後我們會講到。
    所以這個地方需要做一個專門的邀請加載頁面去處理這個事情。
  2. 分離出一個單獨的加載頁面之後,其實我們的工作會變的簡單清晰起來。
    因為我們只需要去做我們這個頁面所需要做的事情就行了。
    根據參數去獲取我們現在的身份,然後以這種身份跳轉到相應的頁面。
  3. 這裏還涉及到一個問題,那就是正常啟動而不是通過通知消息進入的時候,也需要去請求服務端獲取身份信息。
    我給的建議是一定要另外單獨建一個頁面去承載這個功能,而不要將這兩個加載頁面糅合到一起。
    裏面的頁面展示我們可以用組件化的方式去做,但是頁面的邏輯一點更要分開。
    因為這兩種情況真的很容易混雜,也是為了利於後面的維護工作。
  4. 正常啟動時的加載頁面也可以看情況糅合到首頁的onLoad裏面。
    但是如果有可能,還是希望放在單獨的頁面里。
    首頁往往功能很多,代碼量比較大,不要將本來可以分離出去的功能放進去。
    還是那句話,頁面的職責分開。

我這裏講的其實還是一個比較常見的功能,通常我們的業務也不一定像上面這樣簡單。

所以如果涉及到這方面的操作,在需求分析和設計的時候就應該考慮清楚。

如果等到功能開發的時候再去考慮這些事情,那麼等待你的一定是延期或者加班。

退出重進指定頁面(啟動小程序后,退出小程序,從小程序的分享卡片或者微信發送的通知消息進入小程序)

這種場景同樣是第四種場景的進階,但是如果你在第四種場景中使用了我所說的加載頁面,那麼接下來的問題會簡單很多。

這一場景下,首先我們需要明白髮生了什麼:

再次進入小程序 => 進入退出小程序時所在頁面A 不觸發onShow => 觸發頁面A onHide => 觸發頁面A onUnload => 進入邀請加載頁面onLoad => 加載頁面onShow

對於第四種場景中的打車小程序而言,如果按照我們先前所說沒有在onLaunch中獲取身份信息,而是放在了加載頁中,那麼現在什麼都不用改。

如果獲取身份信息的請求放在onLaunch中,現在又得在onLoad中加一道邏輯。

當然這裏還是得注意一個問題,對於這一類型的進入小程序的方式,比如從分享卡片進入和微信的通知消息進入。

即使他們所進入的頁面不同,但是他們都可以使用這個載入頁面去做判斷。

與正常啟動場景的載入頁面是不同的,他們本來就是同一種入口場景。

所以該共用的地方還是得共用,用不同的業務code判斷即可。

總結

總的來說,以上的幾種情況應該能涵蓋絕大多數小程序的入口場景。

整理的目的其實主要是為了做需求分析和設計時參考使用,以避免在考慮業務問題時漏過這些場景導致後期的工作計劃受到影響。

所謂加班和項目延期發布,大都是前期需求分析和設計考慮不周。

我們不可能考慮到所有的場景,但是應該盡善盡美。

謀定而後動,前事不忘後事之師,也算是PDCA了。

【精選推薦文章】

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

想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

帶您來看台北網站建置台北網頁設計,各種案例分享

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

在 Ubuntu 開啟 GO 程序編譯之旅

本文將使用 putty 連接到一台阿里雲 Ubuntu 16.04 服務器,在其上安裝 go 語言的編譯環境,旨在呈現從安裝到“你好,世界!”涉及的方方面面,希望完成這個過程無須覓它處。

1. 安裝

方式一使用 apt-get

apt-get install golang-go

執行完成之後,會把 golang 安裝在這個位置:/usr/lib/go-1.6/,go 命令會在該目錄的 bin 子目錄下,同時,/usr/bin 下會有該命令的文件鏈接。

當然,也許你並不知道到底安裝在哪,可以通過以下命令找找觀察判斷一下。

# 找名字為 go 的文件
find / -name go

執行 /usr/bin/go version,結果如下,显示的版本號為 go1.6.2,版本比較低。

是不是想卸載?使用以下命令可以完成卸載,跟安裝一一對應。

apt-get --purge remove golang-go

方式二使用 wget

直接下載想要的版本進行安裝,一切皆在掌控之中。通過以下兩條命令,我們把 golang 安裝在 /usr/local/go 下。

# 下載
wget https://storage.googleapis.com/golang/go1.9.1.linux-amd64.tar.gz
# 解壓
tar -xzf go1.9.1.linux-amd64.tar.gz -C /usr/local

2. 設置環境變量

這裡會涉及到3個環境變量,分別是 PATH、GOROOT、GOPATH。
PATH,是為了讓 go 命令隨處可敲。
GOROOT,代表 golang 的根目錄,在設置PATH時可以用一下,如 export PATH=$GOROOT/bin。
GOPATH,特別重要,單獨做一節(2.2)來講。

2.1 設置

環境變量可以設置在不同的文件中。
etc/profile : 對所有用戶生效
~/.profile : 對當前用戶生效

配置在哪都行,能用到即可。在配置文件末尾加上以下文本。

export GOROOT=/usr/local/go
export GOPATH=/usr/goprojs
export PATH=$GOROOT/bin:$PATH:$GOPATH/bin

GOPATH、PATH 多個路徑,中間使用冒號分隔。
配置完成后,使用source ~/.profile 讓其立即生效。

2.2 GOPATH

GOPATH 是GO程序找依賴包的路徑。
其子目錄 src 中可放置各個包的源碼,編譯時會通過 GOPATH 去引用它們。
子目錄 bin 則是編譯之後的可執行文件,在PATH 里要加上各$GOPATH/bin 可以讓編譯的運行文件在執行搜索路徑範圍內方便執行。
子目錄 pkg,編譯包的中間文件,不太關心它。

GOPATH 的第一個路徑特別重要
使用 go get 下載的包都會安裝在第一個路徑,所以如果想讓公共包統一在某處,應該要為它單獨建立一個路徑作為GOPATH的第一個路徑,從而使得 go get 總去向那裡。實際項目最好另建路徑加入GOPATH,這樣即在引用範圍 go get 又影響不到。

附 go get 可帶參數:
|參數|描述|
|——|——|
| -v |显示操作流程的日誌及信息 |
| -u |僅下載丟失的包,不更新已存在的 |
| -d | 只下載,不安裝 |
| -insecure | 允許使用HTTP,而不一定要HTTPS |

3. 你好,世界!

3.1 編寫代碼

建立代碼文件。點此可以在線嘗鮮 GO 代碼

vi hello.go
// 輸入以下代碼保存
package main
import "fmt"

func main(){
    fmt.Println("Hello world!")
}

3.2 執行

直接在文件目錄執行以下命令運行。

go run hello.go
// 或者
go build hello.go
./hello

4. 附件

設置環境變量的配置文件,有網友總結:

/etc/profile,/etc/bashrc 是系統全局環境變量設定
~/.profile,~/.bashrc用戶家目錄下的私有環境變量設定
當登入系統時候獲得一個shell進程時,其讀取環境設定檔有三步
1).首先讀入的是全局環境變量設定檔/etc/profile,然後根據其內容讀取額外的設定的文檔,如
/etc/profile.d和/etc/inputrc
2).然後根據不同使用者帳號,去其家目錄讀取~/.bash_profile,如果這讀取不了就讀取~/.bash_login,這個也讀取不了才會讀取
~/.profile,這三個文檔設定基本上是一樣的,讀取有優先關係
3).然後在根據用戶帳號讀取~/.bashrc
~/.profile與~/.bashrc的區別
都具有個性化定製功能
~/.profile可以設定本用戶專有的路徑,環境變量,等,它只能登入的時候執行一次
~/.bashrc也是某用戶專有設定文檔,可以設定路徑,命令別名,每次shell script的執行都會使用它一次

【精選推薦文章】

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

想要讓你的商品在網路上成為最夯、最多人討論的話題?

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

不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師"嚨底家"!!

【拆分版】Docker-compose構建Zookeeper集群管理Kafka集群

寫在前邊

在搭建Logstash多節點之前,想到就算先搭好Logstash啟動會因為日誌無法連接到Kafka Brokers而無限重試,所以這裏先構建下Zookeeper集群管理的Kafka集群。

眾所周知,Zookeeper是一個高效的分佈式協調中間件,可以提供配置信息管理、命名、分佈式同步(分佈式鎖)、集群管理、數據庫切換等服務。這裏主要用它的集群管理功能,它可以確保在網絡狀態不一致,選出一致的Master節點。它是Apache下的一個Java項目,隸屬於Hadroop系統,正如其名”動物管理員”,作為管理員的角色存在。

有興趣了解zookeeper的原理,可以學習Paxos協議與Zab協議。

ps: Hadroop系統下基本上所有的軟件都是動物命名的

在這裏,我們將使用Zookeeper來管理Kafka集群,Kafka是一種消息隊列(Message Queue)中間件,具有高併發、高吞吐量、容錯性強、可擴展等優點。在ELK日誌系統中使用Kafka作為數據的緩衝層,提高了系統的性能與穩定性。

正好今天通過翻看兩者官方的文檔與其Docker鏡像的文檔,終於搭建成功,遂記之分享諸君。鑒於水平有限,如有寫得不對的地方,歡迎大家指正。

本文搭建架構圖

說明:

Zookeeper搭建成集群后,提供命名服務與集群協調服務,Kafka的節點Broker通過domain與ip進行註冊到Zookeeper集群中,通過Zookeeper的協調能力,選出唯一的Leader節點,集群服務啟動並對外提供服務。

環境準備

  • GNU/Debian Stretch 9.9 linux-4.19
  • Docker 18.09.6
  • Docker-Compose 1.17.1

目錄結構

├── docker-kafka-cluster
│   ├── docker-kafka-cluster-down.sh
│   ├── docker-kafka-cluster-up.sh
│   ├── kafka-01
│   │   ├── docker-compose.yml
│   │   └── .env
│   ├── kafka-02
│   │   ├── docker-compose.yml
│   │   └── .env
│   ├── kafka-03
│   │   ├── docker-compose.yml
│   │   └── .env
│   └── kafka-manager
│       ├── docker-compose.yml
│       └── .env
└── docker-zookeeper-cluster
    ├── docker-zk-cluster-down.sh
    ├── docker-zk-cluster-up.sh
    ├── zk-01
    │   ├── docker-compose.yml
    │   └── .env
    ├── zk-02
    │   ├── docker-compose.yml
    │   └── .env
    └── zk-03
        ├── docker-compose.yml
        └── .env

docker-zookeeper-cluster源碼參見我的Git倉庫 https://github.com/hellxz/docker-zookeeper-cluster.git

docker-kafka-cluster源碼參見我的Git倉庫 https://github.com/hellxz/docker-kafka-cluster.git

各節點容器說明列表

Zookeeper集群

節點目錄名 容器名 client port follower port election port
zk-01 zk-01 2181 2888 3888
zk-02 zk-02 2182 2889 3889
zk-03 zk-03 2183 2890 3890

Kafka集群

節點目錄名 容器名 佔用端口
kafka-01 kafka-1 9092
kafka-02 kafka-2 9093
kafka-03 kafka-3 9094
kafka-manager kafka-manager 19000

各文件內容說明

Zookeeper部分

docker-zookeeper-cluster/zk-01目錄下的.env

.env配置文件為docker-compose.yml提供了多個zookeeper的發現服務節點列表

配置格式為 server.x=x節點主機ip:隨從端口:選舉端口;客戶端口 其中xZOO.MY.ID的數值,客戶端口前是;

# set args to docker-compose.yml by default
# set zookeeper servers, pattern is `server.x=ip:follower-port:election-port;client:port`,
# such as "server.1=192.168.1.1:2888:3888;2181 server.2=192.168.1.2:2888:3888;2181", 
# `x` is the `ZOO.MY.ID` in docker-compose.yml, multiple server separator by white space.
# now you can overide the ip for server.1 server.2 server.3, here demonstrate in one machine so ip same.
ZOO_SERVERS=server.1=10.2.114.110:2888:3888;2181 server.2=10.2.114.111:2889:3889;2182 server.3=10.2.114.112:2890:3890;2183

docker-zookeeper-cluster/zk-01目錄下的docker-compose.yml

version: '3'
services:
    zk-01:
        image: zookeeper:3.5.5
        restart: always
        container_name: zk-01
        ports:
            - 2181:2181 # client port
            - 2888:2888 # follower port
            - 3888:3888 # election port
        environment:
            ZOO_MY_ID: 1 # this zookeeper's id, and others zookeeper node distinguishing
            ZOO_SERVERS: ${ZOO_SERVERS} # zookeeper services list
        network_mode: "host"

Kafka部分

kafka-01目錄下的.env 為例

.env配置文件為docker-compose.yml提供了多個zookeeper的ip:client-port列表

# default env for kafka docker-compose.yml
# set zookeeper cluster, pattern is "zk1-host:port,zk2-host:port,zk3-host:port", use a comma as multiple servers separator.
ZOO_SERVERS=10.2.114.110:2181,10.2.114.111:2182,10.2.114.112:2183

kafka-01目錄下的docker-compose.yml,為docker-compse的配置文件

version: "3"
services:
    kafka-1:
        image: wurstmeister/kafka:2.12-2.1.1
        restart: always
        container_name: kafka-1
        environment:
            - KAFKA_BROKER_ID=1 #kafka的broker.id,區分不同broker
            - KAFKA_LISTENERS=PLAINTEXT://kafka1:9092 #綁定監聽9092端口
            - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka1:9092 #綁定發布訂閱的端口
            - KAFKA_ZOOKEEPER_CONNECT=${ZOO_SERVERS} #連接zookeeper的服務地址
            - KAFKA_MESSAGE_MAX_BYTES=2000000 #單條消息最大字節數
            #- KAFKA_CREATE_TOPICS=Topic1:1:3,Topic2:1:1:compact #創建broker時創建的topic:partition-num:replica-num[:clean.policy]
        network_mode: "host"

KAFKA_CREATE_TOPICS使用官方說明:Topic 1 will have 1 partition and 3 replicas, Topic 2 will have 1 partition, 1 replica and a cleanup.policy set to compact. 文檔地址:https://hub.docker.com/r/wurstmeister/kafka

Zookeeper集群使用

  1. 請確保所布署的 1~3 台服務器網絡可以ping通
  2. 確保第一台主機的2181\2888\3888端口未佔用,第二台主機的2182\2889\3889端口未佔用,第三台主機的2183\2890\3890端口未佔用
  3. 複製zk-01到第一台主機、複製zk-02到第二台主機、複製zk-03到第三台主機
  4. 修改zk-01\zk-02\zk-03目錄下的.env中的ZOO_SERVERS的值,按上述配置要求修改。修改完后的配置應該是集群內通用的,可以scp複製過去。
  5. 單台主機請為docker-zk-cluster-up.shdocker-zk-cluster-down.sh授執行權,使用它們進行up和down操作;多台主機請手動分別進入zk-0x目錄,執行docker-compose up -d以啟動,執行docker-compose down以關閉。

Kafka集群使用

  1. 使用前確保各主機可以互相ping通

  2. 確保zookeeper的服務列表與各對應的zookeeper的ip與客戶端口相同,如不同注意修改.env,集群中.env文件相同,可scp複製

  3. 確保zookeeper集群啟動

  4. 複製kafka-01到第一台主機、複製kafka-02到第二台主機、複製kafka-03到第三台主機

  5. 確保這幾台主機對應的佔用端口號不被佔用 kafka-01對應9092kafka-02對應9093kafka-03對應9094kafka-manager對應19000

  6. 分別對每一台kafka-0x所在的主機修改/etc/hosts,例

    10.2.114.110 kafka1
    10.2.114.111 kafka2
    10.2.114.112 kafka3

    其中每個主機只需要設置自己的主機上的host,比如我複製了kafka-01我就寫本機ip kafka1 ,依次類推.

  7. 單台主機部署kafka集群請為docker-kafka-cluster-up.shdocker-kafka-cluster-down.sh授執行權,不要移動目錄,通過這兩個shell腳本來啟動項目;多台主機請手動進入kafka-0x目錄下,執行docker-compose up -d以後台啟動,執行docker-compose down以移除容器

  8. 啟動腳本中沒有啟動kafka-manager,有需要請自行啟動。為了匹配kafka的版本,使用時設置2.1.1即可。

文中配置部分的ip因使用同一台主機做的測試,所以ip相同,為了防止誤解,在文中已經修改了ip,具體詳見:

  • docker-zookeeper-cluster源碼 https://github.com/hellxz/docker-zookeeper-cluster.git

  • docker-kafka-cluster源碼 https://github.com/hellxz/docker-kafka-cluster.git

本文系原創文章,謝絕轉載

【精選推薦文章】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

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

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