.NETCore微服務探尋(一) – 網關

前言

一直以來對於.NETCore微服務相關的技術棧都處於一個淺嘗輒止的了解階段,在現實工作中也對於微服務也一直沒有使用的業務環境,所以一直也沒有整合過一個完整的基於.NETCore技術棧的微服務項目。正好由於最近剛好辭職,有了時間可以寫寫自己感興趣的東西,所以在此想把自己了解的微服務相關的概念和技術框架使用實現記錄在一個完整的工程中,由於本人技術有限,所以錯誤的地方希望大家指出。\

項目地址:https://github.com/yingpanwang/fordotnet/tree/dev

什麼是Api網關

  由於微服務把具體的業務分割成單獨的服務,所以如果直接將每個服務都與調用者直接,那麼維護起來將相當麻煩與頭疼,Api網關擔任的角色就是整合請求並按照路由規則轉發至服務的實例,並且由於所有所有請求都經過網關,那麼網關還可以承擔一系列宏觀的攔截功能,例如安全認證,日誌,熔斷

為什麼需要Api網關

 因為Api網關可以提供安全認證,日誌,熔斷相關的宏觀攔截的功能,也可以屏蔽多個下游服務的內部細節

有哪些有名的Api網關項目

  • Zuul Spring Cloud 集成
  • Kong 一款lua輕量級網關項目
  • Ocelot .NETCore網關項目

Ocelot使用

1.通過Nuget安裝Ocelot

2.準備並編輯Ocelot配置信息

Ocelot.json

{
  "ReRoutes": [
    // Auth
    {
      "UpstreamPathTemplate": "/auth/{action}", // 上游請求路徑模板
      "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ], // 上游請求方法
      "ServiceName": "Auth", // 服務名稱
      "UseServiceDiscovery": true, // 是否使用服務發現
      "DownstreamPathTemplate": "/connect/{action}", // 下游匹配路徑模板
      "DownstreamScheme": "http", // 下游請求
      "LoadBalancerOptions": { // 負載均衡配置
        "Type": "RoundRobin"
      }
      //,
      // 如果不採用服務發現需要指定下游host
      //"DownstreamHostAndPorts": [
      //  {
      //    "Host": "10.0.1.10",
      //    "Port": 5000
      //  },
      //  {
      //    "Host": "10.0.1.11",
      //    "Port": 5000
      //  }
      //]
    }
  ],
  "GlobalConfiguration": { // 全局配置信息
    "BaseUrl": "http://localhost:5000", // 請求 baseurl 
    "ServiceDiscoveryProvider": { //服務發現提供者
      "Host": "106.53.199.185",
      "Port": 8500,
      "Type": "Consul" // 使用Consul
    }
  }
}

3.添加Ocelot json文件到項目中

將Config目錄下的ocelot.json添加到項目中

4.在網關項目中 StartUp ConfigService中添加Ocelot的服務,在Configure中添加Ocelot的中間件(由於我這裏使用了Consul作為服務發現,所以需要添加Consul的依賴的服務AddConsul,如果不需要服務發現的話可以不用添加)

5.將需要發現的服務通過代碼在啟動時註冊到Consul中

我這裏自己封裝了一個註冊服務的擴展(寫的比較隨意沒有在意細節)

appsettings.json 中添加註冊服務配置信息

"ServiceOptions": {
    "ServiceIP": "localhost",
    "ServiceName": "Auth",
    "Port": 5800,
    "HealthCheckUrl": "/api/health",
    "ConsulOptions": {
      "Scheme": "http",
      "ConsulIP": "localhost",
      "Port": 8500
    }
  }

擴展代碼 ConsulExtensions(注意:3.1中 IApplicationLifetime已廢棄 所以使用的是IHostApplicationLifetime 作為程序生命周期注入的方式)


using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;

namespace ForDotNet.Common.Consul.Extensions
{
    /// <summary>
    /// 服務配置信息
    /// </summary>
    public class ServiceOptions
    {
        /// <summary>
        /// 服務ip
        /// </summary>
        public string ServiceIP { get; set; }

        /// <summary>
        /// 服務名稱
        /// </summary>
        public string ServiceName { get; set; }

        /// <summary>
        /// 協議類型http or https
        /// </summary>
        public string Scheme { get; set; } = "http";

        /// <summary>
        /// 端口
        /// </summary>
        public int Port { get; set; }

        /// <summary>
        /// 健康檢查接口
        /// </summary>
        public string HealthCheckUrl { get; set; } = "/api/values";

        /// <summary>
        /// 健康檢查間隔時間
        /// </summary>
        public int HealthCheckIntervalSecond { get; set; } = 10;

        /// <summary>
        /// consul配置信息
        /// </summary>
        public ConsulOptions ConsulOptions { get; set; }
    }

    /// <summary>
    /// consul配置信息
    /// </summary>
    public class ConsulOptions
    {
        /// <summary>
        /// consul ip
        /// </summary>
        public string ConsulIP { get; set; }

        /// <summary>
        /// consul 端口
        /// </summary>
        public int Port { get; set; }

        /// <summary>
        /// 協議類型http or https
        /// </summary>
        public string Scheme { get; set; } = "http";
    }

    /// <summary>
    /// consul註冊客戶端信息
    /// </summary>
    public class ConsulClientInfo
    {
        /// <summary>
        /// 註冊信息
        /// </summary>
        public AgentServiceRegistration RegisterInfo { get; set; }

        /// <summary>
        /// consul客戶端
        /// </summary>
        public ConsulClient Client { get; set; }
    }

    /// <summary>
    /// consul擴展(通過配置文件配置)
    /// </summary>
    public static class ConsulExtensions
    {
        private static readonly ServiceOptions serviceOptions = new ServiceOptions();

        /// <summary>
        /// 添加consul
        /// </summary>
        public static void AddConsulServiceDiscovery(this IServiceCollection services)
        {
            var config = services.BuildServiceProvider().GetService<IConfiguration>();
            config.GetSection("ServiceOptions").Bind(serviceOptions);
            //config.Bind(serviceOptions);

            if (serviceOptions == null)
            {
                throw new Exception("獲取服務註冊信息失敗!請檢查配置信息是否正確!");
            }
            Register(services);
        }

        /// <summary>
        /// 添加consul(通過配置opt對象配置)
        /// </summary>
        /// <param name="app"></param>
        /// <param name="life">引用生命周期</param>
        /// <param name="options">配置參數</param>
        public static void AddConsulServiceDiscovery(this IServiceCollection services, Action<ServiceOptions> options)
        {
            options.Invoke(serviceOptions);
            Register(services);
        }

