三星將進軍電動車市場?已申請多項相關專利

據「華爾街日報」(WSJ)報導,雖然三星集團對WSJ稱,沒有進軍電動車市場的計畫。但三星電子(Samsung Electronics)今年在美國和韓國申請的專利,涵蓋了可用在電動車的零件和技術,這些文件透露出這個財團可能在動擴大營收來源的腦筋。

根據美國專利商標局(U.S. Patent & Trademark Office)和韓國智慧財產權局訊息服務局(Korea Intellectual Property Rights Information Service)公布的專利申請文件,三星遞交的申請文件是電動車會使用的汽車零件製作新技術。三星申請的技術專利包含輪胎、引擎及分享資訊給汽車和駕駛者的車上電子產品。

電動車目前在全球市場的人氣還不高,但這為三星提供了將技術實力發揮在智慧型手機、電視和記憶體以外產品的機會,三星在競爭激烈的電視和行動裝置市場動能已經放慢。

大和證券分析指出,一旦世界進入電動車時代,車廠和電子企業的界線將日益模糊;等到電動車日益普及,三星可輕鬆加入獲利。8月公佈的專利案顯示,三星有意結合汽車和醫療科技,依照駕駛的身體情況,讓車輛接手控制。

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

【其他文章推薦】

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

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

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

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

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

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

PHEV(插電式混合動力汽車)

插電式混合動力汽車(Plug-in hybrid electric vehicle, PHEV)的是一種混合動力車輛。其充電電池可以使用外部電源充電,而電池容量比電動車小、但大多大於普通油電混合動力車。

插電式混合動力車輛是針對通勤族設計的,多數通勤族通勤距離在十幾公里內,插電式混合動力車輛的電池續航力只要有二三十公里即可以滿足多數通勤需求(通勤時不需啟動內燃機)。

在長途駕駛的情況下,既使是續航力數百公里的電動車也可能沒電,此時插電式混合動力車輛則使用內燃機提供能量,方便性與傳統汽柴油車輛相近;引擎運轉模式更接近最高效率的定速運轉,因此也很省油,甚至可以讓轉子引擎、渦輪引擎達到低油耗低污染的目標;有些車輛使用小型引擎、且不使用複雜的傳動系統,可以抵銷電池所增加的重量跟成本。

但混合動力車的缺點,插電式混合動力車輛可能更嚴重,例如鋰礦問題、電池成本、製造電池的環境成本。

發售中的插電式混合動力車輛有:比亞迪F3DM(比亞迪汽車)、雪佛蘭伏特(通用汽車)、普銳斯III PHEV(豐田汽車)等。

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

【asp.net core 系列】12 數據加密算法

0. 前言

這一篇我們將介紹一下.net core 的加密和解密。在Web應用程序中,用戶的密碼會使用MD5值作為密碼數據存儲起來。而在其他的情況下,也會使用加密和解密的功能。

常見的加密算法分為對稱加密和非對稱加密。所謂的對稱加密是指加密密鑰和解密密鑰是同一個,非對稱加密是值加密密鑰和解密迷藥不同。而我們常應用在保存用戶登錄密碼這個過程中的MD5本質上並不是加密算法,而是一種信息摘要算法。不過MD5盡量保證了每個字符串最後計算出來的值都不一樣,所以在密碼保存中常用MD5做為保密值。

1. 常見對稱加密算法

對稱加密算法,簡單的說就是加密和解密使用相同的密鑰進行運算。對於大多數加密算法,解密和加密是一個互逆的運算。對稱加密算法的安全性取決於密鑰的長度,密鑰越長越安全。當然,不建議使用過長的密鑰。

那麼,我們來看看常見的對稱加密算法有哪些吧,以及C#該如何實現。

1.1 DES 和 DESede 算法

DES算法和DESede算法(又稱三重DES算法) 統稱DES系列算法。DES全稱為Data Encryption Standard,即數據加密標準,是一種使用密鑰加密的塊算法。而DESede就是針對同一塊數據做三次DES加密。這裏就不對原理做過多的介紹了,來看看.net core里如何實現DES加/解密吧。

在Utils項目里,創建目錄Security

在Security目錄下,創建DESHelper類:

namespace Utils.Security
{
    public class DesHelper
    {
        
    }
}

