SourceTree使用詳解(連接遠程倉庫,克隆,拉取,提交,推送,新建/切換/合併分支,衝突解決)

前言:

  俗話說的好工欲善其事必先利其器,Git分佈式版本控制系統是我們日常開發中不可或缺的。目前市面上比較流行的Git可視化管理工具有SourceTree、Github Desktop、TortoiseGit,綜合網上的一些文章分析和自己的日常開發實踐心得個人比較推薦開發者使用SourceTree,因為SourceTree同時支持Windows和Mac,並且界面十分的精美簡潔,大大的簡化了開發者與代碼庫之間的Git操作方式。該篇文章主要是對日常開發中使用SourceTree可視化管理工具的一些常用操作進行詳細講解。

SourceTree | Github Desktop | TortoiseGit 可視化管理工具對比:

  https://blog.csdn.net/hmllittlekoi/article/details/104504406/

SourceTree介紹和Atlassian賬號註冊和登錄教程:

https://www.cnblogs.com/Can-daydayup/p/13128511.html

連接Gitee or GitHub,獲取代碼:

注意:這裏介紹的是使用SSH協議獲取關聯遠程倉庫的代碼,大家也可以直接使用過HTTPS協議的方式直接輸入賬號密碼獲取關聯代碼!

全面概述Gitee和GitHub生成/添加SSH公鑰:

https://www.cnblogs.com/Can-daydayup/p/13063280.html

在SourceTree中添加SSH密鑰:

工具=>選擇:

   
添加SSH密鑰位置:C:\Users\xxxxx\.ssh\id_rsa.pub:

SSH客戶端選擇OpenSSH:

 

Clone對應託管平台倉庫(以Gitee為例):

打開碼雲,找到自己需要Clone的倉庫!

 

 

SourceTree設置默認工作目錄:

  由上面我們可以發現每次Clone克隆項目的時候,克隆下來的項目默認存儲位置都是在C盤,因此每次都需要我們去選擇項目存放的路徑,作為一個喜歡偷懶的人而言當然不喜歡這種方式啦,因此我們可以設置一個默認的項目存儲位置。

設置SourceTree默認項目目錄:

點擊工具=>選項=>一般=>找到項目目錄設置Clone項目默認存儲的位置  

SourceTree代碼提交:

1.首先切換到需要修改功能代碼所在的分支:

 

 

2.將修改的代碼提交到暫存區:

3.將暫存區中的代碼提交到本地代碼倉庫:

注意:多人同時開發項目的時候,不推薦默認選中立即推送變更到origin/develop,避免一些不必要的麻煩!

 4.代碼拉取更新本地代碼庫,並將代碼推送到遠程倉庫:

 
 勾選需要推送的分支,點擊推送到遠程分支:  
代碼成功推送到遠程代碼庫:

5.在Gitee中查看推送結果:

SourceTree分支切換,新建,合併:

1.分支切換:

雙擊切換:   單擊鼠標右鍵切換:

2.新建分支:

注意:在新建分支時,我們需要在哪個主分支的基礎上新建分支必須先要切換到對應的主分支才能到該主分支上創建分支,如下我們要在master分支上創建一個feature-0613分支:  

3.合併分支:

注意:在合併代碼之前我們都需要將需要合併的分支拉取到最新狀態(**避免覆蓋別人的代碼,或者丟失一些重要文件)!!!!!   在master分支上點擊右鍵,選擇合併feature-0613至當前分支即可進行合併:   分支合併成功:

SourceTree代碼衝突解決:

首先我們需要製造一個提交文件遇到衝突的情景:

在SoureceTree中在Clone一個新項目,命名為pingrixuexilianxi2,如下圖所示:

 

 

我們以項目中的【代碼合併衝突測試.txt】文件為例:   在pingrixuexilianxi2中添加內容,並提交到遠程代碼庫,添加的內容如下:   在pingrixuexilianxi中添加內容,提交代碼(不選擇立即推送變更到origin/master),拉取代碼即會遇到衝突:  

 

 

  衝突文件中的內容:  

直接打開衝突文件手動解決衝突:

由下面的衝突文件中的衝突內容我們了解到:

<<<<<<< HEAD
6月19日 pingrixuexilianxi添加了內容
=======
6月18日 pingrixuexilianxi2修改了這個文件哦
>>>>>>> a8284fd41903c54212d1105a6feb6c57292e07b5

  <<<<<<< HEAD到 =======裏面的【6月19日 pingrixuexilianxi添加了內容】是自己剛才的Commit提交的內容 =======到 >>>>>>> a8284fd41903c54212d1105a6feb6c57292e07b5裏面的【6月18日 pingrixuexilianxi2修改了這個文件哦】是遠程代碼庫更新的內容(即為pingrixuexilianxi2本地代碼庫推送修改內容)。   手動衝突解決方法:

  根據項目需求刪除不需要的代碼就行了,假如都需要的話我們只需要把 <<<<<<< HEAD=======     >>>>>>> a8284fd41903c54212d1105a6feb6c57292e07b5都刪掉衝突就解決了(注意,在項目中最後這些符號都不能存在,否則可能會報異常)。

  最後將衝突文件標記為已解決,提交到遠程倉庫:  

採用外部文本文件對比工具Beyond Compare解決衝突:

SourceTree配置文本文件對比工具Beyond Compare:

工具=>選項=>比較:  

 

使用Beyond Compare解決衝突:

Beyond Compare使用技巧: 官方全面教程: https://www.beyondcompare.cc/jiqiao/   SourceTree打開外部和合併工具:

 

注意:第一次啟動Beynod Compare軟件需要一會時間,請耐心等待:
    Beynod Compare進行衝突合併:   點擊保存文件后關閉Beynod Compare工具,SourceTree中的衝突就解決了,在SourceTree中我們會發現多了一個 .orig 的文件。接着選中那個.orig文件,單擊右鍵 => 移除,最後我們推送到遠程代碼庫即可:  

Sourcetree中的基本名詞說明:

克隆/新建(clone):從遠程倉庫URL加載創建一個與遠程倉庫一樣的本地倉庫。 提交(commit):將暫存區文件上傳到本地代碼倉庫。
推送(push):將本地倉庫同步至遠程倉庫,一般推送(push)前先拉取(pull)一次,確保一致(十分注意:這樣你才能達到和別人最新代碼同步的狀態,同時也能夠規避很多不必要的問題)。 拉取(pull):從遠程倉庫獲取信息並同步至本地倉庫,並且自動執行合併(merge)操作(git pull=git fetch+git merge)。 獲取(fetch):從遠程倉庫獲取信息並同步至本地倉庫。 分支(branch):創建/修改/刪除分枝。 合併(merge):將多個同名文件合併為一個文件,該文件包含多個同名文件的所有內容,相同內容抵消。 貯藏(git stash):保存工作現場。 丟棄(Discard):丟棄更改,恢復文件改動/重置所有改動,即將已暫存的文件丟回未暫存的文件。 標籤(tag):給項目增添標籤。 工作流(Git Flow):團隊工作時,每個人創建屬於自己的分枝(branch),確定無誤后提交到master分支。 終端(terminal):可以輸入git命令行。 每次拉取和推送的時候不用每次輸入密碼的命令行:git config credential.helper osxkeychain sourcetree。 檢出(checkout):切換不同分支。 添加(add):添加文件到緩存區。 移除(remove):移除文件至緩存區。 重置(reset):回到最近添加(add)/提交(commit)狀態。

Git分佈式版本控制器常用命令和使用:

當然作為一個有逼格的程序員, 一些常用的命令我們還是需要了解和掌握的,詳情可參考我之前寫過的文章:

https://www.cnblogs.com/Can-daydayup/p/10134733.html

 

 

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

Newtonsoft 六個超簡單又實用的特性,值得一試 【下篇】

一:講故事

上一篇介紹的 6 個特性從園子里的反饋來看效果不錯,那這一篇就再帶來 6 個特性同大家一起欣賞。

二:特性分析

1. 像弱類型語言一樣解析 json

大家都知道弱類型的語言有很多,如: nodejs,python,php,它們有一個的地方就是處理json,不需要像 強類型語言 那樣還要給它配一個類,什麼意思呢? 就拿下面的 json 說事。


{
  "DisplayName": "新一代算法模型",
  "CustomerType": 1,
  "Report": {
    "TotalCustomerCount": 1000,
    "TotalTradeCount": 50
  },
  "CustomerIDHash": [1,2,3,4,5]
}

這個 json 如果想灌到 C# 中處理,你就得給它定義一個適配的類,就如 初篇 的客戶算法模型類,所以這裏就有了一個需求,能不能不定義類也可以自由解析上面這串 json 呢??? 哈哈,當然是可以的, 反序列化成 Dictionary 即可,就拿提取 Report.TotalCustomerCountCustomerIDHash 這兩個字段演示一下。


        static void Main(string[] args)
        {
            var json = @"{
                           'DisplayName': '新一代算法模型',
                           'CustomerType': 1,
                           'Report': {
                             'TotalCustomerCount': 1000,
                             'TotalTradeCount': 50
                           },
                           'CustomerIDHash': [1,2,3,4,5]
                         }";

            var dict = JsonConvert.DeserializeObject<Dictionary<object, object>>(json);

            var report = dict["Report"] as JObject;
            var totalCustomerCount = report["TotalCustomerCount"];

            Console.WriteLine($"totalCustomerCount={totalCustomerCount}");

            var arr = dict["CustomerIDHash"] as JArray;
            var list = arr.Select(m => m.Value<int>()).ToList();

            Console.WriteLine($"list={string.Join(",", list)}");
        }

2. 如何讓json中的枚舉保持更易讀的字符串型

這句話是什麼意思呢? 默認情況下, SerializeObject 會將 Model 中的 Enum 變成數值型,大家都知道數值型語義性是非常差的,如下代碼所示:


    static void Main(string[] args)
    {
        var model = new ThreadModel() { ThreadStateEnum = System.Threading.ThreadState.Running };

        var json = JsonConvert.SerializeObject(model);

        Console.WriteLine(json);
    }

    class ThreadModel
    {
        public System.Threading.ThreadState ThreadStateEnum { get; set; }
    }

對吧,確實語義特別差,那能不能直接生成 Running 這種字符串形式呢? 當然可以了。。。改造如下:


  var json = JsonConvert.SerializeObject(model, new StringEnumConverter());

這裏可能就有人鑽牛角尖了,能不能部分指定讓枚舉生成 string,其他的生成 int ,沒關係,這也難不倒我,哪裡使用就用 JsonConverter 標記哪裡。。。


        static void Main(string[] args)
        {
            var model = new ThreadModel()
            {
                ThreadStateEnum = System.Threading.ThreadState.Running,
                TaskStatusEnum = TaskStatus.RanToCompletion
            };

            var json = JsonConvert.SerializeObject(model);

            Console.WriteLine(json);
        }

        class ThreadModel
        {
            public System.Threading.ThreadState ThreadStateEnum { get; set; }

            [JsonConverter(typeof(StringEnumConverter))]
            public TaskStatus TaskStatusEnum { get; set; }
        }        

3. 格式化 json 中的時間類型

在 model 轉化成 json 的過程中,總少不了 時間類型,為了讓時間類型 可讀性更高,通常會 格式化為 YYYY年/MM月/dd日 ,那如何實現呢? 很簡單撒,在 JsonConvert 中也是一個 枚舉 幫你搞定。。。


        static void Main(string[] args)
        {
            var json = JsonConvert.SerializeObject(new Order()
            {
                OrderTitle = "女裝大佬",
                Created = DateTime.Now
            }, new JsonSerializerSettings
            {
                DateFormatString = "yyyy年/MM月/dd日",
            });

            Console.WriteLine(json);
        }
        public class Order
        {
            public string OrderTitle { get; set; }
            public DateTime Created { get; set; }
        }   

對了,我記得很早的時候,C# 自帶了一個 JavaScriptSerializer, 也是用來進行 model 轉 json的,但是它會將 datetime 轉成 時間戳,而不是時間字符串形式,如果你因為特殊原因想通過 JsonConvert 將時間生成時間戳的話,也是可以的, 用 DateFormatHandling.MicrosoftDateFormat 枚舉指定一下即可,如下:

4. 對一些常用設置進行全局化

在之前所有演示的特性技巧中都是在 JsonConvert 上指定的,也就是說 100 個 JsonConvert 我就要指定 100 次,那有沒有類似一次指定,整個進程通用呢? 這麼強大的 Newtonsoft 早就支持啦, 就拿上面的 Order 舉例:


        JsonConvert.DefaultSettings = () =>
        {
            var settings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented
            };
            return settings;
        };

        var order = new Order() { OrderTitle = "女裝大佬", Created = DateTime.Now };

        var json1 = JsonConvert.SerializeObject(order);
        var json2 = JsonConvert.SerializeObject(order);

        Console.WriteLine(json1);
        Console.WriteLine(json2);

可以看到,Formatting.Indented 對兩串 json 都生效了。

5. 如何保證 json 到 model 的嚴謹性 及提取 json 未知字段