        /// <summary>
        /// 註冊consul服務發現
        /// </summary>
        /// <param name="app"></param>
        /// <param name="life"></param>
        public static void UseConsulServiceDiscovery(this IApplicationBuilder app, IHostApplicationLifetime life)
        {
            var consulClientInfo = app.ApplicationServices.GetRequiredService<ConsulClientInfo>();
            if (consulClientInfo != null)
            {
                life.ApplicationStarted.Register( () =>
                {
                     consulClientInfo.Client.Agent.ServiceRegister(consulClientInfo.RegisterInfo).Wait();
                });

                life.ApplicationStopping.Register( () =>
                {
                     consulClientInfo.Client.Agent.ServiceDeregister(consulClientInfo.RegisterInfo.ID).Wait();
                });
            }
            else
            {
                throw new NullReferenceException("未找到相關consul客戶端信息!");
            }
        }

        private static void Register(this IServiceCollection services)
        {
            if (serviceOptions == null)
            {
                throw new Exception("獲取服務註冊信息失敗!請檢查配置信息是否正確!");
            }
            if (serviceOptions.ConsulOptions == null)
            {
                throw new ArgumentNullException("請檢查是否配置Consul信息!");
            }

            string consulAddress = $"{serviceOptions.ConsulOptions.Scheme}://{serviceOptions.ConsulOptions.ConsulIP}:{serviceOptions.ConsulOptions.Port}";

            var consulClient = new ConsulClient(opt =>
            {
                opt.Address = new Uri(consulAddress);
            });

            var httpCheck = new AgentServiceCheck()
            {
                DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(10), // 服務啟動多久后註冊
                Interval = TimeSpan.FromSeconds(serviceOptions.HealthCheckIntervalSecond), // 間隔
                HTTP = $"{serviceOptions.Scheme}://{serviceOptions.ServiceIP}:{serviceOptions.Port}{serviceOptions.HealthCheckUrl}",
                Timeout = TimeSpan.FromSeconds(10)
            };

            var registration = new AgentServiceRegistration()
            {
                Checks = new[] { httpCheck },
                ID = Guid.NewGuid().ToString(),
                Name = serviceOptions.ServiceName,
                Address = serviceOptions.ServiceIP,
                Port = serviceOptions.Port,
            };

            services.AddSingleton(new ConsulClientInfo()
            {
                Client = consulClient,
                RegisterInfo = registration
            });
        }
    }
}

6.啟動運行

  • 啟動consul
  • 啟動 Auth,Gateway項目
  • 通過網關項目訪問Auth

啟動Consul

為了方便演示這裡是以開發者啟動的consul
在consul.exe的目錄下執行
consul agent -dev -ui // 開發者模式運行帶ui

啟動 Auth,Gateway項目

啟動項目和可以發現我的們Auth服務已經註冊進來了

通過網關訪問Auth

我們這裏訪問 http://localhost:5000/auth/token 獲取token

我們可以看到網關項目接收到了請求並在控制台中打印出以下信息

然後在Auth項目中的控制台中可以看到已經成功接收到了請求並響應

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

瑞典船運業推出「海洋鳥」 以風為動力可遠洋運輸

摘錄自2020年9月24日科技新報報導

瑞典造船公司 Wallenius Marine 近日宣布推出以風為主要動力、全新的遠洋運輸船「海洋鳥」(Oceanbird),盼開啟環保航運新的一頁。

由於海洋鳥上的機翼帆採取伸縮式結構,當通過橋下或遭遇強風時便能迅速縮小帆體的表面積來進行控制,船體同時也配備輔助的綠能引擎,做為進出港口使用的安全措施。

由於使用風能為主要航行動力,海洋鳥的航行速度較傳統貨輪來的慢,僅能以約 10 節的平均速度航行,橫渡北大西洋約需要 12 天的時間,但在不需要使用高污染燃油下,海洋鳥可以減少近 90% 的排放。

在瑞典運輸署的支持下,Wallenius 已經建造出海洋鳥的小型模型,將於接下來幾個月進行測試,預計完整設計明年底前將準備就緒,2025 年首艘船將正式登場。

能源轉型
國際新聞
瑞典
航運
海運

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

【其他文章推薦】

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

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

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

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

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

疫情肆虐下 日本民眾提高對太陽能板裝設興趣

文:宋瑞文(加州能源特約撰述)

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

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

印尼就業環保新法救經濟 勞工抗議延燒

摘錄自2020年10月7日中央社報導

疫情重創經濟,印尼政府加速鬆綁勞動及環保法規,日前突襲通過新法,盼改善投資環境增加就業機會,卻引發大規模抗議,遭質疑修法犧牲勞工權益及環保,爭議恐持續延燒。

印尼政府年初提出創造就業綜合法案後,民間抗議聲浪不斷,勞工團體多次上街表達不滿。

印尼國會5日趕在全國性大罷工前夕審查完法案。印尼請願網站當天發起拒絕創造就業綜合法案的連署迅速累積逾120萬人支持,各大城市爆發罷工示威潮。

在國會審議前,印尼總統佐科威(Joko Widodo)重要幕僚、海洋事務統籌部長盧胡特(Luhut Panjaitan)指出,政府因處理武漢肺炎疫情,這項法案自4月延宕至今。該法案是促進投資的關鍵,幾經協商,全國8大主要工會組織中有6大工會組織同意立法。

不過,由印尼工會聯盟(KSPI)、印尼工人工會聯合會(KSPSI)等團體發動的罷工6日在各大城市許多工業區吸引成千上萬勞工參與。

除了勞動法規,創造就業綜合法案通過也影響環保及稅務等超過70個法律,主要目的是降低投資障礙,方便投資者取得土地及相關證照。這部分引起環保團憂心將弱化環境影響評估的把關機制,不利環境永續發展。

印尼綠色和平資深森林專員亞塞普(Asep Komarudin)7日對中央社指出,現行法規有很多嚴格確保環境保護的條文都因創造就業綜合法案通過而遭廢除,例如未來有些投資案可不經環境影響評估,環評也將限制只有受影響者才參與,不再開放公民參與。

亞塞普說,根據創造就業綜合法案,未來開發案與環保衝突時,被視為與國家策略發展相關的計畫都要給予優先考量,主導開發的國家與企業肯定會持續與原住民族發生衝突,巴布亞(Papua)、加里曼丹(Kalimantan)、蘇門答臘(Sumatra)等地的林地面積可能會再減少3成以上,「我們非常擔心」。

