離上一篇微服務的基本概念已經過去了幾個月,在寫那篇博客之前,自己還並未真正的使用微服務架構,很多理解還存在概念上。後面換了公司,新公司既用了SpringCloud也用了Dubbo+Zookeeper,就像上一篇文章說的,當一個服務是面向外部或者是直接提供給前端調用的,那麼就使用SpringCloud ...
離上一篇微服務的基本概念已經過去了幾個月,在寫那篇博客之前,自己還並未真正的使用微服務架構,很多理解還存在概念上。後面換了公司,新公司既用了SpringCloud也用了Dubbo+Zookeeper,就像上一篇文章說的,當一個服務是面向外部或者是直接提供給前端調用的,那麼就使用SpringCloud,而一些內部公用的(如發送簡訊),就使用Dubbo+Zookeeper,因為他在內部調用更像調用接一個介面,效率也會比較高,而一些模塊型的功能,我們則使用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); } }
這裡為了後面顯示集群效果,我們返回埠號
<?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,再啟動一次
這裡啟動了兩個提供者,我們刷新下註冊中心:
增加了一個服務,但是消費者是感受不到的,然後我們在多次刷新消費者,可以看到兩個服務在輪訓調用,這裡我們就實現了負載均衡:
使用這些組件就是這麼簡單,這裡只是做了最簡單的微服務註冊與發現,未做服務中心集群,後面我們將再深入的學習。