有時候我們有這樣的需求,一旦 json 中出現 model 未知的字段,有兩種選擇: 要麼報錯,要麼提取出未知字段,在 Newtonsoft 中默認的情況是忽略,場景大家可以自己找哈。

  • 未知字段報錯

        static void Main(string[] args)
        {
            var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

            var order = JsonConvert.DeserializeObject<Order>(json, new JsonSerializerSettings
            {
                MissingMemberHandling = MissingMemberHandling.Error
            });

            Console.WriteLine(order);
        }

        public class Order
        {
            public string OrderTitle { get; set; }
            public DateTime Created { get; set; }
            public override string ToString()
            {
                return $"OrderTitle={OrderTitle}, Created={Created}";
            }
        }        

  • 提取未知字段

我依稀的記得 WCF 在這種場景下也是使用一個 ExtenstionDataObject 來存儲客戶端傳過來的未知字段,有可能是客戶端的 model 已更新,server端還是舊版本,通常在 json 序列化中也會遇到這種情況,這裏只要使用 JsonExtensionData 特性就可以幫你搞定,在 OnDeserialized 這種AOP方法中進行攔截,如下代碼:


    static void Main(string[] args)
    {
        var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

        var order = JsonConvert.DeserializeObject<Order>(json);

        Console.WriteLine(order);
    }

    public class Order
    {
        public string OrderTitle { get; set; }

        public DateTime Created { get; set; }

        [JsonExtensionData]
        private IDictionary<string, JToken> _additionalData;

        public Order()
        {
            _additionalData = new Dictionary<string, JToken>();
        }

        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            var dict = _additionalData;
        }

        public override string ToString()
        {
            return $"OrderTitle={OrderTitle}, Created={Created}";
        }
    }        

6. 開啟 JsonConvert 詳細日誌功能

有時候在查閱源碼的時候開啟日誌功能更加有利於理解源碼的內部運作,所以這也是一個非常實用的功能,看看如何配置吧。


        static void Main(string[] args)
        {
            var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

            MemoryTraceWriter traceWriter = new MemoryTraceWriter();

            var account = JsonConvert.DeserializeObject<Order>(json, new JsonSerializerSettings
            {
                TraceWriter = traceWriter
            });

            Console.WriteLine(traceWriter.ToString());
        }

        public class Order
        {
            public string OrderTitle { get; set; }

            public DateTime Created { get; set; }

            public override string ToString()
            {
                return $"OrderTitle={OrderTitle}, Created={Created}";
            }
        }

三:總結

嘿嘿,這篇 6 個特性就算說完了, 結合上一篇一共 12 個特性,是不是非常簡單且實用,後面準備給大家帶來一些源碼解讀吧! 希望本篇對您有幫助,謝謝!

如您有更多問題與我互動,掃描下方進來吧~

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

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

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

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

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

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

※回頭車貨運收費標準

貓狗食用市場的終結? 印尼政府決定禁止買賣貓狗肉

摘錄自2018年8月8日香港01報導

印尼政府在本月初的「全國動物福利協調」作出了重大的決定,禁止狗和貓肉貿易,並且不會再提供供人食用的狗、貓肉的健康證明。

在今年1月,印尼動物保護團體「印尼無狗肉」(DMFI)把一封聯署信交給總統Joko Widodo,該信由90多位國內和國際名人簽署,呼籲採取緊急行動去禁止買賣狗、貓肉。 此外,DMFI的全球請願書也由來自世界各地,超過930,000人簽署。

國際人道對待動物協會主席Kitty Block說:「狗、貓肉貿易是殘酷的行為,不但對人類健康有機會構成威脅,並且在很大程度上推動人們犯罪。因此,我們極度讚揚印尼政府承諾終止狗、貓肉貿易。我們希望這一個舉動能向亞洲其他國家發出強烈信息,例如中國、韓國、印度和越南。」

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

用樹的高度來建築  弗班社區找回古城區的感覺

環境資訊中心特約記者 陳文姿報導

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

【其他文章推薦】

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

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

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

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

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

※回頭車貨運收費標準

一起玩轉微服務(11)——一切從簡單開始

介紹

使用Spring Bboot是快樂並且簡單的,不需要繁瑣的配置就能夠完成一套非常強大的應用。

spring boot 2.3.1

Spring Boot 2.3.1 發佈於:2020/06/12,現在已經提交到 Spring 倉庫和 Maven 中央倉庫了。

這個版本包括 127 個 bug 修復、Spring Boot 文檔改進增強、依賴升級等,另外還新增了一些新特性:

•提供基於新的 Maven 坐標 com.oracle.database 對 Oracle JDBC driver 的依賴管理;

•優化 Spring Cloud 的 CachedRandomPropertySource 不能正確適配的問題;•限制使用定製的 YAML 類型;

•增強對 NoSuchMethodErrors 異常失敗分析,能显示基類從哪被加載的;•提供更佳的錯誤消息,如果 Docker 停止運行了;

•優化 SystemEnvironmentPropertyMapper 類;

•提供更佳的診斷信息,當構建 OCI 鏡像失敗時 Docker 響應的 500 錯誤;

•支持通過 alwaysUseFullPath=true 參數來配置 UrlPathHelper;•支持在 Elasticsearch URIs 中使用用戶信息;

•支持在 Spring WebFlux 框架中使用歡迎頁面;

這個小版本還增加了蠻多東西的,大家也沒有必要跟着版本走,可以根據需要進行升級。疫情也擋不住外國友人更新的熱情。

實現

使用STS,可以去官方網站下載最新版。網站地址 https://Spring.io/tools/sts/ Spring Tool Suite™是基於eclipse開發的專門為Spring開發使用的工具包。

新建工程

選擇Spring Starter Project,

輸入工程名 對應的Name 打包方式 對應的Packaging,可以選擇jar或者war的方式。

輸入組織名 對應的Group 輸入描述 對應的Description

輸入包名 對應的Package 點擊next,然後選擇web和mysql

這裏的版本用的是2.3.1 如果沒有本地maven庫或者私庫會下載很長時間。

添加默認請求

進入 Chapter0301Application 添加

@RestController
@SpringBootApplication
public class Chapter0301Application {
    
    @RequestMapping("/")
    String home() {
         return "歡迎使用Spring Boot!";
    }

    public static void main(String[] args) {
        SpringApplication.run(Chapter0301Application.class, args);
    }

}

使用@RestController 相當於@Controller 和 @RequestBody。是Sspring Bboot 基於Sspring MVC的基礎上進行了改進, 將@Controller 與@ResponseBody 進行了合併形成的一個新的註解。 @EnableAutoConfiguration 作用 從classpath中搜索所有META-INF/spring.factories配置文件然後,將其中org.springframework.boot.autoconfigure.EnableAutoConfiguration key對應的配置項加載到spring容器 只有spring.boot.enableautoconfiguration為true(默認為true)的時候,才啟用自動配置 @EnableAutoConfiguration還可以進行排除,排除方式有2種,一是根據class來排除(exclude),二是根據class name(excludeName)來排除 其內部實現的關鍵點有

