本章主要介紹了微服務以及微服務所用到的部分組件,完成了刪除小節時刪除雲端視頻(根據視頻id)和刪除課程時刪除雲端視頻(根據課程id)
一、什么是微服務 二、Nacos 三、Nacos服務注冊
四、Feign 五、熔斷器 六、刪除課程時刪除視頻
一、什么是微服務
1、微服務的由來
2、為什么需要微服務
3、微服務與單體架構區別
(1)單體架構所有的模塊全都耦合在一塊,代碼量大,維護困難。
微服務每個模塊就相當於一個單獨的項目,代碼量明顯減少,遇到問題也相對來說比較好解決。
(2)單體架構所有的模塊都共用一個數據庫,存儲方式比較單一。
微服務每個模塊都可以使用不同的存儲方式(比如有的用redis,有的用mysql等),數據庫也是單個模塊對應自己的數據庫。
(3)單體架構所有的模塊開發所使用的技術一樣。
微服務每個模塊都可以使用不同的開發技術,開發模式更靈活。
4、微服務本質
(1)微服務,關鍵其實不僅僅是微服務本身,而是系統要提供一套基礎的架構,這種架構使得微服務可以獨立的部署、運行、升級,不僅如此,這個系統架構還讓微服務與微服務之間在結構上“松耦合”,而在功能上則表現為一個統一的整體。這種所謂的“統一的整體”表現出來的是統一風格的界面,統一的權限管理,統一的安全策略,統一的上線過程,統一的日志和審計方法,統一的調度方式,統一的訪問入口等等。
(2)微服務的目的是有效的拆分應用,實現敏捷開發和部署 。
(3)微服務提倡的理念團隊間應該是 inter-operate, not integrate 。inter-operate是定義好系統的邊界和接口,在一個團隊內全棧,讓團隊自治,原因就是因為如果團隊按照這樣的方式組建,將溝通的成本維持在系統內部,每個子系統就會更加內聚,彼此的依賴耦合能變弱,跨系統的溝通成本也就能降低。
5、什么樣的項目適合微服務
微服務可以按照業務功能本身的獨立性來划分,如果系統提供的業務是非常底層的,如:操作系統內核、存儲系統、網絡系統、數據庫系統等等,這類系統都偏底層,功能和功能之間有着緊密的配合關系,如果強制拆分為較小的服務單元,會讓集成工作量急劇上升,並且這種人為的切割無法帶來業務上的真正的隔離,所以無法做到獨立部署和運行,也就不適合做成微服務了。
6、微服務開發框架
目前微服務的開發框架,最常用的有以下四個:
Spring Cloud:http://projects.spring.io/spring-cloud(現在非常流行的微服務架構)
Dubbo:http://dubbo.io
Dropwizard:http://www.dropwizard.io (關注單個微服務的開發)
Consul、etcd&etc.(微服務的模塊)
7、什么是Spring Cloud
8、Spring Cloud和Spring Boot是什么關系
9、Spring Cloud相關基礎服務組件
10、Spring Cloud的版本
Spring Cloud並沒有熟悉的數字版本號,而是對應一個開發代號。
開發代號看似沒有什么規律,但實際上首字母是有順序的,比如:Dalston版本,我們可以簡稱 D 版本,對應的 Edgware 版本我們可以簡稱 E 版本。
小版本
Spring Cloud 小版本分為:
SNAPSHOT: 快照版本,隨時可能修改
M: MileStone,M1表示第1個里程碑版本,一般同時標注PRE,表示預覽版版。
SR: Service Release,SR1表示第1個正式版本,一般同時標注GA:(GenerallyAvailable),表示穩定版本。
二、Nacos
1、基本概念
(1)Nacos 是阿里巴巴推出來的一個新開源項目,是一個更易於構建雲原生應用的動態服務發現、配置管理和服務管理平台。Nacos 致力於幫助您發現、配置和管理微服務。Nacos 提供了一組簡單易用的特性集,幫助您快速實現動態服務發現、服務配置、服務元數據及流量管理。Nacos 幫助您更敏捷和容易地構建、交付和管理微服務平台。 Nacos 是構建以“服務”為中心的現代應用架構 (例如微服務范式、雲原生范式) 的服務基礎設施。Nacos即一個注冊中心,相當於房產中介,服務都需要在nacos注冊,再進行不同服務的方法調用。
(2)常見的注冊中心:
1. Eureka(原生,2.0遇到性能瓶頸,停止維護)
2. Zookeeper(支持,專業的獨立產品。例如:dubbo)
3. Consul(原生,GO語言開發)
4. Nacos
相對於 Spring Cloud Eureka 來說,Nacos 更強大。Nacos = Spring Cloud Eureka + Spring Cloud Config
Nacos 可以與 Spring, Spring Boot, Spring Cloud 集成,並能代替 Spring Cloud Eureka, Spring Cloud Config
- 通過 Nacos Server 和 spring-cloud-starter-alibaba-nacos-discovery 實現服務的注冊與發現。
(3)Nacos是以服務為主要服務對象的中間件,Nacos支持所有主流的服務發現、配置和管理。
Nacos主要提供以下四大功能:
1. 服務發現和服務健康監測
2. 動態配置服務
3. 動態DNS服務
4. 服務及其元數據管理
2、Nacos下載和安裝
下載地址:https://github.com/alibaba/nacos/releases
下載版本:nacos-server-1.1.4.tar.gz或nacos-server-1.1.4.zip,解壓任意目錄即可
(2)啟動nacos服務
- Linux/Unix/Mac
啟動命令(standalone代表着單機模式運行,非集群模式)
啟動命令:sh startup.sh -m standalone
- Windows
啟動命令:cmd startup.cmd 或者雙擊startup.cmd運行文件。
訪問:http://localhost:8848/nacos
用戶名密碼:nacos/nacos
三、服務注冊(service_edu為例)
1、在service模塊配置pom
<!--服務注冊--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
2、添加服務配置信息
# nacos服務地址 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
3、添加Nacos客戶端注解
@EnableDiscoveryClient
4、啟動客戶端微服務
四、Feign
- Feign是Netflix開發的聲明式、模板化的HTTP客戶端, Feign可以幫助我們更快捷、優雅地調用HTTP API。
- Feign支持多種注解,例如Feign自帶的注解或者JAX-RS注解等。
- Spring Cloud對Feign進行了增強,使Feign支持了Spring MVC注解,並整合了Ribbon和Eureka,從而讓Feign的使用更加方便。
- Spring Cloud Feign是基於Netflix feign實現,整合了Spring Cloud Ribbon和Spring Cloud Hystrix,除了提供這兩者的強大功能外,還提供了一種聲明式的Web服務客戶端定義的方式。
- Spring Cloud Feign幫助我們定義和實現依賴服務接口的定義。在Spring Cloud feign的實現下,只需要創建一個接口並用注解方式配置它,即可完成服務提供方的接口綁定,簡化了在使用Spring Cloud Ribbon時自行封裝服務調用客戶端的開發量。
1、需求(實現服務調用)
2、在service模塊添加pom依賴
<!--服務調用--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
3、在調用端的啟動類添加注解
@EnableFeignClients
4、創建包和接口
@FeignClient(name = "service-vod")//調用的服務器名稱 @Component public interface VodClient { //定義調用的方法路徑 //根據視頻id刪除阿里雲視頻 @DeleteMapping("/eduvod/video/removeAlyVideo/{id}") public R removeAlyVideo(@PathVariable("id") String id); }
5、調用微服務
在調用端的EduVideoController中調用client中的方法
//刪除小節,刪除對應阿里雲視頻 @DeleteMapping("{id}") public R deleteVideo(@PathVariable String id){ //根據小節id獲取視頻id,調用方法實現視頻刪除 EduVideo eduVideo = videoService.getById(id); String videoSourceId = eduVideo.getVideoSourceId(); //判斷小節里是否有視頻id if (!StringUtils.isEmpty(videoSourceId)) { //根據視頻id,遠程調用實現視頻刪除 R result = vodClient.removeAlyVideo(videoSourceId); if (result.getCode() == 20001) { throw new GuliException(20001,"刪除視頻失敗,熔斷器。。"); } } //刪除小節 videoService.removeById(id); return R.ok(); }
五、熔斷器
5.1、Hystrix基本概念
5.1.1、Spring Cloud調用接口過程
Spring Cloud 在接口調用上,大致會經過如下幾個組件配合:
Feign
----->Hystrix
—>Ribbon
—>Http Client
(apache http components 或者 Okhttp)
具體交互流程上,如下圖所示:
@FeignClient
注解修飾的接口時,在框架內部,將請求轉換成Feign的請求實例
feign.Request
,交由Feign框架處理。
(2)Feign :轉化請求Feign是一個http請求調用的輕量級框架,可以以Java接口注解的方式調用Http請求,封裝了Http調用流程。
(3)Hystrix:熔斷處理機制 Feign的調用關系,會被Hystrix代理攔截,對每一個Feign調用請求,Hystrix都會將其包裝成HystrixCommand
,參與Hystrix的流控和熔斷規則。如果請求判斷需要熔斷,則Hystrix直接熔斷,拋出異常或者使用FallbackFactory
返回熔斷Fallback
結果;如果通過,則將調用請求傳遞給Ribbon
組件。
(4)Ribbon:服務地址選擇 當請求傳遞到Ribbon
之后,Ribbon
會根據自身維護的服務列表,根據服務的服務質量,如平均響應時間,Load等,結合特定的規則,從列表中挑選合適的服務實例,選擇好機器之后,然后將機器實例的信息請求傳遞給Http Client
客戶端,HttpClient
客戶端來執行真正的Http接口調用;
(5)HttpClient :Http客戶端,真正執行Http調用根據上層Ribbon
傳遞過來的請求,已經指定了服務地址,則HttpClient開始執行真正的Http請求
簡單理解:
- 接口化請求調用:設置生產者服務名稱和接口地址
@FeignClient("service-vod")
@DeleteMapping("/eduvod/video/removeAlyVideo/{id}")
- Feign:根據服務名稱service-vod找到接口地址做調用
- Hystrix:熔斷器,當生產者服務器掛了,熔斷器發生熔斷機制,不對生產者服務器進行請求
- Ribbon:對請求進行負載均衡,例如生產者中有一個集群,則會將請求分擔到不同服務器中
- HttpClient :分擔之后則發送請求,進行接口調用
2、Hystrix概念
Hystrix 是一個供分布式系統使用,提供延遲和容錯功能,保證復雜的分布系統在面臨不可避免的失敗時,仍能有其彈性。
比如系統中有很多服務,當某些服務不穩定的時候,使用這些服務的用戶線程將會阻塞,如果沒有隔離機制,系統隨時就有可能會掛掉,從而帶來很大的風險。SpringCloud使用Hystrix組件提供斷路器、資源隔離與自我修復功能。下圖表示服務B觸發了斷路器,阻止了級聯失敗
5.2、feign結合Hystrix使用
改造service-edu模塊
5.2.1、在service的pom中添加依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <!--hystrix依賴,主要是用 @HystrixCommand --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!--服務注冊--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--服務調用--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
5.2.2、在配置文件中添加hystrix配置
#開啟熔斷機制 feign.hystrix.enabled=true # 設置hystrix超時時間,默認1000ms hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000
5.2.3、在service-edu的client包里面創建熔斷器的實現類
@Component public class VodFileDegradeFeignClient implements VodClient { @Override public R removeVideo(String videoId) { return R.error().message("time out"); } @Override public R removeVideoList(List videoIdList) { return R.error().message("time out"); } }
5.2.4、修改VodClient接口的注解
主要加上fallback參數,調用熔斷器的實現類
@FeignClient(name = "service-vod", fallback = VodFileDegradeFeignClient.class) @Component public interface VodClient { @DeleteMapping(value = "/eduvod/vod/{videoId}") public R removeVideo(@PathVariable("videoId") String videoId); @DeleteMapping(value = "/eduvod/vod/delete-batch") public R removeVideoList(@RequestParam("videoIdList") List videoIdList); }
六、刪除課程時刪除視頻
1、需求
2、在service-vod模塊的VodController創建刪除多個視頻接口
//刪除多個阿里雲視頻的方法
//參數為多個視頻id List videoIdList
@DeleteMapping("delete-batch")
public R removeVideoList(
@ApiParam(name = "videoIdList", value = "雲端視頻id", required = true)
@RequestParam("videoIdList") List<String> videoIdList){
vodService.removeVideoList(videoIdList);
return R.ok().message("視頻刪除成功");
}
3、VodService接口
void removeVideoList(List videoIdList);
4、VodServiceImpl實現類
@Override
public void removeVideoList(List videoIdList) {
try {
//初始化對象
DefaultAcsClient client = InitVodClient.initVodClient(ConstantVodUtils.ACCESS_KEY_ID, ConstantVodUtils.ACCESS_KEY_SECRET);
//創建刪除視頻request對象
DeleteVideoRequest request = new DeleteVideoRequest();
//VideoList
String videoIds = org.apache.commons.lang.StringUtils.join(videoIdList.toArray(), ",");
//向request設置視頻id
request.setVideoIds(videoIds);
//調用初始化對象的方法實現刪除
client.getAcsResponse(request);
}catch (Exception e){
e.printStackTrace();
throw new GuliException(20001,"刪除視頻失敗");
}
}
5、在service-edu調用service-vod接口實現刪除多個視頻功能
5.1 在service-edu的VodClient接口添加service-vod中的接口定義
//定義刪除多個視頻的方法 @DeleteMapping("/eduvod/video/delete-batch") public R removeVideoList(@RequestParam("videoIdList") List<String> videoIdList);
5.2 修改根據課程id刪除小節方法
//根據課程id刪除小節 @Override public void removeVideoByCourseId(String courseId) { //1 根據課程id查詢出所有的視頻id QueryWrapper<EduVideo> wrapperVideo = new QueryWrapper<>(); wrapperVideo.eq("course_id",courseId); wrapperVideo.select("video_source_id"); List<EduVideo> videoList = baseMapper.selectList(wrapperVideo); //List<EduVideo>變成List<String> List<String> videoIds = new ArrayList<>(); for (int i = 0; i < videoList.size(); i++) { EduVideo eduVideo = videoList.get(i); String videoSourceId = eduVideo.getVideoSourceId(); if(!StringUtils.isEmpty(videoSourceId)){ videoIds.add(videoSourceId); } } //根據多個視頻id刪除多個視頻 if(videoIds.size()>0){ vodClient.removeVideoList(videoIds); } QueryWrapper<EduVideo> wrapper = new QueryWrapper<>(); wrapper.eq("course_id",courseId); baseMapper.delete(wrapper); }