SpringCloud(二)服務註冊與發現

  • 2019 年 10 月 3 日
  • 筆記

離上一篇微服務的基本概念已經過去了幾個月,在寫那篇部落格之前,自己還並未真正的使用微服務架構,很多理解還存在概念上。後面換了公司,新公司既用了SpringCloud也用了Dubbo+Zookeeper,就像上一篇文章說的,當一個服務是面向外部或者是直接提供給前端調用的,那麼就使用SpringCloud,而一些內部公用的(如發送簡訊),就使用Dubbo+Zookeeper,因為他在內部調用更像調用接一個介面,效率也會比較高,而一些模組型的功能,我們則使用SpringCloud。

在已經存在了成熟的開發框架後,微服務本身也沒什麼技術難點,架構思想才是最重要的,要在不斷的實踐中去探索,廢話不多說,來學習SpringCloud的技術。

一、SpringCloud技術棧

SpringCloud是一套完整的分散式微服務架構,我們可以去官網上看下整體的架構圖

 SpringCloud基於SpringBoot提供了一套微服務解決方案,包括服務註冊與發現,配置中心,全鏈路監控,服務網關,負載均衡,熔斷器等組件,除了基於NetFlix的開源組件做高度抽象封裝之外,還有一些選型中立的開源組件。

而SpringBoot並沒有重複製造輪子,它將目前各家公司開發的比較成熟、經得起實際考驗的服務框架組合起來,通過SpringBoot風格進行再封裝屏蔽掉了複雜的配置和實現原理,最終給開發者留出了一套簡單易懂、易部署和易維護的分散式系統開發工具包。

SpringCloud提供了全家桶式技術解決方案,對我們使用者來說是極其簡單的。但是要學會SpringCloud的前提那必定要學會SpringBoot。

了解完整體的架構圖後,就來進行一個個的技術棧的學習,學習的網站推薦:

Spring Cloud中國社區:http://springcloud.cn/

Spring Cloud中文網: https://www.springcloud.cc/

二、服務註冊與發現

一般架構的開發過程中,我們也會去調用一些外部服務,這個時候都是直接去調用,沒有服務註冊與發現的概念。但在微服務架構中,我們會按照模組將系統分為多個微服務,而且每個服務我們會做成集群,那這些服務的數量是很大的,這些服務之間可能會被前端直接調用,也有可能互相調用,而且調用關係十分複雜。

每個服務實例的網路位置(IP與埠)資訊,而且這些服務有可能會下線(奔潰),也有可能擴容,那這個時候服務之間相互去記錄這些資訊肯定是非常麻煩的,這個時候我們需要一個服務的治理組件。

在定義服務治理之前,我們可以類比一個場景,就是我們工作大樓的物業,公司入駐這棟大樓,就會在物業處註冊自己的一些資訊,並且交物業費,那這個物業管理類似服務治理。公司相當於一個一個服務,當外面的人想要找到公司提供服務時,可以去物業處了解我們的資訊,然後再找到我們,而本身不需要記錄我們公司的資訊,因為他記不想記住這麼多資訊,而且就算記了,我們公司資訊也可能會改變,比如破產倒閉了,或者又發展壯大換了地方了。我們定時向物業交管理費,一旦我們不交物業費了,那物業就認為我們不在這裡了,那其他人在來找也當做公司不存在了,Eureka的服務註冊與發現就有點類似這種場景。

Spring Cloud 封裝了 Netflix 公司開發的 Eureka 模組來實現服務註冊和發現,Eureka 採用了 C-S 的設計架構。Eureka Server 作為服務註冊功能的伺服器,它是服務註冊中心(物業管理)。而系統中的其他微服務(公司),使用 Eureka 的客戶端連接到 Eureka Server,並維持心跳連接(交物業費)。這樣系統的維護人員就可以通過 Eureka Server 來監控系統中各個微服務是否正常運行。Spring Cloud 的一些其他模組(訪問人員)就可以通過 Eureka Server 來發現系統中的其他微服務,並執行相關的邏輯。

Eureka包含兩個組件:Eureka Server和Eureka Client

Eureka Server提供服務註冊服務各個節點啟動後,會在EurekaServer中進行註冊,這樣EurekaServer中的服務註冊表中將會存儲所有可用服務節點的資訊,服務節點的資訊可以在介面中直觀的看到。

EurekaClient是一個Java客戶端,用於簡化Eureka Server的交互,客戶端同時也具備一個內置的、使用輪詢(round-robin)負載演算法的負載均衡器。在應用啟動後,將會向Eureka Server發送心跳(默認周期為30秒)。如果Eureka Server在多個心跳周期內沒有接收到某個節點的心跳,EurekaServer將會從服務註冊表中把這個服務節點移除(默認90秒)。

三大角色:

  • Eureka Server 提供服務註冊和發現。

  • Service Provider服務提供方將自身服務註冊到Eureka,從而使服務消費方能夠找到。

  • Service Consumer服務消費方從Eureka獲取註冊服務列表,從而能夠消費服務。

三、構建

