Google 正式收購 Neverware,CloudReady 系統與 Chrome OS 真正稱兄道弟_包裝設計

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

還記得 Cloudready 系統嗎?這個被譽為讓舊電腦華麗變身成 Chromebook 的系統實際上骨子裡跟 Chrome OS 有著相近的核心,只是 Cloudready 系統是基於 Chromium OS 的系統,雖然核心仍然是 Google,但與 Chrome OS 仍然有著不小的差異。最大的不同就是 Chrome OS 具備 Google 原廠服務,而 Cloudready 沒有 Google 商店等服務內容,也不支援 Android App。不過這個問題或許有了新的解答。因為 Google 正式把 CloudReady 的母公司 Neverware 收購起來,未來 Cloudready 等於直接納入 Google 傘下,成為 Chrome OS 真正意義上的親兄弟,自然也有機會獲得來自 Google 的維護與服務更新:

▲CloudReady 是一套能在一般電腦上運作的類 Chrome OS 系統,其家用版(Home Edition)目前仍然接受一般人下載及免費使用

CloudReady 是一個跟 Chrome OS 形似而實非的產品,這個出自 Neverware 公司之手的系統乃是建立在 Chromium OS 的基礎上設計的作業系統。多數核心功能均與 Chrome OS 十分相似,但部分功能並非 Chrome OS 所具備,且 CloudReady 的部分功能設計也非 Chrome OS 所具備。筆者曾於 2017 年介紹過 CloudReady 可作為 LiveUSB 開機使用的特色,可參見本文。確切的說,CloudReady 並非百分之百照搬 Chrome OS 的系統,但卻做出了 Chrome OS 做不到的特性。像是可透過開機隨身碟在一般電腦上執行的特點,就是 Chrome OS 原版並未加入的功能。

缺乏 Google 核心功能,CloudReady 少了 Chrome OS 原生服務的點綴

很多人或許是透過一些片面的資訊認識到 Chrome OS,也知道目前這個出自 Google 之手的作業系統目前僅隨機安裝使用,換句話說,如果你並未購買 Chromebook ,也就無法使用到純正的 Chrome OS。但對於一些預算有限的用戶來說,Chromebook 雖然多數定價廉宜,卻仍難說服自己為了體驗系統而直接購買未必真正必須的機器。這時 CloudReady 就有其存在意義:

▲Chrome OS 對一般人來說可能僅止於片面資訊,要實際摸過玩過也許除了買台 Chromebook 外,還可以透過 CloudReady 系統。

該系統與 Chrome OS 核心相同,可透過專用軟體安裝在隨身碟上,於一般電腦上開機使用。相對來說系統限制幾乎沒有,而且能讓不熟悉 Chrome OS 或其生態的用戶有了一個試用的機會。即便少了 1% 的官方軟體或服務,但卻能體驗到 99%  相似的 Chrome OS,以交易來說仍然十分划算。就算評估過後仍然要繼續使用 CloudReady 的服務,Neverware 也有額外的付費服務可供參考:

▲CloudReady 透過自家專用軟體可安裝在 USB 隨身碟中,以 LiveUSB 的形式開機使用

不過,隨著 Neverware 正式被 Google 收購開始,CloudReady 系統也將成為 Google 旗下產品之一。具備了親兒子等級的待遇後,CloudReady 的優點或許會被 Chrome OS 吸收。但 CloudReady 也可能就此消失在世人面前。Neverware 也在併購消息傳出後發表聲明指出,CloudReady 現有的客戶仍可享有 Neverware 提供的官方支援,免費下載使用的 Home 版也仍可繼續下載使用:

▲CloudReady Home Edition 目前仍可免費使用,日後的更新也許會跟著 Chrome OS 的步調走(圖片來源)

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

但,收購後的 CloudReady 自此將正式成為 Google 的一份子,未來不論是購買授權或是下載使用的途徑,或許都隨著 Google 高層的一念之間而有所變動。即便這點令人不捨,但這已經成為既成事實,只能祝福 Neverware 與 CloudReady 系統未來能在 Google 手中發光發熱。

值得一提的是,Neverware 與 Google 之間的聯繫其實很久以前就開始,早在三年前 Google 投資 Neverware 的 B 輪融資開始,雙方的合作就越來越緊密,Neverware 與 Google Chrome OS 小組間也有不少交流。雖然不知道 CloudReady 系統的未來為何,但在 Google 的手上,想必比別的公司拿到手還來得好。

消息來源

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(二)_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

系列文章

  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 開發博客項目 – 終結篇之發布項目

上一篇(https://www.cnblogs.com/meowv/p/12971041.html)使用HtmlAgilityPack抓取壁紙數據成功將圖片存入數據庫,本篇繼續來完成一個全網各大平台的熱點新聞數據的抓取。

同樣的,可以先預覽一下我個人博客中的成品:https://meowv.com/hot ,和抓取壁紙的套路一樣,大同小異。

本次要抓取的源有18個,分別是博客園、V2EX、SegmentFault、掘金、微信熱門、豆瓣精選、IT之家、36氪、百度貼吧、百度熱搜、微博熱搜、知乎熱榜、知乎日報、網易新聞、GitHub、抖音熱點、抖音視頻、抖音正能量。

還是將數據存入數據庫,按部就班先將實體類和自定義倉儲創建好,實體取名HotNews。貼一下代碼:

//HotNews.cs
using System;
using Volo.Abp.Domain.Entities;

namespace Meowv.Blog.Domain.HotNews
{
    public class HotNews : Entity<Guid>
    {
        /// <summary>
        /// 標題
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        /// 鏈接
        /// </summary>
        public string Url { get; set; }

        /// <summary>
        /// SourceId
        /// </summary>
        public int SourceId { get; set; }

        /// <summary>
        /// 創建時間
        /// </summary>
        public DateTime CreateTime { get; set; }
    }
}

剩下的大家自己完成,最終數據庫生成一張空的數據表,meowv_hotnews 。

然後還是將我們各大平台放到一個枚舉類HotNewsEnum.cs中。

//HotNewsEnum.cs
using System.ComponentModel;

namespace Meowv.Blog.Domain.Shared.Enum
{
    public enum HotNewsEnum
    {
        [Description("博客園")]
        cnblogs = 1,

        [Description("V2EX")]
        v2ex = 2,

        [Description("SegmentFault")]
        segmentfault = 3,

        [Description("掘金")]
        juejin = 4,

        [Description("微信熱門")]
        weixin = 5,

        [Description("豆瓣精選")]
        douban = 6,

        [Description("IT之家")]
        ithome = 7,

        [Description("36氪")]
        kr36 = 8,

        [Description("百度貼吧")]
        tieba = 9,

        [Description("百度熱搜")]
        baidu = 10,

        [Description("微博熱搜")]
        weibo = 11,

        [Description("知乎熱榜")]
        zhihu = 12,

        [Description("知乎日報")]
        zhihudaily = 13,

        [Description("網易新聞")]
        news163 = 14,

        [Description("GitHub")]
        github = 15,

        [Description("抖音熱點")]
        douyin_hot = 16,

        [Description("抖音視頻")]
        douyin_video = 17,

        [Description("抖音正能量")]
        douyin_positive = 18
    }
}

和上一篇抓取壁紙一樣,做一些準備工作。

.Application.Contracts層添加HotNewsJobItem<T>,在.BackgroundJobs層添加HotNewsJob用來處理爬蟲邏輯,用構造函數方式注入倉儲IHotNewsRepository

//HotNewsJobItem.cs
using Meowv.Blog.Domain.Shared.Enum;

namespace Meowv.Blog.Application.Contracts.HotNews
{
    public class HotNewsJobItem<T>
    {
        /// <summary>
        /// <see cref="Result"/>
        /// </summary>
        public T Result { get; set; }

        /// <summary>
        /// 來源
        /// </summary>
        public HotNewsEnum Source { get; set; }
    }
}
//HotNewsJob.CS
using Meowv.Blog.Domain.HotNews.Repositories;
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace Meowv.Blog.BackgroundJobs.Jobs.HotNews
{
    public class HotNewsJob : IBackgroundJob
    {
        private readonly IHttpClientFactory _httpClient;
        private readonly IHotNewsRepository _hotNewsRepository;

        public HotNewsJob(IHttpClientFactory httpClient,
                          IHotNewsRepository hotNewsRepository)
        {
            _httpClient = httpClient;
            _hotNewsRepository = hotNewsRepository;
        }

        public async Task ExecuteAsync()
        {
            throw new NotImplementedException();
        }
    }
}

接下來明確數據源地址,因為以上數據源有的返回是HTML,有的直接返回JSON數據。為了方便調用,我這裏還注入了IHttpClientFactory

整理好的待抓取數據源列表是這樣的。

...
var hotnewsUrls = new List<HotNewsJobItem<string>>
{
    new HotNewsJobItem<string> { Result = "https://www.cnblogs.com", Source = HotNewsEnum.cnblogs },
    new HotNewsJobItem<string> { Result = "https://www.v2ex.com/?tab=hot", Source = HotNewsEnum.v2ex },
    new HotNewsJobItem<string> { Result = "https://segmentfault.com/hottest", Source = HotNewsEnum.segmentfault },
    new HotNewsJobItem<string> { Result = "https://web-api.juejin.im/query", Source = HotNewsEnum.juejin },
    new HotNewsJobItem<string> { Result = "https://weixin.sogou.com", Source = HotNewsEnum.weixin },
    new HotNewsJobItem<string> { Result = "https://www.douban.com/group/explore", Source = HotNewsEnum.douban },
    new HotNewsJobItem<string> { Result = "https://www.ithome.com", Source = HotNewsEnum.ithome },
    new HotNewsJobItem<string> { Result = "https://36kr.com/newsflashes", Source = HotNewsEnum.kr36 },
    new HotNewsJobItem<string> { Result = "http://tieba.baidu.com/hottopic/browse/topicList", Source = HotNewsEnum.tieba },
    new HotNewsJobItem<string> { Result = "http://top.baidu.com/buzz?b=341", Source = HotNewsEnum.baidu },
    new HotNewsJobItem<string> { Result = "https://s.weibo.com/top/summary/summary", Source = HotNewsEnum.weibo },
    new HotNewsJobItem<string> { Result = "https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total?limit=50&desktop=true", Source = HotNewsEnum.zhihu },
    new HotNewsJobItem<string> { Result = "https://daily.zhihu.com", Source = HotNewsEnum.zhihudaily },
    new HotNewsJobItem<string> { Result = "http://news.163.com/special/0001386F/rank_whole.html", Source = HotNewsEnum.news163 },
    new HotNewsJobItem<string> { Result = "https://github.com/trending", Source = HotNewsEnum.github },
    new HotNewsJobItem<string> { Result = "https://www.iesdouyin.com/web/api/v2/hotsearch/billboard/word", Source = HotNewsEnum.douyin_hot },
    new HotNewsJobItem<string> { Result = "https://www.iesdouyin.com/web/api/v2/hotsearch/billboard/aweme", Source = HotNewsEnum.douyin_video },
    new HotNewsJobItem<string> { Result = "https://www.iesdouyin.com/web/api/v2/hotsearch/billboard/aweme/?type=positive", Source = HotNewsEnum.douyin_positive },
};
...

其中有幾個比較特殊的,掘金、百度熱搜、網易新聞。

掘金需要發送Post請求,返回的是JSON數據,並且需要指定特有的請求頭和請求數據,所以使用IHttpClientFactory創建了HttpClient對象。

百度熱搜、網易新聞兩個老大哥玩套路,網頁編碼是GB2312的,所以要專門為其指定編碼方式,不然取到的數據都是亂碼。

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

...
var web = new HtmlWeb();
var list_task = new List<Task<HotNewsJobItem<object>>>();

hotnewsUrls.ForEach(item =>
{
    var task = Task.Run(async () =>
    {
        var obj = new object();

        if (item.Source == HotNewsEnum.juejin)
        {
            using var client = _httpClient.CreateClient();
            client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.14 Safari/537.36 Edg/83.0.478.13");
            client.DefaultRequestHeaders.Add("X-Agent", "Juejin/Web");
            var data = "{\"extensions\":{\"query\":{ \"id\":\"21207e9ddb1de777adeaca7a2fb38030\"}},\"operationName\":\"\",\"query\":\"\",\"variables\":{ \"first\":20,\"after\":\"\",\"order\":\"THREE_DAYS_HOTTEST\"}}";
            var buffer = data.SerializeUtf8();
            var byteContent = new ByteArrayContent(buffer);
            byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

            var httpResponse = await client.PostAsync(item.Result, byteContent);
            obj = await httpResponse.Content.ReadAsStringAsync();
        }
        else
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            obj = await web.LoadFromWebAsync(item.Result, (item.Source == HotNewsEnum.baidu || item.Source == HotNewsEnum.news163) ? Encoding.GetEncoding("GB2312") : Encoding.UTF8);
        }

        return new HotNewsJobItem<object>
        {
            Result = obj,
            Source = item.Source
        };
    });
    list_task.Add(task);
});
Task.WaitAll(list_task.ToArray());

