文章目錄
- 前言
- 一、微服務概述
- 什么是微服務?
- 微服務與微服務架構
- 微服務優缺點
- 優點
- 缺點
- 微服務技術棧有那些?
- 二.SpringCloud入門概述
- SpringCloud是什么?
- SpringCloud和SpringBoot的關系
- Dubbo 和 SpringCloud技術選型
- 總結
- SpringCloud能干嘛?
- SpringCloud下載
- 三、環境搭建
- SpringCloud版本選擇
- 創建父工程
- 創建子項目
- springcloud-api
- springcloud-provider-dept-8001
- springcloud-consumer-dept-80
前言
Spring Cloud Netflix 是 Spring Cloud 生態體系中基于 Netflix 開源組件(如 Eureka、Hystrix、Zuul、Ribbon 等)封裝的微服務解決方案,旨在簡化分布式系統(微服務架構)的開發。它整合了 Netflix 多年來在分布式系統領域的實踐經驗,提供了服務注冊與發現、負載均衡、服務熔斷與降級、API 網關等核心功能,幫助開發者快速構建高可用、可擴展的微服務架構。
Spring Cloud Netflix 的幾大組件:
- 服務注冊與發現——Netflix Eureka
- 客戶端負載均衡——Netflix Ribbon
- 聲明式服務調用——Feign
- 斷路器——Netflix Hystrix
- 服務網關——Netflix Zuul
- 分布式配置——Spring Cloud Config
Spring Cloud Netflix中有些組件已經不再更新了,并且現在大部分公司使用的還是Spring Cloud Alibaba。所以除了項目需要,建議直接學習Spring Cloud Alibaba。
一、微服務概述
什么是微服務?
微服務(Microservice Architecture) 關于它的概念很難一言以蔽之。究竟什么是微服務呢?我們在此引用ThoughtWorks 公司的首席科學家 Martin Fowler 于2014年提出的一段話:
- 就目前而言,對于微服務,業界并沒有一個統一的,標準的定義。
- 但通常而言,微服務架構是一種架構模式,或者說是一種架構風格,它提倡將單一的應用程序劃分成一組小的服務,每個服務運行在其獨立的自己的進程內,服務之間互相協調,互相配置,為用戶提供最終價值。
- 服務之間采用輕量級的通信機制(HTTP)互相溝通,每個服務都圍繞著具體的業務進行構建,并且能狗被獨立的部署到生產環境中。
- 另外,應盡量避免統一的,集中式的服務管理機制,對具體的一個服務而言,應該根據業務上下文,選擇合適的語言,工具(Maven)對其進行構建,可以有一個非常輕量級的集中式管理來協調這些服務,可以使用不同的語言來編寫服務,也可以使用不同的數據存儲。
再來從技術維度角度理解下:
- 微服務化的核心就是將傳統的一站式應用,根據業務拆分成一個一個的服務,徹底地去耦合,每一個微服務提供單個業務功能的服務,一個服務做一件事情,從技術角度看就是一種小而獨立的處理過程,類似進程的概念,能夠自行單獨啟動或銷毀,擁有自己獨立的數據庫。
原文:https://martinfowler.com/articles/microservices.html
微服務與微服務架構
微服務
強調的是服務的大小,它關注的是某一個點,是具體解決某一個問題/提供落地對應服務的一個服務應用,狹義的看,可以看作是IDEA中的一個個微服務工程,或者Moudel。IDEA 工具里面使用Maven開發的一個個獨立的小Moudel,它具體是使用SpringBoot開發的一個小模塊,專業的事情交給專業的模塊來做,一個模塊就做著一件事情。強調的是一個個的個體,每個個體完成一個具體的任務或者功能。
微服務架構
一種新的架構形式,Martin Fowler 于2014年提出。
微服務架構是一種架構模式,它提倡將單一應用程序劃分成一組小的服務,服務之間相互協調,互相配合,為用戶提供最終價值。每個服務運行在其獨立的進程中,服務與服務之間采用輕量級的通信機制(如HTTP)互相協作,每個服務都圍繞著具體的業務進行構建,并且能夠被獨立的部署到生產環境中,另外,應盡量避免統一的,集中式的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇合適的語言、工具(如Maven)對其進行構建。
微服務優缺點
優點
- 單一職責原則;
- 每個服務足夠內聚,足夠小,代碼容易理解,這樣能聚焦一個指定的業務功能或業務需求;
- 開發簡單,開發效率高,一個服務可能就是專一的只干一件事;
- 微服務能夠被小團隊單獨開發,這個團隊只需2-5個開發人員組成;
- 微服務是松耦合的,是有功能意義的服務,無論是在開發階段或部署階段都是獨立的;
- 微服務能使用不同的語言開發;
- 易于和第三方集成,微服務允許容易且靈活的方式集成自動部署,通過持續集成工具,如jenkins,Hudson,bamboo;
- 微服務易于被一個開發人員理解,修改和維護,這樣小團隊能夠更關注自己的工作成果,無需通過合作才能體現價值;
- 微服務允許利用和融合最新技術;
- 微服務只是業務邏輯的代碼,不會和HTML,CSS,或其他的界面混合;
- 每個微服務都有自己的存儲能力,可以有自己的數據庫,也可以有統一的數據庫;
缺點
- 開發人員要處理分布式系統的復雜性;
- 多服務運維難度,隨著服務的增加,運維的壓力也在增大;
- 系統部署依賴問題;
- 服務間通信成本問題;
- 數據一致性問題;
- 系統集成測試問題;
- 性能和監控問題;
微服務技術棧有那些?
微服務技術條目 | 落地技術 |
---|---|
服務開發 | SpringBoot、Spring、SpringMVC等 |
服務配置與管理 | Netfix公司的Archaius、阿里的Diamond等 |
服務注冊與發現 | Eureka、Consul、Zookeeper等 |
服務調用 | Rest、PRC、gRPC |
服務熔斷器 | Hystrix、Envoy等 |
負載均衡 | Ribbon、Nginx等 |
服務接口調用(客戶端調用服務的簡化工具) | Fegin等 |
消息隊列 | Kafka、RabbitMQ、ActiveMQ等 |
服務配置中心管理 | SpringCloudConfig、Chef等 |
服務路由(API網關) | Zuul等 |
服務監控 | Zabbix、Nagios、Metrics、Specatator等 |
全鏈路追蹤 | Zipkin、Brave、Dapper等 |
數據流操作開發包 | SpringCloud Stream(封裝與Redis,Rabbit,Kafka等發送接收消息) |
時間消息總棧 | SpringCloud Bus |
服務部署 | Docker、OpenStack、Kubernetes等 |
二.SpringCloud入門概述
SpringCloud是什么?
Spring官網:https://spring.io/
SpringCloud和SpringBoot的關系
- SpringBoot專注于開蘇方便的開發單個個體微服務;
- SpringCloud是關注全局的微服務協調整理治理框架,它將SpringBoot開發的一個個單體微服務,整合并管理起來,為各個微服務之間提供:配置管理、服務發現、斷路器、路由、為代理、事件總棧、全局鎖、決策競選、分布式會話等等集成服務;
- SpringBoot可以離開SpringCloud獨立使用,開發項目,但SpringCloud離不開SpringBoot,屬于依賴關系;
- SpringBoot專注于快速、方便的開發單個個體微服務,SpringCloud關注全局的服務治理框架;
Dubbo 和 SpringCloud技術選型
- 分布式+服務治理Dubbo
目前成熟的互聯網架構,應用服務化拆分 + 消息中間件 - Dubbo 和 SpringCloud對比
可以看一下社區活躍度:
https://github.com/dubbo
https://github.com/spring-cloud
Dubbo | SpringCloud | |
---|---|---|
服務注冊中心 | Zookeeper | Spring Cloud Netfilx Eureka |
服務調用方式 | RPC | REST API |
服務監控 | Dubbo-monitor | Spring Boot Admin |
斷路器 | 不完善 | Spring Cloud Netfilx Hystrix |
服務網關 | 無 | Spring Cloud Netfilx Zuul |
分布式配置 | 無 | Spring Cloud Config |
服務跟蹤 | 無 | Spring Cloud Sleuth |
消息總棧 | 無 | Spring Cloud Bus |
數據流 | 無 | Spring Cloud Stream |
批量任務 | 無 | Spring Cloud Task |
最大區別:Spring Cloud 拋棄了Dubbo的RPC通信,采用的是基于HTTP的REST方式
嚴格來說,這兩種方式各有優劣。雖然從一定程度上來說,后者(Spring Cloud)犧牲了服務調用的性能,但也避免了上面提到的原生RPC帶來的問題。而且REST相比RPC更為靈活,服務提供方和調用方的依賴只依靠一紙契約,不存在代碼級別的強依賴,這個優點在當下強調快速演化的微服務環境下,顯得更加合適。
總結
二者解決的問題域不一樣:Dubbo的定位是一款RPC框架,而SpringCloud的目標是微服務架構下的一站式解決方案。
SpringCloud能干嘛?
- Distributed/versioned configuration 分布式/版本控制配置
- Service registration and discovery 服務注冊與發現
- Routing 路由
- Service-to-service calls 服務到服務的調用
- Load balancing 負載均衡配置
- Circuit Breakers 斷路器
- Distributed messaging 分布式消息管理
- …
SpringCloud下載
官網:http://projects.spring.io/spring-cloud/
SpringCloud沒有采用數字編號的方式命名版本號,而是采用了倫敦地鐵站的名稱,同時根據字母表的順序來對應版本時間順序,比如最早的Realse版本:Angel,第二個Realse版本:Brixton,然后是Camden、Dalston、Edgware,目前最新的是Hoxton SR4 CURRENT GA通用穩定版。
自學參考書:
- SpringCloud Netflix 中文文檔:https://springcloud.cc/spring-cloud-netflix.html
- SpringCloud 中文API文檔(官方文檔翻譯版):https://springcloud.cc/spring-cloud-dalston.html
- SpringCloud中國社區:http://springcloud.cn/
- SpringCloud中文網:https://springcloud.cc
三、環境搭建
SpringCloud版本選擇
spring-boot-starter-parent | spring-cloud-dependencles | ||
---|---|---|---|
版本號 | 發布日期 | 版本號 | 發布日期 |
1.5.2.RELEASE | 2017-03 | Dalston.RC1 | 2017-x |
1.5.9.RELEASE | 2017-11 | Edgware.RELEASE | 2017-11 |
1.5.16.RELEASE | 2018-04 | Edgware.SR5 | 2018-10 |
1.5.20.RELEASE | 2018-09 | Edgware.SR5 | 2018-10 |
2.0.2.RELEASE | 2018-05 | Fomchiey.BULD-SNAPSHOT | 2018-x |
2.0.6.RELEASE | 2018-10 | Fomchiey-SR2 | 2018-10 |
2.1.4.RELEASE | 2019-04 | Greenwich.SR1 | 2019-03 |
創建父工程
- 新建父工程項目springcloud-netflix,切記Packageing是pom模式
- 主要是定義POM文件,將后續各個子模塊公用的jar包等統一提取出來,類似一個抽象父類
完整的pom
<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><groupId>com.qf</groupId><artifactId>springcloud-netflix</artifactId><version>1.0-SNAPSHOT</version><!--打包方式 pom--><packaging>pom</packaging><name>springcloud-netflix</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><junit.version>4.12</junit.version><log4j.version>1.2.17</log4j.version><lombok.version>1.16.18</lombok.version></properties><dependencyManagement><dependencies><!--springCloud的依賴--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Greenwich.SR1</version><type>pom</type><scope>import</scope></dependency><!--SpringBoot--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><type>pom</type><scope>import</scope></dependency><!--數據庫--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version></dependency><!--SpringBoot 啟動器--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency><!--日志測試~--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>1.2.3</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency></dependencies></dependencyManagement>
</project>
創建子項目
創建子項目springcloud-api(公共模塊)、springcloud-provider-dept-8001(生產者)、springcloud-consumer-dept-80(消費者)
在父項目上右鍵創建
springcloud-api
pom.xml
<parent><groupId>com.qf</groupId><artifactId>springcloud-netflix</artifactId><version>1.0-SNAPSHOT</version>
</parent><artifactId>springcloud-api</artifactId>
<packaging>jar</packaging><name>springcloud-api</name>
<url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
</dependencies>
實體類
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;@Data
@NoArgsConstructor
@Accessors(chain = true)
public class Dept implements Serializable {private Long deptno;private String dname;private String db_source;
}
springcloud-provider-dept-8001
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.qf</groupId><artifactId>springcloud-netflix</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>springcloud-provider-dept-8001</artifactId><packaging>jar</packaging><name>springcloud-provider-dept-8001</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>com.qf</groupId><artifactId>springcloud-api</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-test</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--jetty--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId></dependency><!--熱部署工具--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency></dependencies>
</project>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><!--開啟二級緩存--><setting name="cacheEnabled" value="true"/></settings>
</configuration>
application.yml
server:port: 8001servlet:context-path: /
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8username: rootpassword: 123456driver-class-name: org.gjt.mm.mysql.Drivermybatis:type-aliases-package: com.qf.pojoconfig-location: classpath:mybatis-config.xmlmapper-locations: classpath:mapper/*.xml
啟動類
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DeptProvider {public static void main(String[] args) {SpringApplication.run(DeptProvider.class,args);}
}
mapper
import com.qf.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;@Mapper
@Repository
public interface DeptDao {public boolean addDept(Dept dept);public Dept queryById(@Param("deptno") Long deptno);public List<Dept> queryAll();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.dao.DeptDao"><insert id="addDept" parameterType="Dept">insert into dept(dname,db_source)values(#{dname},DATABASE());</insert><select id="queryById" resultType="com.qf.pojo.Dept">select * from dept where deptno = #{deptno};</select><select id="queryAll" resultType="com.qf.pojo.Dept">select * from dept;</select>
</mapper>
Service
import com.qf.pojo.Dept;
import org.apache.ibatis.annotations.Param;
import java.util.List;public interface DeptService {public boolean addDept(Dept dept);public Dept queryById(@Param("deptno") Long deptno);public List<Dept> queryAll();}
import com.qf.dao.DeptDao;
import com.qf.pojo.Dept;
import com.qf.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;@Service
public class DeptServiceImpl implements DeptService {@Autowiredprivate DeptDao deptDao;@Overridepublic boolean addDept(Dept dept) {return deptDao.addDept(dept);}@Overridepublic Dept queryById(Long deptno) {return deptDao.queryById(deptno);}@Overridepublic List<Dept> queryAll() {return deptDao.queryAll();}
}
Controller
import com.qf.pojo.Dept;
import com.qf.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;@RestController
public class DeptController {@Autowiredprivate DeptService deptService;@PostMapping("/dept/add")public boolean addDept(Dept dept) {return deptService.addDept(dept);}@GetMapping("/dept/get/{id}")public Dept queryById(@PathVariable("id") Long id) {return deptService.queryById(id);}@GetMapping("/dept/list")public List<Dept> queryAll() {return deptService.queryAll();}
}
springcloud-consumer-dept-80
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.qf</groupId><artifactId>springcloud-netflix</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>springcloud-consumer-dept-80</artifactId><packaging>jar</packaging><name>springcloud-consumer-dept-80</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>com.qf</groupId><artifactId>springcloud-api</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--熱部署工具--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId></dependency></dependencies>
</project>
application.yml
server:port: 80servlet:context-path: /
啟動類
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DeptConsumer {public static void main(String[] args) {SpringApplication.run(DeptConsumer.class,args);}
}
配置類
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;@Configuration
public class ConfigBean {//@Configuration -- spring applicationContext.xml//配置負載均衡實現RestTemplate// IRule// RoundRobinRule 輪詢// RandomRule 隨機// AvailabilityFilteringRule : 會先過濾掉,跳閘,訪問故障的服務~,對剩下的進行輪詢~// RetryRule : 會先按照輪詢獲取服務~,如果服務獲取失敗,則會在指定的時間內進行,重試@Beanpublic RestTemplate getRestTemplate(){return new RestTemplate();}
}
Controller
import com.qf.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;@RestController
public class DeptConsumerController {/*** 理解:消費者,不應該有service層~* RestTemplate .... 供我們直接調用就可以了! 注冊到Spring中* (地址:url, 實體:Map ,Class<T> responseType)* <p>* 提供多種便捷訪問遠程http服務的方法,簡單的Restful服務模板~*/@Autowiredprivate RestTemplate restTemplate;/*** 服務提供方地址前綴* <p>* Ribbon:我們這里的地址,應該是一個變量,通過服務名來訪問*/private static final String REST_URL_PREFIX = "http://localhost:8001";/*** 消費方添加部門信息* @param dept* @return*/@RequestMapping("/consumer/dept/add")public boolean add(Dept dept) {// postForObject(服務提供方地址(接口),參數實體,返回類型.class)return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);}/*** 消費方根據id查詢部門信息* @param id* @return*/@RequestMapping("/consumer/dept/get/{id}")public Dept get(@PathVariable("id") Long id) {// getForObject(服務提供方地址(接口),返回類型.class)return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);}/*** 消費方查詢部門信息列表* @return*/@RequestMapping("/consumer/dept/list")public List<Dept> list() {return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);}
}
Spring Cloud Netflix學習筆記01
Spring Cloud Netflix學習筆記02-Eureka
Spring Cloud Netflix學習筆記03-Ribbon
Spring Cloud Netflix學習筆記04-Feign