spring cloud 微服務架構

       Spring Cloud包含多個子項目,Spring Cloud Config(可擴展配置服務)、Spring Cloud Netflix、Spring Cloud CloudFoundry(開源PaaS雲平臺)、Spring Cloud AWS(亞馬遜雲服務平臺)、Spring Cloud Security、Spring Cloud Commons、pring Cloud Zookeeper、Spring Cloud CLI等項目。

 

一、服務註冊中心

1、eureka服務註冊中心

       Spring Cloud Eureka是Spring Cloud Netflix項目下的服務治理模塊。而Spring Cloud Netflix項目是Spring Cloud的子項目之一,主要內容是對Netflix公司一系列開源產品的包裝,它爲Spring Boot應用提供了自配置的Netflix OSS整合。通過一些簡單的註解,開發者就可以快速的在應用中配置一下常用模塊並構建龐大的分佈式系統。它主要提供的模塊包括:服務發現(Eureka),斷路器(Hystrix),智能路由(Zuul),客戶端負載均衡(Ribbon)等。

      創建服務註冊中心eureka-server,引入eureka依賴:

 

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.4.RELEASE</version>
    <relativePath/>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
    </dependency>
</dependencies>
<dependencyManagement>
    <dependencies>
        <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-dependencies</artifactId>
           <version>Dalston.SR1</version>
           <type>pom</type>
           <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

 

 

 

 

 

通過@EnableEurekaServer註解啓動一個服務註冊中心提供給其他應用進行對話。

 

@EnableEurekaServer
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}


在默認設置下,該服務註冊中心也會將自己作爲客戶端來嘗試註冊它自己,所以我們需要禁用它的客戶端註冊行爲,只需要在application.properties配置文件中增加如下信息:

 

 

#應用名
spring.application.name=eureka-server
#tomcat監聽端口
server.port=1001

#eureka主機地址
eureka.instance.hostname=localhost
#禁用eureka客戶端的註冊行爲
eureka.client.register-with-eureka=false
#如果爲true,啓動時報警.  
eureka.client.fetch-registry=false

在http://127.0.0.1:1001/ 可看到eureka的服務管理頁面

 

     

 2、Consul服務治理

 

       Spring Cloud Consul項目是針對Consul的服務治理實現。Consul是一個分佈式高可用的系統,它包含多個組件,但是作爲一個整體,在微服務架構中爲我們的基礎設施提供服務發現和服務配置的工具。它包含了下面幾個特性:

  • 服務發現
  • 健康檢查
  • Key/Value存儲
  • 多數據中心

    Consul自身提供了服務端,所以我們不需要像之前實現Eureka的時候創建服務註冊中心,直接通過下載consul的服務端程序就可以使用。

   consul服務器啓動命令:

consul agent -dev

 

服務管理界面:http://127.0.0.1:8500

 

二、服務提供方

1、向eureka註冊

      服務的客戶端,並向服務註冊中心註冊自己

     創建一個基本的Spring Boot應用。命名爲eureka-client,在pom.xml中,加入如下配置:

<parent> 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</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>Dalston.SR1</version>
           <type>pom</type>
           <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

 

   配置文件配置:

 

#指定微服務的名稱後續在調用的時候只需要使用該名稱就可以進行服務的訪問
spring.application.name=eureka-client
#修改tomcat端口爲2001
server.port=2001
#指定服務註冊中心的位置
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/


並在服務提供方中嘗試着提供一個接口來獲取當前所有的服務信息。

 

@RestController
public class DcController {
	
	private final Logger log=Logger.getLogger(getClass());
	
	@Autowired
	private DiscoveryClient discoveryClient;//通過DiscoveryClient對象,在日誌中打印出服務實例的相關內容。
    
	@RequestMapping(value="/dc")
	public String dc(){
		String services="eclipse services:"+discoveryClient.getServices();
		log.info(services);
		return services;
	}
}


最後在應用主類中通過加上@EnableDiscoveryClient註解,該註解能激活Eureka中的DiscoveryClient實現,這樣才能實現Controller中對服務信息的輸出。

 

 

