從傳統架構(單體應用) 到 分佈式架構(以項目進行拆分) 到 SOA架構(面向服務架構) 到 微服務架構html
1) 傳統架構:前端
其實就是SSH或者SSM,屬於單點應用,把整個業務模塊都會在一個項目中進行開發,分爲MVC架構,會拆分紅業務邏輯層、業務邏輯層、數據庫訪問層java
缺點:通常只適合於一我的或者適合小團隊開發,耦合度過高,一旦某個模塊致使服務不可用,可能會影響到項目node
2) 分佈式架構mysql
實際上是基於傳統架構演變過來的nginx
分佈式架構基於傳統架構演變過來的,將傳統的項目以項目模塊進行拆分紅n多個子項目,每一個項目都有本身獨立的數據庫等git
總結:分佈式架構與傳統架構區別:項目粒度分的更加細,耦合度下降web
區分是不是分佈式架構在於打的jar包或者war是不是多個jvm項目通信redis
3) SOA架構與微服務架構算法
SOA架構也是基於分佈式架構演變過來的。SOA架構表明面向服務架構,俗稱服務化,能夠理解爲面向於業務邏輯層開發。將共同的業務代碼進行抽取出來,提供給其餘接口進行調用。服務與服務之間通信採用rpc遠程調用技術。
服務概念:將共同的業務邏輯進行拆分,拆分紅獨立的項目進行部署,沒有視圖層,固然也能夠理解爲接口
SOA架構特色:底層基於SOAP(Http協議+XML:如webservice)或者ESB(消息總線)實現,底層使用HTTP或者Https協議+重量級XML數據交換格式進行通信
在後面微服務中,以json格式代替xml
rpc遠程調用技術框架:如httpClient、springCloud、dubbo、grpc
核心底層socket技術或者netty實現
4) 微服務架構產生的緣由
4.1) 首先微服務架構基於SOA架構演變過來的
SOA架構缺點:
一、依賴於中心化服務發現機制
二、由於SOA架構採用SOAP協議(Http+XM),由於XML傳輸協議比較佔用寬帶,整個XML報文中有很是大冗餘數據,因此在微服務架構中以json輕量級方式代替xml報文傳輸。
三、服務管理很是混亂,缺乏服務管理和治理設施不完善
4.2) 微服務架構模式
微服務架構是從SOA架構演變過來的,比SOA架構上粒度更加精細,讓專業的人作專業的事,目的是提升效率。每一個服務與服務之間是互不影響,每一個服務必須獨立部署(獨立數據庫、獨立redis等),微服務架構更加體現輕量級,採用restful風格提升API,也就是Http協議+JSON格式進行傳輸,更加輕巧更加適合於互聯網公司敏捷開發、快速迭代產品。
詳細分析:
微服務架構從SOA架構演變過來
服務化功能自己從SOA這層已經實現,只不過微服務架構在單獨服務層有進行細分服務服務層有進行細分服務
如會員服務:會員服務在微服務有進行細分爲:
4.3) 微服務架構與SOA架構區別
一、爲服務架構基於SOA架構演變過來,繼承SOA架構的優勢,在微服務架構中去除了SOA架構中的ESB消息總線,採用http+json(restful)進行傳輸
二、微服務架構比SOA架構粒度會更加精細,讓專業的人去作專業的事,目的提升效率,每一個服務於服務之間互不影響,微服務架構中,每一個服務必須獨立部署,微服務架構更加輕巧,輕量級
三、SOA架構中可能數據庫存儲會發生共享,微服務強調每一個服務都是單獨數據庫,保證每一個服務與服務之間互不影響
四、項目體現特徵服務架構比SOA架構更加適合於互聯網公司敏捷,快速迭代版本,由於粒度很是精細
一、爲何要使用springCloud
springCloud是目前來講,是一套比較完善的爲微服務解決方案框架。它不像其餘rpc遠程調用框架,只是解決了某個微服務中的問題。能夠把springCloud理解爲一條龍微服務解決方案。微服務全家桶--SpringCloud比較完善
微服務中:
分佈式配置中心
分佈式鎖
分佈式服務治理
分佈式任務調用平臺
總結:
由於SpringCloud出現,對微服務 技術提供了很是大的幫助,以往內SpringCloud提供了一套完整的微服務解決方案,不像其餘框架只是解決了微服務中某個問題
服務治理:阿里巴巴開源的Dubbo和噹噹網在其基礎上擴展的Dubbox、Eureka、Apache的Consul等
分佈式配置中心:百度的disconf、Netfix的Archaius、360的Qconf、SpringCloud、攜程的阿波羅等
分佈式任務:xxl-job、elastic-job、springCloud的task等
服務跟蹤:京東的hyra、springCloud的sleuth等
二、什麼是SpringCloud
SpringCloud是基於SpringBoot基礎上開發的微服務框架,SpringCloud是一套目前很是完整的微服務解決方案框架,其內容包含服務治理、註冊中心、配置管理、斷路器、智能路由、微代理、控制總線、全局鎖、分佈式會話等
springCloud包含衆多的子項目
SpringCloud config 分佈式配置中心
SpringCloud netflix 核心組件
Hystrix:服務保護框架
Ribbon:客戶端負載均衡器
feign:基於ribbo和hystrix的聲明式服務調用組件
Zuul:網關組件,提供智能路由 、訪問過濾等功能
SpringCloud曾經採用Eureka,如今已經閉源了
一、服務治理SpringCloud eureka
1) 什麼是服務治理
在傳統的rpc遠程調用框架中,管理每一個服務與服務之間依賴關係比較複雜,管理比較複雜,因此須要使用服務治理,管理服務於服務之間依賴關係,能夠實現服務調用、負載均衡、容錯等,實現服務發現與註冊。
下圖能夠解釋使用註冊中心的理由:
2)什麼是服務註冊與發現
在服務註冊與發現中,有一個註冊中心,當服務器啓動的時候,會把當前本身服務器的信息 好比 服務地址通信地址等以別名方式註冊到註冊中心上。另外一方(消費者|服務提供者),以該別名的方式去註冊中心上獲取到實際的服務通信地址,而後再實現本地rpc調用
RPC遠程調用框架核心設計思想:在於註冊中心,由於使用註冊中心管理每一個服務與服務之間的一個依賴關係(服務治理概念)
在任何rpc遠程框架中,都會有一個註冊中心(存放服務地址相關信息(接口地址))
如下是搭建一套基於Eureka做爲註冊中心的demo,項目層次圖以下
3) 搭建註冊中心
3.1) 新建maven工程
3.2) 引入maven
注意2.0版本區別1版本在於eureka命名更加規範(1是spring-cloud-starter-eureka-server)
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <!-- 管理依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!--SpringCloud eureka-server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <!-- 注意: 這裏必需要添加, 否者各類依賴有問題 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
3.3) 新建配置文件application.yml
###服務端口號 server: port: 8100 ###eureka 基本信息配置 eureka: instance: ###註冊到eurekaip地址(註冊中心ip) hostname: 127.0.0.1 client: serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #多個用,號隔開 ###由於本身是爲註冊中心,不須要本身註冊本身(集羣須要設置爲true) register-with-eureka: false ###由於本身是爲註冊中心,不須要檢索服務 fetch-registry: false
3.4) 編寫啓動類
@SpringBootApplication @EnableEurekaServer //開啓EurekaServer服務 開啓註冊中心 public class AppEureka { public static void main(String[] args) { SpringApplication.run(AppEureka.class,args); } }
3.5) 訪問Eureka註冊中心管理平臺,經過本機IP+ 配置的端口號:這裏是http://localhost:8100
4) 搭建服務提供者
4.1) 新建生產者服務(service層)子項目
4.2) 添加pom依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <!-- 管理依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- SpringBoot整合Web組件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot整合eureka客戶端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <!-- 注意: 這裏必需要添加, 否者各類依賴有問題 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
4.3) 添加application.yml配置文件
###會員項目服務啓動端口號 server: port: 8000 ###服務名稱(服務註冊到eureka名稱,如serviceId) spring: application: name: app-producer ###服務註冊到eureka地址,8100爲註冊中心端口號 eureka: client: service-url: defaultZone: http://localhost:8100/eureka
4.3) 編輯須要註冊的服務接口
@RestController public class MemberAPIController { @Value("${server.port}") private String serverPort; //用於區分集羣 @RequestMapping("/getMember") public String getMember() { return "this is Mr_佳先森的會員服務,端口號爲:"+serverPort; } }
4.4) 編寫啓動類
@SpringBootApplication @EnableEurekaClient //將當前服務註冊到eureka上 public class AppMember { public static void main(String[] args) { SpringApplication.run(AppMember.class, args); } }
4.5)運行註冊中心,再運行生成者,此時經過訪問控制檯,能看到註冊後的信息
注意:這裏的生產者名是根據生產者配置文件的配置的別名而定(這裏是app-producer)
5) 編寫消費者
5.1) 新建消費者子項目
5.2) 配置pom依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <!-- 管理依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- SpringBoot整合Web組件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot整合eureka客戶端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <!-- 注意: 這裏必需要添加, 否者各類依賴有問題 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
5.3) 編寫消費者配置文件和消費方法
###訂單服務(消費者)啓動端口號 server: port: 8001 ###服務名稱(服務註冊到eureka名稱) spring: application: name: app-order ###服務註冊到eureka地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka,
@RestController public class OrderController { /** * RestTemplate由SpringBoot web組件提供 默認整合ribbo負載均衡器 * rest方式底層是採用httpClient技術 */ @Autowired private RestTemplate restTemplate; /** * 訂單服務調用會員服務: * 在springCloud當中,有兩種方式調用生產的消息 * 1.rest方式 二、fegin(SpringCloud方式) * @return */ @RequestMapping("/getOrder") public String getOrderInfo() { //採用服務別名(生產者)+方法映射id: String url = "http://app-producer/getMember"; String result = restTemplate.getForObject(url, String.class); System.out.println("訂單服務調用會員服務是_:"+result); return result; } }
5.4) 編寫啓動類
基於這裏採用的是ribbon的RestTemplate方式調用生產者,須要啓動類中註冊該類到容器中,並添加@LoadBalanced註解(注意區別於nginx:nginx負載均衡主要
基於服務器(配合一些服務(如mysql,tomcat等)),而ribbon主要基於本地(主要用於微服務),咱們能夠經過更改生產者端口號構建集羣環境,不斷調用消費者@LoadBalanced 註解能實現負載均衡)
@SpringBootApplication @EnableEurekaClient public class AppOrder { public static void main(String[] args) { SpringApplication.run(AppOrder.class, args); } //解決RestTemplate找不到問題:應該把restTemplate註冊到springboot容器中 @Bean @LoadBalanced //開啓負載均衡功能,而且支持別名方式調用生產者(不然沒法使用別名) RestTemplate restTemplage() { return new RestTemplate(); } }
5.5) 在控制檯能夠看到消費者信息並調用服務
6) Eureka集羣高可用環境搭建
6.1)、微服務rpc遠程服務調用最核心的是什麼?
註冊中心。若是註冊中心由於某種緣由出現故障,有可能致使整個爲服務環境不可用
解決辦法:搭建註冊中心集羣--大型互聯網公司註冊中心都是集羣版本。
6.2) 搭建eureka集羣環境
思路:Eureka高可用其實是將本身做爲服務向其餘服務註冊中心註冊本身,這樣就能夠造成一組相互做用的服務註冊中心,從而實現服務清單 的互相調用,達到高可用的效果
6.3) 新建兩個註冊中心項目,參考以上的作法,只是配置文件會發生一點變化
這裏的環境是擁有一臺8100的註冊中心和9100的註冊中心,他們相互註冊
8100:application.yml
server: port: 8100 ##定義服務名稱:集羣環境服務名稱得相同 spring: application: name: springCloud-eureka eureka: instance: hostname: 127.0.0.1 client: serviceUrl: defaultZone: http://${eureka.instance.hostname}:9100/eureka/ register-with-eureka: true fetch-registry: true
9100:application.yml
server: port: 9100 ##定義服務名稱:集羣環境服務名稱得相同 spring: application: name: springCloud-eureka eureka: instance: hostname: 127.0.0.1 client: serviceUrl: defaultZone: http://${eureka.instance.hostname}:8100/eureka/ register-with-eureka: true fetch-registry: true
6.4) 啓動兩個啓動類,第一個可能會報以下錯誤,是由於第二個註冊中心未啓動,從而報沒法識別服務器
6.5) 同時訪問兩個管理平臺能夠看到相互註冊的信息
6.6) 客戶端鏈接註冊中心
此時生成者和消費者只須要分別將服務註冊到兩個註冊中心中去和從兩個註冊中心拿服務
如生成者
###會員項目服務啓動端口號 server: port: 8000 ###服務名稱(服務註冊到eureka名稱,如serviceId) spring: application: name: app-producer ###服務註冊到eureka地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka,http://localhost:9100/eureka ###由於該應用爲註冊中心,不會註冊本身 register-with-eureka: true ###是否須要從eureka上獲取註冊信息 fetch-registry: true
6.6) 啓動
從管理平臺能夠看出,8100爲主,9100爲備
在註冊過程當中,只會保證有一臺註冊中心服務有對應服務信息數據,當主(8100)註冊中心宕機後,自動轉移數據到備(9100)註冊中心上去
6.7) 驗證高可用
若是此時將8100註冊中心關閉,那麼數據會轉移到9100註冊中心上去(8100宕機後,默認是30秒左右數據轉移到備機上)
註冊中心目的爲了作什麼?服務治理,服務註冊與發現可以實現負載均衡,管理服務與服務之間的依賴關係
一、引入話題:
分爲兩種角色:EurekaClient(註冊中心) 和 EurekaServer(註冊中心服務端,即生產者),若是將兩個服務端(端口號不一樣)註冊到註冊中心(集羣),利用消費者從註冊中心中拿取消費。而後將其中一個服務端關閉,會出現以下圖狀況:剛開始服務端能不斷從兩個服務中進行消費(負載均衡),當其中一個生產者(客戶端)宕機,刷新消費時由於負載均衡時而訪問不到(由於宕機)時而能訪問的到(另一臺沒有宕機),可是註冊中心在必定時間內還會存在宕機後的客戶端服務
二、爲何會產生Eureka自我保護機制?
爲了防止EurekaClient能夠正常運行,可是 與 EurekaServer網絡不通狀況下,EurekaServer不會將EurekaClient服務剔除
自我保護機制
三、在什麼狀況下開啓自我保護機制
本地環境:建議在本地環境禁止自我保護機制
生成環境:建議開啓,不能誤刪存活的服務
四、怎麼禁止自我保護
4.1) 註冊中心客戶端增長配置
server: port: 8100 spring: application: name: springCloud-eureka eureka: instance: hostname: 127.0.0.1 client: serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ register-with-eureka: false server: ###開發時關閉自我保護機制,保證不可用服務及時踢除 enable-self-preservation: false eviction-interval-timer-in-ms: 2000
4.2) 生產者客戶端拿取消費
###會員項目服務啓動端口號 server: port: 8002 ###服務名稱(服務註冊到eureka名稱,如serviceId) spring: application: name: app-producer ###服務註冊到eureka地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka #心跳檢測與續約時間 #開發時設置小些,保證服務關閉後註冊中心能即便剔除服務 instance: ###Eureka客戶端向服務端發送心跳的時間間隔,單位爲秒(客戶端告訴服務端本身會按照該規則) lease-renewal-interval-in-seconds: 1 ###Eureka服務端在收到最後一次心跳後等待時間上限,單位爲秒,超過將剔除(客戶端告訴服務端本身會按照該規則) lease-expiration-duration-in-seconds: 2
由於Eureka已經閉源,可是不影響它做爲註冊中心,固然也能夠利用zookeeper進行代替
zookeeper是一個分佈式協調工具,能夠實現註冊中心功能,採用Zookeeper節點類型--臨時節點
整個系統層次結構以下
一、編寫生產者:
1.1)創建maven項目,並配置pom依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <!-- 管理依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- SpringBoot整合Web組件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot整合eureka客戶端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> </dependency> </dependencies> <!-- 注意: 這裏必需要添加, 否者各類依賴有問題 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
1.2) 創建application.yml配置文件
###服務端口號 server: port: 8002 ###服務名稱 spring: application: name: zk-producer cloud: zookeeper: ###註冊到zookeeper地址 connect-string: 192.168.174.128:2181
1.3) 創建邏輯代碼(須要註冊的方法)
/** * @EnableDiscoveryClient * 做用:若是服務使用consul或者zookeeper,該註解用於向註冊中心註冊服務 * @author Administrator * */ @RestController @EnableDiscoveryClient public class ZkProducerController { @Value("${server.port}") private String serverPort; @RequestMapping("/getProduce") public String getProduce() { return "生產者生產服務:端口號爲:"+serverPort; } }
1.4) 創建啓動類
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
1.5) 啓動zookeeper(這裏我採用的是docker啓動zookeeper)
[root@bogon ~]# systemctl start docker [root@bogon ~]# docker run --privileged=true -d --name zookeeper --publish 2181:2181 -d zookeeper:latest ffc45f9fb92ae045c205407d39375a31c639bbf66d1f9b42339cf5e167a7c467
二、編寫消費者
2.1) 建立maven工程,注入依賴,和生成這依賴保持一致
2.2) 編寫application.yml配置文件
###服務端口號 server: port: 8060 ###服務名稱 spring: application: name: zk-consumer cloud: zookeeper: ###註冊到zookeeper地址 connect-string: 192.168.174.128:2181
2.3) 編寫消費服務方法
@RestController @EnableDiscoveryClient public class ZkConsumerController { @Autowired private RestTemplate restTemplate; @RequestMapping("/getOrder") public String getOrderInfo() { //採用服務別名(生產者)+方法映射id: String url = "http://zk-producer/getProduce"; String result = restTemplate.getForObject(url, String.class); System.out.println("訂單服務調用會員服務是_:"+result); return result; } }
2.4) 編寫啓動類,並運行該類
@SpringBootApplication public class ApplicationForConsumer { public static void main(String[] args) { SpringApplication.run(ApplicationForConsumer.class, args); } //解決RestTemplate找不到問題:應該把restTemplate註冊到springboot容器中 @Bean @LoadBalanced //開啓負載均衡功能,而且支持別名方式調用生產者(不然沒法使用別名) RestTemplate restTemplage() { return new RestTemplate(); } }
2.5) 訪問 http://localhost:8060/getOrder 若是能打印指定的返回結果,說明搭建成功
三、關於DiscoveryClient的使用
DiscoveryClient能夠獲取註冊中心中註冊的信息列表,只須要經過該引用的getInstances()方法就能夠獲取指定的列表信息,注意之因此返回值是集合是由於考慮到集羣
@RestController @EnableDiscoveryClient public class ZkConsumerController { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @RequestMapping("/getOrder") public String getOrderInfo() { //採用服務別名(生產者)+方法映射id: String url = "http://zk-producer/getProduce"; String result = restTemplate.getForObject(url, String.class); System.out.println("訂單服務調用會員服務是_:"+result); return result; } //如何獲取到註冊中心上服務列表信息 @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } @RequestMapping("/discoveryClientMember") public List<ServiceInstance> discoveryClientMember() { List<ServiceInstance> instances = discoveryClient.getInstances("zk-producer");//參數來源於生產者配置的服務名 for(ServiceInstance temp:instances) { System.out.println("url:"+temp.getUri()); } return instances; } }
Consul 是一套開源的分佈式服務發現和配置管理系統,由 HashiCorp 公司用 Go 語言開發。
它具備不少優勢。包括: 基於 raft 協議,比較簡潔; 支持健康檢查, 同時支持 HTTP 和 DNS 協議 支持跨數據中心的 WAN 集羣 提供圖形界面 跨平臺,支持 Linux、Mac、Windows
Consul 整合SpringCloud 學習網站:https://springcloud.cc/spring-cloud-consul.html
Consul下載地址https://www.consul.io/downloads.html
一、搭建consul環境
1.1) 下載consul:下載
1.2) 啓動
進入到存放下載好的執行文件,利用命令co
啓動consul命令
consul agent -dev -ui -node=cy
-dev開發服務器模式啓動,-node結點名爲cy,-ui能夠用界面訪問,默認能訪問。
測試訪問地址:http://localhost:8500
nsul agent-dev-ui-node=cy啓動
1、編寫註冊者
一、pom依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> </parent> <!-- 管理依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- SpringBoot整合Web組件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--SpringCloud consul-server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> </dependencies> <!-- 注意: 這裏必需要添加, 否者各類依賴有問題 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
二、編寫配置application.yml
###服務端口號 server: port: 8502 spring: application: name: consul-producer ####consul註冊中心地址 cloud: consul: host: localhost port: 8500 discovery: ###服務地址直接爲ip地址,這個自定義,它將成爲生產者ip hostname: 192.168.174.128 ###默認狀況下服務註冊到註冊中心,地址隨機生成英文
三、編寫邏輯類
@RestController @SpringBootApplication @EnableDiscoveryClient public class Productor { @Value("${server.port}") private String serverPort; @RequestMapping("getMember") public String getMember() { return "服務提供者:端口號爲:"+serverPort; } public static void main(String[] args) { SpringApplication.run(Productor.class, args); } }
四、啓動訪問:http://localhost:8500
一、springCloud之本地緩存Ribbon
1.一、Ribbon是Springcloud(本地)客戶端負載均衡器,當消費者從註冊中心獲取到serviceId以及多以的服務地址後,會緩存到本地(JVM客戶端)。而後在本地實現遠程的rpc調用,就如同繞過註冊中心直接調用生產者拿取服務
只要加上@LoadBalanced註解便可
@Autowired private RestTemplate restTemplate;
@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
2.二、關於負載均衡算法
消費者以服務別名獲取到對應的服務接口地址的時候,可能會多個,存放採用list接口。當實現本地負載均衡時,此時涉及到負載均衡算法
負載均衡算法:接口總請求數%服務器數量 = 實際調用服務器位置下標
如:List [0] value = 127.0.0.1:8080
List [1] value = 127.0.0.1:8081
這裏是兩個相同服務的集羣
當總請求數爲1時: 1 % 2 =1 對應下標位置爲1 ,則得到服務地址爲127.0.0.1:8081
當總請求數位2時: 2 % 2 =0 對應下標位置爲0 ,則得到服務地址爲127.0.0.1:8080
當總請求數位2時: 3 % 2 =1 對應下標位置爲1 ,則得到服務地址爲127.0.0.1:8081
如此類推。。。
2.三、手寫本地負載均衡器
1)編寫簡單的eureka註冊中心,生產者,消費者,最好關閉服務保護
2) 在消費者子模塊中編寫自定義本地緩存器
@RestController public class ExtRibbonController { //能夠獲取註冊中心上的服務列表 @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; //接口的請求總數 private int requestCount; @RequestMapping("/ribbonMember") public String ribbonMember() { //一、獲取對應服務器遠程調用地址 String instanceUrl = getInstance() + "/getMember"; System.out.println("instanceUrl" + instanceUrl); //2.能夠直接使用httpClient技術實現遠程調用 String result = restTemplate.getForObject(instanceUrl, String.class); return result; } private String getInstance() { List<ServiceInstance> instances = discoveryClient.getInstances("app-producer"); if(instances == null || instances.size()<=0) { return null; } //獲取服務器集羣個數 int instanceSize = instances.size(); int index = requestCount % instanceSize; requestCount++; return instances.get(index).getUri().toString(); } }
3) 啓動eureka註冊中心,以集羣方式啓動兩次生產者(這裏端口號爲8002和8003),啓動消費者
4)項目目錄層次以下
5) 輸入請求地址http://localhost:8001/ribbonMember,這裏是調用自定義的負載均衡器請求方法,經過它來調用生產者的服務,經過刷新請求能夠看到端口號的變化
2.4)Ribbon本地負載均衡客戶端與Nginx服務端負載均衡區別
Ribbon本地負載均衡,原理:在調用接口時候,會在eureka註冊中心上獲取註冊信息服務列表以後,會緩存到jvm本地,從而在本地實現rpc遠程服務調用技術。
Nginx是服務器負載均衡,客戶端全部請求都會交給nginx,而後由nginx實現轉發請求。即負載均衡器室友服務端實現的。
應用場景:
本地負載均衡器適用在微服務rpc遠程調用,好比dubbo、springCloud
nginx是服務器負載均衡適用於針對於服務端 好比tomcat、jetty
2.5) SpringCloud 聲明式Feign客戶端
2.5.1)在springCloud中,它支持兩種客戶端調用工具
一、Rest方式,可是它基本不用
@Autowired private RestTemplate restTemplate;
二、Feign客戶端
其之後實際開發中用的最多,它是一個聲明式的Http客戶端調用工具,採用接口+註解方式實現,可讀性比較強
2.5.2)搭建環境
1)編寫eureka註冊中心,生產者,消費者(只須要編寫配置和啓動類)
2)消費者引入feign依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
3)編寫feign接口
注意:feign客戶端是採用接口形式調用服務,其客戶端名與生產端配置的服務名保持一致,須要調用的服務名映射與服務端的映射保持一致
@FeignClient(name="app-producer") //此參數取決 於生產者的服務名 public interface ApiFeign { @RequestMapping("/getMember") //此參數取決於調用生產者的那個方法映射(和生產者保持一致) public String getMember(); }
4)編寫controller層
@RestController public class FeignController { @Autowired private ApiFeign apiFeign; @RequestMapping("/feignMember") public String feighMember() { return apiFeign.getMember(); } }
5)訪問服務http://localhost:8001/feignMember,若是能成功調用說明環境搭建成功
一、什麼是服務雪崩效應
默認狀況下tomcat只有一個線程池去處理客戶端發送的全部的服務請求,這樣在高併發狀況下,若是客戶端全部請求堆積在同一個服務接口上,就會產生tomcat全部線程去處理該服務接口,可能會致使其餘服務接口訪問產生延遲和等待沒法訪問。
tomcat有個線程池,每一個一個線程去處理客戶端發送請求。假設Tomcat最大請求數(同時)20,客戶端發送100個請求。會發生80個請求產生延遲等待。
二、環境:
當利用feign客戶端調用服務時,若是生產者中的一個消息有1.5秒的延遲,那麼在調用服務時會報時間超時
生產者實現類中的一個方法
@RequestMapping("/getUserInfo") public ResponseBase getUserInfo() { try { //服務接口產生1.5秒的延遲 Thread.sleep(1500); }catch(Exception e) { } return setResultSuccess("消費消息成功"); }
異常:
java.net.SocketTimeoutException: Read timed out
三、feign在網絡延遲狀況下的超時時間解決思路
只需在消費者配置中配置延長超時時間便可
###訂單服務(消費者)啓動端口號 server: port: 8001 ###服務名稱(服務註冊到eureka名稱) spring: application: name: app-order ###服務註冊到eureka地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka ###是否註冊本身 register-with-eureka: true ###是否從eureka上獲取註冊上信息 fetch-registry: true ###設置feign客戶端超時時間 ###springCloud默認開啓支持ribbon ribbon: ###指的是創建鏈接所用的時間,適用於網絡情況正常的狀況下,兩端鏈接所用的時間 ReadTimeout: 5000 ###指的是創建鏈接後從服務器讀取到可用資源所用的時間 ConnectTimeout: 5000
具體代碼參考連接:git@gitee.com:MR_JiaXianSen/spring_cloud_template.git
一、微服務高可用技術
大型複雜的分佈式系統中,高可用相關的技術架構很是重要。
高可用架構很是重要的一個環節,就是如何將分佈式系統中的各個服務打形成高可用的服務,從而足以應對分佈式系統環境中的各類各樣的問題,,避免整個分佈式系統被某個服務的故障給拖垮。
好比:
服務間的調用超時
服務間的調用失敗
要解決這些棘手的分佈式系統可用性問題,就涉及到了高可用分佈式系統中的不少重要的技術,包括:
資源隔離
限流與過載保護
熔斷
優雅降級
容錯
超時控制
監控運維
服務雪崩效應產生與服務堆積在同一個線程池中,由於全部的請求都是同一個線程池進行處理,這時候若是在高併發狀況下,全部的請求所有訪問同一個接口,
這時候可能會致使其餘服務沒有線程進行接受請求,這就是服務雪崩效應效應。
在高併發狀況下,防止用戶一直等待,使用服務降級方式(直接返回一個友好的提示給客戶端,調用fallBack方法)
熔斷機制目的爲了保護服務,在高併發狀況下,若是請求達到必定極限(能夠本身設置闊值)若是流量超出了設置閾值,讓後直接拒絕訪問,保護當前服務。使用服務降級方式返回一個友好提示,服務熔斷和服務降級一塊兒使用)
由於默認狀況下,只有一個線程池會維護全部的服務接口,若是大量的請求訪問贊成接口,達到tomcat線程池默認極限,可能會致使其餘服務沒法訪問。
解決服務雪崩效應:
1)線程池隔離
使用服務隔離機制(線程池方式和信號量),使用線程池方式實現隔離的原理: 至關於每一個接口(服務)都有本身獨立的線程池,由於每一個線程池互不影響,這樣的話就能夠解決服務雪崩效應。
線程池隔離:
2) 信號量隔離
使用一個原子計數器(或信號量)來記錄當前有多少個線程在運行,當請求進來時先判斷技術器的數值,若超過設置的最大線程個數則拒絕該請求,若不超過則經過,這時候計數器+1,請求返回成功後技術器-1
服務限流就是對接口訪問進行限制。技術器也能夠進行粗暴限流實現
二、Hystrix簡介
Hystrix是國外知名的視頻網站Netflix所開源的很是流行的高可用架構框架。Hystrix可以完美的解決分佈式系統架構中打造高可用服務面臨的一系列技術難題。
Hystrix 「豪豬」,具備自我保護的能力。hystrix 經過以下機制來解決雪崩效應問題。
在微服務架構中,咱們把每一個業務都拆分紅單個服務模塊。而後當有業務需求是,服務間課互相調用,可是,因爲網絡緣由等因素,有可能出現服務不可用的狀況,當某個服務出現問題時,其餘服務若是繼續調用這個服務,就是可能出現線程阻塞,但若是同時又大量的請求,就會形成線程資源被用完,這樣就可能會致使服務癱瘓,因爲服務間會相互調用,很容易形成蝴蝶效應致使整個系統宕機。而斷路器能夠解決這點。
資源隔離:包括線程池隔離和信號量隔離,限制調用分佈式服務的資源使用,某一個調用的服務出現問題不會影響其餘服務調用。
緩存:提供了請求緩存、請求合併實現。
三、基於Hystrix解決服務雪崩效應原理
1)、服務降級
在高併發狀況下,防止用戶一直等待,使用服務降級方式(返回一個友好的提示直接給客戶端,不會去處理請求,調用fallBack本地方法),在tomcat中沒有線程進行處理客戶端請求時,不該該讓客戶一直在轉圈等待。目的是爲了用戶體驗
如:秒殺服務降級處理-----提示當前請求人數過多,請稍後重試
2)、服務熔斷
服務熔斷的目的是爲了保護服務,在高併發狀況下,若是請求達到了必定的極限(能夠本身設置一個閾值),若是流量超出了設置的閾值狀況下,會自動開啓服務保護功能,使用服務降級方式返回一個友好的提示,服務熔斷機制和服務降級是一塊兒使用的。注意Hystrix默認閾值爲10個,超過十個則開啓服務保護,服務降級
3)、服務隔離
隔離方式分爲線程池隔離和信號量隔離。
四、Hystrix環境搭建
1)在父級pom中引入hystrix依賴
<!-- hystrix斷路器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
2)在消費者配置中開啓hystrix斷路器
###訂單服務(消費者)啓動端口號 server: port: 8001 ###服務名稱(服務註冊到eureka名稱) spring: application: name: app-order ###服務註冊到eureka地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka ###是否註冊本身 register-with-eureka: true ###是否從eureka上獲取註冊上信息 fetch-registry: true ###設置feign客戶端超時時間 ###springCloud默認開啓支持ribbon ribbon: ###指的是創建鏈接所用的時間,適用於網絡情況正常的狀況下,兩端鏈接所用的時間 ReadTimeout: 5000 ###指的是創建鏈接後從服務器讀取到可用資源所用的時間 ConnectTimeout: 5000 ###開啓Hystrix斷路器 feign: hystrix: enabled: true
2)在消費者端定義三個接口及實現類
其中兩個方法中一個方法一個有處理雪崩效應方案,另外一個沒有,還有一個方法時配合前兩個方法查看效果的
首先,要使得某個方法有服務保護,得加上@HystrixCommand註解並有處理時回調的提示方法fallbackMethod
Hystrix有兩種請求方式去配置服務保護
方式一:經過註解和接口形式:@HystrixCommand(fallbackMethod=""),其中fallbackMethod做用是服務降級
@HystrixCommand默認開啓線程池隔離方式、開啓服務降級和服務熔斷機制
@HystrixCommand(fallbackMethod="consumerInfoHystrixFallback") @RequestMapping("/consumerUserInfoHystrix") public ResponseBase consumerUserInfoHystrix() { System.out.println("consumerUserInfoHystrix:線程池名稱:"+Thread.currentThread().getName()); return comsumerFeignService.getUserInfo(); } public ResponseBase consumerInfoHystrixFallback() { return setResultSuccess("返回一個友好提示:服務降級,服務器忙,請稍後重試!"); }
所有代碼以下:
接口層:
public interface ConsumerService {
@RequestMapping("/consumerInfo")
public String consumerInfo(String name);
@RequestMapping("/consumerUserInfo")
public ResponseBase consumerUserInfo();
@RequestMapping("/consumerInfoForHystrix")
public ResponseBase consumerInfoForHystrix();
}
實現層:
方式一:經過註解和接口形式
@RestController public class ComsumerServiceImpl extends BaseApiService implements ConsumerService{ @Autowired private ComsumerFeignService comsumerFeignService; @Override @RequestMapping("/consumerInfo") public String consumerInfo(String name) { UserEntity user = comsumerFeignService.getProduceInfo(name); return user==null?"沒有找到用戶信息":user.toString(); } //該方法時沒有解決服務雪崩效應 @Override @RequestMapping("/consumerUserInfo") public ResponseBase consumerUserInfo() { return comsumerFeignService.getUserInfo(); } //Hystrix有兩種請求方式去配置服務保護 //方式一:經過註解和接口形式:@HystrixCommand(fallbackMethod=""),其中fallbackMethod做用是服務降級 //@HystrixCommand默認開啓線程池隔離方式、開啓服務降級和服務熔斷機制 @HystrixCommand(fallbackMethod="consumerInfoHystrixFallback") @RequestMapping("/consumerUserInfoHystrix") public ResponseBase consumerUserInfoHystrix() { System.out.println("consumerUserInfoHystrix:線程池名稱:"+Thread.currentThread().getName()); return comsumerFeignService.getUserInfo(); } public ResponseBase consumerInfoHystrixFallback() { return setResultSuccess("返回一個友好提示:服務降級,服務器忙,請稍後重試!"); } /** * 用於作對比:查看服務保護效果 */ @Override @RequestMapping("/consumerInfoForHystrix") public ResponseBase consumerInfoForHystrix() { System.out.println("consumerInfoHystrix:線程池名稱:"+Thread.currentThread().getName()); return setResultSuccess(); } }
3) 在消費者啓動類上添加啓動Htstrix註解
@SpringBootApplication @EnableEurekaClient @EnableFeignClients @EnableHystrix public class AppComsuApplication { public static void main(String[] args) { SpringApplication.run(AppComsuApplication.class,args); } }
4)此時經過測壓工具以200併發量測試consumerInfoForHystrix方法,再在瀏覽器中調用consumerUserInfoHystrix方法時,會出現
固然:經過控制檯能夠看到兩個方法調用的線程id是不一樣的
方式二:經過類的方式
方式一是在每一個須要調用的方法上添加註解,這樣代碼既冗餘,而且咱們只是針對getUserInfo的方法進行降級,開放獨立線程池,可是用方法一是其功能包含可整個方法,這樣是不可取的
方式二是經過類的形式實現的
5) 首先新增一個類,這個類須要實現feign客戶端接口,由於feign客戶端接口實現了生產者接口,這樣就間接取獲得了須要降級的方法,這裏只針對getUserInfo做演示
@Component public class ConsumerFallback extends BaseApiService implements ComsumerFeignService{ @Override public UserEntity getProduceInfo(String name) { // TODO Auto-generated method stub return null; } //服務降級的友好提示 @Override public ResponseBase getUserInfo() { return setResultError("服務器忙,請稍後重試!以類的方式進行服務降級"); } }
6) 在feign客戶端中引入回調類
@FeignClient(value="app-producer",fallback = ConsumerFallback.class) public interface ComsumerFeignService extends ProducerService{ }
7) 此時咱們隨便請求一個包含須要調用getUserInfo服務的方法,能起到降級做用(注意:由於getUserInfo方法的實現層採用了失眠1.5秒,而Htstrix超時時間默認爲1秒,因此纔會開啓服務保護進行降級給出友好提示)
如請求方法爲
//方式二:經過類的方式 @RequestMapping("/consumerUserInfoHystrixForClass") public ResponseBase consumerUserInfoHystrixForClass() { System.out.println("consumerUserInfoHystrix:線程池名稱:"+Thread.currentThread().getName()); return comsumerFeignService.getUserInfo(); }
五、Hystrix設置超時時間
若是調用其餘接口超時的時候(默認是1秒時間),若是在一秒中沒有及時響應的話(如調用服務時,服務接口有1.5秒的睡眠),默認狀況下業務邏輯是能夠執行的,可是直接直接執行服務降級方法(即執行fallbackMethod)
@Override @RequestMapping("/getUserInfo") public ResponseBase getUserInfo() { try { //服務接口產生1.5秒的延遲 Thread.sleep(1500); }catch(Exception e) { } return setResultSuccess("消費消息成功"); }
固然咱們能夠禁掉Hystrix超時設置,在消費者配置中配置,去掉後除非是網絡蹦了或者延遲嚴重,纔會走fallbackMethod方法,正常狀況下會成功調用服務
###訂單服務(消費者)啓動端口號 server: port: 8001 ###服務名稱(服務註冊到eureka名稱) spring: application: name: app-order ###服務註冊到eureka地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka ###是否註冊本身 register-with-eureka: true ###是否從eureka上獲取註冊上信息 fetch-registry: true ###設置feign客戶端超時時間 ###springCloud默認開啓支持ribbon ribbon: ###指的是創建鏈接所用的時間,適用於網絡情況正常的狀況下,兩端鏈接所用的時間 ReadTimeout: 5000 ###指的是創建鏈接後從服務器讀取到可用資源所用的時間 ConnectTimeout: 5000 ###開啓Hystrix斷路器 feign: hystrix: enabled: true ###hystrix禁止服務超時時間 hystrix: command: default: execution: timeout: enabled: false
此時咱們再訪問consumerUserInfoHystrix方法(注意該方法調用的 服務就是含有1.5秒睡眠的服務),會正常執行,且返回結果也不是rollbackMethod返回的數據,說明沒有調用
一、爲何使用分佈式的配置中心
在微服務若是使用傳統的方式管理配置文件,配置文件管理很是複雜,若是生產環境配置文件,可能須要發生改變時,須要從新打成war包,從新讀取配置信息在jvm內存中
二、什麼是分佈式配置中心
在微服務當中使用同一個服務管理全部服務配置文件信息,可以實現後臺可管理,當服務器正在運行的時候,若是配置文件須要發生改變,能夠實現不須要重啓服務器實時更改配置文件信息
注意:熱部署其實底層仍是會重啓服務器,不適合生產環境只適合本地開發測試
三、分佈式框架配置中心框架
1) 阿波羅 攜程寫的分佈式配置中心,擁有圖形界面可管理配置文件信息,配置文件信息存放在數據庫裏面
2) springCloud config:沒有後臺可管理分佈式配置中心,配置文件按信息存放在版本控制器裏面(git / svn)
3) 惟品會:使用Zookeeper實現分佈式配置中心,將配置存放在zookeeper,持久節點+事件通知
四、springCloud Config分佈式配置中心原理
首先分析:分佈式配置中心須要哪些組件:
1) web管理系統----後臺可使用圖形界面管理配置文件 SpringCloud config沒有圖形管理配置文件
2) 存放分佈式配置文件按服務器(持久化存儲服務器)----springCloud config使用版本控制器存放配置文件信息(git/svn)
3) ConfigServer:緩存配置文件服務器(臨時緩存存放)
4) ConfigClient:讀取ConfigServer配置文件信息
五、搭建環境
1) 搭建git環境
目的:持久化存儲配置文件信息,git環境上文件夾以項目進行區分
1.2) 公司項目中環境是如何區分的
dev 開發環境
sit 測試環境
pre 預發佈環境
prd 準生產環境
此演練兩個環境sit環境和prd環境,注意在git環境是私有的須要配置密碼,不然沒法訪問
2) 新建maven聚合工程
2.1) 新建eureka 註冊中心
2.2) 新建配置子項目springCloud2.0_config_server
2.2.1)引入依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <!-- 管理依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!--spring-cloud 整合 config-server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <!-- SpringBoot整合eureka客戶端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <!-- 注意: 這裏必需要添加, 否者各類依賴有問題 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
2.2.2) 編寫配置
###註冊中心服務地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka ###服務註冊名稱 spring: application: name: config-server cloud: config: server: git: ###config-server讀取git環境地址(注意:這裏是在git項目中新建的一個config文件夾內的HTTPS地址) uri: https://gitee.com/MR_JiaXianSen/springCloud-config.git search-paths: ###存放的文件目錄服務地址,即文件夾名,"-"表示多個,用"-"區分 - config ###讀取的分支 label: master server: port: 8888
2.2.3) 編寫啓動類
@SpringBootApplication @EnableEurekaClient @EnableConfigServer //開啓config Server服務器 public class AppConfigApplication { public static void main(String[] args) { SpringApplication.run(AppConfigApplication.class, args); } }
2.3) 在git config目錄中建立配置文件,並測試鏈接
注意:配置文件夾的命名規範:服務名稱-環境.properties(如producer-dev.properties),這裏新建兩個配置文件,內容採用key-value形式,且key保持一致
2.4) 此時啓動註冊中心,啓動springCloud2.0_config_server,直接訪問http://localhost:8888/test-configClient-prd.properties或者http://localhost:8888/test-configClient-sit.properties均可直接訪問配置裏的內容(注意若是git環境私有並未配置密碼會報404沒法訪問)
2.5) 新建客戶端子項目springCloud2.0_config_client
該子項目是經過程序訪問config_server服務端的配置信息(即git版本庫中的配置文件信息)
2.5.1)引入依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <!-- 管理依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!--spring-cloud 整合 config-client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency> <!-- SpringBoot整合eureka客戶端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 注意:必定要添加此依賴,不然會啓動不久自動關閉 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <!-- 注意: 這裏必需要添加, 否者各類依賴有問題 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
2.5.2) 添加配置bootstrap.yml
###服務名稱:注意:這裏的服務名要與git配置的文件名(配置名+環境)中的配置名保持一致,項目啓動時他是根據 ###該服務名稱去從git項目目錄中找與之配對的配置文件 spring: application: name: test-configClient cloud: config: ###表示讀取的版本環境 profile: sit discovery: ###表示讀取的config-server環境,此要與config-server子項目中 的服務別名要保持一致 service-id: config-server ###表示開啓讀取權限 enabled: true ###註冊中心服務地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka ###表示服務端口號 server: port: 8882
2.5.3)新增訪問controller即啓動類
@RestController public class TestClientController { @Value("${springCloudLearning}") //此要與git中配置文件的key保持一致 private String key; @RequestMapping("/getInfo") public String getInfo() { return key; } }
@SpringBootApplication @EnableEurekaClient public class AppClientAppliction { public static void main(String[] args) { SpringApplication.run(AppClientAppliction.class, args); } }
2.5.4)訪問:
2.6) 實時刷新配置
當git配置文件中的內容更改後,由於本地緩存緣由,客戶端不能實時得到更改後的配置信息,日常作法是重啓項目
springCloud分佈式配置中心能夠採用手動刷新或者自動刷新實時更新配置文件更改後的內容,手動刷新和自動刷新都不要重啓項目
1) 手動刷新--須要人工調用接口讀取最新配置(監控中心)
2) 自動刷新---消息總線進行實時通知(不建議:對性能很差)
如下演示手動刷新方式
2.6.1) 配置actuator監控中心,先在springCloud_config_client子項目中新增依賴
<!-- actuator監控中心 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
2.6.2) 新增配置
###服務名稱:注意:這裏的服務名要與git配置的文件名(配置名+環境)中的配置名保持一致,項目啓動時他是根據 ###該服務名稱去從git項目目錄中找與之配對的配置文件 spring: application: name: test-configClient cloud: config: ###表示讀取的版本環境 profile: sit discovery: ###表示讀取的config-server環境,此要與config-server子項目中 的服務別名要保持一致 service-id: config-server ###表示開啓讀取權限 enabled: true ###註冊中心服務地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka ###表示服務端口號 server: port: 8882 ###開啓全部的監控端點 management: endpoints: web: exposure: include: "*"
2.6.3) 在controller類中新增@RefreshScope註解
@RestController @RefreshScope public class TestClientController { @Value("${springCloudLearning}") //此要與git中配置文件的key保持一致 private String key; @RequestMapping("/getInfo") public String getInfo() { return key; } }
2.6.4) 此時若是git配置文件信息發生改變,須要以post方式手動調用actuator/refresh接口進行刷新本地緩存,如這裏是http://localhost:8882/actuator/refresh,從而每次調用請求時(http://localhost:8882/getInfo)均可以得到配置文件中最新的信息
配置中心的源碼:git@gitee.com:MR_JiaXianSen/spring_cloud_template.git
一、網關API(接口) Gateway(網關) --接口網關注意:接口沒有界面
二、接口什麼背景下產生的?
在面向服務架構和微服務背景下產生的,目的是爲了解耦,rpc遠程調用中產生的
三、接口如何分類
開發接口----提供給其餘機構合做夥伴進行調用(必須在外網訪問) 如螞蟻開放平臺,微信公衆號開發
須要經過appId(目的受權一些接口受權) +appsecret生成accessToken進行通信
內部接口----通常只能在居於網中進行訪問,服務與服務調用之間關係都在同一個微服務系統中,目的是爲了保證安全
四、網關概念
1) 至關於客戶端請求統一先請求到網關服務器上,而後由網關服務器進行轉發到實際服務器地址上。功能相似於nginx
2) 過濾器與網關區別是什麼?
過濾器是攔截單個tomcat服務器進行攔截請求,網關是攔截整個微服務全部請求
3) Nginx與Zuul的區別
相同點: Zuul和Nginx均可以實現負載均衡、反向代理(掩飾真實IP)、過濾請求,實現網關效果
不一樣點:Nginx採用C語言編寫,Zuul採用java語言編寫
Zuul負載均衡實現:採用ribbon+eruka實現本地負載均衡;Nginx負載均衡實現:採用服務器端實現負載均衡
性能:Nginx相對於Zuul功能更增強大,由於Nginx整合一些腳本語言(如Lua),Nginx適合於服務器端負載均衡
Zuul更適合微服務中,最好是nginx+zuul實現網關(nginx做反向代理,Zuul對微服務實現網關攔截)
注意:Zuul默認開啓Ribbon本地負載均衡
五、環境搭建
5.1) 新建項目,引入依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <!-- 管理依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <!--SpringCloud eureka-server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <!-- 注意: 這裏必需要添加, 否者各類依賴有問題 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
5.2) 新建配置application.yml
###服務註冊地址 eureka: client: serviceUrl: defaultZone: http://localhost:8100/eureka/ ###api網關端口號 server: port: 80 ###網關名稱 spring: application: name: service-zuul zuul: routes: ###定義的服務規則,能夠自定義 api-a: ###當客戶端發送請求127.0.0.1:80/api-producer開頭的 都會轉發到生產者服務 path: /api-producer/** ###生產者服務別名,zuul網關默認整合ribbon自動實現負載均衡輪詢效果 serviceId: app-producer api-b: ###當客戶端發送請求127.0.0.1:80/api-consumer開頭的 都會轉發到消費者服務 path: /api-consumer/** serviceId: app-consumer
5.3) 新增啓動類,開啓Zuul功能
@EnableZuulProxy @EnableEurekaClient @SpringBootApplication public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } }
5.4) 此時能夠測試Zuul的方向代理過程,先啓動Zuul子項目,再啓動eureka註冊中心,再啓動生產者,此時能夠經過網關代理訪問生產者,顯示內容同樣
http://localhost:80/api-producer/getProduce?name=rose
http://localhost:8003/getProduce?name=rose
六、利用Zuul做過濾器
6.1) 編寫過濾器
此過濾功能時請求未傳userToken參數,會提示報錯進行攔截
/** * 網關過濾器 * @author 佳先森 * */ @Component public class TokenFilter extends ZuulFilter{ /** * 編寫過濾器攔截業務邏輯代碼 * 環境:攔截全部服務接口,判斷是否有傳userToken */ public Object run() throws ZuulException { //攔截全部的服務接口,判斷服務接口是否有傳遞UserToken參數 //1.首先要獲取上下文 RequestContext currentContext = RequestContext.getCurrentContext(); //2.獲取request對象 HttpServletRequest request = currentContext.getRequest(); //3.通常獲取token的時候 從請求頭中獲取token參數,這裏只是演示方便 String userToken = request.getParameter("userToken"); if(StringUtils.isEmpty(userToken)) { //不會繼續執行...不會調用服務接口,網關服務直接響應給客戶端 //返回一個錯誤提示 currentContext.setSendZuulResponse(false); currentContext.setResponseBody("userToken is null"); currentContext.setResponseStatusCode(401); return null; } //正行執行調用其餘服務接口... return null; } /** * 判斷過濾器是否生效 */ public boolean shouldFilter() { return true; } /** * 表示過濾器執行順序,當一個請求在同一階段時存在 * 多個過濾器時,多個過濾器執行順序問題 */ public int filterOrder() { return 0; } /** * 表示過濾器類型 "pre"表示請求以前進行 */ public String filterType() { return "pre"; } }
6.2) 繼續啓動項目
七、網關之集羣
7.1) 先配置nginx環境
7.1.1) 找到電腦hosts文件,win7 所在目錄爲C:\Windows\System32\drivers\etc
目的是對虛擬機ip進行映射
192.168.174.128 www.ibo.com
7.1.2) 在nginx配置文件中添加以下代碼
###上游服務器 集羣輪詢機制
upstream backServer{ server 192.168.2.175:81; server 192.168.2.175:82; } server { listen 80; server_name www.ibo.com; location / {
###指定上游服務器負載均衡服務器 proxy_pass http://backServer/; index index.html index.htm; }
此時經過分別開啓以端口號爲81和82的網關配合nginx能夠實現集羣后的負載均衡
一、引入
隨着微服務架構體系的發展和應用,爲了先後端可以更好的集成與對接,爲了項目方便交互,每一個項目都須要提供相應的API文檔
二、爲何須要swagger
傳統:
1) 如先後端分離那些事,若是後端編寫的接口文檔更新沒及時通知前端;API接口返回信息不明確
2) 接口文檔太多,不便於管理
swagger:
1)、功能豐富;支持多種註解,自動生成接口文檔,支持在界面測試API接口功能
2)、及時更新
3)、整合簡單。經過添加pom依賴和簡單配置,內嵌與應用中就能夠同時發佈API接口文檔界面,不須要部署獨立服務
三、搭建環境
1)、引入依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <!-- 管理依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--swagger2--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.8.0</version> </dependency> </dependencies> <!-- 注意: 這裏必需要添加, 否者各類依賴有問題 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
2) 新增配置application.yml
###服務啓動端口號 server: port: 8000 ###服務名稱(服務註冊到eureka名稱) spring: application: name: springboot-swagger
3) 新增配置類
/** * 注意配置的信息能夠存放在分佈式配置中內心面 * @author 佳先森 * */ @Configuration @EnableSwagger2 //開啓swagger2功能 public class SwaggerConfig { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() //生成API掃包範圍(指定哪一個包須要生成api) .apis(RequestHandlerSelectors.basePackage("com.ibo.api")).paths(PathSelectors.any()).build(); } //建立api文檔信息 private ApiInfo apiInfo() { return new ApiInfoBuilder().title("XX項目對接文檔").description("平臺導入功能") .termsOfServiceUrl("http://www.ibo.com") .version("1.0").build(); } }
4) 新增須要生成api的指定controller,注意該controller須要在指定的掃描包中,這裏根據上個配置應該是com.ibo.api
@Api("SwaggerDemo控制器") @RestController public class SwaggerController { @ApiOperation("swagger演示接口")//接口描述 //@RequestMapping("/swaggerIndex")//注意在微服務項目中最好用get/post等請求規範寫法 @GetMapping("/swaggerIndex") public String swaggerIndex() { System.out.println("swaggerIndex"); return "swaggerIndex"; } @ApiOperation("獲取用戶信息") //接口描述 @ApiImplicitParam(name="username",value="用戶信息參數",required=true,dataType="String")//傳參參數描述 @PostMapping("/getUserInfo") public String getUserInfo(String username) { System.out.println("用戶名爲:"+username); return "userName:"+username; } }
5) 新建啓動類
@SpringBootApplication public class SwaggerApplication { public static void main(String[] args) { SpringApplication.run(SwaggerApplication.class, args); } }
6)啓動項目, 訪問平臺管理界面http://localhost:8000/swagger-ui.html#/swagger-controller
四、swagger集羣
在微服務中,swagger是每一個服務集成的,那麼如何將整個微服務中的swagger進行合成。
一、咱們可使用Zuul+Swagger實現管理每一個微服務API文檔
二、可使用Nginx + Swagger以項目不一樣區分跳轉不一樣的接口文檔
SpringBoot支持Swagger管理,只須要Zuul網關添加對應服務Sawgger文檔便可
集羣環境搭建:
4.1) 生產者和消費者和網關子項目中同時引入swagger依賴,此依賴就等同於上面兩個,是spring對其兩個依賴進行了整合
<!-- 引入swagger依賴 --> <dependency> <groupId>com.spring4all</groupId> <artifactId>swagger-spring-boot-starter</artifactId> <version>1.7.0.RELEASE</version> </dependency>
4.2) 分別在生產者和消費者配置中添加swagger配置
###定義swagger掃包範圍 swagger: base-package: com.ibo.serviceImpl
4.3) 在生產者和消費者調用類上添加@Api註解並添加方法描述
如生產者:
@RestController @Api("生產者服務") public class ProduceServiceImpl extends BaseApiService implements ProducerService{ @Value("${server.port}") private String serverPort; @Override @ApiOperation("得到服務信息") @ApiImplicitParam(name="name",value="用戶名",required=true,dataType="String") @GetMapping("/getProduce") public UserEntity getProduceInfo(@RequestParam("name")String name) { UserEntity userEntity = new UserEntity(); userEntity.setName(name+"端口號:"+serverPort); userEntity.setAge(24); return userEntity; }
4.4) 在啓動類中開啓swagger註解
@EnableSwagger2Doc //生成swagger註解文檔 @SpringBootApplication public class AppComsuApplication { public static void main(String[] args) { SpringApplication.run(AppComsuApplication.class,args); } }
4.5) 在網關啓動類中增長配置與開啓swagger功能
@EnableZuulProxy @EnableEurekaClient @EnableSwagger2Doc @SpringBootApplication public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } //zuul配置可以使用config實現實時更新 @RefreshScope @ConfigurationProperties("zuul") public ZuulProperties zuulProperties() { return new ZuulProperties(); } //添加文檔來源 @Component @Primary class DocumentationConfig implements SwaggerResourcesProvider{ @Override public List<SwaggerResource> get() { List<SwaggerResource> resource = new ArrayList<>(); resource.add(swaggerResource("api-producer","/api-producer/v2/api-docs","2.0")); resource.add(swaggerResource("api-consumer","/api-consumer/v2/api-docs","2.0")); return resource; } /** * * @param name 此參數能夠隨意定義,用於查詢接口模塊名稱 * @param location /api-producer/v2/api-docs 中api-producer取自於網關的配置,這是對全部生產者訪問時的域名前綴 * @param version 版本號 * @return */ private SwaggerResource swaggerResource(String name, String location,String version) { SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setName(name); swaggerResource.setLocation(location); swaggerResource.setSwaggerVersion(version); return swaggerResource; } } }
4.6) 此時若是訪問http://localhost:82/swagger-ui.html#/swagger-controller 經過select a spec能夠查看生產者和消費者各自的接口信息
微服務架構是一個分佈式架構,它按業務劃分服務單元,一個分佈式系統每每有不少個服務單元。因爲服務單元數量衆多,業務的複雜性,若是出現了錯誤和異常,很難去定位。主要體如今,一個請求可能須要調用不少個服務,而內部服務的調用複雜性,決定了問題難以定位。因此微服務架構中,必須實現分佈式鏈路追蹤,去跟進一個請求到底有哪些服務參與,參與的順序又是怎樣的,從而達到每一個請求的步驟清晰可見,出了問題,很快定位。而Zipkin能解決此問題
一、環境搭建
1)下載zkpkin jar包:https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/
2)經過java -jar 命令運行jar包
輸入http://localhost:9411進入首界面
3)建立兩個子項目,maven配置保持差很少一致,只是端口號不同
3.1) 引入maven
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <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>
3.2) 引入配置
主意運行的jar包端口號爲9411
server: port: 10013 spring: zipkin: ##對應的運行jar包域名 base-url: http://localhost:9411 application: name: zipkinServiceOne
3.3) 創年啓動類
實例1:
@SpringBootApplication
@RestController public class ZipKinServiceOneApplication { private static Logger logger = Logger.getLogger(ZipKinServiceOneApplication.class); @Autowired RestTemplate restTemplate; public static void main(String[] args) { SpringApplication.run(ZipKinServiceOneApplication.class, args); } @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } @GetMapping("/getInfoOne") public String callHome() { logger.log(Level.INFO,"calling trace service-one"); return restTemplate.getForObject("http://localhost:10014/getInfoTwo", String.class); } @GetMapping("/info") public String info() { logger.log(Level.INFO,"calling trace service-one"); return "i am service-one"; } @Bean public Sampler defaultSampler() { return Sampler.ALWAYS_SAMPLE; } }
實例2:
@SpringBootApplication @RestController public class ZipKinServiceTwoApplication { private static Logger logger = Logger.getLogger(ZipKinServiceTwoApplication.class); @Autowired RestTemplate restTemplate; public static void main(String[] args) { SpringApplication.run(ZipKinServiceTwoApplication.class, args); } @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } @GetMapping("/getInfoTwo") public String callHome() { logger.log(Level.INFO,"calling trace service-two"); return restTemplate.getForObject("http://localhost:10013/info", String.class); } @GetMapping("/info") public String info() { logger.log(Level.INFO,"calling trace service-two"); return "i am service-two"; } @Bean public Sampler defaultSampler() { return Sampler.ALWAYS_SAMPLE; } }
3.4) 訪問10013端口號的getInfoOne服務:http://localhost:10013/getInfoOne
3.4)此時查看zipkin控制太的依賴分析,能夠看到鏈路調用狀況