國際新聞
印尼
修法
環保法
勞工剝削
抗議

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

等同666個台灣 南極臭氧層破洞創近年「最大最深」

摘錄自2020年10月7日自由時報報導

聯合國世界氣象組織(WMO)宣布,南極臭氧層破洞已經創下近年來的「最大最深」,破洞於8月中旬起迅速變大,10月初面積達2400萬平方公里,以台灣面積為3.6萬平方公里來看,相當於666個台灣。

根據美國《ABC新聞》報導,聯合國世界氣象組織指出,目前出現在南極上空的破洞是近幾年來「最大」和「最深」的,強烈的極地渦流是此次臭氧層的導火線,負78度的極度低溫條件下形成「極地平流層雲」,雲中含有冰晶,經太陽光照射後就會產生化學反應,開始大量消耗臭氧。

美國太空總署表示,異常的南極天氣是造成這種情況的原因;歐洲中期天氣預報中心哥白尼大氣監測局局長佩奇(Vincent-Henri Peuch)認為,每年發生的南極臭氧層破洞事件都有很大的差異,這也表明人們需要持續減少排放有害物質,繼續執行《蒙特婁議定書》的規範事項。

氣候變遷
國際新聞
南極
南極臭氧層

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

受農藥所苦的「空中王者」 柬埔寨三種瀕危兀鷲數量持續下降

環境資訊中心綜合外電;黃鈺婷 編譯;林大利 審校

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

GitHub 熱點速覽 Vol.24:程序員自我增值,優雅賺零花錢

摘要:升職加薪,出任 CTO,迎娶白富美/高帥富,走向人生巔峰是很多人的夢想。在本期的熱點速覽中你將了解自由作者 Easy 如何優雅賺取零花錢的方法,以及定投改變命運 —— 讓時間陪你慢慢變富。說到程序員自我增值,除了優雅賺錢之外,還可以研究下各種生活中小工具的代碼實現,例如,收錄 20+ Web 小應用的 vanillawebprojects。將技術應用在生活中點滴,展現你的技術輔助日常“肝”口袋妖精,或者偶爾用技術給自己生活添加點小樂趣,用遺傳算法製作一個繪製圖像過程的小玩具。

以下內容摘錄自微博@HelloGitHub 的 GitHub Trending,選項標準:新發布 | 實用 | 有趣,根據項目 release 時間分類,發布時間不超過 7 day 的項目會標註 New,無該標誌則說明項目 release 超過一周。由於本文篇幅有限,還有部分項目未能在本文展示,望周知

  • 本文目錄
      1. 本周特推
      • 1.1 遺傳算法玩具:genetic-drawing
      • 1.2 馬斯克火箭:SpaceX-API
      1. GitHub Trending 周榜
      • 2.1 Go 語法書:go-ast-book
      • 2.2 數據庫好搭檔:xgenecloud
      • 2.3 前端小玩意:vanillawebprojects
      • 2.4 統計代碼:lihang-code
      • 2.5 Poke 輔助工具:Pokedex
      • 2.6 高性能框架:Fastapi
      • 2.7 JS 面經:javascript-questions
      1. 本周 GitHub Trending #程序員增值# 主題的主力軍
      • 3.1 優雅賺錢:howto-make-more-money
      • 3.2 定投改變命運:regular-investing-in-box
      • 3.3 機器學習課程個人筆記:Coursera-ML-AndrewNg-Notes
      1. 推薦閱讀

1. 本周特推

1.1 遺傳算法玩具:genetic-drawing

本周 star 增長數:1200+

Newgenetic-drawing 作者在 2017 年做的模仿給定目標圖像的繪製過程的玩具項目,效果見下圖。項目受到互聯網上許多基因繪製示例的啟發,由於項目深受歡迎,作者便在近日將其開源。

GitHub 地址→https://github.com/anopara/genetic-drawing

1.2 馬斯克火箭:SpaceX-API

本周 star 增長數:900+

SpaceX-API 是一個用於火箭、核心艙、太空艙、發射台和發射數據的開源 REST API。技術棧

  • 部署在美國中部 Linode 服務器上
  • 使用了 Nodejs 的 Koa 框架
  • 使用了 Redis、Nginx 和 Cloudflare 進行內容緩存
  • 使用了 Jest 和 Supertest 做測試
  • 使用了 Circle CI 進行持續集成/部署
  • 所有的數據存儲在 MongoDB Atlas 3 節點的副本集集群中
  • 使用 mongodump 在晚上進行數據備份

GitHub 地址→https://github.com/r-spacex/SpaceX-API

2. GitHub Trending 周榜

2.1 Go 語法書:go-ast-book

本周 star 增長數:1000+

go-ast-book 是一個 Go 語法樹入門項目。讓我們語法樹這個維度重新審視 Go 語言程序,我們將得到創建Go語言本身的技術。本書簡單介紹語法樹相關包的使用。

GitHub 地址→https://github.com/chai2010/go-ast-book

2.2 數據庫好搭檔:xgenecloud

本周 star 增長數:800+

New xgenecloud 是一個能即時生成任何數據庫上的 REST 和 GraphQL API 工具,它支持 MySQL、PostgreSQL、MsSQL、SQLite、MariaDB。特性:

  • 為現有數據庫生成 REST API
  • 提供用於調試的 GUI
  • 生成的 API 均可基於 Serverless 部署在任意雲平台

GitHub 地址→https://github.com/xgenecloud/xgenecloud

2.3 前端小玩意:vanillawebprojects

本周 star 增長數:1100+

vanillawebprojects 收錄了用前端技術(Javascript、CSS、HTML5)開發的 20+ 款小應用,包括:表單驗證、匯率計算、打字遊戲、語音閱讀、新年倒計時等等。

GitHub 地址→https://github.com/bradtraversy/vanillawebprojects

2.4 統計代碼:lihang-code

本周 star 增長數:10900+

《統計學習方法》可以說是機器學習的入門寶典,許多機器學習培訓班、互聯網企業的面試、筆試題目,很多都參考這本書。本項目收錄了該書的所有代碼實現,特別是監督學習方法,包括感知機、k 近鄰法、樸素貝恭弘=叶 恭弘斯法、決策樹、邏輯斯諦回歸與支持向量機、提升方法、em 算法、隱馬爾可夫模型和條件隨機場等。