@EnableEurekaClient
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}

 

 

 

 

 

2、向consul註冊

 接口和之前一樣,只要改變依賴和配置信息就行

   添加Spring Cloud Consul依賴如下:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

修改application.properties配置文件如下:

 

#consul服務註冊與發現
spring.application.name=consul-client
spring.cloud.consul.host=127.0.0.1
spring.cloud.consul.port=8500

 

 

 

 

三、服務消費方

1、使用Spring Cloud提供的負載均衡器客戶端接口LoadBalancerClient來實現服務的消費

創建一個服務消費者工程,命名爲:eureka-consumer。並在pom.xml中引入依賴:

 

<parent> 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.3.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
  
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
</dependencies>

 

配置application.properties,指定eureka註冊中心的地址:

 

spring.application.name=eureka-consumers
server.port=2101
#服務註冊中心地址
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/

 

創建應用主類。初始化RestTemplate,用來真正發起REST請求。@EnableDiscoveryClient註解用來將當前應用加入到服務治理體系中。

 

@EnableDiscoveryClient//註解用來將當前應用加入到服務治理體系中。
@SpringBootApplication
public class Application {

	@Bean
	public RestTemplate restTemplate(){
		return new RestTemplate();
	}
	
	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}


創建一個接口用來消費eureka-client提供的接口:

 

 

 

 

@RestController
public class DcController {

	@Autowired
	private RestTemplate restTemplate;//利用RestTemplate對象實現對服務提供者接口的調用
	@Autowired 
	private LoadBalancerClient loadBalancerClient;
	
	@GetMapping("/consumer")//註解get請求
	public String dc(){
		//通過loadBalancerClient的choose負載均衡的選出一個eureka-client的服務實例
		ServiceInstance serviceInstance=loadBalancerClient.choose("eureka-client");
		String url="http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/dc";
		System.out.println(url);
		return restTemplate.getForObject(url, String.class);
	}
}

    

        可以看到這裏,我們注入了LoadBalancerClientRestTemplate,並在/consumer接口的實現中,先通過loadBalancerClientchoose函數來負載均衡的選出一個eureka-client的服務實例,這個服務實例的基本信息存儲在ServiceInstance中,然後通過這些對象中的信息拼接出訪問/dc接口的詳細地址,最後再利用RestTemplate對象實現對服務提供者接口的調用。

 

 

2、Spring Cloud Ribbon

       Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端負載均衡的工具。它是一個基於HTTP和TCP的客戶端負載均衡器。它可以通過在客戶端中配置ribbonServerList來設置服務端列表去輪詢訪問以達到均衡負載的作用。

 

導入依賴:

 

<parent> 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.3.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
   
    <!-- 負載均衡的工具 -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-ribbon</artifactId>
      <version>1.3.1.RELEASE</version>
    </dependency>
  </dependencies>

 


 

application.properties配置文件的配置不變,還是從eureka中拿服務。

 

修改應用主類。爲RestTemplate增加@LoadBalanced註解:

 

@EnableDiscoveryClient
@SpringBootApplication
public class Application {

	@Bean
	@LoadBalanced
	public RestTemplate restTemplate(){
		return new RestTemplate();
	}
	
	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}


修改Controller。去掉原來通過LoadBalancerClient選取實例和拼接URL的步驟,直接通過RestTemplate發起請求。

 

 

@RestController
public class DcController {

	@Autowired
	private RestTemplate restTemplate;
	
	@GetMapping("/consumer")
	public String dc(){
		return restTemplate.getForObject("http://eureka-client/dc", String.class);
	}
	/*
	 * Spring Cloud Ribbon有一個攔截器,它能夠在這裏進行實際調用的時候,自動的去選取服務實例,
	 * 並將實際要請求的IP地址和端口替換這裏的服務名,從而完成服務接口的調用。
	 */
}

 

 

 