1.ImportSelector 該接口的方法的返回值都會被納入到spring容器管理中

2.SpringFactoriesLoader 該類可以從classpath中搜索所有META-INF/spring.factories配置文件,並讀取配置

啟動spring boot

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.1.RELEASE)

2020-06-23 13:30:11.611  INFO 9916 --- [           main] com.cloud.sky.Chapter0301Application     : Starting Chapter0301Application on DADI-PC with PID 9916 (D:\java\microservice\chapter0301\target\classes started by Administrator in D:\java\microservice\chapter0301)
2020-06-23 13:30:11.614  INFO 9916 --- [           main] com.cloud.sky.Chapter0301Application     : No active profile set, falling back to default profiles: default
2020-06-23 13:30:12.415  INFO 9916 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-06-23 13:30:12.423  INFO 9916 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-06-23 13:30:12.424  INFO 9916 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.36]
2020-06-23 13:30:12.512  INFO 9916 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-06-23 13:30:12.512  INFO 9916 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 830 ms
2020-06-23 13:30:12.665  INFO 9916 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-06-23 13:30:12.809  INFO 9916 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-06-23 13:30:12.818  INFO 9916 --- [           main] com.cloud.sky.Chapter0301Application     : Started Chapter0301Application in 1.492 seconds (JVM running for 3.109)
2020-06-23 13:30:20.675  INFO 9916 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-06-23 13:30:20.676  INFO 9916 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-06-23 13:30:20.680  INFO 9916 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 4 ms

打開瀏覽器訪問 http://localhost:8080/ 可以得到如下頁面

遇到問題

構建的過程中遇到問題

