一 Spring Cloud特色java
# 約定優於配置mysql
# 開箱即用,快速啓動web
# 適用於各類環境,能夠部署在PC server或者 雲環境算法
# 輕量級的組件spring
# 組件的支持很豐富,功能齊全sql
# 選型中立數據庫
二 服務提供者和服務消費者apache
三 服務發現和註冊編程
爲何須要服務註冊與發現緩存
# 服務重啓或者升級後IP地址變化
# 水平伸縮後服務實例的變化
# 同一個節點運行多個服務
因此須要一種註冊機制,幫助咱們去獲取響應。
核心機制:
將實例的信息註冊到註冊中心
調用者經過註冊中心查找服務
調用者獲取服務實例列表
調用者經過負載均衡通訊
3.1 基本流程
首先:服務消費者和服務註冊者向服務發現組件註冊
其次:服務消費者要調用的時候會從服務發現組件中進行查詢
需求:
# 每個服務實例都會在啓動的時候經過HTTP/REST或者Thrift等方式發佈遠程API
# 服務端實例的具體數量及位置會發生動態變化
# 虛擬機與容器一般會被分配動態IP地址
3.2 服務發現組件的功能
# 服務註冊表: 是一個記錄當前可用服務實例的網絡信息的數據庫,是服務發現機制的核心。服務註冊表提供查詢API和管理API,使用API 得到可用的服務實例,使用管理API實現註冊和註銷。
# 服務註冊
# 健康檢查
3.3 服務發現的方式
3.3.1 客戶端發現
它的主要特色是客戶端決定服務實例的網絡位置,而且對請求進行負載均衡。客戶端查詢服務註冊表(可用服務實例數據庫),使用負載均衡算法選擇一個實例,併發出請求。典型表明Eureka或者ZK
客戶端發現模式的優缺點
優勢:
不須要不少的網絡跳轉
缺點:
客戶端和服務註冊表耦合
須要爲應用程序每一種編程語言、框架等創建客戶端發現邏輯,好比 Netflix Prana就爲非JVM客戶端提供一套基於HTTP代理服務發現方案
# 服務器端發現
向某一服務發送請求,客戶端會經過向運行位置已知的路由器或者負載均衡器發送請求。他們會查詢服務註冊表,並向可用的服務實例轉發該請求。典型表明Consul + Nginx
服務器端發現模式優缺點:
優勢:
客戶端無需實現發現功能,只須要向路由器或者負載均衡器發送請求便可
缺點:
除非成爲雲環境的一部分,不然該路由機制必須做爲另外一系統組件進行安裝與配置。爲實現可用性和必定的接入能力,還須要爲其配置必定數量的副本。
相較於客戶端發現,服務器端發現機制須要更多的網絡跳轉。
3.4 服務發現組件Eureka
3.4.1 什麼是Eureka
Eureka 是Netflix 開源的服務發現組件, Spring Cloud 將其集成在 Spring Cloud Netflix 中,實現服務的註冊和發現。Eureka 主要包含兩個組件: Eureka Server 和 Eureka Client。 二者的做用以下:
3.4.2 Eureka原理
先須要明白AWS幾個概念:
Region: AWS雲服務在全球不一樣的地方都有數據中心,好比北美、南美和歐洲亞洲等。與此對應,根據地理位置咱們把某個地區的基礎設施服務集合稱爲一個區域。不一樣區域之間是相互獨立的。說白了就相似於不一樣地方的機房。
Available Zone: 基於容災背景提出,簡單而言,就是相同region區域不一樣的機房
# 首先是服務註冊到Eureka
# 每30s發送心跳檢測從新進行租約,若是客戶端不能屢次更新租約,它將在90s內從服務器註冊中心移除。
# 註冊信息和更新會被複制到其餘Eureka 節點,來自任何區域的客戶端科能夠查找到註冊中心信息,每30s發生一次複製來定位他們的服務,並進行遠程調用
# 客戶端還能夠緩存一些服務實例信息,因此即便Eureka全掛掉,客戶端也是能夠定位到服務地址的
3.4.3 搭建Euraka Server
首先:搭建parent項目
<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.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<modules>
<module>microservice-consumer</module>
<module>microservice-provider</module>
<module>microservice-discovery-eureka</module>
</modules>
<groupId>com.microservice</groupId>
<artifactId>microservice</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!--資源文件拷貝插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!--java編譯插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--配置tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
其次:搭建Euraka Server
<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.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<parent>
<artifactId>microservice</artifactId>
<groupId>com.microservice</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>microservice-discovery-eureka</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
</project>
而後:在application.xml中配置application.yml文件
security:
basic:
enabled: true
user:
name: user
password: password
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone:http://user:password@localhost:8761/eureka
最後:建立EurekaApplication,並添加@SpringBootApplication
@EnableEurekaServer
public class EurakaApplication{
public static void main(String[] args) throws Exception {
SpringApplication.run(EurakaApplication.class, args);
}
}
就能夠啓動Eureka Server了
3.4 將微服務註冊到Eureka上
首先:確認當前maven環境下是否引入了Eureka相關的配置,而且添加以下依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
其次:在微服務啓動類上添加@EnableEurekaClient註解,使得他成爲一個Eureka Client,在啓動的 時候就向Eureka Server註冊。
而後:在微服務的應用程序中application.yml配置文件中,咱們不要添加以下配置,由於這表示是服務器,客戶端咱們是須要向Eureka註冊的
eureka:
client:
register-with-eureka: false
fetch-registry: false
應該能夠的配置有健康檢查和路徑配置
eureka:
client:
healthcheck:
enabled: true
service-url:
defaultZone:http://nicky:123abcABC@localhost:8761/eureka
最後:在啓動類添加@EnableEurekaClient註解,表示這個類能夠做爲Eureka 客戶端,啓動以後能夠向Eureka註冊
@SpringBootApplication
@EnableEurekaClient
public class UserServiceRunner{
public static void main(String[] args) throws Exception {
SpringApplication.run(UserServiceRunner.class, args);
}
}
3.5 Eureka配置項
eureka.client.allow-redirects:是否容許重定向Eureka客戶端請求到其餘或者備份服務器,默認爲fasle
eureka.client.eureka-connection-idle-timeout-seconds:HTTP鏈接到eureka服務器能夠在關閉以前保持空閒的時間(幾秒鐘)。
eureka.client.eureka-server-connect-timeout-seconds:表示鏈接Eureka服務器,等待多長時間算超時
eureka.client.eureka-server-port: Eureka Server端口
eureka.client.eureka-server-d-n-s-name:獲取要查詢的DNS名稱以得到eureka服務器的列表。
eureka.client.eureka-server-read-timeout-seconds:示在從eureka服務器讀取數據以前須要等待多長時間(以秒爲單位)
eureka.client.eureka-server-total-connections:從eureka客戶端到全部eureka服務器的所容許鏈接總數。
eureka.client.eureka-server-total-connections-per-host:設置每個主機所容許的到Eureka Server鏈接的數量
eureka.client.fetch-registry: 是否容許客戶端向Eureka 註冊表獲取信息,通常服務器爲設置爲false,客戶端設置爲true
eureka.client.register-with-eureka:是否容許向Eureka Server註冊信息,默認true,若是是服務器端,應該設置爲false
eureka.client.fetch-remote-regions-registry:逗號分隔的區域列表,用於獲取eureka註冊信息
eureka.client.g-zip-content:從服務器端獲取數據是否須要壓縮
eureka.client.prefer-same-zone-eureka: 是否優先使選擇相同Zone的實例,默認爲true
eureka.client.registry-fetch-interval-seconds:多長時間從Eureka Server註冊表獲取一次數據,默認30s
eureka.client.service-url:可用區域映射,列出徹底合格的url與eureka服務器通訊。每一個值能夠是一個URL,也能夠是一個逗號分隔的替代位置列表。
eureka.dashboard.enabled: 是否啓用Eureka首頁,默認爲true
eureka.dashboard.path: 默認爲/
eureka.instance.appname:在eureka註冊的應用程序的名稱。
eureka.instance.app-group-name:在eureka註冊的應用程序的組名稱
eureka.instance.health-check-url: 健康檢查絕對路徑
eureka.instance.health-check-url-path:健康檢查相對路徑
eureka.instance.hostname:設置主機名
eureka.instance.instance-id:設置註冊實例的id
eureka.instance.lease-expiration-duration-in-seconds:設置多長時間意味着租約到期,默認90
eureka.instance.lease-renewal-interval-in-seconds:表示Eureka客戶端須要發送心跳到eureka服務器的頻率(以秒爲單位),以代表它仍然存在。指定的期間內若是沒有收到心跳leaseExpirationDurationInSeconds
eureka.instance.metadata-map:能夠設置元數據
eureka.instance.prefer-ip-address: 實例名以IP,可是建議hostname,默認爲false
四 負載均衡
4.1 Ribbon的介紹和架構
實現負載均衡,咱們能夠經過服務器端和客戶端作負載均衡。服務器端作負載均衡,好比可使用Nginx。而客戶端作負載均衡,就是客戶端有一個組件,知道有哪些可用的微服務,實現一個負載均衡的算法。
Ribbon工做流程主要分爲兩步:
第一:先選擇Eureka Server,優先選擇在同一個Zone且負載較少的Server;
第二:再根據用戶指定的策略,再從server取到的服務註冊列表中選擇一個地址。其中Ribbon提供了不少種策略,例如輪詢round bin,隨機Random,根據響應時間加權。
4.2 使用Ribbon進行負載均衡
4.2.1 基本用法
要使用Ribbon進行負載均衡,那麼就須要引入對應的依賴,若是已經因若是Eureka的依賴,那麼就不須要再次引入Ribbon的依賴了。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
而後在啓動類上加上註解@LoadBalanced,則負載均衡生效。
@SpringBootApplication
@EnableEurekaClient
public classMovieServiceRibbonRunner {
@Bean
@LoadBalanced
public RestTemplaterestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(MovieServiceRibbonRunner.class, args);
}
}
咱們能夠修改application.yml文件的instance_id屬性:
eureka:
client:
healthcheck:
enabled: true
service-url:
defaultZone:http://nicky:123abcABC@localhost:8761/eureka
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}}
在消費者Controller中,訪問虛擬的ip,即咱們微服務的名稱
@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/movie/{id}")
public User findById(@PathVariable Long id) {
returnthis.restTemplate.getForObject("http://microservice-provider-user/user/"+id, User.class);
}
}
最後啓動兩個服務提供者實例,能夠修改端口實現。
4.2.2 經過代碼自定義配置Ribbon
首先,該類須要加上@Configuration註解,可是注意,若是加上這個註解,他就不能包含在註解@ComponentScan或者@SpringBootApplication所指定包掃描路徑。
@Configuration
public classRibbonTestConfiguration {
@Autowired
IClientConfig config;
@Bean
public IRuleribbonRule(IClientConfig config){
return new RandomRule();
}
}
其次:在啓動類上加上@RibbonClient註解,指定名稱和configuration文件類
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="microservice-provider-user",configuration=RibbonTestConfiguration.class)
public classMovieServiceRunner {
@Bean
@LoadBalanced
public RestTemplaterestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(MovieServiceRunner.class, args);
}
}
4.2.3 經過配置文件自定義Ribbon
即咱們能夠經過yml或者properties配置文件,進行配置,而後使用咱們配置的選項。
注意:這裏有些優先級的順序問題:
配置文件定義的優先級大於經過使用Java代碼@RibbonClient的優先級大於使用spring默認的優先級
支持如下屬性:
NFLoadBalancerClassName:應該實現 ILoadBalancer
NFLoadBalancerRuleClassName:應該實現 IRule
NFLoadBalancerPingClassName:應該實現IPing
NIWSServerListClassName:應該實現 ServerList
NIWSServerListFilterClassName:應該實現 ServerListFilter
application.yml中配置以下:
microservice-provider-user:
ribbon:
NFLoadBalancerRuleClassName:com.netflix.loadbalancer.RandomRule
五 Feign (聲明式的REST Client)
5.1 簡介及基礎使用
Feign是一個聲明式的web服務客戶端,它使得寫web服務客戶端更加容易。使用Feign建立一個接口並對其進行註解。它具備可插拔的註解支持,包括Feign本身的註解以及jax-rs註釋。Feign還支持可插拔的的編碼器和解碼器。Spring Cloud增長了對Spring MVC註解的支持,並使用了在Spring Web中默認使用的相同的HttpMessageConverters。Spring Cloud集成了Ribbon和靈感,在使用時提供了負載均衡的http客戶端。
加入Feign的依賴到Maven
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
而後建立接口,而且添加註解@EnableFeignClients
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public classMovieServiceRunner {
public static void main(String[] args) {
SpringApplication.run(MovieServiceRunner.class, args);
}
}
寫真正的接口去調用其餘微服務,以供本應用調用
建立接口:
首先在接口加上註解@FeignClient,指定須要調用微服務名字
其次:若是啓動的時候,提示Http Method不正確咱們須要使用之前的老的註解,即:
@RequestMapping(method= RequestMethod.GET, value = "/user/{id}"),而不能使用:
@GetMapping("/user/{id}")
而後:若是提示PathVariableannotation was empty on param 0,那麼咱們就須要:
還有就是:微服務提供方和消費者兩邊的HTTP 方法必須最好一致,若是一邊是GET 請求,另一邊是POST請求,就會報錯。並且若是傳遞的是一個負載對象,即便指定的是GET請求,也會做爲POST請求,通常作法就是不傳遞對象,而是傳遞單個參數
好比@ReqquestParam(「name」) String name,@ReqquestParam(「age」) int age之類的
public UserfindById(@PathVariable("id") Long id); 而不是:
public UserfindById(@PathVariable Long id); 就能夠
這至關因而Feign的坑吧
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id);
}
而後在其餘類中就能夠調用了:
@RestController
public class MovieController {
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("/movie/{id}")
public User findById(@PathVariable Long id) {
return userFeignClient.findById(id);
}
}
5.2 覆寫Feign的配置
Spring Cloud容許你徹底控制Feign 客戶端經過聲明一些額外的註解,即@FeignClient(name = "stores",configuration = FooConfiguration.class)
通常來講,咱們不須要使用在自定義的FooConfiguration上使用@Configuration組件,若是要使用咱們須要把它從包含@ComponentScan或者@SpringBootApplication註解所掃描的包中排除掉。和Ribbon相似。
假設咱們如今定義了一個Configuration1這個類,使用feign默認的契約,而不是使用SpringMvcContract,即:
@Configuration
public class Configuration1 {
@Bean
public ContractfeignContract() {
return newfeign.Contract.Default();
}
}
而後在Feign的接口處,指定@FeignClient的configuration,重寫配置,使用剛纔定義的Configuration1,即:
@FeignClient(name="microservice-provider-user",configuration=Configuration1.class)
public interface UserFeignClient {
@GetMapping("/user/{id}")
public User findById(@PathVariable("id") Long id);
}
可是在這兒咱們依然用的是SpringMVC的註解,GetMapping,這裏雖然沒有報錯,可是啓動的時候就會報錯了。好比java.lang.IllegalStateException: MethodfindById not annotated with HTTP method type (ex. GET, POST)
因此,接口處咱們應該替換爲Feign本身的註解。
@FeignClient(name="microservice-provider-user",configuration=Configuration1.class)
public interface UserFeignClient {
@RequestLine("GET /user/{id}")
public User findById(@Param("id") Long id);
}
Feign支持請求響應GZIP壓縮:
feign.compression.request.enabled=true
feign.compression.response.enabled=true
5.3 feign的日誌
Feign的默認日誌級別是DEBUG 級別。
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
六 常見問題解決方案
6.1 Eureka環境以及cloud配置
Eureka能夠運行AWS環境和非AWS環境上,Eureka也能夠以測試環境和生產環境運行。
若是須要運行在AWS環境上,則須要經過-Deureka.datacenter=cloud指定運行在AWS上,在yml中咱們能夠經過eureka.datacenter:cloud指定。
eureka.datacenter:cloud
若是須要運行在測試或者生產環境,咱們須要經過-Deureka.environment來指定。若是指定測試環境eureka.environment: test, 若是運行在生產環境,則指定eureka.environment:product
6.2 自我保護提示
6.3 Eureka註冊服務慢的問題如何解決
做爲實例還涉及到與註冊中心的週期性心跳,默認持續時間爲30秒(經過serviceUrl)。在實例、服務器、客戶端都在本地緩存中具備相同的元數據以前,服務不可用於客戶端發現(因此可能須要3次心跳)。你可使用eureka.instance.leaseRenewalIntervalInSeconds配置,這將加快客戶端鏈接到其餘服務的過程。
在生產中,最好堅持使用默認值,由於在服務器內部有一些計算,他們對續約作出假設。
6.4如何解決Eureka Server不踢出已關停的節點的問題
服務器端:
# 關閉自我保護
eureka.server.enable-self-preservation:false
# 縮小清理間隔(單位毫秒,默認是60*1000)
eureka.server.eviction-interval-timer-in-ms: 5000
客戶端:
開啓健康檢查(須要spring-boot-starter-actuator依賴)
eureka.client.healthcheck.enabled= true
租期更新時間間隔(默認30秒)
eureka.instance.lease-renewal-interval-in-seconds=10
租期到期時間(默認90秒)
eureka.instance.lease-expiration-duration-in-seconds=30
6.5 Eureka HA配置(假設三個節點)
6.5.1 在hosts文件中加入以下配置
127.0.0.1 peer1
127.0.0.1 peer2
127.0.0.1 peer3
6.5.2 在application.yml中加入如下配置
---
spring:
profiles: peer1
application:
name: EUREKA-HA
server:
port: 8761
eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone:http://peer2:8762/eureka/,http://peer3:8763/eureka/
---
spring:
profiles: peer2
application:
name: EUREKA-HA
server:
port: 8762
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone:http://peer1:8761/eureka/,http://peer3:8763/eureka/
---
spring:
profiles: peer3
application:
name: EUREKA-HA
server:
port: 8763
eureka:
instance:
hostname: peer3
client:
serviceUrl:
defaultZone:http://peer1:8761/eureka/,http://peer2:8762/eureka/
————————————————版權聲明:本文爲CSDN博主「happy19870612」的原創文章。本文收藏主要用於學,感謝happy19870612提供