3、Spring Cloud Feign

       Spring Cloud Feign是一套基於Netflix Feign實現的聲明式服務調用客戶端。它使得編寫Web服務客戶端變得更加簡單。我們只需要通過創建接口並用註解來配置它既可完成對Web服務接口的綁定。它具備可插拔的註解支持,包括Feign註解、JAX-RS註解。它也支持可插拔的編碼器和解碼器。Spring Cloud Feign還擴展了對Spring MVC註解的支持,同時還整合了Ribbon和Eureka來提供均衡負載的HTTP客戶端實現。

pom.xml中的依賴:

 

<parent> 

    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-eureka</artifactId>
      <version>1.3.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-feign</artifactId>
      <version>1.3.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  </dependencies>

 

 


 

 

 

修改應用主類。通過@EnableFeignClients註解開啓掃描Spring Cloud Feign客戶端的功能:

 

 

@EnableFeignClients//註解開啓掃描Spring Cloud Feign客戶端的功能
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}


創建一個Feign的客戶端接口定義。使用@FeignClient註解來指定這個接口所要調用的服務名稱,接口中定義的各個函數使用Spring MVC的註解就可以來綁定服務提供方的REST接口,比如下面就是綁定eureka-client服務的/dc接口的例子:

 

 

/*
 * 通過@FeignClient定義的接口來統一的生成我們需要依賴的微服務接口。而在具體使用的時候就跟調用本地方法一點的進行調用即可
 */
@FeignClient("eureka-client")//使用@FeignClient註解來指定這個接口所要調用的服務名稱
public interface DcClient {

	@GetMapping("/dc")//接口中定義的各個函數使用Spring MVC的註解就可以來綁定服務提供方的REST接口
	public String consumer();
}


修改Controller。通過定義的feign客戶端來調用服務提供方的接口:

 

 

 

 

@RestController
public class DcController {

	@Autowired
	private DcClient dcClient;
	
	@GetMapping("/consumer")
	public String consumer(){
		return dcClient.consumer();
	}
}

 

 

 

四、配置中心

       Spring Cloud Config爲服務端和客戶端提供了分佈式系統的外部化配置支持。配置服務器爲各應用的所有環境提供了一箇中心化的外部配置。配置服務器默認採用git來存儲配置信息,這樣就有助於對環境配置進行版本管理,並且可以通過git客戶端工具來方便的管理和訪問配置內容。

 

1、構建服務端Config-Server

     pom.xml中引入spring-cloud-config-server依賴,完整依賴配置如下:

 

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.4.RELEASE</version>
    <relativePath/>
  </parent>
<dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
    
    <!-- config-server也註冊爲服務,這樣所有客戶端就能以服務的方式進行訪問 -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    
  </dependencies>
  <dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Brixton.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>


application.properties中配置服務信息以及git信息,例如:

 

 

#服務器應用名
spring.application.name=config-server
#遠程倉庫地址
spring.cloud.config.server.git.uri=https://github.com/jlzl123/config-repo-demo.git
spring.cloud.config.server.git.username=jlzl123
spring.cloud.config.server.git.password=*******
          
server.port:1201

#eureka註冊中心配置
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/

 

spring.cloud.config.server.git.uri是git遠程配置文件倉庫地址

 

創建Spring Boot的程序主類,並添加@EnableConfigServer註解,開啓Config Server:

 

package org.config.server.git;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableDiscoveryClient//用來將config-server註冊到上面配置的服務註冊中心上去。
@EnableConfigServer//開啓Spring Cloud Config的服務端功能。
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}


Spring Cloud Config也提供本地存儲配置的方式。我們只需要設置屬性spring.profiles.active=native,Config Server會默認從應用的src/main/resource目錄下檢索配置文件。也可以通過spring.cloud.config.server.native.searchLocations=file:F:/properties/屬性來指定配置文件的位置。雖然Spring Cloud Config提供了這樣的功能,但是爲了支持更好的管理內容和版本控制的功能,還是推薦使用git的方式。

 

 

 

 

完成上面後我們就可以通過URL訪問遠程配置信息了:

http://localhost:1201/config-client-dev/dev/master

 

 

URL與配置文件的映射關係如下:

 

  • /{application}/{profile}[/{label}]
  • /{application}-{profile}.yml
  • /{label}/{application}-{profile}.yml
  • /{application}-{profile}.properties
  • /{label}/{application}-{profile}.properties

上面的url會映射{application}-{profile}.properties對應的配置文件,{label}對應git上不同的分支,默認爲master。profile是配置文件對應的不同開發環境。

 

 

 

比如:要訪問config-label-test分支,didispace應用的prod環境,可以通過這個url:http://localhost:7001/didispace/prod/config-label-test。

刷新配置:bus/refresh

 

2、構建客戶端config-client

pom.xml中引入spring-cloud-starter-config依賴,完整依賴關係如下:

 

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
			<exclusions><!-- 排除對[spring-boot-starter-logging]的依賴,解決log4j依賴jar包衝突 -->
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j</artifactId>
			<version>1.3.8.RELEASE</version>
		</dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Brixton.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
     
    <!--這個元素描述了項目相關的所有資源路徑列表,例如和項目相關的屬性文件,這些資源被包含在最終的打包文件裏。-->
	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>

 

 

 

 

 

創建Spring Boot啓動主類,並利用@EnableDiscoveryClient註解將客戶端以服務的形式註冊到eureka再服務註冊中心。

 

package org.config.client;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}


創建bootstrap.properties配置,來指定config server,例如:

 

 

 

 

#遠程配置倉庫配置文件名
spring.application.name=config-client-dev,test,config-test,datasource
#配置中心服務器地址
spring.cloud.config.uri=http://localhost:1201/
#當前配置環境,相同配置文件的不同環境,通過profile區分
spring.cloud.config.profile=dev
#git倉庫分支,默認爲master
spring.cloud.config.label=master
server.port=2003
#git的yml配置文件的格式好象有問題,最好用properties
#eureka註冊中心配置
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/


這裏需要格外注意:上面這些屬性必須配置在bootstrap.properties中,config部分內容才能被正確加載。因爲config的相關配置會先於application.properties,而bootstrap.properties的加載也是先於application.properties

 

 

 

即配置客戶端啓動,先加載bootstrap.properties配置文件,然後從遠程配置倉庫獲取配置,所以這些配置會優於application.properties的配置被容器加載,即遠程倉庫的配置會覆蓋掉application的配置。這樣,就可以直接把項目配置文件寫在遠程倉庫。

注意,springboot並不加載bootstrap.properties,只用config client的jar封裝ConfigServerBootstrapConfiguration先加bootstrap.properties。

 

創建一個Rest Api來返回配置中心的from屬性,具體如下:

 

@RefreshScope
@RestController
class TestController {
    @Value("${from}")
    private String from;
    @RequestMapping("/from")
    public String from() {
        return this.from;
    }
}

 

 

 

 

 

也可以通過@ConfigurationProperties註解來綁定配置屬性,創建如下配置類:

 

package org.config.client.dataSourceConfig;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
//@ConfigurationProperties註解綁定屬性,prefix爲屬性名的前綴,如:jdbc.url
@ConfigurationProperties(prefix="jdbc",ignoreUnknownFields=false)
public class DataSourceProperties {

	private String driver;
	private String url;
	private String username;
	private String password;
	
	public String getDriver() {
		return driver;
	}
	public void setDriver(String driver) {
		this.driver = driver;
	}
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}	
}

 

 

 

 

 

五、斷路器

        在分佈式架構中,當某個服務單元發生故障(類似用電器發生短路)之後,通過斷路器的故障監控(類似熔斷保險絲),向調用方返回一個錯誤響應,而不是長時間的等待。這樣就不會使得線程因調用故障服務被長時間佔用不釋放,避免了故障在分佈式系統中的蔓延。

        在Spring Cloud中使用了Hystrix 來實現斷路器的功能。Hystrix是Netflix開源的微服務框架套件之一,該框架目標在於通過控制那些訪問遠程系統、服務和第三方庫的節點,從而對延遲和故障提供更強大的容錯能力。Hystrix具備擁有回退機制和斷路器功能的線程和信號隔離,請求緩存和請求打包,以及監控和配置等功能。