加密解密實現:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Utils.Security
{
    public static class DesHelper
    {
        static DesHelper()
        {
            DesHandler =  DES.Create("DES");
            DesHandler.Key = Convert.FromBase64String("L1yzjGB2sI4=");
            DesHandler.IV = Convert.FromBase64String("uEcGI4JSAuY=");
        }

        private static DES DesHandler { get; }

        /// <summary>
        /// 加密字符
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string Encrypt(string source)
        {
            try
            {
                using (var memStream = new MemoryStream())
                using (var cryptStream = new CryptoStream(memStream, DesHandler.CreateEncryptor(DesHandler.Key, DesHandler.IV),
                    CryptoStreamMode.Write))
                {
                    var bytes = Encoding.UTF8.GetBytes(source);
                    cryptStream.Write(bytes, 0, bytes.Length);
                    cryptStream.FlushFinalBlock();
                    
                    return Convert.ToBase64String(memStream.ToArray());
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return null;
            }
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string Decrypt(string source)
        {
            try
            {
                using (var mStream = new MemoryStream(Convert.FromBase64String(source)))
                using (var cryptoStream =
                    new CryptoStream(mStream, DesHandler.CreateDecryptor(DesHandler.Key, DesHandler.IV), CryptoStreamMode.Read))
                using (var reader = new StreamReader(cryptoStream))
                {
                    return reader.ReadToEnd();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return null;
            }
        }
    }
}

每次調用DesHandler = DES.Create("DES"); 都會重新獲得一個DES算法實現實例,這樣每次獲取的實例中Key、IV這兩個屬性的值也會發生變化。如果直接使用會出現這次加密的數據下次就沒法解密了,為了減少這種情況,所以代碼處手動賦值了Key、IV這兩個屬性。

1.2 AES 加密算法

AES算法(Advanced Encryption Standard)也就是高級數據加密標準算法,是為了解決DES算法中的存在的漏洞而提出的算法標準。現行的AES算法核心是Rijndael算法。當然了,這個不用太過於關心。我們直接看看是如何實現吧:

同樣,在Security目錄創建一個AesHelper類:

namespace Utils.Security
{
    public static class AesHelper
    {
        
    }
}

具體的加解密實現:

using System;
using System.IO;
using System.Security.Cryptography;

namespace Utils.Security
{
    public static class AesHelper
    {
        static AesHelper()
        {
            AesHandler = Aes.Create();
            AesHandler.Key = Convert.FromBase64String("lB2BxrJdI4UUjK3KEZyQ0obuSgavB1SYJuAFq9oVw0Y=");
            AesHandler.IV = Convert.FromBase64String("6lra6ceX26Fazwj1R4PCOg==");
        }

        private static Aes AesHandler { get; }

        public static string Encrypt(string source)
        {
            using (var mem = new MemoryStream())
            using (var stream = new CryptoStream(mem, AesHandler.CreateEncryptor(AesHandler.Key, AesHandler.IV),
                CryptoStreamMode.Write))
            {
                using (var writer = new StreamWriter(stream))
                {
                    writer.Write(source);
                }   
                return Convert.ToBase64String(mem.ToArray());
            }
            
        }

        public static string Decrypt(string source)
        {
            var data = Convert.FromBase64String(source);
            using (var mem = new MemoryStream(data))
            using (var crypto = new CryptoStream(mem, AesHandler.CreateDecryptor(AesHandler.Key, AesHandler.IV),
                CryptoStreamMode.Read))
            using (var reader = new StreamReader(crypto))
            {
                return reader.ReadToEnd();
            }
        }
    }
}

2. 常見非對稱加密算法

非對稱加密算法,指的是加密密鑰和解密密鑰並不相同。非對稱加密算法的秘鑰通常成對出現,分為公開密鑰和私有密鑰。公開密鑰可以以公開的形式發給數據交互方,而不會產生泄密的風險。因為非對稱加密算法,無法通過公開密鑰推算私有密鑰,反之亦然。

通常,非對稱加密算法是用公鑰進行加密,使用私鑰進行解密。

2.1 RSA算法

RSA算法是標準的非對稱加密算法,名字來源是三位發明者的姓氏首字母。RSA公開密鑰密碼體制是一種使用不同的加密密鑰與解密密鑰,“由已知加密密鑰推導出解密密鑰在計算上是不可行的”密碼體制 。其安全性取決於密鑰的長度,1024位的密鑰幾乎不可能被破解。

同樣,在Utils.Security下創建RSAHelper類:

namespace Utils.Security
{
    public static class RsaHelper
    {
        
    }
}

具體實現:

using System;
using System.Security.Cryptography;

namespace Utils.Security
{
    public static class RsaHelper
    {
        public static RSAParameters PublicKey { get; private set; }
        public static RSAParameters PrivateKey { get; private set; }

        static RsaHelper()
        {
            
        }

        public static void InitWindows()
        {
            var parameters = new CspParameters()
            {
                KeyContainerName = "RSAHELPER" // 默認的RSA保存密鑰的容器名稱
            };
            var handle = new RSACryptoServiceProvider(parameters);
            PublicKey = handle.ExportParameters(false);
            PrivateKey = handle.ExportParameters(true);
        }

        public static void ExportKeyPair(string publicKeyXmlString, string privateKeyXmlString)
        {
            var handle  = new RSACryptoServiceProvider();
            handle.FromXmlString(privateKeyXmlString);
            PrivateKey = handle.ExportParameters(true);
            handle.FromXmlString(publicKeyXmlString);
            PublicKey = handle.ExportParameters(false);
        }
        public static byte[] Encrypt(byte[] dataToEncrypt)
        {
            try
            {
                byte[] encryptedData;
                using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
                {
                    RSA.ImportParameters(PublicKey);
                    encryptedData = RSA.Encrypt(dataToEncrypt, true);
                }

                return encryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.Message);
                return null;
            }
        }

        public static byte[] Decrypt(byte[] dataToDecrypt)
        {
            try
            {
                byte[] decryptedData;
                using (var rsa = new RSACryptoServiceProvider())
                {
                    rsa.ImportParameters(PrivateKey);
                    decryptedData = rsa.Decrypt(dataToDecrypt, true);
                }
                return decryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.ToString());
                return null;
            }
        }
    }
}

因為RSA的特殊性,需要預先設置好公鑰和私鑰。C# 支持多種方式導入密鑰,這裏就不做過多介紹了。

3. 信息摘要算法

這種算法嚴格意義上並不是加密算法,因為它完全不可逆。也就是說,一旦進行使用該類型算法加密后,無法解密還原出數據。當然了,也正是因為這種特性常常被用來做密碼的保存。因為這樣可以避免某些人拿到數據庫與代碼后,可以簡單反推出用戶的密碼。

3.1 MD5算法

最常用的信息摘要算法就是MD5 加密算法,MD5信息摘要算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函數,可以產生出一個128位(16字節)的散列值(hash value),用於確保信息傳輸完整一致。

原理不解釋,我們看下如何實現,照例現在Security下創建MD5Helper:

namespace Utils.Security
{
    public static class Md5Helper
    {
        
    }
}

具體實現:

using System.Security.Cryptography;
using System.Text;

namespace Utils.Security
{
    public static class Md5Helper
    {
        private static MD5 Hanlder { get; } = new MD5CryptoServiceProvider();

        public static string GetMd5Str(string source)
        {
            var data = Encoding.UTF8.GetBytes(source);
            var security = Hanlder.ComputeHash(data);
            var sb = new StringBuilder();
            foreach (var b in security)
            {
                sb.Append(b.ToString("X2"));
            }

            return sb.ToString();
        }
    }
}

4 總結

這一篇簡單介紹了四種常用的加密算法的實現,當然最常用的就是 MD5,因為這個是大多數系統用來做密碼保存的加密算法。

更多內容煩請關注我的博客《高先生小屋》

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

【其他文章推薦】

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

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

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

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

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

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

01 . 容器編排簡介及Kubernetes核心概念

Kubernetes簡介

Kubernetes是谷歌嚴格保密十幾年的秘密武器—Borg的一個開源版本,是Docker分佈式系統解決方案.2014年由Google公司啟動.

Kubernetes提供了面嚮應用的容器集群部署和管理系統。Kubernetes的目標旨在消除編排物理/虛擬計算,網絡和存儲基礎設施的負擔,並使應用程序運營商和開發人員完全將重點放在以容器為中心的原語上進行自助運營。Kubernetes 也提供穩定、兼容的基礎(平台),用於構建定製化的workflows 和更高級的自動化任務。 Kubernetes 具備完善的集群管理能力,包括多層次的安全防護和准入機制、多租戶應用支撐能力、透明的服務註冊和服務發現機制、內建負載均衡器、故障發現和自我修復能力、服務滾動升級和在線擴容、可擴展的資源自動調度機制、多粒度的資源配額管理能力。 Kubernetes 還提供完善的管理工具,涵蓋開發、部署測試、運維監控等各個環節。

Kubernetes作為雲原生應用的基石,相當於一個雲操作系統,其重要性不言而喻。

容器編排

容器編排引擎三足鼎立:

    Mesos
    Docker Swarm+compose
    Kubernetes

早在 2015 年 5 月,Kubernetes 在 Google 上的搜索熱度就已經超過了 Mesos 和 Docker Swarm,從那兒之後更是一路飆升,將對手甩開了十幾條街,容器編排引擎領域的三足鼎立時代結束。

目前,AWS、Azure、Google、阿里雲、騰訊雲等主流公有雲提供的是基於 Kubernetes 的容器服務;Rancher、CoreOS、IBM、Mirantis、Oracle、Red Hat、VMWare 等無數廠商也在大力研發和推廣基於 Kubernetes 的容器 CaaS 或 PaaS 產品。可以說,Kubernetes 是當前容器行業最炙手可熱的明星。

Google 的數據中心裏運行着超過 20 億個容器,而且 Google 十年前就開始使用容器技術。

最初,Google 開發了一個叫 Borg 的系統(現在命名為 Omega)來調度如此龐大數量的容器和工作負載。在積累了這麼多年的經驗后,Google 決定重寫這個容器管理系統,並將其貢獻到開源社區,讓全世界都能受益。這個項目就是 Kubernetes。簡單的講,Kubernetes 是 Google Omega 的開源版本。

跟很多基礎設施領域先有工程實踐、後有方法論的發展路線不同,Kubernetes 項目的理論基礎則要比工程實踐走得靠前得多,這當然要歸功於 Google 公司在 2015 年 4 月發布的 Borg 論文了。

Borg 系統,一直以來都被譽為 Google 公司內部最強大的”秘密武器”。雖然略顯誇張,但這個說法倒不算是吹牛。

因為,相比於 Spanner、BigTable 等相對上層的項目,Borg 要承擔的責任,是承載 Google 公司整個基礎設施的核心依賴。在 Google 公司已經公開發表的基礎設施體系論文中,Borg 項目當仁不讓地位居整個基礎設施技術棧的最底層。

由於這樣的定位,Borg 可以說是 Google 最不可能開源的一個項目。而幸運地是,得益於 Docker 項目和容器技術的風靡,它卻終於得以以另一種方式與開源社區見面,這個方式就是 Kubernetes 項目。

所以,相比於”小打小鬧”的 Docker 公司、”舊瓶裝新酒”的 Mesos 社區,Kubernetes 項目從一開始就比較幸運地站上了一個他人難以企及的高度:在它的成長階段,這個項目每一個核心特性的提出,幾乎都脫胎於 Borg/Omega 系統的設計與經驗。更重要的是,這些特性在開源社區落地的過程中,又在整個社區的合力之下得到了極大的改進,修復了很多當年遺留在 Borg 體系中的缺陷和問題。

所以,儘管在發布之初被批評是”曲高和寡”,但是在逐漸覺察到 Docker 技術棧的”稚嫩”和 Mesos 社區的”老邁”之後,這個社區很快就明白了:k8s 項目在 Borg 體系的指導下,體現出了一種獨有的”先進性”與”完備性”,而這些特質才是一個基礎設施領域開源項目賴以生存的核心價值。

什麼是編排

一個正在運行的 Linux 容器,可以分成兩部分看待

1 . 容器的靜態視圖

一組聯合掛載在 /var/lib/docker/aufs/mnt 上的 rootfs,這一部分稱為”容器鏡像”(Container Image)

2 . 容器的動態視圖

一個由 Namespace+Cgroups 構成的隔離環境,這一部分稱為”容器運行時”(Container Runtime)

作為一名開發者,其實並不關心容器運行時的差異。在整個”開發 – 測試 – 發布”的流程中,真正承載着容器信息進行傳遞的,是容器鏡像,而不是容器運行時。

這正是容器技術圈在 Docker 項目成功后不久,就迅速走向了”容器編排”這個”上層建築”的主要原因:作為一家雲服務商或者基礎設施提供商,我只要能夠將用戶提交的 Docker 鏡像以容器的方式運行起來,就能成為這個非常熱鬧的容器生態圖上的一個承載點,從而將整個容器技術棧上的價值,沉澱在我的這個節點上。

更重要的是,只要從這個承載點向 Docker 鏡像製作者和使用者方向回溯,整條路徑上的各個服務節點,比如 CI/CD、監控、安全、網絡、存儲等等,都有可以發揮和盈利的餘地。這個邏輯,正是所有雲計算提供商如此熱衷於容器技術的重要原因:通過容器鏡像,它們可以和潛在用戶(即,開發者)直接關聯起來。

從一個開發者和單一的容器鏡像,到無數開發者和龐大的容器集群,容器技術實現了從”容器”到”容器雲”的飛躍,標志著它真正得到了市場和生態的認可。

這樣,容器就從一個開發者手裡的小工具,一躍成為了雲計算領域的絕對主角;而能夠定義容器組織和管理規範的”容器編排”技術,則當仁不讓地坐上了容器技術領域的”頭把交椅”。

最具代表性的容器編排工具

# 1. Docker 公司的 Compose+Swarm 組合
# 2. Google 與 RedHat 公司共同主導的 Kubernetes 項目

編排工具

Swarm與CoreOS

Docker 公司發布 Swarm 項目

Docker 公司在 2014 年發布 Swarm 項目. 一個有意思的事實:雖然通過”容器”這個概念完成了對經典 PaaS 項目的”降維打擊”,但是 Docker 項目和 Docker 公司,兜兜轉轉了一年多,卻還是回到了 PaaS 項目原本深耕多年的那個戰場:如何讓開發者把應用部署在我的項目上

Docker 項目從發布之初就全面發力,從技術、社區、商業、市場全方位爭取到的開發者群體,實際上是為此後吸引整個生態到自家”PaaS”上的一個鋪墊。只不過這時,”PaaS”的定義已經全然不是 Cloud Foundry 描述的那個樣子,而是變成了一套以 Docker 容器為技術核心,以 Docker 鏡像為打包標準的、全新的”容器化”思路。

這正是 Docker 項目從一開始悉心運作”容器化”理念和經營整個 Docker 生態的主要目的。

Docker 公司在 Docker 項目已經取得巨大成功后,執意要重新走回 PaaS 之路的原因:

雖然 Docker 項目備受追捧,但用戶們最終要部署的,還是他們的網站、服務、數據庫,甚至是雲計算業務。只有那些能夠為用戶提供平台層能力的工具,才會真正成為開發者們關心和願意付費的產品。而 Docker 項目這樣一個只能用來創建和啟停容器的小工具,最終只能充當這些平台項目的”幕後英雄”。

Docker 公司的老朋友和老對手 CoreOS:

CoreOS 是一個基礎設施領域創業公司。 核心產品是一個定製化的操作系統,用戶可以按照分佈式集群的方式,管理所有安裝了這個操作系統的節點。從而,用戶在集群里部署和管理應用就像使用單機一樣方便了。

Docker 項目發布后,CoreOS 公司很快就認識到可以把”容器”的概念無縫集成到自己的這套方案中,從而為用戶提供更高層次的 PaaS 能力。所以,CoreOS 很早就成了 Docker 項目的貢獻者,並在短時間內成為了 Docker 項目中第二重要的力量。

2014 年底,CoreOS 公司與 Docker 公司停止合作,並推出自己研製的 Rocket(後來叫 rkt)容器。

原因是 Docker 公司對 Docker 項目定位的不滿足。Docker 公司的解決方法是讓 Docker 項目提供更多的平台層能力,即向 PaaS 項目進化。這與 CoreOS 公司的核心產品和戰略發生了嚴重衝突。

Docker 公司在 2014 年就已經定好了平台化的發展方向,並且絕對不會跟 CoreOS 在平台層面開展任何合作。這樣看來,Docker 公司在 2014 年 12 月的 DockerCon 上發布 Swarm 的舉動,也就一點都不突然了。

CoreOS 項目

依託於一系列開源項目(比如 Container Linux 操作系統、Fleet 作業調度工具、systemd 進程管理和 rkt 容器),一層層搭建起來的平台產品

Swarm 項目:

以一個完整的整體來對外提供集群管理功能。Swarm 的最大亮點是它完全使用 Docker 項目原本的容器管理 API 來完成集群管理,比如:

單機 Docker 項目
docker run 我的容器

多機 Docker 項目

“docker run -H ” 我的 Swarm 集群 API 地址 ” ” 我的容器 “`

在部署了 Swarm 的多機環境下,用戶只需使用原先的 Docker 指令創建一個容器,這個請求就會被 Swarm 攔截下來處理,然後通過具體的調度算法找到一個合適的 Docker Daemon 運行起來。

這個操作方式簡潔明了,對於已經了解過 Docker 命令行的開發者們也很容易掌握。所以,這樣一個”原生”的 Docker 容器集群管理項目一經發布,就受到了已有 Docker 用戶群的熱捧。相比之下,CoreOS 的解決方案就顯得非常另類,更不用說用戶還要去接受完全讓人摸不着頭腦、新造的容器項目 rkt 了。

Swarm 項目只是 Docker 公司重新定義”PaaS”的關鍵一環。2014 年到 2015 年這段時間里,Docker 項目的迅速走紅催生出了一個非常繁榮的”Docker 生態”。在這個生態里,圍繞着 Docker 在各個層次進行集成和創新的項目層出不窮.

cncf(Fig/Compose)

Fig 項目

被docker收購后改名為 Compose

Fig 項目基本上只是靠兩個人全職開發和維護的,可它卻是當時 GitHub 上熱度堪比 Docker 項目的明星。

Fig 項目受歡迎的原因

是它在開發者面前第一次提出”容器編排”(Container Orchestration)的概念。

“編排”(Orchestration)在雲計算行業里不算是新詞彙,主要是指用戶如何通過某些工具或者配置來完成一組虛擬機以及關聯資源的定義、配置、創建、刪除等工作,然後由雲計算平台按照這些指定的邏輯來完成的過程。

容器時代,”編排”就是對 Docker 容器的一系列定義、配置和創建動作的管理。而 Fig 的工作實際上非常簡單:假如現在用戶需要部署的是應用容器 A、數據庫容器 B、負載均衡容器 C,那麼 Fig 就允許用戶把 A、B、C 三個容器定義在一個配置文件中,並且可以指定它們之間的關聯關係,比如容器 A 需要訪問數據庫容器 B。

接下來,只需執行一條非常簡單的指令:# fig up

Fig 就會把這些容器的定義和配置交給 Docker API 按照訪問邏輯依次創建,一系列容器就都啟動了;而容器 A 與 B 之間的關聯關係,也會交給 Docker 的 Link 功能通過寫入 hosts 文件的方式進行配置。更重要的是,你還可以在 Fig 的配置文件里定義各種容器的副本個數等編排參數,再加上 Swarm 的集群管理能力,一個活脫脫的 PaaS 呼之欲出。

它成了 Docker 公司到目前為止第二大受歡迎的項目,一直到今也依然被很多人使用。

當時的這個容器生態里,還有很多開源項目或公司。比如:

專門負責處理容器網絡的 SocketPlane 項目(後來被 Docker 公司收購)

專門負責處理容器存儲的 Flocker 項目(後來被 EMC 公司收購)

專門給 Docker 集群做圖形化管理界面和對外提供雲服務的 Tutum 項目(後來被 Docker 公司收購)等等。

Mesosphere與Mesos

老牌集群管理項目 Mesos 和它背後的創業公司 Mesosphere:Mesos 社區獨特的競爭力:

超大規模集群的管理經驗

Mesos 早已通過了萬台節點的驗證,2014 年之後又被廣泛使用在 eBay 等大型互聯網公司的生產環境中。

Mesos 是 Berkeley 主導的大數據套件之一,是大數據火熱時最受歡迎的資源管理項目,也是跟 Yarn 項目殺得難捨難分的實力派选手。

大數據所關注的計算密集型離線業務,其實並不像常規的 Web 服務那樣適合用容器進行託管和擴容,也沒有對應用打包的強烈需求,所以 Hadoop、Spark 等項目到現在也沒在容器技術上投下更大的賭注;

但對於 Mesos 來說,天生的兩層調度機制讓它非常容易從大數據領域抽身,轉而去支持受眾更加廣泛的 PaaS 業務。

在這種思路指導下,Mesosphere 公司發布了一個名為 Marathon 的項目,這個項目很快就成為 Docker Swarm 的一個有力競爭對手。

通過 Marathon 實現了諸如應用託管和負載均衡的 PaaS 功能之後,Mesos+Marathon 的組合實際上進化成了一個高度成熟的 PaaS 項目,同時還能很好地支持大數據業務。

Mesosphere 公司提出”DC/OS”(數據中心操作系統)的口號和產品:

旨在使用戶能夠像管理一台機器那樣管理一個萬級別的物理機集群,並且使用 Docker 容器在這個集群里自由地部署應用。這對很多大型企業來說具有着非同尋常的吸引力。

這時的容器技術生態, CoreOS 的 rkt 容器完全打不開局面,Fleet 集群管理項目更是少有人問津,CoreOS 完全被 Docker 公司壓制了。

RedHat 也是因為對 Docker 公司平台化戰略不滿而憤憤退出。但此時,它竟只剩下 OpenShift 這個跟 Cloud Foundry 同時代的經典 PaaS 一張牌可以打,跟 Docker Swarm 和轉型后的 Mesos 完全不在同一個”競技水平”之上。

google與k8s

2014 年 6 月,基礎設施領域的翹楚 Google 公司突然發力,正宣告了一個名叫 Kubernetes 項目的誕生。這個項目,不僅挽救了當時的 CoreOS 和 RedHat,還如同當年 Docker 項目的橫空出世一樣,再一次改變了整個容器市場的格局。

這段時間,也正是 Docker 生態創業公司們的春天,大量圍繞着 Docker 項目的網絡、存儲、監控、CI/CD,甚至 UI 項目紛紛出台,也湧現出了很多 Rancher、Tutum 這樣在開源與商業上均取得了巨大成功的創業公司。

在 2014~2015 年間,整個容器社區可謂熱鬧非凡。

這令人興奮的繁榮背後,卻浮現出了更多的擔憂。這其中最主要的負面情緒,是對 Docker 公司商業化戰略的種種顧慮。

事實上,很多從業者也都看得明白,Docker 項目此時已經成為 Docker 公司一個商業產品。而開源,只是 Docker 公司吸引開發者群體的一個重要手段。不過這麼多年來,開源社區的商業化其實都是類似的思路,無非是高不高調、心不心急的問題罷了。

而真正令大多數人不滿意的是,Docker 公司在 Docker 開源項目的發展上,始終保持着絕對的權威和發言權,並在多個場合用實際行動挑戰到了其他玩家(比如,CoreOS、RedHat,甚至谷歌和微軟)的切身利益。

那麼,這個時候,大家的不滿也就不再是在 GitHub 上發發牢騷這麼簡單了。

相信很多容器領域的老玩家們都聽說過,Docker 項目剛剛興起時,Google 也開源了一個在內部使用多年、經歷過生產環境驗證的 Linux 容器:lmctfy(Let Me Container That For You)。

然而,面對 Docker 項目的強勢崛起,這個對用戶沒那麼友好的 Google 容器項目根本沒有招架之力。所以,知難而退的 Google 公司,向 Docker 公司表示了合作的願望:關停這個項目,和 Docker 公司共同推進一个中立的容器運行時(container runtime)庫作為 Docker 項目的核心依賴。

不過,Docker 公司並沒有認同這個明顯會削弱自己地位的提議,還在不久后,自己發布了一個容器運行時庫 Libcontainer。這次匆忙的、由一家主導的、並帶有戰略性考量的重構,成了 Libcontainer 被社區長期詬病代碼可讀性差、可維護性不強的一個重要原因。

至此,Docker 公司在容器運行時層面上的強硬態度,以及 Docker 項目在高速迭代中表現出來的不穩定和頻繁變更的問題,開始讓社區叫苦不迭。

這種情緒在 2015 年達到了一個高潮,容器領域的其他幾位玩家開始商議”切割”Docker 項目的話語權。而”切割”的手段也非常經典,那就是成立一个中立的基金會。

於是,2015 年 6 月 22 日,由 Docker 公司牽頭,CoreOS、Google、RedHat 等公司共同宣布,Docker 公司將 Libcontainer 捐出,並改名為 RunC 項目,交由一個完全中立的基金會管理,然後以 RunC 為依據,大家共同制定一套容器和鏡像的標準和規範。

這套標準和規範,就是 OCI( Open Container Initiative )。OCI 的提出,意在將容器運行時和鏡像的實現從 Docker 項目中完全剝離出來。這樣做,一方面可以改善 Docker 公司在容器技術上一家獨大的現狀,另一方面也為其他玩家不依賴於 Docker 項目構建各自的平台層能力提供了可能。

不過,OCI 的成立更多的是這些容器玩家出於自身利益進行干涉的一個妥協結果。儘管 Docker 是 OCI 的發起者和創始成員,它卻很少在 OCI 的技術推進和標準制定等事務上扮演關鍵角色,也沒有動力去积極地推進這些所謂的標準。

這也是迄今為止 OCI 組織效率持續低下的根本原因。

OCI 並沒能改變 Docker 公司在容器領域一家獨大的現狀,Google 和 RedHat 等公司於是把第二把武器擺上了檯面。

Docker 之所以不擔心 OCI 的威脅,原因就在於它的 Docker 項目是容器生態的事實標準,而它所維護的 Docker 社區也足夠龐大。可是,一旦這場鬥爭被轉移到容器之上的平台層,或者說 PaaS 層,Docker 公司的競爭優勢便立刻捉襟見肘了。

在這個領域里,像 Google 和 RedHat 這樣的成熟公司,都擁有着深厚的技術積累;而像 CoreOS 這樣的創業公司,也擁有像 Etcd 這樣被廣泛使用的開源基礎設施項目。

可是 Docker 公司卻只有一個 Swarm。

所以這次,Google、RedHat 等開源基礎設施領域玩家們,共同牽頭髮起了一個名為 CNCF(Cloud Native Computing Foundation)的基金會。這個基金會的目的其實很容易理解:它希望,以 Kubernetes 項目為基礎,建立一個由開源基礎設施領域廠商主導的、按照獨立基金會方式運營的平台級社區,來對抗以 Docker 公司為核心的容器商業生態。

為了打造出一個圍繞 Kubernetes 項目的”護城河”,CNCF 社區就需要至少確保兩件事情:

# 1. Kubernetes 項目必須能夠在容器編排領域取得足夠大的競爭優勢
# 2. CNCF 社區必須以 Kubernetes 項目為核心,覆蓋足夠多的場景

CNCF 社區如何解決 Kubernetes 項目在編排領域的競爭力的問題:

在容器編排領域,Kubernetes 項目需要面對來自 Docker 公司和 Mesos 社區兩個方向的壓力。Swarm 和 Mesos 實際上分別從兩個不同的方向講出了自己最擅長的故事:Swarm 擅長的是跟 Docker 生態的無縫集成,而 Mesos 擅長的則是大規模集群的調度與管理。

這兩個方向,也是大多數人做容器集群管理項目時最容易想到的兩個出發點。也正因為如此,Kubernetes 項目如果繼續在這兩個方向上做文章恐怕就不太明智了。

Kubernetes 選擇的應對方式是:Borg

k8s 項目大多來自於 Borg 和 Omega 系統的內部特性,這些特性落到 k8s 項目上,就是 Pod、Sidecar 等功能和設計模式。

這就解釋了,為什麼 Kubernetes 發布后,很多人”抱怨”其設計思想過於”超前”的原因:Kubernetes 項目的基礎特性,並不是幾個工程師突然”拍腦袋”想出來的東西,而是 Google 公司在容器化基礎設施領域多年來實踐經驗的沉澱與升華。這正是 Kubernetes 項目能夠從一開始就避免同 Swarm 和 Mesos 社區同質化的重要手段。

CNCF 接下來的任務是如何把這些先進的思想通過技術手段在開源社區落地,並培育出一個認同這些理念的生態?

RedHat 發揮了重要作用。當時,Kubernetes 團隊規模很小,能夠投入的工程能力十分緊張,這恰恰是 RedHat 的長處。RedHat 更是世界上為數不多、能真正理解開源社區運作和項目研發真諦的合作夥伴。

RedHat 與 Google 聯盟的成立,不僅保證了 RedHat 在 Kubernetes 項目上的影響力,也正式開啟了容器編排領域”三國鼎立”的局面。

Mesos 社區與容器技術的關係,更像是”借勢”,而不是這個領域真正的參与者和領導者。這個事實,加上它所屬的 Apache 社區固有的封閉性,導致了 Mesos 社區雖然技術最為成熟,卻在容器編排領域鮮有創新。

一開始,Docker 公司就把應對 Kubernetes 項目的競爭擺在首要位置:
一方面,不斷強調”Docker Native”的”重要性”
一方面,與 k8s 項目在多個場合進行了直接的碰撞。

這次競爭的發展態勢,很快就超過了 Docker 公司的預期。

Kubernetes 項目並沒有跟 Swarm 項目展開同質化的競爭
所以 “Docker Native”的說辭並沒有太大的殺傷力
相反 k8s 項目讓人耳目一新的設計理念和號召力,很快就構建出了一個與眾不同的容器編排與管理的生態。

Kubernetes 項目在 GitHub 上的各項指標開始一騎絕塵,將 Swarm 項目遠遠地甩在了身後.

CNCF 社區如何解決第二個問題:

在已經囊括了容器監控事實標準的 Prometheus 項目后,CNCF 社區迅速在成員項目中添加了 Fluentd、OpenTracing、CNI 等一系列容器生態的知名工具和項目。

而在看到了 CNCF 社區對用戶表現出來的巨大吸引力之後,大量的公司和創業團隊也開始專門針對 CNCF 社區而非 Docker 公司制定推廣策略。

2016 年,Docker 公司宣布了一個震驚所有人的計劃:放棄現有的 Swarm 項目,將容器編排和集群管理功能全部內置到 Docker 項目當中。

Docker 公司意識到了 Swarm 項目目前唯一的競爭優勢,就是跟 Docker 項目的無縫集成。那麼,如何讓這種優勢最大化呢?那就是把 Swarm 內置到 Docker 項目當中。

從工程角度來看,這種做法的風險很大。內置容器編排、集群管理和負載均衡能力,固然可以使得 Docker 項目的邊界直接擴大到一個完整的 PaaS 項目的範疇,但這種變更帶來的技術複雜度和維護難度,長遠來看對 Docker 項目是不利的。

不過,在當時的大環境下,Docker 公司的選擇恐怕也帶有一絲孤注一擲的意味。

k8s 的應對策略

是反其道而行之,開始在整個社區推進”民主化”架構,即:從 API 到容器運行時的每一層,Kubernetes 項目都為開發者暴露出了可以擴展的插件機制,鼓勵用戶通過代碼的方式介入到 Kubernetes 項目的每一個階段。

Kubernetes 項目的這個變革的效果立竿見影,很快在整個容器社區中催生出了大量的、基於 Kubernetes API 和擴展接口的二次創新工作,比如:
目前熱度極高的微服務治理項目 Istio;
被廣泛採用的有狀態應用部署框架 Operator;
還有像 Rook 這樣的開源創業項目,它通過 Kubernetes 的可擴展接口,把 Ceph 這樣的重量級產品封裝成了簡單易用的容器存儲插件。

在鼓勵二次創新的整體氛圍當中,k8s 社區在 2016 年後得到了空前的發展。更重要的是,不同於之前局限於”打包、發布”這樣的 PaaS 化路線,這一次容器社區的繁榮,是一次完全以 Kubernetes 項目為核心的”百花爭鳴”。

面對 Kubernetes 社區的崛起和壯大,Docker 公司也不得不面對自己豪賭失敗的現實。但在早前拒絕了微軟的天價收購之後,Docker 公司實際上已經沒有什麼迴旋餘地,只能選擇逐步放棄開源社區而專註於自己的商業化轉型。

所以,從 2017 年開始,Docker 公司先是將 Docker 項目的容器運行時部分 Containerd 捐贈給 CNCF 社區,標志著 Docker 項目已經全面升級成為一個 PaaS 平台;緊接着,Docker 公司宣布將 Docker 項目改名為 Moby,然後交給社區自行維護,而 Docker 公司的商業產品將佔有 Docker 這個註冊商標。

Docker 公司這些舉措背後的含義非常明確:它將全面放棄在開源社區同 Kubernetes 生態的競爭,轉而專註於自己的商業業務,並且通過將 Docker 項目改名為 Moby 的舉動,將原本屬於 Docker 社區的用戶轉化成了自己的客戶。

2017 年 10 月,Docker 公司出人意料地宣布,將在自己的主打產品 Docker 企業版中內置 Kubernetes 項目,這標志著持續了近兩年之久的”編排之爭”至此落下帷幕。

2018 年 1 月 30 日,RedHat 宣布斥資 2.5 億美元收購 CoreOS。

2018 年 3 月 28 日,這一切紛爭的始作俑者,Docker 公司的 CTO Solomon Hykes 宣布辭職,曾經紛紛擾擾的容器技術圈子,到此塵埃落定。

容器技術圈子在短短几年裡發生了很多變數,但很多事情其實也都在情理之中。就像 Docker 這樣一家創業公司,在通過開源社區的運作取得了巨大的成功之後,就不得不面對來自整個雲計算產業的競爭和圍剿。而這個產業的垄斷特性,對於 Docker 這樣的技術型創業公司其實天生就不友好。

在這種局勢下,接受微軟的天價收購,在大多數人看來都是一個非常明智和實際的選擇。可是 Solomon Hykes 卻多少帶有一些理想主義的影子,既然不甘於”寄人籬下”,那他就必須帶領 Docker 公司去對抗來自整個雲計算產業的壓力。

只不過,Docker 公司最後選擇的對抗方式,是將開源項目與商業產品緊密綁定,打造了一個極端封閉的技術生態。而這,其實違背了 Docker 項目與開發者保持親密關係的初衷。相比之下,Kubernetes 社區,正是以一種更加溫和的方式,承接了 Docker 項目的未盡事業,即:以開發者為核心,構建一個相對民主和開放的容器生態。

這也是為何,Kubernetes 項目的成功其實是必然的。

很難想象如果 Docker 公司最初選擇了跟 Kubernetes 社區合作,如今的容器生態又將會是怎樣的一番景象。不過我們可以肯定的是,Docker 公司在過去五年裡的風雲變幻,以及 Solomon Hykes 本人的傳奇經歷,都已經在雲計算的長河中留下了濃墨重彩的一筆。

小結
# 1. 容器技術的興起源於 PaaS 技術的普及;
# 2. Docker 公司發布的 Docker 項目具有里程碑式的意義;
# 3. Docker 項目通過"容器鏡像",解決了應用打包這個根本性難題。

# 容器本身沒有價值,有價值的是"容器編排"。
# 也正因為如此,容器技術生態才爆發了一場關於"容器編排"的"戰爭"。而這次戰爭,最終以 Kubernetes 項目和 CNCF 社區的勝利而告終。

Kubernetes核心概念

什麼是Kubernetes?

Kubernetes是一個完備的分佈式系統支撐平台。

Kubernetes具有完備的集群管理能力,包括多層次的安全防護和准入機制/多租戶應用支撐能力、透明的服務註冊和服務發現機制、內建智能負載均衡器、強大的故障發現和自我修復功能、服務滾動升級和在線擴容能力、可擴展的資源自動調度機制,以及多粒度的資源配額管理能力。同時kubernetes提供了完善的管理工具,這些工具覆蓋了包括開發、測試部署、運維監控在內的各個環節;因此kubernetes是一個全新的基於容器技術的分佈式架構解決方案,並且是一個一站式的完備的分佈式系統開發和支撐平台.

Kubernetes Service介紹

Service是分佈式集群結構的核心,一個Server對象有以下關鍵特徵:

# 1. 擁有一個唯一指定的名字(比如mysql-server)
# 2. 擁有一個虛擬IP(Cluster IP,Service IP或VIP和端口號)
# 3. 能夠提供某種遠程服務能力
# 4. 被映射到了提供這種服務能力的一組容器應用上.

Service的服務進程目前都基於Socker通信方式對外提供服務,比如redis、memcache、MySQL、Web Server,或者是實現了某個具體業務的一個特定的TCP Server進程。雖然一個Service通常由多個相關的服務進程來提供服務,每個服務進程都有一個獨立的Endpoint(IP+Port)訪問點,但Kubernetes 能夠讓我們通過Service虛擬Cluster IP+Service Port連接到指定的Service上。有了Kubernetes內建的透明負載均衡和故障恢復機制,不管後端有多少服務進程,也不管某個服務進程是否會由於發生故障而重新部署到其他機器,都不會影響到我們對服務的正常調用。更重要的是這個Service本身一旦創建就不再變化,這意味着Kubernetes集群中,我們再也不用為了服務的IP地址變來變去的問題而頭疼。

Kubernetes Pod介紹

Pod概念 Pod運行在一個我們稱之為Node的環境中,可以是私有雲也可以是公有雲的虛擬機或者物理機上,通常在一個節點上運行幾百個Pod,每個Pod運行着一個特殊的稱之為Pause的容器,其他容器則為業務容器,這些業務容器共享着Pause容器的網絡棧和Volume掛載卷,因此他們之間的通訊和數據交換更為高效,在設計時我們充分利用這一特徵將一組密切相關的服務進程放入同一個Pod中.

並不是每個Pod和它裏面的容器都映射到一個Service上,只是那些提供服務(無論是內還是對外)的一組Pod才會被映射成一個服務.

Service和Pod如何關聯

容器提供了強大的隔離功能,所以有必要把Service提供服務的這組容器放入到容器中隔離,Kubernetes設計了Pod服務,將每個服務進程包裝成相應的Pod中,使其成為Pod中運行的一個容器Container,為了建立Service和Pod間的關聯關係,Kubernetes首先給每個Pod貼上了一個標籤Label,給運行Mysql的Pod貼上了name=mysql標籤,給運行PHP貼上name=php標籤,然後給相應的Service定義標籤選擇器Label Selector,比如Mysql Service的標籤選擇器選擇條件為name=mysql,意為該Service要作用於所有包含name=mysql Label的Pod上,這樣就巧妙的解決了Service和Pod關聯的問題.

Kubernetes RC介紹

RC介紹在Kubernetes集群中,你只需要為需要擴容的Service關聯的Pod創建一個RC(Replication Controller),則該Service的擴容以至於後來的Service升級等頭疼問題都可以迎刃而解,定義一個RC文件包含以下3個關鍵點.

# 1. 目標Pod的定義
# 2. 目標Pod需要運行的副本數量(Replicas)
# 3. 要監控的目標Pod的標籤(Label)

在創建好RC系統自動創建號Pod后,Kubernetes會通過RC中定義的Label篩選出對應的Pod實例並監控其狀態和數量,如果實例數量少於定義的副本數量Replicas則會用RC中定義的Pod模板來創建一個新的Pod,然後將Pod調度到合適的Node上運行,直到Pod實例的數量達到預定目標,這個過程完全是自動化的,無需人干預,只需要修改RC中的副本數量即可.

Kubernetes Master介紹

Kubernetes 里的Master指的是集群控制節點,每個Kubernetes集群里需要有一個Master節點來負責整個集群的管理和控制,基本上Kubernetes所有的控制命令都發給它,它負責具體的執行過程,我們後面執行的所有命令基本上都是在Master節點上運行的。如果Master宕機或不可用,那麼集群內容器的管理都將失效.

Master節點上運行一下一組關鍵進程:

  1. Kubernetes API Server: 提供了HTTP Rest接口的關鍵服務進程,是Kubernetes里所有資源的增刪改查等操作的唯一入口,也是集群控制的入門進程.
  2. Kubernetes Controller Manager 里所有的資源對象的自動化控制中心.
  3. Kubernetes Scheduler: 負責資源調度(Pod調度)的進程

另外在Master節點還需要啟動一個etcd服務,因為Kubernetes里所有資源對象的數據全部保存在etcd中.

Kubernetes Node介紹

除了Master,集群中其他機器稱為Node節點,每個Node都會被分配一些工作負載Docker容器,當某個Node宕機,其上的工作負載都會被Master自動轉移到其他節點上去.

每個Node節點上都運行着以下一組關鍵進程

# 1. kubelet: 負責Pod對應的創建、停止等服務,同時與Master節點密切協作,實現集群管理的基本功能.
# 2. kube-proxy: 實現Kubernetes Service的通信與負載均衡機制的重要組件.
# 3. Docker Engine: Docker引擎,負責本機的容器創建和管理工作

在集群管理方面,Kubernetes將集群中的機器劃分為一個Master節點和一群工作節點(Node)中,在Master節點上運行着集群管理相關的一組進程kube-apiserver,kube-controller-manager和kube-scheduler,這些進程實現了整個集群的資源管理,Pod調度,彈性伸縮,安全控制,系統監控和糾錯等管理功能,並且都是全自動完成的、Node作為集群中的工作節點,運行真正的應用程序,在Node上Kubernetes最小運行單元是Pod,Node上運行着Kubernetes的Kubelet、kube-proxy服務進程,這些服務進程負責Pod創建、啟動、監控、重啟、銷毀以及軟件模式的負載均衡.

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

Netty源碼分析之自定義編解碼器

在日常的網絡開發當中,協議解析都是必須的工作內容,Netty中雖然內置了基於長度、分隔符的編解碼器,但在大部分場景中我們使用的都是自定義協議,所以Netty提供了  MessageToByteEncoder<I>  與  ByteToMessageDecoder  兩個抽象類,通過繼承重寫其中的encode與decode方法實現私有協議的編解碼。這篇文章我們就對Netty中的自定義編解碼器進行實踐與分析。

一、編解碼器的使用

下面是MessageToByteEncoder與ByteToMessageDecoder使用的簡單示例,其中不涉及具體的協議編解碼。

創建一個sever端服務

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        final CodecHandler codecHandler = new CodecHandler();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            if (sslCtx != null) {
                                p.addLast(sslCtx.newHandler(ch.alloc()));
                            }
                            //添加編解碼handler
                            p.addLast(new MessagePacketDecoder(),new MessagePacketEncoder());
                            //添加自定義handler
                            p.addLast(codecHandler);
                        }
                    });

            // Start the server.
            ChannelFuture f = b.bind(PORT).sync();

繼承MessageToByteEncoder並重寫encode方法,實現編碼功能

public class MessagePacketEncoder extends MessageToByteEncoder<byte[]> {

    @Override
    protected void encode(ChannelHandlerContext ctx, byte[] bytes, ByteBuf out) throws Exception {
        //進行具體的編碼處理 這裏對字節數組進行打印
        System.out.println("編碼器收到數據:"+BytesUtils.toHexString(bytes));
        //寫入並傳送數據
        out.writeBytes(bytes);
    }
}

繼承ByteToMessageDecoder 並重寫decode方法,實現解碼功能

public class MessagePacketDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out){
        try {
            if (buffer.readableBytes() > 0) {
                // 待處理的消息包
                byte[] bytesReady = new byte[buffer.readableBytes()];
                buffer.readBytes(bytesReady);
                //進行具體的解碼處理
                System.out.println("解碼器收到數據:"+ByteUtils.toHexString(bytesReady));
                //這裏不做過多處理直接把收到的消息放入鏈表中,並向後傳遞
                out.add(bytesReady);
            
            }
        }catch(Exception ex) {
            
        }

    }

}

實現自定義的消息處理handler,到這裏其實你拿到的已經是編解碼后的數據

public class CodecHandler extends ChannelInboundHandlerAdapter{
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("CodecHandler收到數據:"+ByteUtils.toHexString((byte[])msg));
        byte[] sendBytes = new byte[] {0x7E,0x01,0x02,0x7e};
        ctx.write(sendBytes);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}

運行一個客戶端模擬發送字節0x01,0x02,看一下輸出的執行結果

解碼器收到數據:0102
CodecHandler收到數據:0102
編碼器收到數據:7E01027E

 根據輸出的結果可以看到消息的入站與出站會按照pipeline中自定義的順序傳遞,同時通過重寫encode與decode方法實現我們需要的具體協議編解碼操作。

二、源碼分析

 通過上面的例子可以看到MessageToByteEncoder<I>與ByteToMessageDecoder分別繼承了ChannelInboundHandlerAdapter與ChannelOutboundHandlerAdapter,所以它們也是channelHandler的具體實現,並在創建sever時被添加到pipeline中, 同時為了方便我們使用,netty在這兩個抽象類中內置與封裝了一些其操作;消息的出站和入站會分別觸發write與channelRead事件方法,所以上面例子中我們重寫的encode與decode方法,也都是在父類的write與channelRead方法中被調用,下面我們就別從這兩個方法入手,對整個編解碼的流程進行梳理與分析。

1、MessageToByteEncoder

編碼需要操作的是出站數據,所以在MessageToByteEncoder的write方法中會調用我們重寫的encode具體實現, 把我們內部定義的消息實體編碼為最終要發送的字節流數據發送出去。

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ByteBuf buf = null;
        try {
            if (acceptOutboundMessage(msg)) {//判斷傳入的msg與你定義的類型是否一致
                @SuppressWarnings("unchecked")
                I cast = (I) msg;//轉為你定義的消息類型
                buf = allocateBuffer(ctx, cast, preferDirect);//包裝成一個ByteBuf
                try {
                    encode(ctx, cast, buf);//傳入聲明的ByteBuf,執行具體編碼操作
                } finally {
                    /**
                     * 如果你定義的類型就是ByteBuf 這裏可以幫助你釋放資源,不需要在自己釋放
                     * 如果你定義的消息類型中包含ByteBuf,這裡是沒有作用,需要你自己主動釋放
                     */
                    ReferenceCountUtil.release(cast);//釋放你傳入的資源
                }

                //發送buf
                if (buf.isReadable()) {
                    ctx.write(buf, promise);
                } else {
                    buf.release();
                    ctx.write(Unpooled.EMPTY_BUFFER, promise);
                }
                buf = null;
            } else {
                //類型不一致的話,就直接發送不再執行encode方法,所以這裏要注意如果你傳遞的消息與泛型類型不一致,其實是不會執行的
                ctx.write(msg, promise);
            }
        } catch (EncoderException e) {
            throw e;
        } catch (Throwable e) {
            throw new EncoderException(e);
        } finally {
            if (buf != null) {
                buf.release();//釋放資源
            }
        }
    }

 MessageToByteEncoder的write方法要實現的功能還是比較簡單的,就是把你傳入的數據類型進行轉換和發送;這裡有兩點需要注意:

  • 一般情況下,需要通過重寫encode方法把定義的泛型類型轉換為ByteBuf類型, write方法內部自動幫你執行傳遞或發送操作;
  • 代碼中雖然有通過ReferenceCountUtil.release(cast)釋放你定義的類型資源,但如果定義的消息類中包含ByteBuf對象,仍需要主動釋放該對象資源;

2、ByteToMessageDecoder

從命名上就可以看出ByteToMessageDecoder解碼器的作用是把字節流數據編碼轉換為我們需要的數據格式

作為入站事件,解碼操作的入口自然是channelRead方法

 @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof ByteBuf) {//如果消息是bytebuff
            CodecOutputList out = CodecOutputList.newInstance();//實例化一個鏈表
            try {
                ByteBuf data = (ByteBuf) msg;
                first = cumulation == null;
                if (first) {
                    cumulation = data;
                } else {
                    cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data);
                }
                callDecode(ctx, cumulation, out);//開始解碼
            } catch (DecoderException e) {
                throw e;
            } catch (Exception e) {
                throw new DecoderException(e);
            } finally {
                if (cumulation != null && !cumulation.isReadable()) {//不為空且沒有可讀數據,釋放資源
                    numReads = 0;
                    cumulation.release();
                    cumulation = null;
                } else if (++ numReads >= discardAfterReads) {
                    // We did enough reads already try to discard some bytes so we not risk to see a OOME.
                    // See https://github.com/netty/netty/issues/4275
                    numReads = 0;
                    discardSomeReadBytes();
                }

                int size = out.size();
                decodeWasNull = !out.insertSinceRecycled();
                fireChannelRead(ctx, out, size);//向下傳遞消息
                out.recycle();
            }
        } else {
            ctx.fireChannelRead(msg);
        }
    }

callDecode方法內部通過while循環的方式對ByteBuf數據進行解碼,直到其中沒有可讀數據 

    protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        try {
            while (in.isReadable()) {//判斷ByteBuf是還有可讀數據
                int outSize = out.size();//獲取記錄鏈表大小

                if (outSize > 0) {//判斷鏈表中是否已經有數據
                    fireChannelRead(ctx, out, outSize);//如果有數據繼續向下傳遞
                    out.clear();//清空鏈表

                    // Check if this handler was removed before continuing with decoding.
                    // If it was removed, it is not safe to continue to operate on the buffer.
                    //
                    // See:
                    // - https://github.com/netty/netty/issues/4635
                    if (ctx.isRemoved()) {
                        break;
                    }
                    outSize = 0;
                }

                int oldInputLength = in.readableBytes();
                decodeRemovalReentryProtection(ctx, in, out);//開始調用decode方法

                // Check if this handler was removed before continuing the loop.
                // If it was removed, it is not safe to continue to operate on the buffer.
                //
                // See https://github.com/netty/netty/issues/1664
                if (ctx.isRemoved()) {
                    break;
                }

                //這裏如果鏈表為空且bytebuf沒有可讀數據,就跳出循環
                if (outSize == out.size()) {
                    if (oldInputLength == in.readableBytes()) {
                        break;
                    } else {//有可讀數據繼續讀取
                        continue;
                    }
                }

                if (oldInputLength == in.readableBytes()) {//beytebuf沒有讀取,但卻進行了解碼
                    throw new DecoderException(
                            StringUtil.simpleClassName(getClass()) +
                                    ".decode() did not read anything but decoded a message.");
                }

                if (isSingleDecode()) {//是否設置了每條入站數據只解碼一次,默認false
                    break;
                }
            }
        } catch (DecoderException e) {
            throw e;
        } catch (Exception cause) {
            throw new DecoderException(cause);
        }
    }

decodeRemovalReentryProtection方法內部會調用我們重寫的decode解碼實現

    final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
            throws Exception {
        decodeState = STATE_CALLING_CHILD_DECODE;//標記狀態
        try {
            decode(ctx, in, out);//調用我們重寫的decode解碼實現
        } finally {
            boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING;
            decodeState = STATE_INIT;
            if (removePending) {//這裏判斷標記,防止handlerRemoved事件與解碼操作衝突
                handlerRemoved(ctx);
            }
        }
    }

channelRead方法中接受到數據經過一系列邏輯處理,最終會調用我們重寫的decode方法實現具體的解碼功能;在decode方法中我們只需要ByteBuf類型的數據解析為我們需要的數據格式直接放入 List<Object> out鏈表中即可,ByteToMessageDecoder會自動幫你向下傳遞消息。

三、總結

通過上面的講解,我們可以對Netty中內置自定義編解碼器MessageToByteEncoder與ByteToMessageDecoder有一定的了解,其實它們本質上是Netty封裝的一組專門用於自定義編解碼的channelHandler實現類。在實際開發當中基於這兩個抽象類的實現非常具有實用性,所以在這裏稍作分析, 其中如有不足與不正確的地方還望指出與海涵。

 

關注微信公眾號,查看更多技術文章。

 

 

轉載說明:未經授權不得轉載,授權后務必註明來源(註明:來源於公眾號:架構空間, 作者:大凡)

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

【其他文章推薦】

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

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

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

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

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

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

CPU明明8個核,網卡為啥拚命折騰一號核?

中斷機制

我是CPU一號車間的阿Q,我又來了!

我們日常的工作就是不斷執行代碼指令,不過這看似簡單的工作背後其實也並不輕鬆。

咱不能悶着頭啥也不管一個勁的只管執行代碼,還得和連接在主板上的其他單位打交道。經常保持聯繫的有鍵盤、鼠標、磁盤,哦對,還有網卡,這傢伙最近把我惹到了,待會再說這事兒。

原以為內存那傢伙已經夠慢的了,沒想到跟上面這幾位通個信比他更慢,咱CPU工廠的時間一刻值千金,不能幹等着,耽誤工夫。後來廠里一合計,想了個叫中斷的辦法。

在我們車間裝了個大燈,這些單位想聯繫我們辦事兒,就先給我們發一个中斷信號,大燈就會自動亮起。我們平時工作執行代碼指令的時候,每執行一條指令就會瞅一眼看看大燈有沒有亮起來。一旦發現燈亮了,就把手頭的工作先放一邊,去處理一下。

我們記性很差的,等會處理了完了還得回來接着原來的活繼續干,為了等會回來還能接的起來,走之前得把當前執行的這個線程的各個寄存器的值,執行到哪裡了等等這些信息都保存在這個線程的棧里去。

不過有時候我們在執行非常重要的事情的時候,就不想被他們打斷。於是我們又在車間里那個eflags寄存器中設置了一個標記,如果是1我們才允許被打斷,如果是0那就算天王老子找我們也不管了。

哦不對,還有一種不可以屏蔽的中斷NMI,走得是綠色通道。不過我可不期望有這種事情發生,因為一般都沒有好事,不是電源斷電就是溫度過高,或者總線出了錯誤等這之類嚴重的事情。

8259A PIC

還有一個問題,找我們辦事兒的單位有很多,我們得要區分開來,到底是誰來消息了,而且要是他們一起來找,按什麼樣優先級順序處理,也是一件頭疼的事情。

為此,廠里單獨組建了一個全資的子公司來負責這事兒,他就是可編程中斷控制器PIC,外號8259A,其他單位想聯繫我們都得通過這個PIC,我們只需要和PIC進行對接就可以了。

我們給辦事單位都分配了一個編號,叫做中斷向量。我們還準備了一個表格叫中斷描述符表IDT,表格里記錄了很多信息,其中就有處理這个中斷號對應的函數地址。我們找PIC拿到編號后就執行處理函數就OK了。

這個表格有點大,足足有256項,咱CPU車間空間有限,放不下,就把它放在內存那傢伙那裡了,為了能快速找到這個表,專門添置了一個叫idtr的寄存器指向這個表格。

其實除了中斷,我們在執行指令的時候如果遇到了異常情況,也會去這個表裡執行異常處理函數,最常見的比如遇到了除數是0,內存地址錯誤等等情況。

這種情況下,我們必須主動放下手裡的活,去處理異常,所以我們也說異常是同步的,而中斷不知道什麼時候發生,所以是異步的。

APIC

8259A乾的挺不錯的,不過後來咱們廠擴大規模,從單核CPU變成了多核,他就有點應付不過來了。

終於有一天,廠里召開會議,把8259A給撤了,成立了一個新的全資子公司叫高級可編程中斷控制器APIC,名字就多了個高級兩個字,乾的活還是一樣的。

不過你還別說,這兩個字還真不是吹噓,比8259A不知道高到哪裡去了。

這個APIC的新公司一上台,就成立了兩個部門,一個叫I/O APIC,負責接待那些要找我們辦事兒的單位,一個叫Local APIC,以外包的形式入駐到我CPU的各個車間工作,因為就挨着我們辦公,所以取名叫Local。

I/O APIC收到中斷信號以後,根據自己的策略就分發到對應的Local APIC,咱們八個車間就可以專心處理了,為我們省了不少事兒。

不僅如此,通過這個外包團隊,我們八個車間還能向彼此發起中斷請求,我們把這個叫做處理器間中斷Inter-Processor Interrupt,簡稱IPI

中斷親和性

每當網絡中有數據包到來,網卡那傢伙就發送一个中斷消息過來,告訴我們去處理。

不過最近不知道怎麼回事,網絡數據量激增。咱們廠里明明有8個車間,他非得一個勁的只給我們發消息,搞得我們手頭的工作老是被打斷,忙得不可開交。

終於,我忍不住了,去找網卡那傢伙理論了一番。不過他告訴我,這也不能怪他,分發給誰處理,那是APIC在負責。

想想也是,回頭我就去了APIC那裡,要求他們分攤一點給別的車間處理。

APIC表示這他們做不了主,得讓廠里來決定。

沒過幾天,廠里開了個會,參會的有各車間代表、APIC負責人,還請了操作系統那邊的相關代表過來。

會上,大家為了此事爭執不休。

二號車間虎子:“阿Q,誰叫你們一號車間是Bootstrap Processor,你們就多辛苦一點嘛”

三號車間代表:“你這話說的不合適,大家是一個Team,要互相幫助!要不這樣,既然有這麼多單位要聯繫我們,咱就分下工,比如一號車間負責網卡,二號負責磁盤,我們三號負責鍵盤,以此類推”

五號車間代表:“你想的倒是挺美哦,鍵盤一天能發多少中斷,網卡一天要發多少中斷,你凈挑輕鬆的干。這樣吧,咱就用隨機分發進行負載均衡你們覺得怎麼樣?”

八號車間代表:“隨機個啥啊,多麻煩,依我看吶咱8個車間就輪流來唄”

這時,領導問操作系統代表有沒有什麼建議。

這代表站起身來,推了推眼鏡說到:“幾位有沒有聽過線程的CPU親和性?”

大家都搖了搖頭,問到:“這是個什麼意思?”

“就是有些線程想綁定在你們之中的某一個核上面執行,不希望一會兒在這個核執行,一會兒在那個核執行”

我接過他的話:“好像是有這麼回事兒,之前有遇到過,有個線程一直被分配到我們一號車間,不過我們對這個不用關心吧,執行誰不是幹活啊,對我們都一個樣”

代表搖了搖頭,“唉,這可不一樣!你們每個核的一二級緩存都是自己在管理,要是換到別的核,這緩存多半就沒用了,又得重新來建立,這換來換去的豈不是瞎耽誤功夫嘛!對於一般的線程他們倒是不關心,但是有些線程執行大量的內存訪問和運算處理,又對性能要求很高的話,那就很在意這個問題了”

我們幾個都恍然大悟,紛紛點頭。

虎子起身問到:“那你們是如何實現這個親和性的呢?這跟我們今天的會議又有什麼關係呢?”

代表繼續回答說到:“我先回答你的第一個問題。線程調度是我們操作系統完成的工作,我們提供了API接口,線程通過調用這些接口表明自己的親和性意願,我們在調度的時候就能按照他們的意願把線程分配給你們來執行。”

代表喝了一口水接着說到:“我再回答你的第二個問題。既然線程可以有親和性,那中斷也可以按照這個思路來分發啊!APIC默認有一套分發策略,但是也提供親和性的設置,可以指定誰哪些核來處理,這樣不用把規矩定死,靈活可變,豈不更好?”

剛說完,會議室門口突然出現一年輕少年,揮手將操作系統代表喚了出去。

接下來,我們詳細討論了這種方案的可行性,最後大家一致決定,就照這麼辦,我們一起提出了一個叫中斷親和性的東西,操作系統那邊提供一個可配置的入口smp_affinity,可以通過設置各處理器核的掩碼來決定中斷交由誰來處理,APIC回去負責落地支持。

有了這套方案,再遇到網絡高峰期,咱們一號車間的壓力就有辦法緩解了。

我們剛剛達成一致,操作系統代表返回會議室,神色凝重的說到:“不好意思各位,操作系統那邊有點事情需要趕回去處理一下,先走一步了”

未完待續······

彩蛋

隨着網卡的一聲中斷,一個新的數據包來到了這片土地。

帝國網絡部新來的年輕人顯然沒有意識到危險的到來······

預知後事如何,請關注後續精彩······

往期TOP5文章

真慘!連各大編程語言都擺起地攤了!

因為一個跨域請求,我差點丟了飯碗

完了!CPU一味求快出事兒了!

哈希表哪家強?幾大編程語言吵起來了!

一個HTTP數據包的奇幻之旅

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

擔心成為新的傾倒場 泰國將禁止電子垃圾進口

摘錄自2018年8月16日中央社報導

環境部一名官員今天(16日)說,泰國將在6個月內禁止進口432種廢棄電子用品。中國今年禁止高科技廢棄物進口以來,泰國成為最新呼應的國家。

泰國下達禁令數週前,區域鄰國越南表示,將停止核發新執照給廢棄物進口,並將取締非法運送廢紙、塑膠和金屬。

泰國環境部一名高級官員今天告訴路透社,禁令涵蓋432種電子廢棄物,從電路板到老舊電視機和收音機零件等等,將在6個月內生效。

他並說,禁令是環境部長素拉薩(Surasak Kanchanarat)昨天主持會議時做成決定。

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

【其他文章推薦】

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

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

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

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

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

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

宣示2050年零碳排 全球87大企業領頭邁向1.5℃目標

環境資訊中心綜合外電;姜唯 編譯;彭瑞祥 審校

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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