GitHub 地址→https://github.com/fengdu78/lihang-code

2.5 Poke 輔助工具:Pokedex

本周 star 增長數:500+

NewPokedex 使用基於 MVVM 架構的 Dagger Hilt、Motion、Coroutines、Jetpack 開發的 Poke(口袋妖精)輔助工具。這個項目專註實現依賴注入的新庫,支持從網絡獲取數據,並通過存儲庫模式集成數據庫中的持久化數據。

GitHub 地址→https://github.com/skydoves/Pokedex

2.6 高性能框架:Fastapi

本周 star 增長數:1300+

Fastapi 是一個基於 python 的框架,該框架鼓勵使用 Pydantic 和 OpenAPI 進行文檔編製,使用 Docker 進行快速開發和部署以及基於 Starlette 框架進行的簡單測試。特性:

  • 高性能
  • 快速編寫代碼:將功能開發的速度提高大約 200% 至 300%
  • 錯誤更少:減少約40%的人為錯誤(開發人員)
  • 直觀:強大的編輯器支持。完成無處不在。調試時間更少
  • 簡易:旨在易於使用和學習。減少閱讀文檔的時間
  • 短:最小化代碼重複。每個參數聲明中的多個功能,更少的錯誤
  • 健壯:獲取可用於生產的代碼,具有自動交互式文檔。
  • 基於標準:基於(並完全兼容)API的開放標準

GitHub 地址→https://github.com/tiangolo/fastapi

2.7 JS 面經:javascript-questions

本周 star 增長數:800+

從基礎到高級,JavaScript Questions 收錄了 JS 相關的面試題及解法。

GitHub 地址→https://github.com/lydiahallie/javascript-questions

3. 本周 GitHub Trending #程序員增值#主題的主力軍

在本期主題模塊,小魚乾這裏選取了 3 個和增值相關的小工具,希望能提高你生活、工作的幸福值。

3.1 優雅賺錢:howto-make-more-money

howto-make-more-money 是一個程序員@Easy 現身講述優雅的掙零花錢的項目,雖然是一個教你如何賺零花錢的項目,但是通過閱讀本賺零花錢小書你可理清自己的核心資源,以及如何創造資產。

GitHub 地址→https://github.com/easychen/howto-make-more-money

3.2 定投改變命運:regular-investing-in-box

定投改變命運 —— 讓時間陪你慢慢變富。regular-investing-in-box 這本書要講的是普通人擺脫階層固化的路徑 —— 絕對可行,毫無水分,並且全靠你自己。這裏所說的普通人,不分國界、不分地域、不分種族、不分性別、不分年齡、不分高矮胖瘦美醜、不分何種性取向…… 關鍵在於,甚至壓根不分智商和學歷!換言之,這個解決方案,甚至對在北京跑腿送外賣的小哥都適用……

GitHub 地址→https://github.com/xiaolai/regular-investing-in-box

3.3 機器學習課程個人筆記:Coursera-ML-AndrewNg-Notes

Coursera-ML-AndrewNg-Notes 是吳恩達老師的機器學習課程個人筆記,旨在提供了一個廣泛的介紹機器學習、數據挖掘、統計模式識別的課程。主題包括:

  • 監督學習(參數/非參數算法,支持向量機,核函數,神經網絡)。
  • 無監督學習(聚類,降維,推薦系統,深入學習推薦)。
  • 在機器學習的最佳實踐(偏差/方差理論;在機器學習和人工智能創新過程)。

項目還將使用大量的案例研究,你可學習到如何運用學習算法構建智能機器人(感知,控制),文本的理解(Web 搜索,反垃圾郵件),計算機視覺,醫療信息,音頻,數據挖掘,和其他領域。

GitHub 地址→https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes

推薦閱讀

  • GitHub 熱點速覽 Vol.23:前後端最佳實踐
  • GitHub 熱點速覽 Vol.22:如何打造超級技術棧
  • GitHub 熱點速覽 Vol.21:Go 新手起手式,學就完事兒了

以上為 2020 年第 23 個工作周的 GitHub Trending 如果你 Pick 其他好玩、實用的 GitHub 項目,記得來 HelloGitHub issue 區和我們分享下喲

HelloGitHub 交流群現已全面開放,添加微信號:HelloGitHub 為好友入群,可同前端、Java、Go 等各界大佬談笑風生、切磋技術~

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

【其他文章推薦】

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

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

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

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

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

基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(七)

系列文章

  1. 基於 abp vNext 和 .NET Core 開發博客項目 – 使用 abp cli 搭建項目
  2. 基於 abp vNext 和 .NET Core 開發博客項目 – 給項目瘦身,讓它跑起來
  3. 基於 abp vNext 和 .NET Core 開發博客項目 – 完善與美化,Swagger登場
  4. 基於 abp vNext 和 .NET Core 開發博客項目 – 數據訪問和代碼優先
  5. 基於 abp vNext 和 .NET Core 開發博客項目 – 自定義倉儲之增刪改查
  6. 基於 abp vNext 和 .NET Core 開發博客項目 – 統一規範API,包裝返回模型
  7. 基於 abp vNext 和 .NET Core 開發博客項目 – 再說Swagger,分組、描述、小綠鎖
  8. 基於 abp vNext 和 .NET Core 開發博客項目 – 接入GitHub,用JWT保護你的API
  9. 基於 abp vNext 和 .NET Core 開發博客項目 – 異常處理和日誌記錄
  10. 基於 abp vNext 和 .NET Core 開發博客項目 – 使用Redis緩存數據
  11. 基於 abp vNext 和 .NET Core 開發博客項目 – 集成Hangfire實現定時任務處理
  12. 基於 abp vNext 和 .NET Core 開發博客項目 – 用AutoMapper搞定對象映射
  13. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(一)
  14. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(二)
  15. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(三)
  16. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(一)
  17. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(二)
  18. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(三)
  19. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(四)
  20. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(五)
  21. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(一)
  22. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(二)
  23. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(三)
  24. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(四)
  25. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(五)
  26. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(六)
  27. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(七)
  28. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(八)
  29. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(九)
  30. 基於 abp vNext 和 .NET Core 開發博客項目 – 終結篇之發布項目

上一篇完成了後台分類模塊的所有功能,本篇繼續將標籤模塊和友情鏈接模塊的增刪改查完成。

標籤管理

實現方式和之前的分類管理是一樣的,在Admin文件夾下面添加Tags.razor組件,設置路由@page "/admin/tags"

同樣的內容也需要放在AdminLayout組件下面,添加幾個參數:彈窗狀態bool Open、新增或更新時標籤字段string tagName, displayName、更新時的標籤Idint id、API返回的標籤列表接收參數ServiceResult<IEnumerable<QueryTagForAdminDto>> tags

/// <summary>
/// 默認隱藏Box
/// </summary>
private bool Open { get; set; } = false;

/// <summary>
/// 新增或者更新時候的標籤字段值
/// </summary>
private string tagName, displayName;

/// <summary>
/// 更新標籤的Id值
/// </summary>
private int id;

/// <summary>
/// API返回的標籤列表數據
/// </summary>
private ServiceResult<IEnumerable<QueryTagForAdminDto>> tags;
//QueryTagForAdminDto.cs
namespace Meowv.Blog.BlazorApp.Response.Blog
{
    public class QueryTagForAdminDto : QueryTagDto
    {
        /// <summary>
        /// 主鍵
        /// </summary>
        public int Id { get; set; }
    }
}

在初始化方法OnInitializedAsync()中獲取數據。

/// <summary>
/// 初始化
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
    var token = await Common.GetStorageAsync("token");
    Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");

    tags = await FetchData();
}

/// <summary>
/// 獲取數據
/// </summary>
/// <returns></returns>
private async Task<ServiceResult<IEnumerable<QueryTagForAdminDto>>> FetchData()
{
    return await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryTagForAdminDto>>>("/blog/admin/tags");
}

注意需要設置請求頭,進行授權訪問,然後頁面上綁定數據。

<AdminLayout>
    @if (tags == null)
    {
        <Loading />
    }
    else
    {
        <div class="post-wrap tags">
            <h2 class="post-title">-&nbsp;Tags&nbsp;-</h2>
            @if (tags.Success && tags.Result.Any())
            {
                <div class="categories-card">
                    @foreach (var item in tags.Result)
                    {
                        <div class="card-item">
                            <div class="categories">
                                <NavLink title="刪除" @onclick="@(async () => await DeleteAsync(item.Id))"></NavLink>
                                <NavLink title="編輯" @onclick="@(() => ShowBox(item))"></NavLink>
                                <NavLink target="_blank" href="@($"/tag/{item.DisplayName}")">
                                    <h3>@item.TagName</h3>
                                    <small>(@item.Count)</small>
                                </NavLink>
                            </div>
                        </div>
                    }
                    <div class="card-item">
                        <div class="categories">
                            <NavLink><h3 @onclick="@(() => ShowBox())">~~~ 新增標籤 ~~~</h3></NavLink>
                        </div>
                    </div>
                </div>
            }
            else
            {
                <ErrorTip />
            }
        </div>

        <Box OnClickCallback="@SubmitAsync" Open="@Open">
            <div class="box-item">
                <b>DisplayName:</b><input type="text" @bind="@displayName" @bind:event="oninput" />
            </div>
            <div class="box-item">
                <b>TagName:</b><input type="text" @bind="@tagName" @bind:event="oninput" />
            </div>
        </Box>
    }
</AdminLayout>

tags沒獲取到數據的時候显示<Loading />組件內容,循環遍曆數據進行綁定,刪除按鈕綁定點擊事件調用DeleteAsync()方法。新增和編輯按鈕點擊事件調用ShowBox()方法显示彈窗。新增的時候不需要傳遞參數,編輯的時候需要將當前item即QueryTagForAdminDto傳遞進去。

<Box>組件中綁定了標籤的兩個參數,是否打開參數Opne和確認按鈕回調事件方法SubmitAsync()

刪除標籤的方法DeleteAsync(...)如下:

// 彈窗確認
bool confirmed = await Common.InvokeAsync<bool>("confirm", "\n真的要幹掉這個該死的標籤嗎");

if (confirmed)
{
    var response = await Http.DeleteAsync($"/blog/tag?id={id}");

    var result = await response.Content.ReadFromJsonAsync<ServiceResult>();

    if (result.Success)
    {
        tags = await FetchData();
    }
}

刪除之前進行二次確認,避免誤傷,刪除成功重新加載一遍數據。

彈窗的方法ShowBox(...)如下:

/// <summary>
/// 显示box,綁定字段
/// </summary>
/// <param name="dto"></param>
private void ShowBox(QueryTagForAdminDto dto = null)
{
    Open = true;
    id = 0;

    // 新增
    if (dto == null)
    {
        displayName = null;
        tagName = null;
    }
    else // 更新
    {
        id = dto.Id;
        displayName = dto.DisplayName;
        tagName = dto.TagName;
    }
}

最後在彈窗中確認按鈕的回調事件方法SubmitAsync()如下:

/// <summary>
/// 確認按鈕點擊事件
/// </summary>
/// <returns></returns>
private async Task SubmitAsync()
{
    var input = new EditTagInput()
    {
        DisplayName = displayName.Trim(),
        TagName = tagName.Trim()
    };

    if (string.IsNullOrEmpty(input.DisplayName) || string.IsNullOrEmpty(input.TagName))
    {
        return;
    }

    var responseMessage = new HttpResponseMessage();

    if (id > 0)
        responseMessage = await Http.PutAsJsonAsync($"/blog/tag?id={id}", input);
    else
        responseMessage = await Http.PostAsJsonAsync("/blog/tag", input);

    var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();
    if (result.Success)
    {
        tags = await FetchData();
        Open = false;
    }
}

輸入參數EditTagInput

namespace Meowv.Blog.BlazorApp.Response.Blog
{
    public class EditTagInput : TagDto
    {
    }
}

最終執行新增或者更新數據都在點擊事件中進行,將變量的值賦值給EditTagInput,根據id判斷走新增還是更新,成功后重新加載數據,關掉彈窗。

標籤管理頁面全部代碼如下:

點擊查看代碼

@page "/admin/categories"

<AdminLayout>
    @if (categories == null)
    {
        <Loading />
    }
    else
    {
        <div class="post-wrap categories">
            <h2 class="post-title">-&nbsp;Categories&nbsp;-</h2>
            @if (categories.Success && categories.Result.Any())
            {
                <div class="categories-card">
                    @foreach (var item in categories.Result)
                    {
                        <div class="card-item">
                            <div class="categories">
                                <NavLink title="刪除" @onclick="@(async () => await DeleteAsync(item.Id))"></NavLink>
                                <NavLink title="編輯" @onclick="@(() => ShowBox(item))"></NavLink>
                                <NavLink target="_blank" href="@($"/category/{item.DisplayName}")">
                                    <h3>@item.CategoryName</h3>
                                    <small>(@item.Count)</small>
                                </NavLink>
                            </div>
                        </div>
                    }
                    <div class="card-item">
                        <div class="categories">
                            <NavLink><h3 @onclick="@(() => ShowBox())">~~~ 新增分類 ~~~</h3></NavLink>
                        </div>
                    </div>
                </div>
            }
            else
            {
                <ErrorTip />
            }
        </div>

        <Box OnClickCallback="@SubmitAsync" Open="@Open">
            <div class="box-item">
                <b>DisplayName:</b><input type="text" @bind="@displayName" @bind:event="oninput" />
            </div>
            <div class="box-item">
                <b>CategoryName:</b><input type="text" @bind="@categoryName" @bind:event="oninput" />
            </div>
        </Box>
    }
</AdminLayout>

@code {
    /// <summary>
    /// 默認隱藏Box
    /// </summary>
    private bool Open { get; set; } = false;

    /// <summary>
    /// 新增或者更新時候的分類字段值
    /// </summary>
    private string categoryName, displayName;

    /// <summary>
    /// 更新分類的Id值
    /// </summary>
    private int id;

    /// <summary>
    /// API返回的分類列表數據
    /// </summary>
    private ServiceResult<IEnumerable<QueryCategoryForAdminDto>> categories;

    /// <summary>
    /// 初始化
    /// </summary>
    /// <returns></returns>
    protected override async Task OnInitializedAsync()
    {
        var token = await Common.GetStorageAsync("token");
        Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");

        categories = await FetchData();
    }

    /// <summary>
    /// 獲取數據
    /// </summary>
    /// <returns></returns>
    private async Task<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>> FetchData()
    {
        return await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>>("/blog/admin/categories");
    }

    /// <summary>
    /// 刪除分類
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    private async Task DeleteAsync(int id)
    {
        Open = false;

        // 彈窗確認
        bool confirmed = await Common.InvokeAsync<bool>("confirm", "\n真的要幹掉這個該死的分類嗎");

        if (confirmed)
        {
            var response = await Http.DeleteAsync($"/blog/category?id={id}");

            var result = await response.Content.ReadFromJsonAsync<ServiceResult>();

            if (result.Success)
            {
                categories = await FetchData();
            }
        }
    }

    /// <summary>
    /// 显示box,綁定字段
    /// </summary>
    /// <param name="dto"></param>
    private void ShowBox(QueryCategoryForAdminDto dto = null)
    {
        Open = true;
        id = 0;

        // 新增
        if (dto == null)
        {
            displayName = null;
            categoryName = null;
        }
        else // 更新
        {
            id = dto.Id;
            displayName = dto.DisplayName;
            categoryName = dto.CategoryName;
        }
    }

    /// <summary>
    /// 確認按鈕點擊事件
    /// </summary>
    /// <returns></returns>
    private async Task SubmitAsync()
    {
        var input = new EditCategoryInput()
        {
            DisplayName = displayName.Trim(),
            CategoryName = categoryName.Trim()
        };

        if (string.IsNullOrEmpty(input.DisplayName) || string.IsNullOrEmpty(input.CategoryName))
        {
            return;
        }

        var responseMessage = new HttpResponseMessage();

        if (id > 0)
            responseMessage = await Http.PutAsJsonAsync($"/blog/category?id={id}", input);
        else
            responseMessage = await Http.PostAsJsonAsync("/blog/category", input);

        var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();
        if (result.Success)
        {
            categories = await FetchData();
            Open = false;
        }
    }
}

友鏈管理

實現方式都是一樣的,這個就不多說了,直接上代碼。

先將API返回的接收參數和新增編輯的輸入參數添加一下。

//QueryFriendLinkForAdminDto.cs
namespace Meowv.Blog.BlazorApp.Response.Blog
{
    public class QueryFriendLinkForAdminDto : FriendLinkDto
    {
        /// <summary>
        /// 主鍵
        /// </summary>
        public int Id { get; set; }
    }
}

//EditFriendLinkInput.cs
namespace Meowv.Blog.BlazorApp.Response.Blog
{
    public class EditFriendLinkInput : FriendLinkDto
    {
    }
}
@page "/admin/friendlinks"

<AdminLayout>
    @if (friendlinks == null)
    {
        <Loading />
    }
    else
    {
        <div class="post-wrap categories">
            <h2 class="post-title">-&nbsp;FriendLinks&nbsp;-</h2>
            @if (friendlinks.Success && friendlinks.Result.Any())
            {
                <div class="categories-card">
                    @foreach (var item in friendlinks.Result)
                    {
                        <div class="card-item">
                            <div class="categories">
                                <NavLink title="刪除" @onclick="@(async () => await DeleteAsync(item.Id))"></NavLink>
                                <NavLink title="編輯" @onclick="@(() => ShowBox(item))"></NavLink>
                                <NavLink target="_blank" href="@item.LinkUrl">
                                    <h3>@item.Title</h3>
                                </NavLink>
                            </div>
                        </div>
                    }
                    <div class="card-item">
                        <div class="categories">
                            <NavLink><h3 @onclick="@(() => ShowBox())">~~~ 新增友鏈 ~~~</h3></NavLink>
                        </div>
                    </div>
                </div>
            }
            else
            {
                <ErrorTip />
            }
        </div>

        <Box OnClickCallback="@SubmitAsync" Open="@Open">
            <div class="box-item">
                <b>Title:</b><input type="text" @bind="@title" @bind:event="oninput" />
            </div>
            <div class="box-item">
                <b>LinkUrl:</b><input type="text" @bind="@linkUrl" @bind:event="oninput" />
            </div>
        </Box>
    }
</AdminLayout>

