參考:http://blog.csdn.net/a60782885/article/details/69267934
服務發現:(Eureka)
前面提到,Eureka分爲服務端和客戶端,服務端是服務註冊中心,而客戶端是提供服務的。
創建服務註冊中心(服務端)
首先在pom文件中加入以下依賴:
-
<parent>
-
<span style="white-space:pre"> </span><groupId>org.springframework.boot</groupId>
-
<span style="white-space:pre"> </span><artifactId>spring-boot-starter-parent</artifactId>
-
<version>1.3.8.RELEASE</version>
-
<relativePath />
-
</parent>
-
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-test</artifactId>
-
<scope>test</scope>
-
</dependency>
-
<span style="white-space:pre"> </span><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>Brixton.SR7</version>
-
<type>pom</type>
-
<scope>import</scope>
-
</dependency>
-
</dependencies>
-
</dependencyManagement>
在Application文件中增加@EnableEurekaServer註解,表明這是服務端
-
@EnableEurekaServer
-
@SpringBootApplication
-
public class SpringcloudEurekaServerApplication {
-
-
public static void main(String[] args) {
-
SpringApplication.run(SpringcloudEurekaServerApplication.class, args);
-
}
-
}
在默認設置下,該服務註冊中心也會將自己作爲客戶端來嘗試註冊它自己,所以我們需要禁用它的客戶端註冊行爲。在application.properties或yml中禁止客戶端註冊行爲,否則會報錯。
-
server.port=1111
-
-
eureka.client.register-with-eureka=false
-
eureka.client.fetch-registry=false
-
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
服務端就寫好了,可以訪問localhost:1111進行訪問。可以看到,這時候是沒有服務的。
接下來創建客戶端
創建客戶端,並在註冊中心中註冊自己。
還是首先添加依賴:這裏和上面不同的地方只有對Eureka的依賴。
-
<parent>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-parent</artifactId>
-
<version>1.3.8.RELEASE</version>
-
<relativePath />
-
</parent>
-
-
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-eureka</artifactId>
-
</dependency>
-
<span style="white-space:pre"> </span><dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-test</artifactId>
-
<scope>test</scope>
-
</dependency>
-
</dependencies>
-
-
<dependencyManagement>
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-dependencies</artifactId>
-
<version>Brixton.SR7</version>
-
<type>pom</type>
-
<scope>import</scope>
-
</dependency>
-
</dependencies>
-
</dependencyManagement>
在Application文件中加入@EnableDiscoveryClient註解,表明這是一個客戶端。
-
@EnableDiscoveryClient
-
@SpringBootApplication
-
public class SpringcloudEurekaServiceApplication {
-
-
public static void main(String[] args) {
-
SpringApplication.run(SpringcloudEurekaServiceApplication.class, args);
-
}
-
}
然後我們需要向註冊中心中註冊自己,只需要在application.properties或yml文件中增加配置即可:
-
spring.application.name=compute-service
-
server.port=2222
-
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
defaultZone指定了服務註冊中心的位置。
註冊了之後我們就可以在客戶端編寫客戶端提供的服務了。比如這裏提供了add加法服務。
-
@RestController
-
public class ComputeController {
-
-
private final Logger logger = Logger.getLogger(getClass());
-
-
@Autowired
-
private DiscoveryClient client;
-
-
@RequestMapping(value = "/add" ,method = RequestMethod.GET)
-
public Integer add(@RequestParam Integer a, @RequestParam Integer b) {
-
ServiceInstance instance = client.getLocalServiceInstance();
-
Integer r = a + b;
-
logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r);
-
return r;
-
}
-
}
在提供的服務中,只需要使用DiscoveryClient就可以得到關於調用者的信息。
客戶端也完成了,這個時候可以再次訪問localhost:1111。
可以發現,客戶端已經在服務端上註冊了。
我們看一下架構圖:
很顯然,工作流程是這樣的:Service Consumer -> Proxy Server -> Client(Load Balancer) -> Servie Discovery -> Target Service
我們編寫好了上圖的紅色的部分,接下來應該對服務進行負載均衡了,也就是對客戶端的負載均衡,我們使用Ribbon來完成這件事。
首先我們啓動服務端和兩個客戶端,客戶端分別開在2222和2223端口。即:
開啓了兩個服務,接下來我們使用Ribbon進行負載均衡。
新建一個項目。
在pom文件中添加如下依賴:
-
<parent>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-parent</artifactId>
-
<version>1.3.5.RELEASE</version>
-
<relativePath/>
-
</parent>
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-ribbon</artifactId>
-
</dependency>
-
<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>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-test</artifactId>
-
<scope>test</scope>
-
</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>
使用@EnableDiscoveryClient註解在將其註冊在註冊中心。並且提供一個返回RestTemplate的bean實例,使用@LoadBalanced註解對這個實例進行負載均衡這樣,使用這個實例去獲取服務的時候,就會幫我們進行負載均衡了。
-
@SpringBootApplication
-
@EnableDiscoveryClient
-
public class RibbonApplication {
-
-
@Bean
-
@LoadBalanced
-
RestTemplate restTemplate() {
-
return new RestTemplate();
-
}
-
-
public static void main(String[] args) {
-
SpringApplication.run(RibbonApplication.class, args);
-
}
-
}
在application.properties中註冊:
-
spring.application.name=ribbon-consumer
-
server.port=3333
-
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
創建了bean實例,我們就可以通過@Autowired來獲取這個RestTemplate了,並且使用這個實例去調用服務。
-
@RestController
-
public class ConsumerController {
-
-
@Autowired
-
RestTemplate restTemplate;
-
-
@RequestMapping(value = "/add", method = RequestMethod.GET)
-
public String add() {
-
return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody();
-
}
-
}
啓動這個項目,並且訪問多次localhost:3333/add可以發現確實進行了負載均衡。
在微服務架構中,服務被拆分成一個一個的單元,各個單元之間相互依賴調用。如果某個單元由於網絡原因或者別的單元故障奔潰,那麼會直接導致調用方對外的服務也發生延遲。若此時調用方的請求不斷增加,最後就會出現因等待出現故障的依賴方響應而形成任務積壓,最終導致自身服務的癱瘓,這樣的結構相對於傳統的web開發模式來說更加可怕,爲了解決這個問題,我們可以採用斷路器。
在分佈式架構中,當某個服務單元發生故障,使用斷路器返回一個錯誤的相應,而不是進行長時間的等待,這樣就不會使得線程被長時間佔用,導致服務器的癱瘓。
Spring Cloud提供了Hystrix這麼一個組件來實現斷路器的功能。
首先啓動Eureka服務端和客戶端。
對Ribbon的項目進行以下修改:
首先在pom文件中增加以下依賴:
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-hystrix</artifactId>
-
</dependency>
在application中增加@EnableCircuitBreaker註解,表明使用斷路器。
-
@SpringBootApplication
-
@EnableDiscoveryClient
-
@EnableCircuitBreaker
-
public class RibbonApplication {
-
-
@Bean
-
@LoadBalanced
-
RestTemplate restTemplate() {
-
return new RestTemplate();
-
}
-
-
public static void main(String[] args) {
-
SpringApplication.run(RibbonApplication.class, args);
-
}
-
}
Hystrix使用消息隊列的方式,如果連接的服務崩潰,則異步回調某個方法進行處理。
新增一個Service類,在這個類中定義好如何訪問,以及失敗之後的返回等。
-
@Service
-
public class ComputeService {
-
-
@Autowired
-
RestTemplate restTemplate;
-
-
@HystrixCommand(fallbackMethod = "addServiceFallback")
-
public String addService() {
-
return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody();
-
}
-
-
public String addServiceFallback() {
-
return "error";
-
}
-
}
這裏重點是@HystrixCommand註解:表明該方法爲hystrix包裹,可以對依賴服務進行隔離、降級、快速失敗、快速重試等等hystrix相關功能
列舉幾個屬性:
1. fallbackMethod 降級方法
2. commandProperties 普通配置屬性,可以配置HystrixCommand對應屬性,例如採用線程池還是信號量隔離、熔斷器熔斷規則等等
3. ignoreExceptions 忽略的異常,默認HystrixBadRequestException不計入失敗
4. groupKey() 組名稱,默認使用類名稱
5. commandKey 命令名稱,默認使用方法名
最後修改一下Controller即可:
-
@RestController
-
public class ConsumerController {
-
-
@Autowired
-
private ComputeService computeService;
-
-
@RequestMapping(value = "/add", method = RequestMethod.GET)
-
public String add() {
-
return computeService.addService();
-
}
-
}
對於斷路器,針對生產環境,,Netflix還給我們準備了一個非常好用的運維工具, 那就是Hystrix Dashboard和Turbine.可以幫助我們更好的觀察。
當客戶端關閉時訪問:
客戶端啓動時訪問:
成功!