了解了概念,我們現在來實踐一下,因為還會學習更多的的組件,那麼我們創建工程也是從整體來創建,還要了解的一點是,我們現在做的是微服務項目,那其實這些微服務就是一個個獨立的項目,這些項目可以是完全分開的,跟之前的模組概念是不一樣的。

3.1 創建整體項目

直接先創建一個名為spring-cloud-learn 的文件夾,這個文件夾是為了放各個工程的,然後通過idea 打開這個文件夾,然後在這個文件夾下面創建一個文件夾:spring-cloud-learn-parent

 然後在這個文件夾下增加一個pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">      <modelVersion>4.0.0</modelVersion>      <parent>          <groupId>org.springframework.boot</groupId>          <artifactId>spring-boot-starter-parent</artifactId>          <version>2.0.2.RELEASE</version>      </parent>      <groupId>com.yuanqinnan</groupId>      <artifactId>spring-cloud-learn-parent</artifactId>      <version>1.0.0-SNAPSHOT</version>      <packaging>pom</packaging>      <properties>          <!-- Environment Settings -->          <java.version>1.8</java.version>          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>          <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>          <maven.compiler.source>1.8</maven.compiler.source>          <maven.compiler.target>1.8</maven.compiler.target>          <!-- Spring Settings -->          <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>      </properties>      <dependencyManagement>          <dependencies>                  <dependency>                      <groupId>org.springframework.cloud</groupId>                      <artifactId>spring-cloud-dependencies</artifactId>                      <version>${spring-cloud.version}</version>                      <type>pom</type>                      <scope>import</scope>                  </dependency>          </dependencies>      </dependencyManagement>      <build>          <finalName>spring-cloud-learn-parent</finalName>          <!-- 資源文件配置 -->          <resources>              <resource>                  <directory>src/main/java</directory>                  <excludes>                      <exclude>**/*.java</exclude>                  </excludes>              </resource>              <resource>                  <directory>src/main/resources</directory>              </resource>          </resources>          <plugins>              <plugin>                  <groupId>org.apache.maven.plugins</groupId>                  <artifactId>maven-resources-plugin</artifactId>                  <configuration>                      <delimiters>                          <delimit>$</delimit>                      </delimiters>                  </configuration>              </plugin>          </plugins>      </build>  </project>

將這個項目手動託管成maven項目,這個項目是用於管理依賴的,管理一些公共的依賴,就是一些簡單的依賴,需要主要以的是SpringCloud的版本很讓人頭疼,他不僅有數字,還有字母,這些字母是倫敦地鐵站的開頭字母。

3.2 創建服務註冊中心

主要的項目創建完成之後,我們來創建一個用於服務註冊的項目,創建過程與spring-cloud-learn-parent相同,也是創建一個文件夾spring-cloud-learn-eureka,然後在文件夾下增加pom.xml文件,然後再手動託管

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">      <modelVersion>4.0.0</modelVersion>        <parent>          <groupId>com.yuanqinnan</groupId>          <artifactId>spring-cloud-learn-parent</artifactId>          <version>1.0.0-SNAPSHOT</version>      </parent>      <artifactId>>spring-cloud-learn-eureka</artifactId>      <packaging>jar</packaging>      <dependencies>          <!--eureka-server服務端 -->          <dependency>              <groupId>org.springframework.cloud</groupId>              <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>          </dependency>      </dependencies>    </project>

然後再按照maven的目錄結構來創建目錄

 然後創建一個啟動類,這些都是Springboot項目中的知識,然後再增加一個啟動類,上面增加@EnableEurekaServer

@EnableEurekaServer  @SpringBootApplication  public class EurekaApplication {      public static void main(String[] args) {          SpringApplication.run(EurekaApplication.class, args);      }  }

然後增加配置文件application.yml

spring:    application:      name: spring-cloud-learn-eureka    server:    port: 8761    eureka:    instance:      hostname: localhost    client:      #表示是否將自己註冊到Eureka Server,默認為true。      registerWithEureka: false      #表示是否從Eureka Server獲取註冊資訊,默認為true。      fetchRegistry: false      serviceUrl:        #設置與Eureka Server交互的地址,查詢服務和註冊服務都需要依賴這個地址。默認是http://localhost:8761/eureka ;多個地址可使用 , 分隔        defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

需要注意的配置都寫在上面了,很好理解,這個時候我們可以啟動項目了:

這個時候相當於已經創建好註冊中心了,也就是Eureka Server,那現在再來創建服務提供者

3.3 創建服務提供者

按照上面創建註冊服務的方式我們再創建一個部門服務提供者,pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">      <modelVersion>4.0.0</modelVersion>        <parent>          <groupId>com.yuanqinnan</groupId>          <artifactId>spring-cloud-learn-parent</artifactId>          <version>1.0.0-SNAPSHOT</version>      </parent>        <artifactId>spring-cloud-learn-provider-dept</artifactId>      <packaging>jar</packaging>      <dependencies>          <!-- Spring Boot Begin -->          <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter-test</artifactId>              <scope>test</scope>          </dependency>          <!-- Spring Boot End -->            <!-- Spring Cloud Begin -->          <dependency>              <groupId>org.springframework.cloud</groupId>              <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>          </dependency>          <!-- Spring Cloud End -->      </dependencies>    </project>