@code {
    /// <summary>
    /// 默認隱藏Box
    /// </summary>
    private bool Open { get; set; } = false;

    /// <summary>
    /// 新增或者更新時候的友鏈字段值
    /// </summary>
    private string title, linkUrl;

    /// <summary>
    /// 更新友鏈的Id值
    /// </summary>
    private int id;

    /// <summary>
    /// API返回的友鏈列表數據
    /// </summary>
    private ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>> friendlinks;

    /// <summary>
    /// 初始化
    /// </summary>
    /// <returns></returns>
    protected override async Task OnInitializedAsync()
    {
        var token = await Common.GetStorageAsync("token");
        Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");

        friendlinks = await FetchData();
    }

    /// <summary>
    /// 獲取數據
    /// </summary>
    /// <returns></returns>
    private async Task<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>> FetchData()
    {
        return await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>>("/blog/admin/friendlinks");
    }

    /// <summary>
    /// 刪除分類
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    private async Task DeleteAsync(int id)
    {
        Open = false;

        // 彈窗確認
        bool confirmed = await Common.InvokeAsync<bool>("confirm", "\n真的要幹掉這個該死的分類嗎");

        if (confirmed)
        {
            var response = await Http.DeleteAsync($"/blog/friendlink?id={id}");

            var result = await response.Content.ReadFromJsonAsync<ServiceResult>();

            if (result.Success)
            {
                friendlinks = await FetchData();
            }
        }
    }

    /// <summary>
    /// 显示box,綁定字段
    /// </summary>
    /// <param name="dto"></param>
    private void ShowBox(QueryFriendLinkForAdminDto dto = null)
    {
        Open = true;
        id = 0;

        // 新增
        if (dto == null)
        {
            title = null;
            linkUrl = null;
        }
        else // 更新
        {
            id = dto.Id;
            title = dto.Title;
            linkUrl = dto.LinkUrl;
        }
    }

    /// <summary>
    /// 確認按鈕點擊事件
    /// </summary>
    /// <returns></returns>
    private async Task SubmitAsync()
    {
        var input = new EditFriendLinkInput()
        {
            Title = title.Trim(),
            LinkUrl = linkUrl.Trim()
        };

        if (string.IsNullOrEmpty(input.Title) || string.IsNullOrEmpty(input.LinkUrl))
        {
            return;
        }

        var responseMessage = new HttpResponseMessage();

        if (id > 0)
            responseMessage = await Http.PutAsJsonAsync($"/blog/friendlink?id={id}", input);
        else
            responseMessage = await Http.PostAsJsonAsync("/blog/friendlink", input);

        var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();
        if (result.Success)
        {
            friendlinks = await FetchData();
            Open = false;
        }
    }
}

截至目前為止,還剩下文章模塊的功能還沒做了,今天到這裏吧,明天繼續剛,未完待續…

開源地址:https://github.com/Meowv/Blog/tree/blog_tutorial

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

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

C# 9.0 新特性之目標類型推導 new 表達式

閱讀本文大概需要 2 分鐘。

呼~~,每次過完一個周末,寫作就失去了動力,一两天才能緩過來。儘管如此,還是要堅持寫好每一篇文章的。寧缺毋濫嘛,寧願發文的頻率低一點,也要保證文章的質量,至少排版不能差,行文要流暢,錯別字不能有。

關於類型推導想必大家都很熟悉,它是在 var 關鍵字引入的時候引入 C# 的。

var i = 10;
var u = new User();

編譯器會通過右邊的字面量自動推導左邊變量的類型,這種推導方式可以歸納為:從上下文右邊推導出左邊的類型。我們不妨把它稱為源類型推導(Source-typed inferring,參考 Target-typed 自創的術語)。

相應的,有源類型推導就有目標類型推導 (Target-typed inferring),它是指從上下文左邊推導出右邊的類型。比如數組的初始化和 Lambda 表達式常常是目標類型推導的表達式。舉個例子:

// 沒有使用類型推導
string[] s = new string[] { "a", "b" };
// 目標類型推導(左推右)
string[] s = new { "a", "b" };
string[] s = new [] { "a", "b" };

// 沒有使用類型推導
Users.FirstOrDefault<User>(u => u.id = 123);
// 目標類型推導(左推右)
Users.FirstOrDefault(u => u.id = 123);

這次在 C# 9 中,增加了用戶定義類型 new 表達式的目標類型推導,即通過上下文左邊自動推導 new 表達式的類型,從而在使用 new 構造時省略類型的指定,請看示例:

// C# 9 之前
Point p = new Point(3, 5);

// C# 9
Point p = new (3, 5);

除此之外,C# 9 也增加了操作符 ???: 的目標類型推導支持。之前這兩個操作符必須要求兩邊的操作對象都是相同的類型,否則會編譯報錯。而在 C# 9 中,只要目標類型是操作對象共同的基類就不再會編譯報錯了,比如:

// Student 和 Customer 擁有共同的父類 Person
Person person = (Person)(student ?? customer); // C# 9 之前
Person person = student ?? customer; // C# 9

// 可空類型,0 和 null 都可以隱式轉換為 int? 類型
int? result = b ? 0 : (int?)null; // C# 9 之前
int? result = b ? 0 : null; // C# 9

其實本文的核心就一句代碼:

Point p = new (3, 5);

卻一不小心啰嗦了這麼一堆。但講真,學習新的知識不是要死記硬背,而要學會歸類推理,舉一反三,經常思考,最好能形成自己的一種思維習慣,這樣學習才會變成一件水到渠成的事。多看我的文章,希望你能學到的不僅僅是生硬的編程知識點,也希望我的行文風格和思維習慣對你有所啟發。

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

什麼?一個核同時執行兩個線程?

CPU里的時間

Hi,好久不見,我是CPU一號車間的阿Q,不認識我的話,可以看看:完了!CPU一味求快出事兒了!

真的是好久不見了,人類有個說法叫天上一天,地上一年,而在我們的世界里,人類一天,我們不知要過多少年~~

在我所在的CPU這座工廠里,時間的概念有些不太一樣。工廠大門外的中央廣場上掛着一個大大的鐘錶,整個計算機世界里的居民能夠掐着時間過日子全都仰仗它,你們人類把它叫做晶振

這個鐘錶每隔66000000分之一秒就會報一次時,比人類的鐘錶不知道快到哪裡去了。

早些年還是夠用的,不過隨着我們CPU工廠生產效率的不斷提升,我們多次向晶振提出提升報時的精度,想讓他報時報的的更快一些,不過都被拒絕了。給我們的理由是內存那傢伙聯合主板上其他單位帶頭反對,說他們受條件限制,沒辦法像我們這麼快。

靠人不如靠己,為此,咱們工廠專門設立了一個叫倍頻器的部門進一步把這個報時細分,達到了3600000000分之1秒,作為我們工廠內部工作作息的時鐘周期,這数字實在是太長了,人類為了好記,取了一個叫主頻的名字,表示1秒鐘報時的次數,就是3.6GHz。

一不小心扯遠了,這次想給大家說一件事兒······

指令依賴