[INFO] Scanning for projects...
[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[FATAL] Non-parseable POM D:\java\apache-maven-3.1.1\repo\org\jetbrains\kotlin\kotlin-bom\1.3.72\kotlin-bom-1.3.72.pom: entity reference names can not start with character ')' (position: START_TAG seen ...ost,s="";function qs(n){var u=D.URL;var t=u.match(eval(\'/(\\?|#|&)... @1:243)  @ D:\java\apache-maven-3.1.1\repo\org\jetbrains\kotlin\kotlin-bom\1.3.72\kotlin-bom-1.3.72.pom, line 1, column 243
 @ 
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]   
[ERROR]   The project com.cloudskyme:chapter0301:0.0.1 (D:\java\microservice\chapter0301\pom.xml) has 1 error
[ERROR]     Non-parseable POM D:\java\apache-maven-3.1.1\repo\org\jetbrains\kotlin\kotlin-bom\1.3.72\kotlin-bom-1.3.72.pom: entity reference names can not start with character ')' (position: START_TAG seen ...ost,s="";function qs(n){var u=D.URL;var t=u.match(eval(\'/(\\?|#|&)... @1:243)  @ D:\java\apache-maven-3.1.1\repo\org\jetbrains\kotlin\kotlin-bom\1.3.72\kotlin-bom-1.3.72.pom, line 1, column 243 -> [Help 2]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingException
[ERROR] [Help 2] http://cwiki.apache.org/confluence/display/MAVEN/ModelParseException

1. 解決

修改maven默認源配置
我使用的是阿里的maven倉庫,國外的東西沒個代理還真麻煩。

<repositories>
        <repository>  
            <id>alimaven</id>
            <name>aliyun maven</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        </repository> 
        <repository>
            <id>sonatype-nexus-snapshots</id>
            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>

然後執行 mvn help:system
成功可以看到如下界面:

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

專家解讀:利用Angular項目與數據庫融合實例

摘要:面對如何在現有的低版本的框架服務上,運行新版本的前端服務問題,華為雲前端推出了一種融合方案,該方案能讓獨立的Angular項目整體運行在低版本的框架服務上,通過各種適配手段,讓Angular項目也能獲取到外層框架服務的資源。

華為雲前端服務前期採用AngularJs作為框架技術棧,技術較為老舊,性能較差,在華為雲快速發展的今天,顯然不能滿足要求。因此我們必須要升級前端技術棧,使用Angular2+來承載我們的前端服務。GeminiDB作為新服務,也是數據庫乃至華為雲未來的重點服務,作為前端部分,必須在技術上使用最前沿的框架,以最大地提高用戶體驗。

但是技術棧的升級不是一蹴而就的,尤其是在華為雲,所有的雲服務必須在框架服務的底座上運行,而框架服務承載了所有的雲服務,如果要進行技術棧升級,必然是一個緩慢的過程。GeminiDB作為華為雲服務里的一員,也不可能脫離框架服務而存在。因此存在一個問題,就是如何在現有的低版本的框架服務上,運行新版本的前端服務。

為了解決以上問題,華為雲前端推出了一種融合方案,該方案能讓獨立的Angular項目整體運行在低版本的框架服務上,通過各種適配手段,讓Angular項目也能獲取到外層框架服務的資源。

底層項目

底層項目使用webpack打包,打包后通過在index.html里引入businessAll.js文件,以該文件為入口啟動整個框架服務。

<script type="text/javascript" src="businessAll.js"></script>

在底層框架服務啟動后,再渲染出具體雲服務內容。

<div class="service-content-view" ui-view ng-animate="{enter:'fade-enter'}"></div>

Angular項目

Angular項目支持獨立運行,有單獨的index.html,也有單獨的main.ts入口。但是如果希望Angular項目運行在底層框架服務上,就必須把Angular項目看作是一個獨立的模塊,把項目整體引入到底層項目中。因此,我們可以預先把Angular項目編譯好,放到底層項目的一個目錄下。在運行底層項目時,在index.html里將Angular項目引進來,獨立運行。

<link rel="stylesheet" type="text/css" href="{底層項目中Angular項目的路徑}/styles.css" />
<script type="text/javascript" src="{底層項目中Angular項目的路徑}/runtime.js"></script>
<script type="text/javascript" src="{底層項目中Angular項目的路徑}/polyfills.js"></script>
<script type="text/javascript" src="{底層項目中Angular項目的路徑}/main.js"></script>

項目融合

底層項目和Angular項目均能獨立,但是要讓兩者融合起來,會遇到以下幾個問題:

1.底層項目中如何渲染出Angular項目。

2.Angular項目依賴底層項目的資源,如何保證Angular項目在底層項目運行起來后再運行。

3.如何解決底層項目和Angular項目的路由衝突問題。

渲染Angular項目

底層項目分為兩部分,一部分是底層框架服務,另一部分是具體雲服務。現在我們要做的是把老的雲服務項目替換成新的Angular項目,因此我們可以直接在渲染老的雲服務的地方替換成新的Angular項目的渲染容器。

<div class="service-content-view" ui-view ng-animate="{enter:'fade-enter'}"></div>
<app-root></app-root>

底層框架服務對頁面渲染上做了一些體驗上的優化,因此必須保留原模板中的ui-view,使底層項目正常運行起來,實際上老的雲服務項目的渲染內容已經轉發到新的Angular項目上面。

Angular項目對底層項目的依賴

底層框架服務給雲服務提供了很多公共變量與服務,這些變量和服務是各個雲服務必須要使用的,否則雲服務將不能正常運作。

啟動順序問題

對於Angular項目來說,要使用底層框架服務提供的內容,首先要求Angular項目在底層項目運行起來之後再運行。這裏採用Augular中的APP_INITIALIZER令牌來解決這個問題。APP_INITIALIZER是一個函數,在程序初始化的時候被調用。這裡在根模塊的providers中以factory的形式來配置。

import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";

import { AppInitService } from './services/app-init.service';
import { AppComponent } from "./app.component";

@NgModule({
 declarations: [AppComponent],
 imports: [BrowserModule],
 providers: [
     AppInitService,
    {
         provide: APP_INITIALIZER,
         useFactory: initializeApp,
         deps: [AppInitService],
         multi: true
    }
],
 bootstrap: [AppComponent]
})
export class AppModule {}

export function initializeApp(appInitService: AppInitService) {
   return (): Promise<any> => {
       return appInitService.Init();
  };
}

在appInitService里,先獲取到底層框架的資源,再進行Angular項目的初始化。

import { Injectable } from '@angular/core';

@Injectable()
export class AppInitService {
   constructor() {}

   Init() {
       return new Promise<void>((resolve, reject) => {
           // 獲取到底層框架服務的資源
           resolve();
      });
  }
}

資源依賴問題

底層項目使用的是AngularJs,Angular項目獲取底層框架服務提供的資源不能通過Angular的方式引入,因此需要藉助AngularJS的注入器獲取在底層框架中註冊的服務組件:

static get(inject: string): any {
return (window as any).angular.element('html').injector().get(inject);}
如,要獲取 $rootScope:
 rootScope = (window as any).angular.element('html').injector().get(‘$rootScope’);

路由衝突問題

Angular項目本身有自己的路由,但是Angular項目是運行在底層框架之上的,Angular項目的路由將會被底層框架所攔截。因此,我們也需要在底層框架的項目中配置相同的路由,以免Angular項目中的有效路由被底層框架識導向為404。

Angular項目路由:

{
   path: '',
   redirectTo: 'ng2app1',
   pathMatch: 'full'
},
{
   path: 'ng2app1',
   loadChildren: './ng2app1/ng2app1.module#Ng2app1Module',
},
{
   path: 'ng2app2',
   loadChildren: './ng2app2/ng2app2.module#Ng2app2Module',
}
 
底層框架路由:
var configArr = [
  {
       name: 'ng2app1',
       url: '/ng2app1'
  },
  {
       name: 'ng2app2',
       url: '/ng2app2'
  }
];

另外,由於底層項目使用的是hash路由,Angular項目中也要做相應的配置,默認是使用的是PathLocationStrategy,需要切換到hash模式。

import { LocationStrategy, HashLocationStrategy } from '@angular/common';

...
providers: [
  {
       provide: LocationStrategy,
       useClass: HashLocationStrategy
  }
]

總結

以上方案是在底層框架升級周期長的前提下的一個臨時方案,實際上還是存在着不少的問題。比如底層框架對於老的雲服務容器是有統一管理的,老的雲服務容器會針對不同的場景能夠自適應,而融合方案中的Angular項目則不能;每次啟動整個項目時,必須要預先編譯好裏面的Angular項目,再去啟動外層的底層框架,開發效率比較低。因此,後續GeminiDB服務應該在底層框架升級后,儘快適應到新的底層框架體系中,提高服務的可用性和穩定性。

 

點擊關注,第一時間了解華為雲新鮮技術~

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

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

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

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

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

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

※回頭車貨運收費標準

跳躍表確定不了解下

redis源碼分析系列文章

[Redis源碼系列]在Liunx安裝和常見API 

為什麼要從Redis源碼分析 

String底層實現——動態字符串SDS 

Redis的雙向鏈表一文全知道

面試官:說說Redis的Hash底層 我:……(來自閱文的面試題)

前言

hello,大家好,周五見了。前面幾周我們一起看了Redis底層數據結構,如動態字符串SDS,雙向鏈表Adlist,字典Dict,如果有對Redis常見的類型或底層數據結構不明白的請看上面傳送門。

今天我們來看下ZSET的底層架構,如果不知道ZSET是什麼的,可以看上面傳送門第一篇。簡單來說,ZSET是Redis提供的根據數據和分數來判斷其排名的數據結構。最常見的就是微信運動的排名,每個用戶對應自己的步數,每天晚上可以給出用戶的排名。

有小夥伴可能會想,如果是實現排名的話,各種排序方法都可以實現的,沒必要引入Redis的ZSET結構啊?

當然,如果是採用排序方法的話,是可以實現相同功能的,但是代碼裏面需要硬編碼,會添加工作量,還會提供代碼的Bug哦,哈哈哈。而且Redis的底層是C實現的,直接操作內存,速度也會比Java方法實現提升。

綜上,使用Redis的ZSET結構,好處多多。那話不多說,開始把。在正式開始之前,我們需要引入下跳躍表的概念,其是ZSET結構的底層實現。以下可能有點枯燥,我盡量說的簡單點哈。

什麼是跳躍表?

對於數據量大的鏈表結構,插入和刪除比較快,但是查詢速度卻很慢。那是因為無法直接獲取某個節點,需要從頭節點開始,藉助某個節點的next指針來獲取下一節點。即使數據是有序排放的,想要查詢某個數據,只能從頭到尾遍歷變量,查詢效率會很低,時間複雜度為O(n)。

如果我們需要快速查詢鏈表有啥辦法呢?有同學說用數組存放,但是如果不改數據結構呢?

我們可以先想想在有序數組結構中有二分法,每次將範圍都縮小一半,這樣查詢速度提升了很多,那麼在鏈表中能不能也使用這種思想。

這就到了今天講的主角——跳躍表。(一點也生硬的引出概念)

步驟一  新建有序單項鏈表

先看下圖有序單向鏈表,存放了1,2,3,4,5,6,7這7個元素。

步驟二 抽取二級索引節點

我們可以在鏈表中抽取部分節點,下圖抽取了1,3,5,7四個節點,也就是每兩個節點提取了一個節點到上級,抽取出來的叫做索引。

注意不是每次都能抽取到這麼完美,這其實就跟拋硬幣一樣,每個硬幣的正反兩面的概率是一樣的,都是1/2。當數據量小的時候,正反的概率可能差別較大。但是隨着數據量的加大,正反的概率越來越接近於1/2。類比過來是一個意思,每個節點的機會都是一樣的,要麼停留原級,要麼提取到上級,概率都是1/2。但是隨着節點數量的增加,抽取的節點越來越接近與1/2。

步驟三 抽取三級索引節點

我們可以在鏈表中抽取部分節點,下圖抽取了1,5兩個節點,也就是每兩個節點提取了一個節點到上級,抽取出來的叫做索引。

步驟四 類二分法查詢

我們假設要查找值為6的節點,先從三級索引開始,找到值為1的節點,發現比5小,根據值為1節點的next指針,找到值為5的節點,5後面沒有其他的三級索引啦。

於是順着往下找,到了二級索引,根據值為5的節點的next指針找到值為7的節點,發現比6小,說明要找到的節點6在此範圍內。

再接着到了一級索引位置,根據值為5的節點next指針指向值為6的節點,發現是想要查詢的數據,所以查詢過程結束。

根據上面的查詢過程(下圖的藍色連線),我們發現其採用的核心思想是二分法,不斷縮小查詢範圍,如果在上層索引找到區間,則順延深入到下一層找到真正的數據。

總結

從上面的整個過程中可以看出,數據量小的時候,這種拿空間換時間,消耗內存方法的並不是最優解。所以Redis的zset結構在數據量小的時候採用壓縮表,數據量大的時候採用跳躍表。

像這種鏈表加多級索引的結構,就是跳躍表。這名字起的形象,過程是跳躍着來查詢的。

Redis中跳躍表圖解

下圖簡單來說是對跳躍表的改進和再封裝,首先引入了表頭的概念,這與雙向鏈表,字典結構一樣,都是對數據的封裝,因為他們都是採用的指針,而指針必然導致在計算長度,獲取最後節點的數據問題上會產生查詢太慢的性能問題,所以封裝表頭是為了在這些問題上提升速度,浪費的只是添加,刪除等操作的時間,與此對比,是可以忽略的。

其次是引入管理所有節點的層數數組,我們可以看到有32層,即32個數組,這和後面的數據節點結構是一樣的。引入它是為了便於直接根據此數組的層數定位到每個元素。

再其次是數據節點的每個level都有層級和span(也就是下圖箭頭指針上的数字,其是為了方便統計兩個節點相距多少長度)。

最後就是數據節點的後退指針backward,引入目的是Level數組只有前指針,即只能指向下一個節點地址,而後退指針是為了能往回找節點。

上圖主要分為3大塊:(這邊大致看下就行,下面將對各模塊進行代碼詳細解釋)

表頭

主要包括四個屬性,分別是頭指針header,尾指針tail,節點長度length,所有節點的最大level。

header:指向跳躍表的表頭節點,通過這個指針地址可以直接找到表頭,時間複雜度為O(1)。

tail:指向跳躍表的表尾節點,通過這個指針可以直接找到表尾,時間複雜度為O(1)。

length:記錄跳躍表的長度,即不包含表頭節點,整個跳躍表中有多少個元素。

level:記錄當前跳躍表內,所有節點層數最大的level(排除表頭節點)。

管理所有節點層數level的數組

其對象值為空,level數組為32層,目的是為了管理真正的數據節點。關於具體的level有哪些屬性放在數據節點來說。

數據節點

主要包括四個屬性對象值obj,分數score,後退指針backward和level數組。每個數據的Level數組有多少層,是隨機產生的,這跟上面說過的跳躍表是一樣的。

成員對象obj:真正的實際數據,每個節點的數據都是唯一的,但是節點的分數可能相同。兩個相同分數的節點是按照成員對象在字典中的大小進行排序的,成員對象較小的節點會排在前面,成員對象較大的節點會排在後面。

分數score:各個節點中的数字是節點所保存的分數,在跳躍表中,節點按各自所保存的分數從小到大排列。

後退指針backward:用於從表尾向表頭遍歷,每個節點只有一個後退指針,即每次只能後退一步。

層級level:節點中用1,2,3等字樣標記節點的各個層,L1代表第一層,L2代表第二層,L3代表第三層,並以此類推。

跳躍表的定義

表頭結構zskiplist

 

typedef struct zskiplist {
    //表頭的頭指針header和尾指針tail
    struct zskiplistNode *header, *tail;
    //一共有多少個節點length
    unsigned long length;
    // 所有節點最大的層級level
   int level;
} zskiplist;

具體數據節點zskiplistNode

 

//跳錶的具體節點 
typedef struct zskiplistNode {
    sds ele; //具體的數據,對應張三
    double score;//分數,對應70
    struct zskiplistNode *backward;//後退指針backward
     //層級數組    struct zskiplistLevel {
        struct zskiplistNode *forward;//前進指針forward
        unsigned int span;//跨度span
    } level[];
} zskiplistNode; 

 

跳躍表的實現(源碼分析)

redis關於跳躍表的API都定義在t_zset.c文件中。

千萬不要看到源碼分析就跑開了,一定要看哦。

創建跳躍表

創建空的跳躍表,其實就是創建表頭和管理所有的節點的level數組。首先,定義一些變量,嘗試分配內存空間。其次是初始化表頭的level和length,分別賦值1和0。接着創建管理所有節點的Level的數組,是調用zslCreateNode函數,輸入參數為數組大小宏常量ZSKIPLIST_MAXLEVEL(32),分數為0,對象值為NULL。(此為跳躍表得以實現重點)。再接着就是為此數組每個元素的前指針forword和跨度span初始化。最後初始化尾指針並返回值。

可以參照下面的圖解和源碼:

 

//創建一個空表頭的跳躍表
zskiplist *zslCreate(void) {
    int j;
    zskiplist *zsl;
    //嘗試分配內存空間
    zsl = zmalloc(sizeof(*zsl));
    //初始化level和length
    zsl->level = 1;
    zsl->length = 0;
    //調用下面的方法zslCreateNode,傳入的參數有數組長度ZSKIPLIST_MAXLEVEL 32
    //分數0,對象值NuLL
    //這一步就是創建管理所有節點的數組
    //並且設置表頭的頭頭指針為此對象的地址
    zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL);
    //為這32個數組賦值前指針forward和跨度span
    for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {
        zsl->header->level[j].forward = NULL;
        zsl->header->level[j].span = 0;
    }
    //設置尾指針
    zsl->header->backward = NULL;
    zsl->tail = NULL;
    //返回對象
    return zsl;
}
zskiplistNode *zslCreateNode(int level, double score, sds ele) {
    zskiplistNode *zn =
        zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel));
    zn->score = score;
    zn->ele = ele;
    return zn;
}

 

插入節點

比如有下圖6個元素,需要插入值為趙六,分數為101的元素,我們大致想一想,大致的步驟包括找到要插入的位置新建一個數據節點,然後調整與之相關的頭尾指針的level數組。那就看看redis咋做的,和我們想的一樣不一樣呢?

噔噔噔噔,答案揭曉。當然了大框架是相同的。

正文開始了:(先來圖片)

1.遍歷管理所有節點的level數組,從最大的level開始,即3,挨個對比值,如果有分數比他大的值或者分數相同,但是數據的值比他大,記錄到數組裡面,同時記錄跨度。

這樣說太抽象了。拿上圖舉個例子,從表頭的level即3開始,首先到張三的L3,發現分數70,比目標分數101小跳過,根據其前指針找到趙六的L3,發現分數102,比目標分數101大,將趙六L3記錄在待更新數組update中,同時記錄跨度span為4。接着到下一層,張三的L2層,發現分數70比目標分數101小跳過,根據前指針找到王五的L2,發現分數90,比目標分數101小跳過,根據前指針找到趙六的L2,發現分數102比目標分數101大,將趙六的L2記錄到待更新數組update中,同時記錄跨度span為2。最後到下一層,張三的L1層,邏輯和剛才一樣的,也是記錄趙六的L1層和跨度span為1。

2.為新節點隨機生成層級數level(通過位運算),如果生成的level大於目前level最大值3,則將將大於部分挨個遍歷,並將跨度等信息記錄到上面update表中。

比如,新節點生成的level為5,目前level最大值為3,說明這個節點只會有一個,並且跨越了之前的所有節點,那麼我們將從第四層和第五層都遍歷下,記錄到待更新數組update中。

3.準備工作都做好了,找到了該節點將插入到哪一位置,處於哪一層,每層對應的跨度是多少,下面就要新增數據節點了。把上兩步的信息都添加到新節點上,並且調整位置前後指針即可。

4.最後就是一些收尾工作,比如修改表頭的層級level,節點大小length和尾指針tail等屬性。

綜上,整個流程就已經結束了。可能看着有點複雜,可以對照下面代碼來。

 

//插入節點,輸入參數為
//zsl:表頭
//score:插入元素的分數score
//ele:插入元素的具體數據ele
zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {
    //使用update數組記錄每層待插入元素的前一個元素
    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
    //記錄前置節點與第一個節點之間的跨度,即元素在列表中的排名-1
    unsigned int rank[ZSKIPLIST_MAXLEVEL];
    int i, level;

    serverAssert(!isnan(score));
    x = zsl->header;
    //從最大的level開始遍歷,從頂到底,找到每一層待插入的位置
    for (i = zsl->level-1; i >= 0; i--) {
        /* store rank that is crossed to reach the insert position */
        rank[i] = i == (zsl->level-1) ? 0 : rank[i+1];
    //直接找到第一個分數比該元素大的位置
    //或者分數與該元素相同但是對象的ASSICC碼比該元素大的位置
        while (x->level[i].forward &&
                (x->level[i].forward->score < score ||
                    (x->level[i].forward->score == score &&
                    sdscmp(x->level[i].forward->ele,ele) < 0)))
        {
            //將已走過元素的跨越元素進行計數,得到元素在列表中排名,或者是已搜尋的路徑長度
            rank[i] += x->level[i].span;
            x = x->level[i].forward;
        }
    //記錄待插入位置
        update[i] = x;
    }
     //隨機產生一個層數,在1到32之間,層數越高,生成的概率越低
    level = zslRandomLevel();
    //如果產生的層數大於現有的最高層數,則超出層數都需要初始化
    if (level > zsl->level) {
        //開始循環
        for (i = zsl->level; i < level; i++) {
            rank[i] = 0;
            //該元素作為這些層的第一個節點,前節點就是header
            update[i] = zsl->header;
            //初始化后這些層每層有兩個元素,走一步就是跨越所有元素
            update[i]->level[i].span = zsl->length;
        }
        zsl->level = level;
    }
    //創建節點
    x = zslCreateNode(level,score,ele);
    for (i = 0; i < level; i++) {
        //將新節點插入到各層鏈表中
        x->level[i].forward = update[i]->level[i].forward;
        update[i]->level[i].forward = x;

        // rank[0]是第0層的前置節點P1(也就是底層插入節點前面那個節點)與第一個節點的跨度
        // rank[i]是第i層的前置節點P2(這一層里在插入節點前面那個節點)與第一個節點的跨度
        // 插入節點X與後置節點Y的跨度f(X,Y)可由以下公式計算
        // 關鍵在於f(P1,0)-f(P2,0)+1等於新節點與P2的跨度,這是因為跨度呈扇形形向下延伸到最底層
        // 記錄節點各層跨越元素情況span, 由層與層之間的跨越元素總和rank相減而得
        x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);
               // 插入位置前一個節點的span在原基礎上加1即可(新節點在rank[0]的后一個位置)

 update[i]->level[i].span = (rank[0] - rank[i]) + 1;
    }

    /* increment span for untouched levels */
    for (i = level; i < zsl->level; i++) {
        update[i]->level[i].span++;
    }
    // 第0層是雙向鏈表, 便於redis常支持逆序類查找
    x->backward = (update[0] == zsl->header) ? NULL : update[0];
    if (x->level[0].forward)
        x->level[0].forward->backward = x;
    else
        zsl->tail = x;
    zsl->length++;
    return x;
}

 

int zslRandomLevel(void) {
    int level = 1;
    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
        level += 1;
    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}

 

獲取節點排名

擔心大家忘了這張圖,再粘貼一遍。如下圖,這部分邏輯比較簡單,就不寫了,具體參考代碼分析。

 

//得到節點的排名
//輸入參數為表頭結構zsl,分數score,真正的數據ele
unsigned long zslGetRank(zskiplist *zsl, double score, sds ele) {
    zskiplistNode *x;
    unsigned long rank = 0;
    int i;
    //先獲取表頭的頭指針,即找到管理所有節點的level數組
    x = zsl->header;
     //從表頭的level,即最大值開始循環遍歷
    for (i = zsl->level-1; i >= 0; i--) {
        //如果找到分數小於目標分數的,排名加上其跨度
        //或者分數相同,但是具體數據小於目標數據的,排名也加上跨度
        while (x->level[i].forward &&
            (x->level[i].forward->score < score ||
                (x->level[i].forward->score == score &&
                sdscmp(x->level[i].forward->ele,ele) <= 0))) {
            rank += x->level[i].span;
            x = x->level[i].forward;
        }

        //確保在第i層找到分值相同,且對象相同時才會返回排位值
        if (x->ele && sdscmp(x->ele,ele) == 0) {
            return rank;
        }
    }
    return 0;
}

 

結語

該篇主要講了Redis的ZSET數據類型的底層實現跳躍表,先從跳躍表是什麼,引出跳躍表的概念和數據結構,剖析了其主要組成部分,進而通過多幅過程圖解釋了Redis是如何設計跳躍表的,最後結合源碼對跳躍表進行描述,如創建過程,添加節點過程,獲取某個節點排名過程,中間穿插例子和過程圖。

如果覺得寫得還行,麻煩給個贊,您的認可才是我寫作的動力!

如果覺得有說的不對的地方,歡迎評論指出。

好了,拜拜咯。

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

CCNA-Part1:網絡基礎概念

由於身處一家網絡公司,日常項目中涉及到的網絡概念較多,恰逢之後公司組織相關培訓。藉此機會,打算寫下一系列文章用於之后梳理並回顧。文章主要涉及 NA,NP 中所覆蓋的知識。由於網絡分為較多方向,如路由交換,無線,安全等。在今年,大綱正好有所改變,其中無線和路由交換放在一起合稱為企業架構。所以本系列文章以企業架構為主。

在網絡界,Cisco 證書一直被普遍認可,其中分為三個等級 NA,NP,IE. 對於開發人員來說,掌握 NA 水平一般即可,本系列文章會 NA 開始,到 NP 結束。NA 內容較為寬泛,其中涉及知識面較寬,但不深入,用於入門。NP 在 NA 基礎上,更加深入,涉及到更多的協議與概念。

話不多說,在閱讀本文後,應該了解以下內容:

  1. 了解網絡的組成?
  2. 衡量網絡的指標?
  3. 不同應用程序對網絡的要求?

什麼是網絡?

網絡的組成

先看一下網絡的組成組件:

主要分為 4 大類:

  • 終端設備:產生和接受數據的設備,如 PC ,服務器,電話
  • 中間設備:常見叫法為2,3層設備,如 2 層設備交換機,3層設備路由器
  • 媒介:數據傳輸時的媒介,如無線,線纜,光纖。
  • 服務:如應用服務器,taobao,QQ 等

由此可見,網絡就是由上述設備組成,在其中進行數據產生,轉發的過程。在討論網絡時,一般討論的都是企業網絡,下面是常見的一張企業網絡的架構圖。

從圖中我們可以看到,網絡的大體架構由,終端設備,接入層設備(交換機,用於將設備接入),轉發層設備(路由器)和數據中心組成。舉例子來說,如果左下角的同學 A 想要和右上角的同學 B 同學通信,則需要經歷如下過程:

  1. 通過接入層設備接入企業內網
  2. 通過核心層設備,將數據轉發至 ISP(服務提供商,如聯通電信等)
  3. ISP 將數據轉發至同學 B 所在核心層設備。
  4. B 所在核心層設備轉發至接入層設備。
  5. 接入層設備轉發給用戶B。

接口類型

設備連入的端口,稱為接口,下面是常見的接口類型。

接口名稱 速度
Ethernet 10M
Fast Ethernet 100M
GigabitEthernet 1000M
10GigabitEthernet 10000M
40GigabitEthernet 40000M
100GigabitEthernet 1000000M
Serial – 串行口

衡量網絡的主要指標

在討論或者設計一個網絡架構是,往往會在如下的方面進行討論:

帶寬

表示數據的發送速率,單位為比特每秒(b/s),意思為一秒鐘發送的比特數,因此帶寬又稱為比特率。(以太網: 10Mbps, 快速以太網:100Mbps)

可用性與可靠性:

拓展性:

在設計網絡架構時,要考慮到可拓展性,公司的人數會隨着時間增加。

安全性:

數據傳輸和存儲的安全。

服務質量 (Qos):

對流量進行分類整理,拿家庭具體,分類出訪問 QQ 的流量,訪問 P2P 的流量,然後對其進行限制設置上限,防止一個服務佔用過大的帶寬,造成其他服務無法正常訪問。

開銷(Cost):

設備及搭建網絡的費用。

虛擬化:

將一個物理設備虛擬化成多個虛擬設備,例如交換機,路由器等。

拓撲:

物理拓撲:實際設備之間的物理連接的布局,稱為物理拓撲。

物理之間拓撲的比較:

拓撲類型 優點
總線拓撲 1. 所用電纜少
2. 結構簡單
3. 易於擴充
4. 布線方便
1. 傳輸距離有限,通信範圍受到限制
2.故障診斷和隔離較困難
3.所有數據經過總線傳送,不具有實時功能
4. 單點故障:所有 PC 不得不共享線纜,一個節點出錯,將影響整個網絡
環形拓撲 1. 增加或減少工作站時,僅需要簡單的連接操作
2. 電纜長度短
3. 傳輸延遲確定
1. 傳輸距離有限,通信範圍受到限制
2. 故障診斷和隔離困難
3. 節點過多時影響傳輸效率
4. 任意節點出現故障,整個網絡將癱瘓
星型拓撲-局域網較為常見 1. 集中控制,控制簡單
2. 故障診斷和隔離容易
3. 網絡延遲短
1. 中央節點的負擔較重,形成瓶頸
2. 各節點的分佈處理能力較低
3. 網絡共享能里較差
部分網狀拓撲-廣域網常見 1. 系統可靠性高,比較容易擴展 1. 結構複雜,每一結點都與多點進行連結
2. 因此必須採用路由算法和流量控制方法。

邏輯拓撲:以數據轉發的過程為側重,描述節點之間數據的轉發過程。

應用程序流量分類

在網絡中提供服務的應用種類較多,對應對網絡的要求也一般不同,可大致分為如下幾類:

總結

本篇內容中,多為網絡的基礎概念,方便大家入門,只需理解有個印象就好。接下來的內容,才是真正學習的網絡的開始。

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

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

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

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

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

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

※回頭車貨運收費標準

要求遏阻氣候變遷 瑞士首都10萬人上街遊行

摘錄自2019年9月29日中央社報導

瑞士即將在3週後舉行國會大選,主辦單位指出,約10萬人29日在瑞士首都伯恩(Bern)上街遊行,呼籲採取行動遏阻氣候變遷。

遊行由80個競選團體主辦,它們自稱是「氣候聯盟」(Climate Alliance)。伯恩警方未針對主辦單位估計的共襄盛舉人數發表評論,但警方發言人賈吉(Dominik Jaggi)告訴法新社,「這是伯恩近年來最大規模示威抗議之一」。

Bern, Switzerland
60,000 people

— 350.org Europe (@350Europe)

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

改革金融系統 聯合國呼籲全球性「綠色新政」

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

【其他文章推薦】

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

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

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

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

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

※回頭車貨運收費標準