1、eureka-consumer-ribbon添加斷路器

pom.xml中引入依賴hystrix依賴:

 

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-hystrix</artifactId>
      <version>1.3.1.RELEASE</version>
    </dependency>


在eureka-consumer-ribbon的主類Application中使用@EnableCircuitBreaker註解開啓斷路器功能:

 

 

package org.eureka.consumer.ribbon;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@SpringBootApplication
@EnableCircuitBreaker//@EnableCircuitBreaker註解開啓斷路器功能
public class Application {

	@Bean
	@LoadBalanced//加載負載均衡
	public RestTemplate restTemplate(){
		return new RestTemplate();
	}
	
	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}


改造原來的服務消費方式,新增ComputeService類,在使用ribbon消費服務的函數上增加@HystrixCommand註解來指定回調方法。

 

 

 

 

package org.eureka.consumer.ribbon.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@Service
public class ComputeService {

	@Autowired
	private RestTemplate restTemplate;
	
	//註解實現斷路器回調,當調用服務失敗後,調用addServiceFallback方法
	@HystrixCommand(fallbackMethod="addServiceFallback")
	public String addService(){
		return restTemplate.getForObject("http://eureka-client/add?a=10&b=20", String.class);
	}
	
	@SuppressWarnings("unused")
	private String addServiceFallback(){
		return "error";
	}
}


提供rest接口的Controller改爲調用ComputeService的addService

 

 

 

 

	@RequestMapping(value="/addService",method=RequestMethod.GET)
	public String addService(){
		return computeService.addService();
	}

這樣添加斷路器後,調用/addService接口服務,當服務提供者斷線或獲取服務超時,會調用fallback。

 

 

2、eureka-consumer-feign

fegin集成了hystrix斷路器,只要在配置文件中配置下面參數開啓就可以了,不用導入hystrix依賴。

 

feign.hystrix.enabled=true


使用@FeignClient註解中的fallback屬性指定回調類:

 

 

package org.eureka.consumer.feign.service;

import org.eureka.consumer.feign.fallback.DcClientHystrix;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/*
 * 通過@FeignClient定義的接口來統一的生成我們需要依賴的微服務接口。而在具體使用的時候就跟調用本地方法一點的進行調用即可
 */
//使用@FeignClient註解來指定這個接口所要調用的服務名稱,如過開啓了斷路器,通過fallback參數指定熔斷後的回調類,該回調類實現當前接口
@FeignClient(value="eureka-client",fallback=DcClientHystrix.class)
public interface DcClient {

	@GetMapping("/dc")//接口中定義的各個函數使用Spring MVC的註解就可以來綁定服務提供方的REST接口
	public String consumer();
}


創建回調類ComputeClientHystrix,實現@FeignClient的接口,此時實現的方法就是對應@FeignClient接口中映射的fallback函數。

 

 

 

 

package org.eureka.consumer.feign.fallback;

import org.eureka.consumer.feign.service.DcClient;
import org.springframework.stereotype.Component;

/*
 * 回調類,實現@FeignClient的接口,此時實現的方法就是對應@FeignClient接口中映射的fallback函數。
 */
@Component
public class DcClientHystrix implements DcClient{

	@Override
	public String consumer() {
		// TODO Auto-generated method stub
		return "調用失敗,執行回調函數";
	}

}

 

 

 

 

六、服務網關

    服務網關對微服務架構的作用:

  • 不僅僅實現了路由功能來屏蔽諸多服務細節,更實現了服務級別、均衡負載的路由。
  • 實現了接口權限校驗與微服務業務邏輯的解耦。通過服務網關中的過濾器,在各生命週期中去校驗請求的內容,將原本在對外服務層做的校驗前移,保證了微服務的無狀態性,同時降低了微服務的測試難度,讓服務本身更集中關注業務邏輯的處理。
  • 實現了斷路器,不會因爲具體微服務的故障而導致服務網關的阻塞,依然可以對外服務。

 