我們這座工廠的任務就是不斷的執行人類編寫的程序指令,咱廠里有8個車間,大家開足了馬力,就能同時執行8個線程,那速度那叫一個快。

可是廠里的老闆還是嫌我們不夠快,那天居然告訴我們要每個車間執行兩個線程,實現八核十六線程,是要把我們的勞動力壓榨到極致!我們都滿肚子怨言······

事情的起因是這樣的~~

有一次,我們一號車間的四人組趁着工作的空當,又鬥起了地主,突然領導過來視察。

“你們怎麼又在玩?是工作量不飽和嗎?”,見我們幾個閑着,領導一下就不高興了。

我趕緊上前解釋到:“不好意思領導,咱們剛剛執行了一條指令,需要內存中的一塊數據,剛好又不在緩存中,所以找內存那傢伙要數據去了,這不您也知道那傢伙向來很慢,我們閑着也是閑着所以就稍微放鬆了一下······”

聽了我的話領導一下皺起了眉頭,“還給我狡辯,廠里現在不是用上了亂序執行技術嗎?有這閑功夫你們可以先執行後面的指令啊”

“這我們當然知道,這不您看,我們把後面那幾條指令也都處理了,現在遇到了一條沒法提前執行的指令才停下來的”

領導看了一下問到:“為啥那條不能提前執行?”

“那是一個加法指令,加數依賴於現在正在處理的指令的運算結果呢,所以內存那傢伙不來消息,我們只能擱置着了”,我繼續解釋到。

領導聽完,一臉不高興的離開了。

資源閑置

過了幾天,領導又來到咱們一號車間來了,也不知道怎麼回事,這明明有八個車間,領導怎麼老愛往我們這邊跑。

不過這一次,我們沒有斗地主,正在辛辛苦苦的工作着。

當時,我正在執行一個浮點數運算,領導過來一看,拍了拍我的肩膀說到:“喲,阿Q,忙着吶,這是在做什麼啊?”

我笑着說到:“領導好,我剛剛用浮點數運算電路單元做了一個浮點數乘法,正在等待計算結果呢”

領導點了點頭,往周邊巡視一圈,指着一堆設備問到:“這一堆是什麼?”

“哦,那是整數運算電路單元,這條指令用不到它”

領導再次點了點頭,若有所思的離開了。

超線程技術

又過了幾天,廠里召開了一次會議,八個車間都派了代表參會。

會上,領導發話了:“前段時間我到各個車間視察,發現現在咱們廠里資源浪費的情況很嚴重!”

二號車間的虎子一聽就坐不住了,“領導,咱們大傢伙工作都挺賣力的,哪裡有浪費啊?”

領導瞥了一眼,繼續說到:“一方面,廠里的計算資源——電路設備得不到充分利用,另一方面,又因為內存讀取緩慢、指令依賴等方面的原因,浪費大家太多時間花在等待上”

八號車間的代表向來愛拍馬屁,接着領導的話問到:“領導是有什麼指示?我們八號車間絕對支持!”

“我們幾個管理層經過討論,決定讓你們一個車間由現在執行一個線程,變成執行兩個線程!

領導這話一出,會場竊竊私語此起彼伏。虎子偏頭小聲對我說到:“這資本家改不了剝削的本色,這壓榨的也太狠了!”

領導咳嗽了幾聲,會場再次安靜了下來。

我起身問到:“領導,這咱們一個車間怎麼能執行兩個線程呢,每個車間的寄存器只有一套,這用起來豈不是要亂掉?”

“這個你不用擔心,我們會給每個車間配兩套寄存器!”

五號車間的代表一聽說到:“要不再給我們添點人手吧,這樣效率肯定提升快!”

領導一聽笑着說到:“還添人手?要不要再給你們添點運算設備?那我不如再增加幾個車間,還開這會幹嘛?這次會議的主題就是如何讓我們現有的資源得到最大程度的利用,減少浪費現象!”

會場一度陷入了尷尬又緊張的氛圍。

還是虎子打破了安靜,“領導,這兩個線程的工作該怎麼開展,我們心底沒有數啊!”

領導滿意的笑了一下:“這才是你們該問的問題嘛!每個車間回去重新分配一下工作,劃分為兩套班子,各自維護一套寄存器,對外宣稱你們是兩個不同的物理核心,但各車間的緩存和計算資源還是只有一套。你們內部協調好,在執行代碼指令的時候,充分利用等待的時間執行另一個線程的指令,這樣也不用擔心指令依賴的問題。”

大家一邊聽一邊做着筆記。

“還有,如果遇到資源閑置的情況,也可以同時執行兩個線程的指令。比如一個線程是執行整數運算指令,一個線程是執行浮點數運算指令,就可以一起來,讓工廠的計算資源充分用起來,別閑置。”

看我們都認真的記着筆記,領導露出了滿意的笑容,“都記好了吧,我們給這項革命性的技術取了個特別酷的名字,叫超線程技術!”

散會後,大家都紛紛抱怨,把大家逼得這麼緊,以後上班看來是沒法摸魚了,這日子真是越來越難過了。

毀譽參半的超線程

不過,抱怨歸抱怨,大家還是得按照新規來執行。

很快,廠里就落地了這項技術,咱們一個車間搖身一變,變成了倆,咱們原來八核八線程的CPU一下變成了八核十六線程。操作系統那幫人都被我們給騙了,還以為咱們是十六核的CPU呢!

不過畢竟計算資源還是只有一份,遇到兩個線程都要使用同樣的計算單元時,還是得要排隊,還要花時間在兩個線程之前的協調工作上,所以整體工作效率的根本沒有2倍,絕大多數時候能提升個20%-30%就不錯了。

不僅如此,車間改造后,增加了新的邏輯電路單元,咱這CPU工廠的功耗也更大了,工廠門口那座巨大的風扇也得加大馬力給我們降溫了。

廠子里對這項技術的反對聲音開始不絕於耳。

不過後來發生了一件事,讓人們不得不關閉這項技術。聽聞這個消息,我們都樂開了花,看來又可以繼續摸魚了······

彩蛋

每當有網絡數據包到來,網卡那傢伙就通過中斷告訴我們CPU去處理。

可咱明明有8個車間,它非得一個勁的只給我們車間發中斷,搞得我們都沒法好好工作。

終於,我忍不住了······

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

說明

超線程技術出現時間其實早於多核技術。本故事僅為敘述方便,不代表二者真實的發展順序。

往期TOP5文章

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

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

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

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

一個HTTP數據包的奇幻之旅

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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