循環 hotnewsUrls ,可以看到HotNewsJobItem我們返回的是object類型,因為有JSON又有HtmlDocument對象。所以這裏為了能夠統一接收,就是用了object。

針對掘金做了單獨處理,使用HttpClient發送Post請求,返回JSON字符串數據。

針對百度熱搜和網易新聞,使用Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);註冊編碼提供程序,然後在web.LoadFromWebAsync(...)加載網頁數據的時候指定網頁編碼,我使用了一個三元表達式來處理。

完成上面這一步,就可以循環 list_task,使用XPath語法,或者解析JSON數據,去拿到數據了。

...
var hotNews = new List<HotNews>();
foreach (var list in list_task)
{
    var item = await list;
    var sourceId = (int)item.Source;

    ...

    if (hotNews.Any())
    {
        await _hotNewsRepository.DeleteAsync(x => true);
        await _hotNewsRepository.BulkInsertAsync(hotNews);
    }
}

這個爬蟲同樣很簡單,只要拿到標題和鏈接即可,所以主要目標是尋找到頁面上的a標籤列表。這個我覺得也沒必要一個個去分析了,直接上代碼。

// 博客園
 if (item.Source == HotNewsEnum.cnblogs)
 {
     var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//div[@class='post_item_body']/h3/a").ToList();
     nodes.ForEach(x =>
     {
         hotNews.Add(new HotNews
         {
             Title = x.InnerText,
             Url = x.GetAttributeValue("href", ""),
             SourceId = sourceId,
             CreateTime = DateTime.Now
         });
     });
 }
// V2EX
if (item.Source == HotNewsEnum.v2ex)
{
    var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//span[@class='item_title']/a").ToList();
    nodes.ForEach(x =>
    {
        hotNews.Add(new HotNews
        {
            Title = x.InnerText,
            Url = $"https://www.v2ex.com{x.GetAttributeValue("href", "")}",
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    });
}
 // SegmentFault
 if (item.Source == HotNewsEnum.segmentfault)
 {
     var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//div[@class='news__item-info clearfix']/a").Where(x => x.InnerText.IsNotNullOrEmpty()).ToList();
     nodes.ForEach(x =>
     {
         hotNews.Add(new HotNews
         {
             Title = x.SelectSingleNode(".//h4").InnerText,
             Url = $"https://segmentfault.com{x.GetAttributeValue("href", "")}",
             SourceId = sourceId,
             CreateTime = DateTime.Now
         });
     });
 }
// 掘金
if (item.Source == HotNewsEnum.juejin)
{
    var obj = JObject.Parse((string)item.Result);
    var nodes = obj["data"]["articleFeed"]["items"]["edges"];
    foreach (var node in nodes)
    {
        hotNews.Add(new HotNews
        {
            Title = node["node"]["title"].ToString(),
            Url = node["node"]["originalUrl"].ToString(),
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    }
}
// 微信熱門
if (item.Source == HotNewsEnum.weixin)
{
    var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//ul[@class='news-list']/li/div[@class='txt-box']/h3/a").ToList();
    nodes.ForEach(x =>
    {
        hotNews.Add(new HotNews
        {
            Title = x.InnerText,
            Url = x.GetAttributeValue("href", ""),
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    });
}
// 豆瓣精選
if (item.Source == HotNewsEnum.douban)
{
    var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//div[@class='channel-item']/div[@class='bd']/h3/a").ToList();
    nodes.ForEach(x =>
    {
        hotNews.Add(new HotNews
        {
            Title = x.InnerText,
            Url = x.GetAttributeValue("href", ""),
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    });
}
// IT之家
if (item.Source == HotNewsEnum.ithome)
{
    var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//div[@class='lst lst-2 hot-list']/div[1]/ul/li/a").ToList();
    nodes.ForEach(x =>
    {
        hotNews.Add(new HotNews
        {
            Title = x.InnerText,
            Url = x.GetAttributeValue("href", ""),
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    });
}
// 36氪
if (item.Source == HotNewsEnum.kr36)
{
    var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//div[@class='hotlist-main']/div[@class='hotlist-item-toptwo']/a[2]|//div[@class='hotlist-main']/div[@class='hotlist-item-other clearfloat']/div[@class='hotlist-item-other-info']/a").ToList();
    nodes.ForEach(x =>
    {
        hotNews.Add(new HotNews
        {
            Title = x.InnerText,
            Url = $"https://36kr.com{x.GetAttributeValue("href", "")}",
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    });
}
// 百度貼吧
if (item.Source == HotNewsEnum.tieba)
{
    var obj = JObject.Parse(((HtmlDocument)item.Result).ParsedText);
    var nodes = obj["data"]["bang_topic"]["topic_list"];
    foreach (var node in nodes)
    {
        hotNews.Add(new HotNews
        {
            Title = node["topic_name"].ToString(),
            Url = node["topic_url"].ToString().Replace("amp;", ""),
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    }
}
// 百度熱搜
if (item.Source == HotNewsEnum.baidu)
{
    var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//table[@class='list-table']//tr/td[@class='keyword']/a[@class='list-title']").ToList();
    nodes.ForEach(x =>
    {
        hotNews.Add(new HotNews
        {
            Title = x.InnerText,
            Url = x.GetAttributeValue("href", ""),
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    });
}
// 微博熱搜
if (item.Source == HotNewsEnum.weibo)
{
    var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//table/tbody/tr/td[2]/a").ToList();
    nodes.ForEach(x =>
    {
        hotNews.Add(new HotNews
        {
            Title = x.InnerText,
            Url = $"https://s.weibo.com{x.GetAttributeValue("href", "").Replace("#", "%23")}",
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    });
}
// 知乎熱榜
if (item.Source == HotNewsEnum.zhihu)
{
    var obj = JObject.Parse(((HtmlDocument)item.Result).ParsedText);
    var nodes = obj["data"];
    foreach (var node in nodes)
    {
        hotNews.Add(new HotNews
        {
            Title = node["target"]["title"].ToString(),
            Url = $"https://www.zhihu.com/question/{node["target"]["id"]}",
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    }
}
// 知乎日報
if (item.Source == HotNewsEnum.zhihudaily)
{
    var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//div[@class='box']/a").ToList();
    nodes.ForEach(x =>
    {
        hotNews.Add(new HotNews
        {
            Title = x.InnerText,
            Url = $"https://daily.zhihu.com{x.GetAttributeValue("href", "")}",
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    });
}
// 網易新聞
if (item.Source == HotNewsEnum.news163)
{
    var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//div[@class='area-half left']/div[@class='tabBox']/div[@class='tabContents active']/table//tr/td[1]/a").ToList();
    nodes.ForEach(x =>
    {
        hotNews.Add(new HotNews
        {
            Title = x.InnerText,
            Url = x.GetAttributeValue("href", ""),
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    });
}
// GitHub
if (item.Source == HotNewsEnum.github)
{
    var nodes = ((HtmlDocument)item.Result).DocumentNode.SelectNodes("//article[@class='Box-row']/h1/a").ToList();
    nodes.ForEach(x =>
    {
        hotNews.Add(new HotNews
        {
            Title = x.InnerText.Trim().Replace("\n", "").Replace(" ", ""),
            Url = $"https://github.com{x.GetAttributeValue("href", "")}",
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    });
}
// 抖音熱點
if (item.Source == HotNewsEnum.douyin_hot)
{
    var obj = JObject.Parse(((HtmlDocument)item.Result).ParsedText);
    var nodes = obj["word_list"];
    foreach (var node in nodes)
    {
        hotNews.Add(new HotNews
        {
            Title = node["word"].ToString(),
            Url = $"#{node["hot_value"]}",
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    }
}
// 抖音視頻 & 抖音正能量
if (item.Source == HotNewsEnum.douyin_video || item.Source == HotNewsEnum.douyin_positive)
{
    var obj = JObject.Parse(((HtmlDocument)item.Result).ParsedText);
    var nodes = obj["aweme_list"];
    foreach (var node in nodes)
    {
        hotNews.Add(new HotNews
        {
            Title = node["aweme_info"]["desc"].ToString(),
            Url = node["aweme_info"]["share_url"].ToString(),
            SourceId = sourceId,
            CreateTime = DateTime.Now
        });
    }
}

item.Result轉換成指定類型,最終拿到數據后,我們先刪除所有數據后再批量插入。

然後新建擴展方法UseHotNewsJob(),在模塊類中調用。

//MeowvBlogBackgroundJobsExtensions.cs
...
        /// <summary>
        /// 每日熱點數據抓取
        /// </summary>
        /// <param name="context"></param>
        public static void UseHotNewsJob(this IServiceProvider service)
        {
            var job = service.GetService<HotNewsJob>();

            RecurringJob.AddOrUpdate("每日熱點數據抓取", () => job.ExecuteAsync(), CronType.Hour(1, 2));
        }
...

指定定時任務為每2小時運行一次。

...
        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            ...
            var service = context.ServiceProvider;
            ...
            service.UseHotNewsJob();
        }

編譯運行,此時周期性作業就會出現我們的定時任務了。

默認時間沒到是不會執行的,我們手動執行等待一會看看效果。

執行完成后,成功將所有熱點數據保存在數據庫中,說明我們的爬蟲已經搞定了,並且Hangfire會按照給定的規則去循環執行,你學會了嗎?

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

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

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

分佈式事務_網頁設計公司

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

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

 

  
眾所周知,數據庫能實現本地事務,也就是在同一個數據庫中,你可以允許一組操作要麼全都正確執行,要麼全都不執行。這裏特彆強調了本地事務,也就是目前的數據庫只能支持同一個數據庫中的事務。但現在的系統往往採用微服務架構,業務系統擁有獨立的數據庫,因此就出現了跨多個數據庫的事務需求,這種事務即為“分佈式事務”。那麼在目前數據庫不支持跨庫事務的情況下,我們應該如何實現分佈式事務呢?本文首先會為大家梳理分佈式事務的基本概念和理論基礎,然後介紹幾種目前常用的分佈式事務解決方案。廢話不多說,那就開始吧~

1 什麼是事務?

  事務由一組操作構成,我們希望這組操作能夠全部正確執行,如果這一組操作中的任意一個步驟發生錯誤,那麼就需要回滾之前已經完成的操作。也就是同一個事務中的所有操作,要麼全都正確執行,要麼全都不要執行。

2 事務的四大特性 ACID

  說到事務,就不得不提一下事務著名的四大特性。

  • 原子性

      原子性要求,事務是一個不可分割的執行單元,事務中的所有操作要麼全都執行,要麼全都不執行。

  • 一致性

      一致性要求,事務在開始前和結束后,數據庫的完整性約束沒有被破壞。

  • 隔離性

      事務的執行是相互獨立的,它們不會相互干擾,一個事務不會看到另一個正在運行過程中的事務的數據。

  • 持久性

      持久性要求,一個事務完成之後,事務的執行結果必須是持久化保存的。即使數據庫發生崩潰,在數據庫恢復後事務提交的結果仍然不會丟失

 

注意:事務只能保證數據庫的高可靠性,即數據庫本身發生問題后,事務提交后的數據仍然能恢復;而如果不是數據庫本身的故障,如硬盤損壞了,那麼事務提交的數據可能就丟失了。這屬於『高可用性』的範疇。因此,事務只能保證數據庫的『高可靠性』,而『高可用性』需要整個系統共同配合實現。

3 事務的隔離級別

  這裏擴展一下,對事務的隔離性做一個詳細的解釋。

  在事務的四大特性ACID中,要求的隔離性是一種嚴格意義上的隔離,也就是多個事務是串行執行的,彼此之間不會受到任何干擾。這確實能夠完全保證數據的安全性,但在實際業務系統中,這種方式性能不高。因此,數據庫定義了四種隔離級別,隔離級別和數據庫的性能是呈反比的,隔離級別越低,數據庫性能越高,而隔離級別越高,數據庫性能越差。

3.1 事務併發執行會出現的問題

  我們先來看一下在不同的隔離級別下,數據庫可能會出現的問題:

    1. 更新丟失

      當有兩個併發執行的事務,更新同一行數據,那麼有可能一個事務會把另一個事務的更新覆蓋掉。 當數據庫沒有加任何鎖操作的情況下會發生。

    2. 臟讀

      一個事務讀到另一個尚未提交的事務中的數據。 該數據可能會被回滾從而失效。 如果第一個事務拿着失效的數據去處理那就發生錯誤了。

    3. 不可重複讀

      不可重複度的含義:一個事務對同一行數據讀了兩次,卻得到了不同的結果。它具體分為如下兩種情況:

      •   虛讀:在事務1兩次讀取同一記錄的過程中,事務2對該記錄進行了修改,從而事務1第二次讀到了不一樣的記錄。
      •   幻讀:事務1在兩次查詢的過程中,事務2對該表進行了插入、刪除操作,從而事務1第二次查詢的結果發生了變化。

不可重複讀 與 臟讀 的區別? 臟讀讀到的是尚未提交的數據,而不可重複讀讀到的是已經提交的數據,只不過在兩次讀的過程中數據被另一個事務改過了。

3.2 數據庫的四種隔離級別

  數據庫一共有如下四種隔離級別:

  1. Read uncommitted 讀未提交

    在該級別下,一個事務對一行數據修改的過程中,不允許另一個事務對該行數據進行修改,但允許另一個事務對該行數據讀。 因此本級別下,不會出現更新丟失,但會出現臟讀、不可重複讀。

  2. Read committed 讀提交

    在該級別下,未提交的寫事務不允許其他事務訪問該行,因此不會出現臟讀;但是讀取數據的事務允許其他事務的訪問該行數據,因此會出現不可重複讀的情況。

  3. Repeatable read 重複讀

    在該級別下,讀事務禁止寫事務,但允許讀事務,因此不會出現同一事務兩次讀到不同的數據的情況(不可重複讀),且寫事務禁止其他一切事務。

  4. Serializable 序列化

    該級別要求所有事務都必須串行執行,因此能避免一切因併發引起的問題,但效率很低。

  隔離級別越高,越能保證數據的完整性和一致性,但是對併發性能的影響也越大。對於多數應用程序,可以優先考慮把數據庫系統的隔離級別設為Read Committed。它能夠避免臟讀取,而且具有較好的併發性能。儘管它會導致不可重複讀、幻讀和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,可以由應用程序採用悲觀鎖或樂觀鎖來控制。

4 什麼是分佈式事務?

  到此為止,所介紹的事務都是基於單數據庫的本地事務,目前的數據庫僅支持單庫事務,並不支持跨庫事務。而隨着微服務架構的普及,一個大型業務系統往往由若干個子系統構成,這些子系統又擁有各自獨立的數據庫。往往一個業務流程需要由多個子系統共同完成,而且這些操作可能需要在一個事務中完成。在微服務系統中,這些業務場景是普遍存在的。此時,我們就需要在數據庫之上通過某種手段,實現支持跨數據庫的事務支持,這也就是大家常說的“分佈式事務”。

這裏舉一個分佈式事務的典型例子——用戶下單過程。 當我們的系統採用了微服務架構后,一個電商系統往往被拆分成如下幾個子系統:商品系統、訂單系統、支付系統、積分系統等。整個下單的過程如下:

  1. 用戶通過商品系統瀏覽商品,他看中了某一項商品,便點擊下單
  2. 此時訂單系統會生成一條訂單
  3. 訂單創建成功后,支付系統提供支付功能
  4. 當支付完成后,由積分系統為該用戶增加積分

上述步驟2、3、4需要在一個事務中完成。對於傳統單體應用而言,實現事務非常簡單,只需將這三個步驟放在一個方法A中,再用Spring的@Transactional註解標識該方法即可。Spring通過數據庫的事務支持,保證這些步驟要麼全都執行完成,要麼全都不執行。但在這個微服務架構中,這三個步驟涉及三個系統,涉及三個數據庫,此時我們必須在數據庫和應用系統之間,通過某項黑科技,實現分佈式事務的支持。

5 CAP理論

CAP理論說的是:在一個分佈式系統中,最多只能滿足C、A、P中的兩個需求。

CAP的含義:

  • C:Consistency 一致性

    同一數據的多個副本是否實時相同。

  • A:Availability 可用性

    可用性:一定時間內 & 系統返回一個明確的結果 則稱為該系統可用。

  • P:Partition tolerance 分區容錯性

    將同一服務分佈在多個系統中,從而保證某一個系統宕機,仍然有其他系統提供相同的服務。

  CAP理論告訴我們,在分佈式系統中,C、A、P三個條件中我們最多只能選擇兩個。那麼問題來了,究竟選擇哪兩個條件較為合適呢?

對於一個業務系統來說,可用性和分區容錯性是必須要滿足的兩個條件,並且這兩者是相輔相成的。業務系統之所以使用分佈式系統,主要原因有兩個:

  • 提升整體性能

    當業務量猛增,單個服務器已經無法滿足我們的業務需求的時候,就需要使用分佈式系統,使用多個節點提供相同的功能,從而整體上提升系統的性能,這就是使用分佈式系統的第一個原因。

  • 實現分區容錯性

    單一節點 或 多個節點處於相同的網絡環境下,那麼會存在一定的風險,萬一該機房斷電、該地區發生自然災害,那麼業務系統就全面癱瘓了。為了防止這一問題,採用分佈式系統,將多個子系統分佈在不同的地域、不同的機房中,從而保證系統高可用性。

  這說明分區容錯性是分佈式系統的根本,如果分區容錯性不能滿足,那使用分佈式系統將失去意義。

  此外,可用性對業務系統也尤為重要。在大談用戶體驗的今天,如果業務系統時常出現“系統異常”、響應時間過長等情況,這使得用戶對系統的好感度大打折扣,在互聯網行業競爭激烈的今天,相同領域的競爭者不甚枚舉,系統的間歇性不可用會立馬導致用戶流向競爭對手。因此,我們只能通過犧牲一致性來換取系統的可用性分區容錯性。這也就是下面要介紹的BASE理論。

6 BASE理論

  CAP理論告訴我們一個悲慘但不得不接受的事實——我們只能在C、A、P中選擇兩個條件。而對於業務系統而言,我們往往選擇犧牲一致性來換取系統的可用性和分區容錯性。不過這裏要指出的是,所謂的“犧牲一致性”並不是完全放棄數據一致性,而是犧牲強一致性換取弱一致性。下面來介紹下BASE理論。

  • BA:Basic Available 基本可用S:Soft State:柔性狀態 同一數據的不同副本的狀態,可以不需要實時一致。
    • 整個系統在某些不可抗力的情況下,仍然能夠保證“可用性”,即一定時間內仍然能夠返回一個明確的結果。只不過“基本可用”和“高可用”的區別是:
      • “一定時間”可以適當延長 當舉行大促時,響應時間可以適當延長
      • 給部分用戶返回一個降級頁面 給部分用戶直接返回一個降級頁面,從而緩解服務器壓力。但要注意,返回降級頁面仍然是返回明確結果。
  • E:Eventual Consisstency:最終一致性 同一數據的不同副本的狀態,可以不需要實時一致,但一定要保證經過一定時間后仍然是一致的。

7 酸鹼平衡

  ACID能夠保證事務的強一致性,即數據是實時一致的。這在本地事務中是沒有問題的,在分佈式事務中,強一致性會極大影響分佈式系統的性能,因此分佈式系統中遵循BASE理論即可。但分佈式系統的不同業務場景對一致性的要求也不同。如交易場景下,就要求強一致性,此時就需要遵循ACID理論,而在註冊成功后發送短信驗證碼等場景下,並不需要實時一致,因此遵循BASE理論即可。因此要根據具體業務場景,在ACID和BASE之間尋求平衡。

8 分佈式事務協議

  下面介紹幾種實現分佈式事務的協議。

8.1 兩階段提交協議 2PC

分佈式系統的一個難點是如何保證架構下多個節點在進行事務性操作的時候保持一致性。為實現這個目的,二階段提交算法的成立基於以下假設:

  • 該分佈式系統中,存在一個節點作為協調者(Coordinator),其他節點作為參与者(Cohorts)。且節點之間可以進行網絡通信。
  • 所有節點都採用預寫式日誌,且日誌被寫入后即被保持在可靠的存儲設備上,即使節點損壞不會導致日誌數據的消失。
  • 所有節點不會永久性損壞,即使損壞后仍然可以恢復。

1. 第一階段(投票階段):

  1. 協調者節點向所有參与者節點詢問是否可以執行提交操作(vote),並開始等待各參与者節點的響應。
  2. 參与者節點執行詢問發起為止的所有事務操作,並將Undo信息和Redo信息寫入日誌。(注意:若成功這裏其實每個參与者已經執行了事務操作)
  3. 各參与者節點響應協調者節點發起的詢問。如果參与者節點的事務操作實際執行成功,則它返回一個”同意”消息;如果參与者節點的事務操作實際執行失敗,則它返回一個”中止”消息。

2. 第二階段(提交執行階段):

  當協調者節點從所有參与者節點獲得的相應消息都為”同意”時:

  1. 協調者節點向所有參与者節點發出”正式提交(commit)”的請求。
  2. 參与者節點正式完成操作,並釋放在整個事務期間內佔用的資源。
  3. 參与者節點向協調者節點發送”完成”消息。
  4. 協調者節點受到所有參与者節點反饋的”完成”消息后,完成事務。

  如果任一參与者節點在第一階段返回的響應消息為”中止”,或者 協調者節點在第一階段的詢問超時之前無法獲取所有參与者節點的響應消息時:

  1. 協調者節點向所有參与者節點發出”回滾操作(rollback)”的請求。
  2. 參与者節點利用之前寫入的Undo信息執行回滾,並釋放在整個事務期間內佔用的資源。
  3. 參与者節點向協調者節點發送”回滾完成”消息。
  4. 協調者節點受到所有參与者節點反饋的”回滾完成”消息后,取消事務。

  不管最後結果如何,第二階段都會結束當前事務。

  二階段提交看起來確實能夠提供原子性的操作,但是不幸的事,二階段提交還是有幾個缺點的:

  1. 執行過程中,所有參与節點都是事務阻塞型的。當參与者佔有公共資源時,其他第三方節點訪問公共資源不得不處於阻塞狀態。
  2. 參与者發生故障。協調者需要給每個參与者額外指定超時機制,超時后整個事務失敗。(沒有多少容錯機制)
  3. 協調者發生故障。參与者會一直阻塞下去。需要額外的備機進行容錯。(這個可以依賴後面要講的Paxos協議實現HA)
  4. 二階段無法解決的問題:協調者再發出commit消息之後宕機,而唯一接收到這條消息的參与者同時也宕機了。那麼即使協調者通過選舉協議產生了新的協調者,這條事務的狀態也是不確定的,沒人知道事務是否被已經提交。

為此,Dale Skeen和Michael Stonebraker在“A Formal Model of Crash Recovery in a Distributed System”中提出了三階段提交協議(3PC)。

8.2 三階段提交協議 3PC

  與兩階段提交不同的是,三階段提交有兩個改動點。

  • 引入超時機制。同時在協調者和參与者中都引入超時機制。
  • 在第一階段和第二階段中插入一個準備階段。保證了在最後提交階段之前各參与節點的狀態是一致的。

  也就是說,除了引入超時機制之外,3PC把2PC的準備階段再次一分為二,這樣三階段提交就有CanCommit、PreCommit、DoCommit三個階段。

1. CanCommit階段

  3PC的CanCommit階段其實和2PC的準備階段很像。協調者向參与者發送commit請求,參与者如果可以提交就返回Yes響應,否則返回No響應。

  1. 事務詢問

    協調者向參与者發送CanCommit請求。詢問是否可以執行事務提交操作。然後開始等待參与者的響應。

  2. 響應反饋

    參与者接到CanCommit請求之後,正常情況下,如果其自身認為可以順利執行事務,則返回Yes響應,並進入預備狀態。否則反饋No

2. PreCommit階段

  協調者根據參与者的反應情況來決定是否可以記性事務的PreCommit操作。根據響應情況,有以下兩種可能。 假如協調者從所有的參与者獲得的反饋都是Yes響應,那麼就會執行事務的預執行。

  1. 發送預提交請求

    協調者向參与者發送PreCommit請求,並進入Prepared階段。

  2. 事務預提交

    參与者接收到PreCommit請求后,會執行事務操作,並將undo和redo信息記錄到事務日誌中。

  3. 響應反饋

    如果參与者成功的執行了事務操作,則返回ACK響應,同時開始等待最終指令。

假如有任何一個參与者向協調者發送了No響應,或者等待超時之後,協調者都沒有接到參与者的響應,那麼就執行事務的中斷。

  1. 發送中斷請求

    協調者向所有參与者發送abort請求。

  2. 中斷事務

    參与者收到來自協調者的abort請求之後(或超時之後,仍未收到協調者的請求),執行事務的中斷。

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

3. doCommit階段 該階段進行真正的事務提交,也可以分為以下兩種情況。

  該階段進行真正的事務提交,也可以分為以下兩種情況。

3.1 執行提交

  1. 發送提交請求

    協調接收到參与者發送的ACK響應,那麼他將從預提交狀態進入到提交狀態。並向所有參与者發送doCommit請求。

  2. 事務提交

    參与者接收到doCommit請求之後,執行正式的事務提交。並在完成事務提交之後釋放所有事務資源。

  3. 響應反饋

    事務提交完之後,向協調者發送Ack響應。

  4. 完成事務

    協調者接收到所有參与者的ack響應之後,完成事務。

3.2 中斷事務 協調者沒有接收到參与者發送的ACK響應(可能是接受者發送的不是ACK響應,也可能響應超時),那麼就會執行中斷事務。

  1. 發送中斷請求

    協調者向所有參与者發送abort請求

  2. 事務回滾

    參与者接收到abort請求之後,利用其在階段二記錄的undo信息來執行事務的回滾操作,並在完成回滾之後釋放所有的事務資源。

  3. 反饋結果

    參与者完成事務回滾之後,向協調者發送ACK消息

  4. 中斷事務

    協調者接收到參与者反饋的ACK消息之後,執行事務的中斷。

9 分佈式事務的解決方案

  分佈式事務的解決方案有如下幾種:

  • 全局消息
  • 基於可靠消息服務的分佈式事務
  • TCC
  • 最大努力通知

9.1 方案1:全局事務(DTP模型)

  全局事務基於DTP模型實現。DTP是由X/Open組織提出的一種分佈式事務模型——X/Open Distributed Transaction Processing Reference Model。它規定了要實現分佈式事務,需要三種角色:

  • AP:Application 應用系統

    它就是我們開發的業務系統,在我們開發的過程中,可以使用資源管理器提供的事務接口來實現分佈式事務。

  • TM:Transaction Manager 事務管理器

    • 分佈式事務的實現由事務管理器來完成,它會提供分佈式事務的操作接口供我們的業務系統調用。這些接口稱為TX接口。
    • 事務管理器還管理着所有的資源管理器,通過它們提供的XA接口來同一調度這些資源管理器,以實現分佈式事務。
    • DTP只是一套實現分佈式事務的規範,並沒有定義具體如何實現分佈式事務,TM可以採用2PC、3PC、Paxos等協議實現分佈式事務。
  • RM:Resource Manager 資源管理器

    • 能夠提供數據服務的對象都可以是資源管理器,比如:數據庫、消息中間件、緩存等。大部分場景下,數據庫即為分佈式事務中的資源管理器。
    • 資源管理器能夠提供單數據庫的事務能力,它們通過XA接口,將本數據庫的提交、回滾等能力提供給事務管理器調用,以幫助事務管理器實現分佈式的事務管理。
    • XA是DTP模型定義的接口,用於向事務管理器提供該資源管理器(該數據庫)的提交、回滾等能力。
    • DTP只是一套實現分佈式事務的規範,RM具體的實現是由數據庫廠商來完成的。
  1. 有沒有基於DTP模型的分佈式事務中間件?
  1. DTP模型有啥優缺點?

9.2 方案2:基於可靠消息服務的分佈式事務

  這種實現分佈式事務的方式需要通過消息中間件來實現。假設有A和B兩個系統,分別可以處理任務A和任務B。此時系統A中存在一個業務流程,需要將任務A和任務B在同一個事務中處理。下面來介紹基於消息中間件來實現這種分佈式事務。

 

 

  • 在系統A處理任務A前,首先向消息中間件發送一條消息
  • 消息中間件收到后將該條消息持久化,但並不投遞。此時下游系統B仍然不知道該條消息的存在。
  • 消息中間件持久化成功后,便向系統A返回一個確認應答;
  • 系統A收到確認應答后,則可以開始處理任務A;
  • 任務A處理完成后,向消息中間件發送Commit請求。該請求發送完成后,對系統A而言,該事務的處理過程就結束了,此時它可以處理別的任務了。 但commit消息可能會在傳輸途中丟失,從而消息中間件並不會向系統B投遞這條消息,從而系統就會出現不一致性。這個問題由消息中間件的事務回查機制完成,下文會介紹。
  • 消息中間件收到Commit指令后,便向系統B投遞該消息,從而觸發任務B的執行;
  • 當任務B執行完成后,系統B向消息中間件返回一個確認應答,告訴消息中間件該消息已經成功消費,此時,這個分佈式事務完成。

上述過程可以得出如下幾個結論:

  1. 消息中間件扮演者分佈式事務協調者的角色。
  2. 系統A完成任務A后,到任務B執行完成之間,會存在一定的時間差。在這個時間差內,整個系統處於數據不一致的狀態,但這短暫的不一致性是可以接受的,因為經過短暫的時間后,系統又可以保持數據一致性,滿足BASE理論。

上述過程中,如果任務A處理失敗,那麼需要進入回滾流程,如下圖所示:

 

  • 若系統A在處理任務A時失敗,那麼就會向消息中間件發送Rollback請求。和發送Commit請求一樣,系統A發完之後便可以認為回滾已經完成,它便可以去做其他的事情。

  • 消息中間件收到回滾請求后,直接將該消息丟棄,而不投遞給系統B,從而不會觸發系統B的任務B。

此時系統又處於一致性狀態,因為任務A和任務B都沒有執行。

  上面所介紹的Commit和Rollback都屬於理想情況,但在實際系統中,Commit和Rollback指令都有可能在傳輸途中丟失。那麼當出現這種情況的時候,消息中間件是如何保證數據一致性呢?——答案就是超時詢問機制。

 

 

  系統A除了實現正常的業務流程外,還需提供一個事務詢問的接口,供消息中間件調用。當消息中間件收到一條事務型消息后便開始計時,如果到了超時時間也沒收到系統A發來的Commit或Rollback指令的話,就會主動調用系統A提供的事務詢問接口詢問該系統目前的狀態。該接口會返回三種結果:

  • 提交

    若獲得的狀態是“提交”,則將該消息投遞給系統B。

  • 回滾

    若獲得的狀態是“回滾”,則直接將條消息丟棄。

  • 處理中

    若獲得的狀態是“處理中”,則繼續等待。

消息中間件的超時詢問機制能夠防止上游系統因在傳輸過程中丟失Commit/Rollback指令而導致的系統不一致情況,而且能降低上游系統的阻塞時間,上游系統只要發出Commit/Rollback指令后便可以處理其他任務,無需等待確認應答。而Commit/Rollback指令丟失的情況通過超時詢問機制來彌補,這樣大大降低上游系統的阻塞時間,提升系統的併發度。

  下面來說一說消息投遞過程的可靠性保證。 當上游系統執行完任務並向消息中間件提交了Commit指令后,便可以處理其他任務了,此時它可以認為事務已經完成,接下來消息中間件**一定會保證消息被下游系統成功消費掉!**那麼這是怎麼做到的呢?這由消息中間件的投遞流程來保證。

  消息中間件向下游系統投遞完消息后便進入阻塞等待狀態,下游系統便立即進行任務的處理,任務處理完成后便向消息中間件返回應答。消息中間件收到確認應答后便認為該事務處理完畢!

  如果消息在投遞過程中丟失,或消息的確認應答在返回途中丟失,那麼消息中間件在等待確認應答超時之後就會重新投遞,直到下游消費者返回消費成功響應為止。當然,一般消息中間件可以設置消息重試的次數和時間間隔,比如:當第一次投遞失敗后,每隔五分鐘重試一次,一共重試3次。如果重試3次之後仍然投遞失敗,那麼這條消息就需要人工干預。

 

 

 

有的同學可能要問:消息投遞失敗後為什麼不回滾消息,而是不斷嘗試重新投遞?

這就涉及到整套分佈式事務系統的實現成本問題。 我們知道,當系統A將向消息中間件發送Commit指令后,它便去做別的事情了。如果此時消息投遞失敗,需要回滾的話,就需要讓系統A事先提供回滾接口,這無疑增加了額外的開發成本,業務系統的複雜度也將提高。對於一個業務系統的設計目標是,在保證性能的前提下,最大限度地降低系統複雜度,從而能夠降低系統的運維成本。

不知大家是否發現,上游系統A向消息中間件提交Commit/Rollback消息採用的是異步方式,也就是當上游系統提交完消息后便可以去做別的事情,接下來提交、回滾就完全交給消息中間件來完成,並且完全信任消息中間件,認為它一定能正確地完成事務的提交或回滾。然而,消息中間件向下游系統投遞消息的過程是同步的。也就是消息中間件將消息投遞給下游系統后,它會阻塞等待,等下游系統成功處理完任務返回確認應答后才取消阻塞等待。為什麼這兩者在設計上是不一致的呢?

  首先,上游系統和消息中間件之間採用異步通信是為了提高系統併發度。業務系統直接和用戶打交道,用戶體驗尤為重要,因此這種異步通信方式能夠極大程度地降低用戶等待時間。此外,異步通信相對於同步通信而言,沒有了長時間的阻塞等待,因此系統的併發性也大大增加。但異步通信可能會引起Commit/Rollback指令丟失的問題,這就由消息中間件的超時詢問機制來彌補。

  那麼,消息中間件和下游系統之間為什麼要採用同步通信呢?

  異步能提升系統性能,但隨之會增加系統複雜度;而同步雖然降低系統併發度,但實現成本較低。因此,在對併發度要求不是很高的情況下,或者服務器資源較為充裕的情況下,我們可以選擇同步來降低系統的複雜度。 我們知道,消息中間件是一個獨立於業務系統的第三方中間件,它不和任何業務系統產生直接的耦合,它也不和用戶產生直接的關聯,它一般部署在獨立的服務器集群上,具有良好的可擴展性,所以不必太過於擔心它的性能,如果處理速度無法滿足我們的要求,可以增加機器來解決。而且,即使消息中間件處理速度有一定的延遲那也是可以接受的,因為前面所介紹的BASE理論就告訴我們了,我們追求的是最終一致性,而非實時一致性,因此消息中間件產生的時延導致事務短暫的不一致是可以接受的。

9.3 方案3:最大努力通知(定期校對)

  最大努力通知也被稱為定期校對,其實在方案二中已經包含,這裏再單獨介紹,主要是為了知識體系的完整性。這種方案也需要消息中間件的參与,其過程如下:

 

 

  • 上游系統在完成任務后,向消息中間件同步地發送一條消息,確保消息中間件成功持久化這條消息,然後上游系統可以去做別的事情了;
  • 消息中間件收到消息后負責將該消息同步投遞給相應的下游系統,並觸發下游系統的任務執行;
  • 當下游系統處理成功后,向消息中間件反饋確認應答,消息中間件便可以將該條消息刪除,從而該事務完成。

上面是一個理想化的過程,但在實際場景中,往往會出現如下幾種意外情況:

  1. 消息中間件向下游系統投遞消息失敗
  2. 上游系統向消息中間件發送消息失敗

  對於第一種情況,消息中間件具有重試機制,我們可以在消息中間件中設置消息的重試次數和重試時間間隔,對於網絡不穩定導致的消息投遞失敗的情況,往往重試幾次后消息便可以成功投遞,如果超過了重試的上限仍然投遞失敗,那麼消息中間件不再投遞該消息,而是記錄在失敗消息表中,消息中間件需要提供失敗消息的查詢接口,下游系統會定期查詢失敗消息,並將其消費,這就是所謂的“定期校對”。

如果重複投遞和定期校對都不能解決問題,往往是因為下游系統出現了嚴重的錯誤,此時就需要人工干預。

  對於第二種情況,需要在上游系統中建立消息重發機制。可以在上游系統建立一張本地消息表,並將 任務處理過程向本地消息表中插入消息 這兩個步驟放在一個本地事務中完成。如果向本地消息表插入消息失敗,那麼就會觸發回滾,之前的任務處理結果就會被取消。如果這量步都執行成功,那麼該本地事務就完成了。接下來會有一個專門的消息發送者不斷地發送本地消息表中的消息,如果發送失敗它會返回重試。當然,也要給消息發送者設置重試的上限,一般而言,達到重試上限仍然發送失敗,那就意味着消息中間件出現嚴重的問題,此時也只有人工干預才能解決問題。

  對於不支持事務型消息的消息中間件,如果要實現分佈式事務的話,就可以採用這種方式。它能夠通過重試機制+定期校對實現分佈式事務,但相比於第二種方案,它達到數據一致性的周期較長,而且還需要在上游系統中實現消息重試發布機制,以確保消息成功發布給消息中間件,這無疑增加了業務系統的開發成本,使得業務系統不夠純粹,並且這些額外的業務邏輯無疑會佔用業務系統的硬件資源,從而影響性能。

因此,盡量選擇支持事務型消息的消息中間件來實現分佈式事務,如RocketMQ。

9.4 方案4:TCC(兩階段型、補償型)

  TCC即為Try Confirm Cancel,它屬於補償型分佈式事務。顧名思義,TCC實現分佈式事務一共有三個步驟:

  • Try:嘗試待執行的業務
    • 這個過程並未執行業務,只是完成所有業務的一致性檢查,並預留好執行所需的全部資源
  • Confirm:執行業務
    • 這個過程真正開始執行業務,由於Try階段已經完成了一致性檢查,因此本過程直接執行,而不做任何檢查。並且在執行的過程中,會使用到Try階段預留的業務資源。
  • Cancel:取消執行的業務
    • 若業務執行失敗,則進入Cancel階段,它會釋放所有佔用的業務資源,並回滾Confirm階段執行的操作。

下面以一個轉賬的例子來解釋下TCC實現分佈式事務的過程。

假設用戶A用他的賬戶餘額給用戶B發一個100元的紅包,並且餘額系統和紅包系統是兩個獨立的系統。

  • Try

    • 創建一條轉賬流水,並將流水的狀態設為交易中
    • 將用戶A的賬戶中扣除100元(預留業務資源)
    • Try成功之後,便進入Confirm階段
    • Try過程發生任何異常,均進入Cancel階段
  • Confirm

    • 向B用戶的紅包賬戶中增加100元
    • 將流水的狀態設為交易已完成
    • Confirm過程發生任何異常,均進入Cancel階段
    • Confirm過程執行成功,則該事務結束
  • Cancel

    • 將用戶A的賬戶增加100元
    • 將流水的狀態設為交易失敗

  在傳統事務機制中,業務邏輯的執行和事務的處理,是在不同的階段由不同的部件來完成的:業務邏輯部分訪問資源實現數據存儲,其處理是由業務系統負責;事務處理部分通過協調資源管理器以實現事務管理,其處理由事務管理器來負責。二者沒有太多交互的地方,所以,傳統事務管理器的事務處理邏輯,僅需要着眼於事務完成(commit/rollback)階段,而不必關注業務執行階段。

TCC全局事務必須基於RM本地事務來實現全局事務

  TCC服務是由Try/Confirm/Cancel業務構成的, 其Try/Confirm/Cancel業務在執行時,會訪問資源管理器(Resource Manager,下文簡稱RM)來存取數據。這些存取操作,必須要參与RM本地事務,以使其更改的數據要麼都commit,要麼都rollback。

這一點不難理解,考慮一下如下場景:

 

 

  假設圖中的服務B沒有基於RM本地事務(以RDBS為例,可通過設置auto-commit為true來模擬),那麼一旦[B:Try]操作中途執行失敗,TCC事務框架後續決定回滾全局事務時,該[B:Cancel]則需要判斷[B:Try]中哪些操作已經寫到DB、哪些操作還沒有寫到DB:假設[B:Try]業務有5個寫庫操作,[B:Cancel]業務則需要逐個判斷這5個操作是否生效,並將生效的操作執行反向操作。

  不幸的是,由於[B:Cancel]業務也有n(0<=n<=5)個反向的寫庫操作,此時一旦[B:Cancel]也中途出錯,則後續的[B:Cancel]執行任務更加繁重。因為,相比第一次[B:Cancel]操作,後續的[B:Cancel]操作還需要判斷先前的[B:Cancel]操作的n(0<=n<=5)個寫庫中哪幾個已經執行、哪幾個還沒有執行,這就涉及到了冪等性問題。而對冪等性的保障,又很可能還需要涉及額外的寫庫操作,該寫庫操作又會因為沒有RM本地事務的支持而存在類似問題。。。可想而知,如果不基於RM本地事務,TCC事務框架是無法有效的管理TCC全局事務的。

反之,基於RM本地事務的TCC事務,這種情況則會很容易處理:[B:Try]操作中途執行失敗,TCC事務框架將其參与RM本地事務直接rollback即可。後續TCC事務框架決定回滾全局事務時,在知道“[B:Try]操作涉及的RM本地事務已經rollback”的情況下,根本無需執行[B:Cancel]操作。

換句話說,基於RM本地事務實現TCC事務框架時,一個TCC型服務的cancel業務要麼執行,要麼不執行,不需要考慮部分執行的情況。

TCC事務框架應該提供Confirm/Cancel服務的冪等性保障

  一般認為,服務的冪等性,是指針對同一個服務的多次(n>1)請求和對它的單次(n=1)請求,二者具有相同的副作用。

在TCC事務模型中,Confirm/Cancel業務可能會被重複調用,其原因很多。比如,全局事務在提交/回滾時會調用各TCC服務的Confirm/Cancel業務邏輯。執行這些Confirm/Cancel業務時,可能會出現如網絡中斷的故障而使得全局事務不能完成。因此,故障恢復機制後續仍然會重新提交/回滾這些未完成的全局事務,這樣就會再次調用參与該全局事務的各TCC服務的Confirm/Cancel業務邏輯。

  既然Confirm/Cancel業務可能會被多次調用,就需要保障其冪等性。 那麼,應該由TCC事務框架來提供冪等性保障?還是應該由業務系統自行來保障冪等性呢? 個人認為,應該是由TCC事務框架來提供冪等性保障。如果僅僅只是極個別服務存在這個問題的話,那麼由業務系統來負責也是可以的;然而,這是一類公共問題,毫無疑問,所有TCC服務的Confirm/Cancel業務存在冪等性問題。TCC服務的公共問題應該由TCC事務框架來解決;而且,考慮一下由業務系統來負責冪等性需要考慮的問題,就會發現,這無疑增大了業務系統的複雜度。


作者:大閑人柴毛毛


鏈接:https://juejin.im/post/5ab0d1a3f265da23731448e0


來源:掘金


著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

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

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

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

現在合資SUV門檻越來越低了 質量好設計好 15萬以內就能落地_包裝設計

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

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

3廣汽菲克Jeep-自由

時下汽車市場對SUV的熱度是史無前例的,能夠有錢買路虎,買雷克薩斯固然是好,但是預算不多的朋友,想購買一輛SUV往往只能選擇售價十萬左右的小型SUV。事實上,省油、耐操,是國人對汽車的普遍需求,那小型SUV領域,這些合資車表現又如何呢?

1長安福特-翼博

指導價:9.48-12.78萬元

一句話:底盤好於預期,1.5L動力伐善可陳

翼博的造型給人短小精悍的感覺,正面看過去映入眼帘的是那個多邊形的大面積進氣格柵,彷彿與短小的車身不太協調,但是單看車頭的話確實是蠻大氣的。側面可以看到車身線條是稍微前傾的,下包圍是黑色塑料材質,顯得車身更高。車尾造型方正,外掛的備胎很有特色。

內飾中控和嘉年華如出一轍,中控面板微微斜傾,比較親近駕駛者,內飾主要是塑料材質,不太有高級感,但按鍵的質感並不差,中控有一個尺寸較小的單色屏,集成了SYNC多媒體系統,雖然不是最新的SYNC3,但是基本的多媒體功能還是能夠滿足的。

翼博搭載1.0T+5MT或者1.5L+5MT/6速雙離合的動力系統,最大馬力分別是125pS和110pS,1.5L版本的動力表現確實不太积極,而且13年上市的車型,雙離合的調校還未夠火候,低擋位的頓挫還是比較明顯。至於1.0T版本的動力就积極很多,1500轉左右就能感受到翼博已經活躍起來,只可惜沒有自動擋車型。

有人可能會擔心,翼博這個短而高的車身,會不會造成比較差的行駛穩定性?然而事實並不是,翼博的高速穩定性十分出色,

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

甚至超出了同級別的水準。高速併線時車身十分穩定,可以感受到翼博的懸挂質感十分紮實,整體調校是偏舒適的。

2東風標緻-2008

指導價:9.97-16.37萬元

一句話:內飾做工有待提升,行駛品質驚喜滿滿

標緻2008造型圓潤,車身姿勢較低,外形看起來更加偏向於一輛跨界車。前臉造型比較含蓄,鍍鉻中網沒有使用大口式設計,車標也被放在了引擎蓋上,這也是標緻上一代車型比較普遍的一個設計。側面造型看起來更像一輛跨界車了,還能看到尾燈位置有一個“C”字造型,很有設計感。

2008的內飾簡潔內斂而不是時尚感,整體氛圍十分和諧。內飾面板分上下兩層,分別是硬質塑料盒軟質的織物材料,手觸及的地方都是柔軟的。只是內飾在做工上還是有不完美的地方,如空調的旋鈕,天窗控制區乾脆的塑料板,在做工上都不夠好。

動力方面,標緻2008搭載1.2T、1.6L和1.6T的發動機,自吸版本的變速箱為4AT,渦輪版本的變速箱為6AT,不怕老實說,最推薦的還是1.2T的車型,136pS的馬力,放棄了老舊的4AT,無論是動力表現、燃油經濟性,還是性價比,都是三個排量裏面最好的,家用足夠,平時代步也不會十分寒酸。

底盤和轉向的調校一直都是法系車的強項,我們確實不用擔心。標緻2008的懸挂調校偏向舒適,但並不是那種無底線的柔軟,在支撐性方面也有一定的表現,整體操控雖然不能說是指哪打哪,但是併線和轉彎的時候絲毫不用擔心會有嚴重的側傾,家用車有這個行駛品質,算是很不錯的了。

3廣汽菲克Jeep-自由本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

不可不知的輔助測試的Fiddler小技巧,Fiddler抓包工具使用詳解,利用Fiddler攔截接口請求並篡改數據,Fiddler使用過程中容易忽略的小技巧,Mock測試,結合Fiddler輕鬆搞定不同場景_包裝設計

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

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

在以前的博文中,時常有分享Fiddler的一些使用技巧,今天再貼下。

Fiddler抓包工具使用詳解

利用Fiddler攔截接口請求並篡改數據

Fiddler使用過程中容易忽略的小技巧

Mock測試,結合Fiddler輕鬆搞定不同場景

以上這些,就是以前的一些實踐總結。今天,再來分享一個不可不知的小技巧,實際工作當中,是可以輔助自己完成測試的。

模擬弱網

相信大家在進行測試過程中,都會關注到弱網測試。web端是可以通過瀏覽器的開發者工具中的Network設置網絡數據,app端也可以通過切換網絡的形式來進行測試。

其實,Fiddler也是支持模擬弱網測試的。通過Fiddler設置后,web和app都可以一併模式。

固定延遲設置方式

啟動Fiddler工具>找到FiddlerScript頁簽>按m_SimulateModem搜索。

找到如下所示內容【限速函數】,修改參數即可:

修改代碼中的參數,將300修改為3000,也就是3S,如下:

if (m_SimulateModem) {
            // Delay sends by 300ms per KB uploaded.
            oSession["request-trickle-delay"] = "3000"; 
            // Delay receives by 150ms per KB downloaded.
            oSession["response-trickle-delay"] = "150"; 
        }  

此時,保存修改后的Script代碼即可,如下所示:

數據保存后,如何使其生效呢?按如下操作即可。

在Fiddler中,按如下路徑配置即可:Rules>Performance>Simulate Modem Speeds。

具體操作,如下圖所示:

此時,再刷新頁面,我們可以從Fiddler面板中的Time參數看出,每個接口的響應時間都延時3s,如下所示:

網絡延遲時間如何計算?

比如你要模擬上傳速度100KBps的網絡,那上傳延遲就是1KB/100KBps=0.01s=10ms,就改成10。

隨機延遲設置方式

我們從上述操作看出,配置限速參數后,每個接口都是固定的延遲時間。但實際使用場景中,這種情況是比較少的。基本上都是有時快,有時慢的情況。那是否可以設置隨機延遲呢?答案肯定是可以的。我們繼續來看。

我們依然在FiddlerScript頁簽中操作,加入如下代碼,代碼如下所示:

static function randInt(min, max) {
  return Math.round(Math.random()*(max-min)+min);
  }
  if (m_SimulateModem) {
  // Delay sends by 300ms per KB uploaded.
  oSession["request-trickle-delay"] = ""+randInt(1,2000);
  // Delay receives by 150ms per KB downloaded.
  oSession["response-trickle-delay"] = ""+randInt(1,2000);
  }  

保存修改后的代碼,並重新啟用配置即可。

模擬發包

測試過程中,想模擬一個接口請求,一般是通過Postman、Jmeter、soupUI,常用的是Postman。我們Fiddler能不能模擬接口請求呢?這個是可以的,我們來看。

我們切換到Composer頁簽,如下所示:

從圖中可知,是可以模擬很多種請求方式的。我們就來實際操作一番。

POST請求

我們先來操作個POST請求,選擇POST請求方式,填寫接口與參數如下:

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

接口與參數填寫正確后,點擊右上角的Execute按鈕即可請求接口。我們來看下請求數據:

我們先來看請求頭,接口請求頭中的User-Agent參數,如下所示:

用戶代理來源是Fiddler,就是剛才我們通過Fiddler工具請求的數據。

我們再來看響應,接口請求成功,正常響應並獲取到token數據,如下所示:

GET請求

剛才操作的是POST請求,現在來看個GET請求。操作方式都一樣,將請求方式切換成GET,填寫接口與參數,並點擊Execute按鈕,配置如下所示:

我們依然來看接口數據,先看請求頭參數:

 

User-Agent參數依然是Fiddler,說明是從Fiddler工具中發出的請求。

再來看響應數據,如下所示:

以上就是通過Fiddler模擬請求POST和GET請求了,其他方式類似。

模擬併發

在我們測試中,也會關注到,一個接口同時請求多次,會不會產生錯誤,比如:重複數據。那這次就需要考慮併發情況了。

Fiddler工具依然還是支持該操作,具體如下:

我們先選中已抓取的任一接口,點擊右鍵,並選擇Replay>Reissue Sequentially,也可以快捷鍵【Shift + R】如下所示:

點擊后,會彈出如下彈框,設置請求次數,如下所示:

默認是5,那我們改成3,並點擊OK按鈕,來看界面,如下所示:

我們可以看到,同時請求了3次,簡單模擬了併發測試,但實際是不及Jmeter工具的,畢竟這個也只是模擬,而Jmeter要專業許多。

了解完了併發使用,但還有些其他參數沒有介紹,我們來了解下其他參數,說不準以後也有用途。如下所示:

  • Reissue Requests:重發選中的請求
  • Reissue Unconditinally:無條件重發選中的請求
  • Reissue and eidt:重發並編輯,會打開一個加了斷點的請求,可以進行請求和響應數據的修改
  • Reissue and verify:重發並驗證,重新發送請求,並驗證其請求結果,會自動加標識(背景顏色)
  • Reissue Sequentially:重發序列,打開一個數量設置界面,標識需要重發多少次請求
  • Reissue from Composer:重發並打開composer界面,可以編輯或發送
  • Revisit in IE:在IE上發起這個請求

問題總結

在使用Fiddler模擬請求發包時,有時會操作失敗,比如通過百度查詢數據,GET接口,有時Caching參數不帶private參數就響應了302,正常帶該參數,是可以響應成功的,如下所示:

不知為何會出現,不帶該參數的情況。有遇到的博友,望提點一二。

 

好了,今天的這三個Fiddler技巧,就分享到這了,個人覺得在實際測試過程中,這三個技巧還是有幫助的。

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

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

丈母娘收割機 試駕地表最帥SUV_包裝設計

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

Levante目前有兩個版本的車型,分3。0T高低功率。全系標配空氣懸挂和雙邊四處排氣,最重要的是,Levante的排氣聲浪是相當迷人。Levante採用了Ghibli的平台,採用了很多輕量化的鋁材料,懸挂的形式和結構也都和Ghibli一樣,但是廠家針對SUV的特性,為了越野,還是加強了空氣彈簧的強度和拉伸幅度,滿足SUV的形式需要。

作為一個血液里流淌着運動基因的超豪華品牌,當瑪莎拉蒂在11年法蘭克福車展帶來kubang概念車的時候,我們就知道,這傢伙會未來絕不會老老實實的只做一個SUV。當今年Levante正式發布后,這個超級炫酷的大傢伙在馬雲粑粑的網上首批100台18秒就被搶光了!都跟不要錢似的!

瑪莎拉蒂Levante是一台長度超過5米的SUV、他的長度和寬度都超過了對手保時捷卡宴。實車比之前看圖片還要驚艷,瑪莎拉蒂標誌性的進氣口,配上細長的大燈,看起來相當兇狠。把車停在任何地方,即便不認識瑪莎拉蒂Logo的人,也一定能看得出來這是一輛豪車,因為前臉造型設計太酷太有型了。

不過,內飾就沒有外觀那麼驚艷,設計感有所欠缺,但依然是我們很熟悉的瑪莎拉蒂內飾風格,相當簡約。傳統的儀錶盤,既沒有炫酷的界面和显示效果,也沒有現在很有逼格的全液晶儀錶。好在對於駕駛者來說,握感和造型都不錯的方向盤、以及碩大的純金屬換擋撥片一定能讓他們感到欣慰。

皮質包裹的中控台很顯檔次,觸感細膩。但是這100萬的車,沒有360全景影像就算了,普通的倒車影像畫面還這麼模糊,就有點說不過去了,

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

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

一些地方的蓋板、按鍵也比較鬆散。

或許,內飾當中的一些小瑕疵不重要,因為這是一台瑪莎拉蒂的SUV。能給你高逼格,能讓你開着爽才是這輛車的關鍵所在。Levante目前有兩個版本的車型,分3.0T高低功率。全系標配空氣懸挂和雙邊四處排氣,最重要的是,Levante的排氣聲浪是相當迷人!

Levante採用了Ghibli的平台,採用了很多輕量化的鋁材料,懸挂的形式和結構也都和Ghibli一樣,但是廠家針對SUV的特性,為了越野,還是加強了空氣彈簧的強度和拉伸幅度,滿足SUV的形式需要。雙邊四齣的排氣,內側兩根帶有閥門,可以選擇手動開啟。

舒適模式下,整台車的轉向非常輕盈,動力的輸出也很平滑,升降檔反應迅速,乘坐感受也是偏舒適,這時的Levante就是一輛普通的豪華SUV。

開啟運動模式后,Levante就完全變了,只要給深一點油,排氣開始浪叫,底盤降至最低,突如其來的騷,閃了我的腰!車輛整體表現得非常敏捷,車尾跟得上車頭的動作,車身動態十分緊緻。你會忘掉這是一台超過五米長的SUV,只知道這是一台瑪莎拉蒂。

瑪莎拉蒂帶給我們的是一款駕駛者的SUV,它不玩大空間高配置,不講究多豪華多氣派。它可以讓你帶着家人輕鬆舒服的自駕游,也可以讓你在壓力過大的時候找個地撒撒野炸炸街。這就是Levante。

Levante官方售價為99.98-147.8萬元

鳴謝:廣東駿佳汽車服務有限公司本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

TCP 半連接隊列和全連接隊列滿了會發生什麼?又該如何應對?_網頁設計

※推薦評價好的iphone維修中心

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

前言

網上許多博客針對增大 TCP 半連接隊列和全連接隊列的方式如下:

  • 增大 TCP 半連接隊列的方式是增大 /proc/sys/net/ipv4/tcp_max_syn_backlog;
  • 增大 TCP 全連接隊列的方式是增大 listen() 函數中的 backlog;

這裏先跟大家說下,上面的方式都是不準確的。

“你怎麼知道不準確?”

很簡單呀,因為我做了實驗和看了 TCP 協議棧的內核源碼,發現要增大這兩個隊列長度,不是簡簡單單增大某一個參數就可以的。

接下來,就會以實戰 + 源碼分析,帶大家解密 TCP 半連接隊列和全連接隊列。

“源碼分析,那不是勸退嗎?我們搞 Java 的看不懂呀”

放心,本文的源碼分析不會涉及很深的知識,因為都被我刪減了,你只需要會條件判斷語句 if、左移右移操作符、加減法等基本語法,就可以看懂。

另外,不僅有源碼分析,還會介紹 Linux 排查半連接隊列和全連接隊列的命令。

“哦?似乎很有看頭,那我姑且看一下吧!”

行,沒有被勸退的小夥伴,值得鼓勵,下面這圖是本文的提綱:

本文提綱

正文

什麼是 TCP 半連接隊列和全連接隊列?

在 TCP 三次握手的時候,Linux 內核會維護兩個隊列,分別是:

  • 半連接隊列,也稱 SYN 隊列;
  • 全連接隊列,也稱 accepet 隊列;

服務端收到客戶端發起的 SYN 請求后,內核會把該連接存儲到半連接隊列,並向客戶端響應 SYN+ACK,接着客戶端會返回 ACK,服務端收到第三次握手的 ACK 后,內核會把連接從半連接隊列移除,然後創建新的完全的連接,並將其添加到 accept 隊列,等待進程調用 accept 函數時把連接取出來。

半連接隊列與全連接隊列

不管是半連接隊列還是全連接隊列,都有最大長度限制,超過限制時,內核會直接丟棄,或返回 RST 包。

實戰 – TCP 全連接隊列溢出

如何知道應用程序的 TCP 全連接隊列大小?

在服務端可以使用 ss 命令,來查看 TCP 全連接隊列的情況:

但需要注意的是 ss 命令獲取的 Recv-Q/Send-Q 在「LISTEN 狀態」和「非 LISTEN 狀態」所表達的含義是不同的。從下面的內核代碼可以看出區別:

在「LISTEN 狀態」時,Recv-Q/Send-Q 表示的含義如下:

  • Recv-Q:當前全連接隊列的大小,也就是當前已完成三次握手並等待服務端 accept() 的 TCP 連接;
  • Send-Q:當前全連接最大隊列長度,上面的輸出結果說明監聽 8088 端口的 TCP 服務,最大全連接長度為 128;

在「非 LISTEN 狀態」時,Recv-Q/Send-Q 表示的含義如下:

  • Recv-Q:已收到但未被應用進程讀取的字節數;
  • Send-Q:已發送但未收到確認的字節數;

如何模擬 TCP 全連接隊列溢出的場景?

測試環境

實驗環境:

  • 客戶端和服務端都是 CentOs 6.5 ,Linux 內核版本 2.6.32
  • 服務端 IP 192.168.3.200,客戶端 IP 192.168.3.100
  • 服務端是 Nginx 服務,端口為 8088

這裏先介紹下 wrk 工具,它是一款簡單的 HTTP 壓測工具,它能夠在單機多核 CPU 的條件下,使用系統自帶的高性能 I/O 機制,通過多線程和事件模式,對目標機器產生大量的負載。

本次模擬實驗就使用 wrk 工具來壓力測試服務端,發起大量的請求,一起看看服務端 TCP 全連接隊列滿了會發生什麼?有什麼觀察指標?

客戶端執行 wrk 命令對服務端發起壓力測試,併發 3 萬個連接:

在服務端可以使用 ss 命令,來查看當前 TCP 全連接隊列的情況:

其間共執行了兩次 ss 命令,從上面的輸出結果,可以發現當前 TCP 全連接隊列上升到了 129 大小,超過了最大 TCP 全連接隊列。

當超過了 TCP 最大全連接隊列,服務端則會丟掉後續進來的 TCP 連接,丟掉的 TCP 連接的個數會被統計起來,我們可以使用 netstat -s 命令來查看:

上面看到的 41150 times ,表示全連接隊列溢出的次數,注意這個是累計值。可以隔幾秒鐘執行下,如果這個数字一直在增加的話肯定全連接隊列偶爾滿了。

從上面的模擬結果,可以得知,當服務端併發處理大量請求時,如果 TCP 全連接隊列過小,就容易溢出。發生 TCP 全連接隊溢出的時候,後續的請求就會被丟棄,這樣就會出現服務端請求數量上不去的現象。

全連接隊列溢出

Linux 有個參數可以指定當 TCP 全連接隊列滿了會使用什麼策略來回應客戶端。

實際上,丟棄連接只是 Linux 的默認行為,我們還可以選擇向客戶端發送 RST 複位報文,告訴客戶端連接已經建立失敗。

tcp_abort_on_overflow 共有兩個值分別是 0 和 1,其分別表示:

  • 0 :如果全連接隊列滿了,那麼 server 扔掉 client 發過來的 ack ;
  • 1 :如果全連接隊列滿了,server 發送一個 reset 包給 client,表示廢掉這個握手過程和這個連接;

如果要想知道客戶端連接不上服務端,是不是服務端 TCP 全連接隊列滿的原因,那麼可以把 tcp_abort_on_overflow 設置為 1,這時如果在客戶端異常中可以看到很多 connection reset by peer 的錯誤,那麼就可以證明是由於服務端 TCP 全連接隊列溢出的問題。

通常情況下,應當把 tcp_abort_on_overflow 設置為 0,因為這樣更有利於應對突發流量。

舉個例子,當 TCP 全連接隊列滿導致服務器丟掉了 ACK,與此同時,客戶端的連接狀態卻是 ESTABLISHED,進程就在建立好的連接上發送請求。只要服務器沒有為請求回復 ACK,請求就會被多次重發。如果服務器上的進程只是短暫的繁忙造成 accept 隊列滿,那麼當 TCP 全連接隊列有空位時,再次接收到的請求報文由於含有 ACK,仍然會觸發服務器端成功建立連接。

所以,tcp_abort_on_overflow 設為 0 可以提高連接建立的成功率,只有你非常肯定 TCP 全連接隊列會長期溢出時,才能設置為 1 以儘快通知客戶端。

如何增大 TCP 全連接隊列呢?

是的,當發現 TCP 全連接隊列發生溢出的時候,我們就需要增大該隊列的大小,以便可以應對客戶端大量的請求。

TCP 全連接隊列足最大值取決於 somaxconn 和 backlog 之間的最小值,也就是 min(somaxconn, backlog)。從下面的 Linux 內核代碼可以得知:

  • somaxconn 是 Linux 內核的參數,默認值是 128,可以通過 /proc/sys/net/core/somaxconn 來設置其值;
  • backloglisten(int sockfd, int backlog) 函數中的 backlog 大小,Nginx 默認值是 511,可以通過修改配置文件設置其長度;

前面模擬測試中,我的測試環境:

  • somaxconn 是默認值 128;
  • Nginx 的 backlog 是默認值 511

所以測試環境的 TCP 全連接隊列最大值為 min(128, 511),也就是 128,可以執行 ss 命令查看:

現在我們重新壓測,把 TCP 全連接隊列搞大,把 somaxconn 設置成 5000:

接着把 Nginx 的 backlog 也同樣設置成 5000:

最後要重啟 Nginx 服務,因為只有重新調用 listen() 函數 TCP 全連接隊列才會重新初始化。

重啟完后 Nginx 服務后,服務端執行 ss 命令,查看 TCP 全連接隊列大小:

從執行結果,可以發現 TCP 全連接最大值為 5000。

增大 TCP 全連接隊列后,繼續壓測

客戶端同樣以 3 萬個連接併發發送請求給服務端:

服務端執行 ss 命令,查看 TCP 全連接隊列使用情況:

從上面的執行結果,可以發現全連接隊列使用增長的很快,但是一直都沒有超過最大值,所以就不會溢出,那麼 netstat -s 就不會有 TCP 全連接隊列溢出個數的显示:

說明 TCP 全連接隊列最大值從 128 增大到 5000 后,服務端抗住了 3 萬連接併發請求,也沒有發生全連接隊列溢出的現象了。

如果持續不斷地有連接因為 TCP 全連接隊列溢出被丟棄,就應該調大 backlog 以及 somaxconn 參數。

實戰 – TCP 半連接隊列溢出

如何查看 TCP 半連接隊列長度?

很遺憾,TCP 半連接隊列長度的長度,沒有像全連接隊列那樣可以用 ss 命令查看。

但是我們可以抓住 TCP 半連接的特點,就是服務端處於 SYN_RECV 狀態的 TCP 連接,就是在 TCP 半連接隊列。

於是,我們可以使用如下命令計算當前 TCP 半連接隊列長度:

如何模擬 TCP 半連接隊列溢出場景?

模擬 TCP 半連接溢出場景不難,實際上就是對服務端一直發送 TCP SYN 包,但是不回第三次握手 ACK,這樣就會使得服務端有大量的處於 SYN_RECV 狀態的 TCP 連接。

這其實也就是所謂的 SYN 洪泛、SYN 攻擊、DDos 攻擊。

測試環境

實驗環境:

  • 客戶端和服務端都是 CentOs 6.5 ,Linux 內核版本 2.6.32
  • 服務端 IP 192.168.3.200,客戶端 IP 192.168.3.100
  • 服務端是 Nginx 服務,端口為 8088

注意:本次模擬實驗是沒有開啟 tcp_syncookies,關於 tcp_syncookies 的作用,後續會說明。

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

本次實驗使用 hping3 工具模擬 SYN 攻擊:

當服務端受到 SYN 攻擊后,連接服務端 ssh 就會斷開了,無法再連上。只能在服務端主機上執行查看當前 TCP 半連接隊列大小:

同時,還可以通過 netstat -s 觀察半連接隊列溢出的情況:

上面輸出的數值是累計值,表示共有多少個 TCP 連接因為半連接隊列溢出而被丟棄。隔幾秒執行幾次,如果有上升的趨勢,說明當前存在半連接隊列溢出的現象

大部分人都說 tcp_max_syn_backlog 是指定半連接隊列的大小,是真的嗎?

很遺憾,半連接隊列的大小並不單單隻跟 tcp_max_syn_backlog 有關係。

上面模擬 SYN 攻擊場景時,服務端的 tcp_max_syn_backlog 的默認值如下:

但是在測試的時候發現,服務端最多只有 256 個半連接隊列,而不是 512,所以半連接隊列的最大長度不一定由 tcp_max_syn_backlog 值決定的

接下來,走進 Linux 內核的源碼,來分析 TCP 半連接隊列的最大值是如何決定的。

TCP 第一次握手(收到 SYN 包)的 Linux 內核代碼如下,其中縮減了大量的代碼,只需要重點關注 TCP 半連接隊列溢出的處理邏輯:

從源碼中,我可以得出共有三個條件因隊列長度的關係而被丟棄的:

  1. 如果半連接隊列滿了,並且沒有開啟 tcp_syncookies,則會丟棄;
  2. 若全連接隊列滿了,且沒有重傳 SYN+ACK 包的連接請求多於 1 個,則會丟棄;
  3. 如果沒有開啟 tcp_syncookies,並且 max_syn_backlog 減去 當前半連接隊列長度小於 (max_syn_backlog >> 2),則會丟棄;

關於 tcp_syncookies 的設置,後面在詳細說明,可以先給大家說一下,開啟 tcp_syncookies 是緩解 SYN 攻擊其中一個手段。

接下來,我們繼續跟一下檢測半連接隊列是否滿的函數 inet_csk_reqsk_queue_is_full 和 檢測全連接隊列是否滿的函數 sk_acceptq_is_full :

從上面源碼,可以得知:

  • 連接隊列的最大值是 sk_max_ack_backlog 變量,sk_max_ack_backlog 實際上是在 listen() 源碼里指定的,也就是 min(somaxconn, backlog)
  • 連接隊列的最大值是 max_qlen_log 變量,max_qlen_log 是在哪指定的呢?現在暫時還不知道,我們繼續跟進;

我們繼續跟進代碼,看一下是哪裡初始化了半連接隊列的最大值 max_qlen_log:

從上面的代碼中,我們可以算出 max_qlen_log 是 8,於是代入到 檢測半連接隊列是否滿的函數 reqsk_queue_is_full :

也就是 qlen >> 8 什麼時候為 1 就代表半連接隊列滿了。這計算這不難,很明顯是當 qlen 為 256 時,256 >> 8 = 1

至此,總算知道為什麼上面模擬測試 SYN 攻擊的時候,服務端處於 SYN_RECV 連接最大隻有 256 個。

可見,半連接隊列最大值不是單單由 max_syn_backlog 決定,還跟 somaxconn 和 backlog 有關係。

在 Linux 2.6.32 內核版本,它們之間的關係,總體可以概況為:

  • 當 max_syn_backlog > min(somaxconn, backlog) 時, 半連接隊列最大值 max_qlen_log = min(somaxconn, backlog) * 2;
  • 當 max_syn_backlog < min(somaxconn, backlog) 時, 半連接隊列最大值 max_qlen_log = max_syn_backlog * 2;

半連接隊列最大值 max_qlen_log 就表示服務端處於 SYN_REVC 狀態的最大個數嗎?

依然很遺憾,並不是。

max_qlen_log 是理論半連接隊列最大值,並不一定代表服務端處於 SYN_REVC 狀態的最大個數。

在前面我們在分析 TCP 第一次握手(收到 SYN 包)時會被丟棄的三種條件:

  1. 如果半連接隊列滿了,並且沒有開啟 tcp_syncookies,則會丟棄;
  2. 若全連接隊列滿了,且沒有重傳 SYN+ACK 包的連接請求多於 1 個,則會丟棄;
  3. 如果沒有開啟 tcp_syncookies,並且 max_syn_backlog 減去 當前半連接隊列長度小於 (max_syn_backlog >> 2),則會丟棄;

假設條件 1 當前半連接隊列的長度 「沒有超過」理論的半連接隊列最大值 max_qlen_log,那麼如果條件 3 成立,則依然會丟棄 SYN 包,也就會使得服務端處於 SYN_REVC 狀態的最大個數不會是理論值 max_qlen_log。

似乎很難理解,我們繼續接着做實驗,實驗見真知。

服務端環境如下:

配置完后,服務端要重啟 Nginx,因為全連接隊列最大和半連接隊列最大值是在 listen() 函數初始化。

根據前面的源碼分析,我們可以計算出半連接隊列 max_qlen_log 的最大值為 256:

客戶端執行 hping3 發起 SYN 攻擊:

服務端執行如下命令,查看處於 SYN_RECV 狀態的最大個數:

可以發現,服務端處於 SYN_RECV 狀態的最大個數並不是 max_qlen_log 變量的值。

這就是前面所說的原因:如果當前半連接隊列的長度 「沒有超過」理論半連接隊列最大值 max_qlen_log,那麼如果條件 3 成立,則依然會丟棄 SYN 包,也就會使得服務端處於 SYN_REVC 狀態的最大個數不會是理論值 max_qlen_log。

我們來分析一波條件 3 :

從上面的分析,可以得知如果觸發「當前半連接隊列長度 > 192」條件,TCP 第一次握手的 SYN 包是會被丟棄的。

在前面我們測試的結果,服務端處於 SYN_RECV 狀態的最大個數是 193,正好是觸發了條件 3,所以處於 SYN_RECV 狀態的個數還沒到「理論半連接隊列最大值 256」,就已經把 SYN 包丟棄了。

所以,服務端處於 SYN_RECV 狀態的最大個數分為如下兩種情況:

  • 如果「當前半連接隊列」沒超過「理論半連接隊列最大值」,但是超過 max_syn_backlog – (max_syn_backlog >> 2),那麼處於 SYN_RECV 狀態的最大個數就是 max_syn_backlog – (max_syn_backlog >> 2);
  • 如果「當前半連接隊列」超過「理論半連接隊列最大值」,那麼處於 SYN_RECV 狀態的最大個數就是「理論半連接隊列最大值」;

每個 Linux 內核版本「理論」半連接最大值計算方式會不同。

在上面我們是針對 Linux 2.6.32 版本分析的「理論」半連接最大值的算法,可能每個版本有些不同。

比如在 Linux 5.0.0 的時候,「理論」半連接最大值就是全連接隊列最大值,但依然還是有隊列溢出的三個條件:

如果 SYN 半連接隊列已滿,只能丟棄連接嗎?

並不是這樣,開啟 syncookies 功能就可以在不使用 SYN 半連接隊列的情況下成功建立連接,在前面我們源碼分析也可以看到這點,當開啟了 syncookies 功能就不會丟棄連接。

syncookies 是這麼做的:服務器根據當前狀態計算出一個值,放在己方發出的 SYN+ACK 報文中發出,當客戶端返回 ACK 報文時,取出該值驗證,如果合法,就認為連接建立成功,如下圖所示。

開啟 syncookies 功能

syncookies 參數主要有以下三個值:

  • 0 值,表示關閉該功能;
  • 1 值,表示僅當 SYN 半連接隊列放不下時,再啟用它;
  • 2 值,表示無條件開啟功能;

那麼在應對 SYN 攻擊時,只需要設置為 1 即可:

如何防禦 SYN 攻擊?

這裏給出幾種防禦 SYN 攻擊的方法:

  • 增大半連接隊列;
  • 開啟 tcp_syncookies 功能
  • 減少 SYN+ACK 重傳次數

方式一:增大半連接隊列

在前面源碼和實驗中,得知要想增大半連接隊列,我們得知不能只單純增大 tcp_max_syn_backlog 的值,還需一同增大 somaxconn 和 backlog,也就是增大全連接隊列。否則,只單純增大 tcp_max_syn_backlog 是無效的。

增大 tcp_max_syn_backlog 和 somaxconn 的方法是修改 Linux 內核參數:

增大 backlog 的方式,每個 Web 服務都不同,比如 Nginx 增大 backlog 的方法如下:

最後,改變了如上這些參數后,要重啟 Nginx 服務,因為半連接隊列和全連接隊列都是在 listen() 初始化的。

方式二:開啟 tcp_syncookies 功能

開啟 tcp_syncookies 功能的方式也很簡單,修改 Linux 內核參數:

方式三:減少 SYN+ACK 重傳次數

當服務端受到 SYN 攻擊時,就會有大量處於 SYN_REVC 狀態的 TCP 連接,處於這個狀態的 TCP 會重傳 SYN+ACK ,當重傳超過次數達到上限后,就會斷開連接。

那麼針對 SYN 攻擊的場景,我們可以減少 SYN+ACK 的重傳次數,以加快處於 SYN_REVC 狀態的 TCP 連接斷開。

巨人的肩膀

[1] 系統性能調優必知必會.陶輝.極客時間.

[2] https://www.cnblogs.com/zengkefu/p/5606696.html

[3] https://blog.cloudflare.com/syn-packet-handling-in-the-wild/

小林是專為大家圖解的工具人,Goodbye,我們下次見!

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

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

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

一篇文章,全面掌握Git_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

版本控制

版本控制就是記錄項目文件的歷史變化。它為我們查閱日誌回退協作等方面提供了有力的幫助。

版本控制一般分為集中化版本控制和分佈式版本控制。

集中化主要的版本數據都保存服務端。

分佈式版本數據分散在多端。

Git

Git屬於分佈式版本控制,也是現在比較流行的一種版本管理工具。

Git項目有三個區塊:工作區 / 暫存區 / 版本庫

  • 工作區存放從版本庫提取出來的文件,供我們編輯修改;
  • 暫存區保存了下一次要提交的目錄信息;
  • 版本庫保存項目版本元數據和Objects數據,後文會詳解。

Git工作流程

# 下載
<<==== clone 
# 上傳
====>> add ====>> commit ====>> push
# 更新
<<==== merge|rebase <<===== fetch

區分 Pull vs Fetch

我們將一個更新操作拆分為數據更新+合併處理兩部分,這樣來看 fetch 只是進行數據更新。而pull 其實是 ( fetch + (merge|rebase) )組合操作,它執行數據更新同時執行合併處理。pull 默認是fetch+merge 組合 ,也可以通過參數 –rebase 指定為 fetch + rebase。

區分Merge vs Rebase

合併處理是Git很重要的一塊知識。兩個命令在工作中也經常使用,區分它們對我們很有用。

場景如下

項目有一個mywork分支。C2時間點我和小明各自下載項目進行功能開發,小明效率比較高,先推送了C3 C4 到遠程倉庫。我本地倉庫現在有C5 C6兩個提交,要推送到遠程倉庫,需先同步遠程倉庫版本。

如果通過 fetch + merge 方式,Git會將遠程最新(C4)和本地最新(C6)進行合併併產生一個新的(C7)。

衝突處理步驟

git merge # 發生衝突會出現衝突標記
“<<<<<<< HEAD
40
=======
41
>>>>>>> 41”
# 手動處理衝突
git add .
git commit -m 'fix conflict'
git push origin HEAD

如果通過 fetch + rebase 方式,git會先將C5 C6存儲到.git/rebase零時目錄,合併成功后刪除。

衝突處理步驟

git rebase # 發生衝突會出現衝突標記
“<<<<<<< HEAD
40
=======
41
>>>>>>> 41”
# 手動處理衝突
git add .
git rebase --continue
git push origin HEAD

小結

git merge 會產生大量Merge日誌,可能會對查看帶來不便。不過大家還是根據實際情況進行選取。

關於撤銷回退幾種場景

提交后發現有文件漏了,又不想提交兩次。此時通過 “git commit –amend” 可以合併為一個提交。

git commit -m 'initial commit'
git add .gitignore
git commit --amend

如果文件想撤回且尚未提交,執行下面命令撤出暫存空間(index)

git reset HEAD <file>...

關於 reset 其它用法

# 重置到指定版本,之前提交內容將丟失
git reset --hard HEAD 
# 重置到指定版本,保留更改的文件但未標記為提交
git reset --mixed HEAD 
# 重置到指定版本,保留所有改動文件
git reset –soft HEAD 

特別注意 當你使用 “git reset –hard HEAD” 重置到某一版本,發現搞錯了想回退。這時你可能會執行“git log”,但是發現已經沒有以前的版本記錄,怎麼辦?送你一瓶後悔葯如下

# reflog 是Git操作的全日誌記錄
git reflog

6241462 (HEAD -> master) HEAD@{0}: reset: moving to 6241462
ea9b5ab HEAD@{1}: reset: moving to ea9b5ab
6241462 (HEAD -> master) HEAD@{2}: commit: Hello
34cd1e3 HEAD@{3}: commit: 3
ea9b5ab HEAD@{4}: commit: 2
729a8b1 (origin/master) HEAD@{5}: commit (initial): 1

# 找到最左邊對應hash值就可以回退到任意位置
git reset --hard {index}

如果想撤迴文件修改內容且文件尚未提交,執行下面命令

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

git checkout -- <file>

如果創建的分支名稱需要更改

git branch -m old new

# 如果分支已經推送到遠程,先刪除再推送新分支
git push origin --delete old
git push origin new

如果需要撤回的提交已經推送到了遠程倉庫,那麼補救的方式只有創建新的提交。

可以利用revert快速撤回到需要回退的版本。

# 還原最近一個提交
git revert HEAD
# 還原倒數第二個
git revert HEAD^
# 還原倒數第第四個
git revert HEAD~3

版本庫 Objects

這一節介紹一下Git版本庫的存儲模型。

項目歷史變動信息都記錄在object文件。文件名稱是通過哈希算法 ( 這裡是SHA1(對象內容) ) 產生的40位字符。

這種做法的一個優點就是“在對比兩對象是否相同時,只需要比較文件名稱就能迅速得出結果”

哈希算法:簡單來說就是向函數輸入一些內容,輸出長度固定的字符串。這裏SHA1函數固定輸出40長度字符。

object文件分 blob tree commit tag 四種類型

  • blob 存儲文件數據,一般是一個文件;

  • tree 存儲目錄和樹的引用(子文件目錄);

  • commit 存儲單一樹引用,時間點,提交作者,上一次提交指針;

  • tag 標記特定的commit 比如說發版。

特別注意:Subversion,CVS,Perforce,Mercurial等是存儲前後兩次提交的差異數據。Gi-每次提交時,它都會以樹狀結構存儲項目中所有文件的外觀快照。

Blob

Blob 是二進制數據塊,不會引用其它東西。如果目錄樹(或存儲庫中多個不同版本)中的兩個文件具有內容相同,它們將共享相同的Blob對象。

Tree

Tree 存儲blob和tree的引用。

# 我查詢 add1a1306e20...
git ls-tree add1a1306e20...

100644 blob 4661b39c3460a5c1f9e9309e6341962e0499b037	README.md
040000 tree ad46b24a4b0648ede3ca090dde32c89b89f7f2c1	src
...

Commit

Commit 包含下面幾個信息

  1. tree 提交時間點的目錄;
  2. parent 上一個提交;
  3. author 提交人;
git show -s --pretty=raw add1a1306e....

commit add1a1306e....
tree 81d4e4271a56575da7f992dc0dfc72ff7ddff94c
parent cd397e4c373013b19825b857b43ad8f677607f5d
author lixingping <lixingping233@gmail.com> 1589783810 +0800
committer lixingping <lixingping233@gmail.com> 1589783810 +0800

Tag

git cat-file tag v_1.0

object 24d16acd6aa08f74556c7ce551fa571b4bfe4079
type commit
tag v_1.0
tagger lixingping <lixingping233@gmail.com> 1588591122 +0800

例子

假設項目目錄結構如下,我們進行一個初始提交。幾種文件關係如下圖

|-- read.txt
    --| lib
      --| hello.java

附上一些常用命令

生成SSH key

ssh-keygen -t rsa -b 4096 -C "email@example.com"
# 指定生成的文件
ssh-keygen -t rsa -b 4096 -C "email@example.com" -f ~/.ssh/id_rsa_example
# id_rsa_example.pub 粘貼遠程倉庫

# 配置多個遠程倉庫
touch ~/.ssh/config

#添加一下內容
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_github

Host example.com
HostName example.com
User git
IdentityFile ~/.ssh/id_rsa_example

配置

git config –global user.name “xxx”
git config –global user.email “xxx@email.com“
git config --global core.autocrlf true # 建議配置 windows mac換行符不統一問題
git config --global core.editor vim # 配置默認編輯器
git config --global core.excludesfile ~/.gitignore_global # 配置全局忽略文件
git config –list # 查看配置信息

分支管理

git branch --list # 羅列本地所有分支
git branch --all  # 羅列本地和遠程所有分支
git branch -r     # 羅列遠程所有分支
git branch -v     # 显示各分支最後提交信息
git checkout <branch name> # 切換分支
git checkout -b <new branch name> # 創建新分支
git push origin <new branch name> # 推送新分支到遠程
git checkout -m <old branch> <new branch> # 重命名分支名稱
git branch -d <[list]branch name> # 刪除本地分支
git push origin --delete <branch name> # 刪除遠程分支

標籤管理

git tag -l # 羅列本地所有標籤
git show <tag name> # 显示指定標籤
git tag -a v_1.0.0 -m "備註" # 創建標籤
git push origin <tag name> # 推送標籤到遠程
git tag -d <tag name> # 刪除本地標籤
git push --delete origin <tag name> # 刪除遠程標籤

總結

工作多年以來一直在使用Git,但是對Git沒有一個系統了解,所以寫這篇文章歸整一下。

歡迎大家留言交流,一起學習分享!!!

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

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

SUV確實很火 但這幾款真正品質出色的SUV為何沒啥人關注?_網頁設計

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

39-30。19萬福特 撼路者售價:26。58-36。08萬英菲尼迪 QX50售價:34。98-44。98萬總結:其實一款汽車真的在市場上的反響不好,可能有着各種的原因。有些事可能目前整個市場的份額開始萎縮,但究其基本,一款本就比較出色的車型沒有收到消費者的歡迎,最終還是價格不夠親民影響了它最後的銷量,因此做好一款車需要時間與金錢,同時賣好一款車也同樣需要智慧。

對於目前的汽車市場,相比前些年已經發生了不少的變化。之前的一段時間可能大家買車的時候都在關注小型車為主,有些改善型買家則偏向於一些高品質的中型車為主,只是時代是在發展的。消費者的胃口也是跟着發生變化,

※推薦評價好的iphone維修中心

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

這些年當中,SUV已經從當年的嶄露頭角變成了消費者購車的首選了。但就是這樣的背景下,一些品質非常出眾,但市場的反應卻依然不太好,為何有這樣的原因呢,今天就給你好好說道說道。

售價:22.48-28.98萬

售價:19.39-30.19萬

售價:26.58-36.08萬

售價:34.98-44.98萬

總結:其實一款汽車真的在市場上的反響不好,可能有着各種的原因。有些事可能目前整個市場的份額開始萎縮,但究其基本,一款本就比較出色的車型沒有收到消費者的歡迎,最終還是價格不夠親民影響了它最後的銷量,因此做好一款車需要時間與金錢,同時賣好一款車也同樣需要智慧。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

神差距!工信部油耗是5個 為什麼你總是開出來8個多油?_貨運

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

音響基本都開吧。等等,而這些,同樣也會讓油耗值發生一定的變化,要明白,一輛車絕大部分的設備都是在靠油為燃料去運作的,雖然影響不算太多。>>>>為什麼不人工測試。這很簡單,那麼多需要測試的車輛,如果要一輛一個人去測試的話,很浪費人力物力,而且也存在一定的誤差,也就是所說個人駕駛習慣。

油耗,是一個老生常談的話題,對於同一樣車而言,可能會出現好幾種油耗,畢竟開的人不同,但是都有一個共同點,基本是很少人能開到工信部的油耗,都是比工信部油耗比較高一些,那麼,就會很多人問了,為什麼我怎麼開都開不到工信部的油耗,其實很簡單,方式不同。

工信部的油耗是怎麼測出來?

工信部測試油耗的的車輛,基本都是過了3000公里磨合期的車。而測試的方式也很多種,有台架測試,實際道路測試等,而工信部所採用的測試方式,就是台架測試,簡單說,就是放在一個測試機器上,通過各種路況,車速,換擋等等情況模擬去測試,最後通過測量碳排放,利用碳平衡的方法來測量油耗。

所以簡單來說,工信部的測試過程,是很流暢的,是非常極致的理想工況模擬,類似你上高速前清零了,

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

然後直接上高速,開着巡航的瞬間油耗,在加上工信部的測試中,並沒有計算到風阻,各種突發的情況,加上個人的駕駛習慣,而三種都會讓油耗產生一定的變化,特別是個人的駕駛習慣。

還有一點就是,在日常用車當中,空調不可能不開吧?燈光不可能不開吧?音響基本都開吧?等等,而這些,同樣也會讓油耗值發生一定的變化,要明白,一輛車絕大部分的設備都是在靠油為燃料去運作的,雖然影響不算太多。

為什麼不人工測試?

這很簡單,那麼多需要測試的車輛,如果要一輛一個人去測試的話,很浪費人力物力,而且也存在一定的誤差,也就是所說個人駕駛習慣。而工信部目前的一輛車測試的時間不到20分鐘,在這個時間裏面,測試的工況有四個市區和一個郊區,並且是理想的,平均下來一個模擬工況才4分鐘左右,最後根據一些方式計算出相信燃油消耗。

可不可信?

這沒有可不可信的問題,工信部油耗之所以叫工信部油耗,就是給你在買車的時候一個參考值而已,因為,所有的車輛都是這樣測試出來,還是會有車輛油耗的高低之分,這對於選車買車時候有一定幫助。當然,在一些垂直汽車網站上也可以得到用戶的口碑的油耗,從中和工信部油耗做個對比,在和你選擇車型之間做個對比,自然就可以得出大概的結論,哪輛車是真的比較省油,哪輛車比較耗油,不過,到了真正使用的時候,還是得看你個人的駕駛情況了。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念