整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          100W請求秒殺架構體系-靜態資源處理

          標1:秒殺設計-業務設計、架構設計、表設計

          目標2:工程講解

          目標3:商品詳情頁開發

          目標4:數據同步Canal學習

          目標5:分布式任務調度elastic-job學習

          目標6:靜態頁動態更新


          1 秒殺設計

          業務流程

          電商項目中,秒殺屬于技術挑戰最大的業務。后臺可以發布秒殺商品后或者將現有商品列入秒殺商品,熱點分析系統會對商品進行分析,對熱點商品做特殊處理。商城會員可以在秒殺活動開始的時間內進行搶購,搶購后可以在線進行支付,支付完成的訂單由平臺工作人員發貨,超時未支付訂單會自動取消。

          當前秒殺系統中一共涉及到管理員后臺、搜索系統、秒殺系統、搶單流程系統、熱點數據發現系統,如下圖:


          秒殺架構

          B2B2C商城秒殺商品數據一般都是非常龐大,流量特別高,尤其是雙十一等節日,所以設計秒殺系統,既要考慮系統抗壓能力,也要考慮系統數據存儲和處理能力。秒殺系統雖然流量特別高,但往往高流量搶購的商品為數不多,因此我們系統還需要對搶購熱門的商品進行有效識別

          商品詳情頁的內容除了數量變更頻率較高,其他數據基本很少發生變更,像這類變更頻率低的數據,我們可以考慮采用模板靜態化技術處理。

          秒殺系統需要考慮抗壓能力,編程語言的選擇也有不少講究。項目發布如果采用Tomcat,單臺Tomcat抗壓能力能調整到大約1000左右,占用資源較大。Nginx抗壓能力輕飄的就能到5萬,并且Nginx占用資源極小,運行穩定。如果單純采用Java研發秒殺系統,用Tomcat發布項目,在抗壓能力上顯然有些不足,如果采用Lua腳本開發量大的功能,采用Nginx+Lua處理用戶的請求,那么并發處理能力將大大提升。

          下面是當前秒殺系統的架構圖:



          數據庫設計

          數據庫名字: seckill_goods


          秒殺訂單數據庫

          秒殺訂單表: tb_order


          管理員數據庫

          管理員表: tb_admin


          用戶數據庫

          用戶表: tb_user



          項目介紹

          技術棧介紹


          商品詳情頁

          分析

          秒殺活動中,熱賣商品的詳情頁訪問頻率非常高,詳情頁的數據加載,我們可以采用直接從數據庫查詢加載,但這種方式會給數據庫帶來極大的壓力,甚至崩潰,這種方式我們并不推薦。

          商品詳情頁主要有商品介紹、商品標題、商品圖片、商品價格、商品數量等,大部分數據幾乎不變,可能只有數量會變,因此我們可以考慮把商品詳情頁做成靜態頁,每次訪問只需要加載庫存數量,這樣就可以大大降低數據庫的壓力。

          我們這里將采用freemarker來實現商品詳情頁的靜態化,關于freemarker的語法我們就不在這里講解了,大家可以自行去網上查閱相關API。并發處理能力

          1、降低了數據庫查詢頻率

          2、使用Nginx實現詳情頁訪問效率遠高于Tomcat

          Canal增量數據同步利器

          canal主要用途是基于 MySQL 數據庫增量日志解析,并能提供增量數據訂閱和消費,應用場景十分豐富。

          github地址:https://github.com/alibaba/canal

          版本下載地址:https://github.com/alibaba/canal/releases

          文檔地址:https://github.com/alibaba/canal/wiki/Docker-QuickStart

          Canal應用場景

          1.電商場景下商品、用戶實時更新同步到至Elasticsearch、solr等搜索引擎;

          2.價格、庫存發生變更實時同步到redis;

          3.數據庫異地備份、數據同步;

          4.代替使用輪詢數據庫方式來監控數據庫變更,有效改善輪詢耗費數據庫資源。



          MySQL主從復制原理

          1. MySQL master 將數據變更寫入二進制日志( binary log , 其中記錄叫做二進制日志事件 binary log events ,可以通過 show binlog events 進行查看) 2. MySQL slave 將 master 的 binary log events 拷貝到它的中繼日志( relay log ) 3. MySQL slave 重放 relay log 中事件,將數據變更反映它自己的數據

          Canal工作原理

          1.canal 模擬 MySQL slave 的交互協議,偽裝自己為 MySQL slave ,向 MySQL master 發送dump 協議

          2. MySQL master 收到 dump 請求,開始推送 binary log 給 slave (即 canal ) 3.canal 解析 binary log 對象(原始為 byte流)


          Canal安裝

          參考文檔:https://github.com/alibaba/canal/wiki/QuickStart

          MySQL Bin-log開啟

          1)MySQL開啟bin-log

          a.進入mysql容器

          docker exec ‐it ‐u root mysql /bin/bash

          cd /etc/mysql/mysql.conf.d

          b.開啟mysql的binlog

          在mysqld.cnf最下面添加如下配置

          # 開啟 binlog

          log‐bin=/var/lib/mysql/mysql‐bin

          # 選擇 ROW 模式

          binlog‐format=ROW

          # 配置 MySQL replaction 需要定義,不要和 canal 的 slaveId 重復

          server‐id=12345

          c.創建賬號并授權

          授權 canal 鏈接 MySQL 賬號具有作為 MySQL slave 的權限, 如果已有賬戶可直接 grant:

          create user canal@'%' IDENTIFIED by 'canal';

          GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO 'canal'@'%';

          FLUSH PRIVILEGES;

          d.重啟mysql

          docker restart mysql

          開啟bin-log后,我們可以用sql語句查看下:

          show variables like '%log_bin%'

          效果如下:



          Canal安裝

          1)拉取鏡像

          docker pull canal/canal‐server:v1.1.1

          2)安裝容器

          a.安裝canal-server容器

          docker run ‐p 11111:11111 ‐‐name canal ‐d docker.io/canal/canal‐server

          b.配置canal-server

          修改 /home/admin/canal-server/conf/canal.properties ,將它的id屬性修改成和mysql數據庫中server-id不同的值,如下圖:



          c.修改 /home/admin/canal-server/conf/example/instance.properties ,配置要監聽的數據庫服務地址和監聽數據變化的數據庫以及表,修改如下:



          指定監聽數據庫表的配置如下 canal.instance.filter.regex :

          mysql 數據解析關注的表,Perl正則表達式.

          多個正則之間以逗號(,)分隔,轉義符需要雙斜杠(\)

          常見例子:

          1. 所有表:.* or .*\..*

          2. canal schema下所有表: canal\..*

          3. canal下的以canal打頭的表:canal\.canal.*

          4. canal schema下的一張表:canal.test1

          5. 多個規則組合使用:canal\..*,mysql.test1,mysql.test2 (逗號分隔)

          注意:此過濾條件只針對row模式的數據有效(ps. mixed/statement因為不解析sql,所以無法準確提取tableName進行過濾)

          重啟canal

          docker restart canal

          Canal微服務

          我們搭建一個微服務,用于讀取canal監聽到的變更日志,微服務名字叫 seckill-canal 。該項目我們需要引入canal-spring-boot-autoconfigure 包,并且需要實現 EntryHandler<T> 接口,該接口中有3個方法,分別為insert 、 update 、 delete ,這三個方法用于監聽數據增刪改變化。

          參考地址:https://github.com/NormanGyllenhaal/canal-client


          靜態頁同步

          只需要添加Feign包,注入SkuPageFeign,根據增刪改不同的需求實現生成靜態頁或刪除靜態頁。修改SkuHandler


          分布式任務調度

          分布式任務調度介紹

          很多時候,我們需要定時執行一些程序完成一些預定要完成的操作,如果手動處理,一旦任務量過大,就非常麻煩,所以用定時任務去操作是個非常不錯的選項。

          現在的應用多數是分布式或者微服務,所以我們需要的是分布式任務調度,那么現在分布式任務調度流行的主要有elastic-job、xxl-job、quartz等

          elastic-job講解

          官網:http://elasticjob.io/index_zh.html

          靜態任務案例

          使用elastic-job很容易,我們接下來學習下elastic-job的使用,這里的案例我們先實現靜態任務案例,靜態任務案例也就是執行時間事先寫好。

          實現步驟:

          1.引入依賴包

          2.配置zookeeper節點以及任務名稱命名空間

          3.實現自定義任務,需要實現SimpleJob接口

          靜態頁動態更新

          索引和靜態資源的更新功能已經完成,所有秒殺商品都只是參與一段時間活動,活動時間過了需要將秒殺商品從索引中移除,同時刪除靜態頁。我們需要有這么一個功能,在秒殺商品活動結束的時候,將靜態頁刪除、索引庫數據刪除。

          此時我們可以使用elastic-job定時執行該操作,我們看如下活動表,活動表中有一個活動開始時間和活動結束時間,我們可以在每次增加、修改的時候,動態創建一個定時任務,把活動結束時間作為任務執行時間。

          學習記錄的代碼,部分參考自gitee碼云的如下兩個工程。這兩個個工程有詳盡的Spingboot1.x和2.x教程。鳴謝!

          https://gitee.com/didispace/SpringBoot-Learning.git

          https://gitee.com/jeff1993/springboot-learning-example.git

          本學習記錄的示例代碼克隆地址,分支為develop

          https://gitee.com/kutilion/MyArtifactForEffectiveJava.git

          靜態資源

          通常實際的項目中會引入大量的靜態資源。比如圖片,樣式表css,腳本js,靜態html頁面等。這章主要學習引入模板來實現訪問靜態資源。

          一般Springboot提供的默認靜態資源存放位置是/resources之下。html的文件一般存放在/resources/templates中。

          渲染靜態頁面通常會用到模板。模板種類很多,這里介紹兩種:

          • Thymeleaf
          • FreeMarker

          另外比較常用的模板還有velocity,但是velocity在Springboot1.5開始就不被支持了。

          示例相關代碼如下:

          Thymeleaf

          • studySpringboot.n06.webframework.web.WebController.java
          • templates/06_webframework/thymeleaf.html

          FreeMarker

          • studySpringboot.n06.webframework.web.WebController.java
          • templates/06_webframework/freemarker.ftl

          Thymeleaf

          build.gradle

          為了使用Thymeleaf模板,需要在build.gradle腳本中引入模板引擎的依賴

          implementation 'org.springframework.boot:spring-boot-starter-thymeleaf
          

          WebController.java

          控制器類中聲明訪問路徑,并且為模板添加一個變量

           @RequestMapping("/thymeleaf")
           public String ThymeleafTest(ModelMap map) {
           map.addAttribute("host", "http://blog.kutilionThymeleaf.com");
           return "06_webframework/thymeleaf"; 
           }
          

          注意這個方法的返回值,因為靜態頁面沒有直接放在templates文件夾下,而是放在templates文件夾的子文件夾06_webframework中,所以返回值中要把路徑帶上

          thymeleaf.html

          靜態頁面中使用了el表達式,可以將java變量反映到頁面上

          <!DOCTYPE html>
          <html>
          <head lang="en">
           <meta charset="UTF-8" />
           <title></title>
          </head>
          <body>
          <h1 th:text="${host}">This Thymeleaf framework test page.</h1>
          </body>
          </html>
          

          執行結果:

          FreeMarker

          原理和Thymeleaf基本是一樣的

          build.gradle

          implementation 'org.springframework.boot:spring-boot-starter-freemarker'
          

          WebController.java

           @RequestMapping("/freemarker")
           public String FreeMarkerTest(ModelMap map) {
           map.addAttribute("host", "http://blog.kutilionFreemarker.com");
           return "06_webframework/thymeleaf";
           }
          

          freemarker.ftl

          <!DOCTYPE html>
          <html>
          <head lang="en">
           <meta charset="UTF-8" />
           <title></title>
          </head>
          <body>
          <h1 th:text="${host}">This Freemarker framework test page.</h1>
          </body>
          </html>
          

          執行結果:

          總結

          • 引入并且使用了Thymeleaf和FreeMarker模板
          • Springboot默認已經配置好這兩種模板了,如果需要手動配置需要參考兩種模板的文檔

          們可以把一個方法賦值給類的函數本身,而不是賦給它的 "prototype"。這樣的方法被稱為 靜態的(static)。

          在一個類中,它們以 static 關鍵字開頭,如下所示:

          class User {
            static staticMethod() {
              alert(this === User);
            }
          }
          
          User.staticMethod(); // true

          這實際上跟直接將其作為屬性賦值的作用相同:

          class User { }
          
          User.staticMethod = function() {
            alert(this === User);
          };
          
          User.staticMethod(); // true

          User.staticMethod() 調用中的 this 的值是類構造器 User 自身(“點符號前面的對象”規則)。

          通常,靜態方法用于實現屬于該類但不屬于該類任何特定對象的函數。

          例如,我們有對象 Article,并且需要一個方法來比較它們。一個自然的解決方案就是添加 Article.compare 方法,像下面這樣:

          class Article {
            constructor(title, date) {
              this.title = title;
              this.date = date;
            }
          
            static compare(articleA, articleB) {
              return articleA.date - articleB.date;
            }
          }
          
          // 用法
          let articles = [
            new Article("HTML", new Date(2019, 1, 1)),
            new Article("CSS", new Date(2019, 0, 1)),
            new Article("JavaScript", new Date(2019, 11, 1))
          ];
          
          articles.sort(Article.compare);
          
          alert( articles[0].title ); // CSS

          這里 Article.compare 代表“上面的”文章,意思是比較它們。它不是文章的方法,而是整個 class 的方法。

          另一個例子是所謂的“工廠”方法。想象一下,我們需要通過幾種方法來創建一個文章:

          1. 通過用給定的參數來創建(title,date 等)。
          2. 使用今天的日期來創建一個空的文章。
          3. ……其它方法。

          第一種方法我們可以通過 constructor 來實現。對于第二種方式,我們可以創建類的一個靜態方法來實現。

          就像這里的 Article.createTodays()

          class Article {
            constructor(title, date) {
              this.title = title;
              this.date = date;
            }
          
            static createTodays() {
              // 記住 this = Article
              return new this("Today's digest", new Date());
            }
          }
          
          let article = Article.createTodays();
          
          alert( article.title ); // Today's digest

          現在,每當我們需要創建一個今天的文章時,我們就可以調用 Article.createTodays()。再說明一次,它不是一個文章的方法,而是整個 class 的方法。

          靜態方法也被用于與數據庫相關的公共類,可以用于搜索/保存/刪除數據庫中的條目, 就像這樣:

          // 假定 Article 是一個用來管理文章的特殊類
          // 靜態方法用于移除文章:
          Article.remove({id: 12345});

          一、靜態屬性

          靜態的屬性也是可能的,它們看起來就像常規的類屬性,但前面加有 static

          class Article {
            static publisher = "Levi Ding";
          }
          
          alert( Article.publisher ); // Levi Ding

          這等同于直接給 Article 賦值:

          Article.publisher = "Levi Ding";

          二、繼承靜態屬性和方法

          靜態屬性和方法是可被繼承的。

          例如,下面這段代碼中的 Animal.compareAnimal.planet 是可被繼承的,可以通過 Rabbit.compareRabbit.planet 來訪問:

          class Animal {
            static planet = "Earth";
          
            constructor(name, speed) {
              this.speed = speed;
              this.name = name;
            }
          
            run(speed = 0) {
              this.speed += speed;
              alert(`${this.name} runs with speed ${this.speed}.`);
            }
          
            static compare(animalA, animalB) {
              return animalA.speed - animalB.speed;
            }
          
          }
          
          // 繼承于 Animal
          class Rabbit extends Animal {
            hide() {
              alert(`${this.name} hides!`);
            }
          }
          
          let rabbits = [
            new Rabbit("White Rabbit", 10),
            new Rabbit("Black Rabbit", 5)
          ];
          
          rabbits.sort(Rabbit.compare);
          
          rabbits[0].run(); // Black Rabbit runs with speed 5.
          
          alert(Rabbit.planet); // Earth

          現在我們調用 Rabbit.compare 時,繼承的 Animal.compare 將會被調用。

          它是如何工作的?再次,使用原型。你可能已經猜到了,extendsRabbit[[Prototype]] 指向了 Animal。

          所以,Rabbit extends Animal 創建了兩個 [[Prototype]] 引用:

          1. Rabbit 函數原型繼承自 Animal 函數。
          2. Rabbit.prototype 原型繼承自 Animal.prototype

          結果就是,繼承對常規方法和靜態方法都有效。

          這里,讓我們通過代碼來檢驗一下:

          class Animal {}
          class Rabbit extends Animal {}
          
          // 對于靜態的
          alert(Rabbit.__proto__ === Animal); // true
          
          // 對于常規方法
          alert(Rabbit.prototype.__proto__ === Animal.prototype); // true

          三、總結

          靜態方法被用于實現屬于整個類的功能。它與具體的類實例無關。

          舉個例子, 一個用于進行比較的方法 Article.compare(article1, article2) 或一個工廠(factory)方法 Article.createTodays()。

          在類生命中,它們都被用關鍵字 static 進行了標記。

          靜態屬性被用于當我們想要存儲類級別的數據時,而不是綁定到實例。

          語法如下所示:

          class MyClass {
            static property = ...;
          
            static method() {
              ...
            }
          }

          從技術上講,靜態聲明與直接給類本身賦值相同:

          MyClass.property = ...
          MyClass.method = ...

          靜態屬性和方法是可被繼承的。

          對于 class B extends A,類 B 的 prototype 指向了 AB.[[Prototype]] = A。因此,如果一個字段在 B 中沒有找到,會繼續在 A 中查找。

          任務


          主站蜘蛛池模板: 日韩精品电影一区| 国产精品亚洲产品一区二区三区| 日本一区二区视频| 中文字幕亚洲综合精品一区| 国产福利微拍精品一区二区| 精品国产亚洲第一区二区三区| 影院无码人妻精品一区二区| 国产免费一区二区三区在线观看| 亚洲电影唐人社一区二区| 波多野结衣中文字幕一区| 高清国产AV一区二区三区| 国产福利日本一区二区三区| 亚洲福利视频一区二区| 亚洲国产美国国产综合一区二区| 国产SUV精品一区二区四| 国精产品一区二区三区糖心| 亚洲午夜电影一区二区三区| 人成精品视频三区二区一区 | 中字幕一区二区三区乱码| 免费观看日本污污ww网站一区| 久久久精品一区二区三区| 免费精品一区二区三区在线观看| 久久久久国产一区二区三区| 国产韩国精品一区二区三区久久| 四虎一区二区成人免费影院网址| 亚洲国产成人一区二区精品区| 国产麻豆剧果冻传媒一区| 日韩精品无码中文字幕一区二区| 国偷自产视频一区二区久| 精品无码一区在线观看| 精品成人一区二区三区免费视频| 三上悠亚一区二区观看| 亲子乱av一区区三区40岁| 国偷自产一区二区免费视频| 国产嫖妓一区二区三区无码| 亚洲码一区二区三区| 免费看一区二区三区四区| 精品国产一区二区三区香蕉事 | 国精品无码一区二区三区左线| 性色av无码免费一区二区三区| 日韩人妻无码一区二区三区久久99|