使用Spring Cloud Netflix中的Zuul做服務網關,引入依賴spring-cloud-starter-zuul、spring-cloud-starter-eureka,如果不是通過指定serviceId的方式,eureka依賴不需要,但是爲了對服務集羣細節的透明性,還是用serviceId來避免直接引用url的方式吧。

 

 

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>


應用主類使用@EnableZuulProxy註解開啓Zuul

 

 

package org.api.gateway;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@EnableZuulProxy//註解開啓Zuul服務網關
@SpringCloudApplication//它整合了@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker等
public class GatewayApplication {

	public static void main(String[] args) {
		new SpringApplicationBuilder(GatewayApplication.class).web(true).run(args);
	}
}

 

 

 

 

 

application.properties配置文件配置:

 

spring.application.name=api-gateway
server.port=5555

#配置註冊服務中心地址,如果下面路由不用serviceId就不用配置,也不用導入eureka依賴
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/

#路由配置
#配置屬性zuul.routes.api-a-url.path中的api-a-url部分爲路由的名字,可以任意定義.
zuul.routes.api-a.path=/eureka-client/**
zuul.routes.api-a.url=http://127.0.0.1:2001/

zuul.routes.ribbon.path=/ribbon/**
zuul.routes.ribbon.serviceId=eureka-ribbon-consumer

zuul.routes.api-b.path=/feign/**
zuul.routes.api-b.serviceId=eureka-feign-consumer


這樣/api-a/**的訪問都映射到http://localhost:2001/上,也就是說當我們訪問http://localhost:5555/api-a/add?a=1&b=2的時候,Zuul會將該請求路由到:http://localhost:2222/add?a=1&b=2上。

 

 

 

 

       服務網關還可以做服務過濾,在完成了服務路由之後,我們對外開放服務還需要一些安全措施來保護客戶端只能訪問它應該訪問到的資源。所以我們需要利用Zuul的過濾器來實現我們對外服務的安全控制。在服務網關中定義過濾器只需要繼承ZuulFilter抽象類實現其定義的四個抽象函數就可對請求進行攔截與過濾。

 

package org.api.gateway.filter;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Component;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

@Component//需要實例化該過濾器
public class AccessFilter extends ZuulFilter{

	/*
	 * shouldFilter:返回一個boolean類型來判斷該過濾器是否要執行
	 */
	@Override
	public boolean shouldFilter() {
		// TODO Auto-generated method stub
		return true;
	}

	/*
	 * filterOrder:通過int值來定義過濾器的執行順序
	 */
	@Override
	public int filterOrder() {
		// TODO Auto-generated method stub
		return 0;
	}

	/*
	 * filterType:返回一個字符串代表過濾器的類型,在zuul中定義了四種不同生命週期的過濾器類型,具體如下:   
             pre:可以在請求被路由之前調用
             routing:在路由請求時候被調用
             post:在routing和error過濾器之後被調用
             error:處理請求時發生錯誤時被調用
	 */
	@Override
	public String filterType() {
		// TODO Auto-generated method stub
		return "pre";
	}

	/*
	 * run:過濾器的具體邏輯。
	 */
	@Override
	public Object run() {
		// TODO Auto-generated method stub
		RequestContext ctx=RequestContext.getCurrentContext();
		HttpServletRequest request=ctx.getRequest();
		String token=request.getParameter("token");
		if(token==null){
			ctx.setSendZuulResponse(false);
//			ctx.getResponse().setCharacterEncoding("utf-8");
			//設置瀏覽器解碼
			ctx.getResponse().setContentType("text/html;charset=utf-8");
			ctx.setResponseBody("非法訪問!!");
			ctx.setResponseStatusCode(401);
			return null;
		}
		return null;
	}
}

 

 

網關作爲爲外部提供訪問內部服務的接口,外網通過該接口訪問內部服務,所以只要在網關統一做認證和鑑權就好了,而內部服務都部署在內網,不提供外網訪問接口,網網通過內網映射路由到相關服務。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章