配置文件:

spring:    application:      name: spring-cloud-learn-provider-dept    server:    port: 8762    eureka:    client:      serviceUrl:        #服務註冊地址        defaultZone: http://localhost:8761/eureka/

然後創建啟動類:

@EnableEurekaClient  @SpringBootApplication  public class ProviderDeptApplication {      public static void main(String[] args) {          SpringApplication.run(ProviderDeptApplication.class, args);      }  }

啟動時idea會彈出此對話框,選擇第一個這個時候我們可以方便的管理多個啟動服務

@Configuration  public class RestTemplateConfiguration {        @Bean      @LoadBalanced      public RestTemplate restTemplate() {          return new RestTemplate();      }  }

然後我們需要創建一個service,用來請求服務,這裡調用的地方指定了服務名稱,不用管ip 地址與埠

@Service  public class DeptService {      @Autowired      private RestTemplate restTemplate;        public String sayHi(String message) {          //這裡指指定了服務名稱,不用管ip 地址與埠          return restTemplate.getForObject("http://SPRING-CLOUD-LEARN-PROVIDER-DEPT/hi?message=" + message, String.class);      }  }

然後創建一個controller,給前端介面調用

@RestController  public class DeptController {      @Autowired      private DeptService deptService;        @RequestMapping(value = "hi", method = RequestMethod.GET)      public String sayHi(@RequestParam String message) {          return deptService.sayHi(message);      }  }

 啟動成功後,刷新Eureka 服務可以看到服務已經註冊上來了,這裡的紅色提示是指Eureka 服務只部署了一台,不具備高可用,後面我們再來部署集群

 不過這個時候服務者沒有提供確切的服務,添加一個方法

@RestController  public class DeptController {      @Value("${server.port}")      private String port;        @RequestMapping(value = "hi", method = RequestMethod.GET)      public String sayHi(@RequestParam(value = "message") String message) {          return String.format("Hi,your message is : %s i am from port : %s", message, port);      }  }

這裡為了後面顯示集群效果,我們返回埠號

3.4 創建服務消費者

上面的註冊中心和提供者都已建好,那現在來創建一個消費者,我們使用Ribbon,先不用管這個,依然按照上面的創建方式再創建一個工程,pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">      <modelVersion>4.0.0</modelVersion>        <parent>          <groupId>com.yuanqinnan</groupId>          <artifactId>spring-cloud-learn-parent</artifactId>          <version>1.0.0-SNAPSHOT</version>      </parent>        <artifactId>spring-cloud-learn-consumer-dept-ribbon</artifactId>      <packaging>jar</packaging>        <dependencies>          <!-- Spring Boot Begin -->          <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter-web</artifactId>          </dependency>          <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter-tomcat</artifactId>          </dependency>          <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter-test</artifactId>              <scope>test</scope>          </dependency>          <!-- Spring Boot End -->            <!-- Spring Cloud Begin -->          <dependency>              <groupId>org.springframework.cloud</groupId>              <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>          </dependency>          <dependency>              <groupId>org.springframework.cloud</groupId>              <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>          </dependency>          <!-- Spring Cloud End -->      </dependencies>    </project>

配置文件:

spring:    application:      name: spring-cloud-learn-consumer-dept-ribbon  server:    port: 8764    eureka:    client:      serviceUrl:        defaultZone: http://localhost:8761/eureka/

啟動項:

@EnableDiscoveryClient  @SpringBootApplication  public class ConsumerDeptRibbonApplication {      public static void main(String[] args) {          SpringApplication.run(ConsumerDeptRibbonApplication.class, args);      }  }

這個服務我們稍微要給一個配置,因為我們要調用服務提供者,會使用到RestTemplate調用方式,添加一個配置項,這裡面還有一個負載均衡功能,用起來也很簡單

@RestController  public class DeptController {      @Autowired      private DeptService deptService;        @RequestMapping(value = "hi", method = RequestMethod.GET)      public String sayHi(@RequestParam String message) {          return deptService.sayHi(message);      }  }

這樣消費者就算完成了,我們可以訪問這個消費者了,這個消費者調用的是提供者的方法

 

 這樣就已經完成了服務的註冊中心開發,提供者開發及消費者開發,用起來非常簡單,這裡我們看到有@LoadBalanced這個註解,但是服務只有一個,所有沒有效果,我們可以再啟動一個提供者,這裡我們可以直接修改埠號再啟動,只要注意修改一個地方的配置

 

 我們把提供者的埠號改成8763,再啟動一次

 

 這裡啟動了兩個提供者,我們刷新下註冊中心:

 

 增加了一個服務,但是消費者是感受不到的,然後我們在多次刷新消費者,可以看到兩個服務在輪訓調用,這裡我們就實現了負載均衡:

 

 使用這些組件就是這麼簡單,這裡只是做了最簡單的微服務註冊與發現,未做服務中心集群,後面我們將再深入的學習。

 

Tags: