天天看點

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

1.eureka 和 “服務提供者”的高可用

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

1.1 item-service 高可用

啟動參數 --server.port 可以覆寫yml中的端口配置

1.1.1 配置啟動參數

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試
Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

1.1.2 啟動測試

通路 eureka 檢視 item-service 注冊資訊

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

通路兩個端口測試

http://localhost:8001/35
http://localhost:8002/35
           

1.2 eureka 高可用

添加兩個伺服器的 profile 配置檔案

1.2.1 application-eureka1.yml

eureka:
  instance:
    hostname: eureka1
  client:
    register-with-eureka: true  #profile的配置會覆寫公用配置
    fetch-registry: true        #profile的配置會覆寫公用配置
    service-url: 
      defaultZone: http://eureka2:2002/eureka  #eureka1啟動時向eureka2注冊
           

1.2.2 application-eureka2.yml

eureka:
  instance:
    hostname: eureka2
  client:
    register-with-eureka: true  #profile的配置會覆寫公用配置
    fetch-registry: true        #profile的配置會覆寫公用配置
    service-url: 
      defaultZone: http://eureka1:2001/eureka  #eureka2啟動時向eureka1注冊
      
           

1.2.3 java源檔案

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

1.2.4 配置啟動參數 --spring.profiles.active 和 --server.port

eureka1 啟動參數:

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試
Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

eureka2 啟動參數:

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

如果在指令行運作,可以在指令行中添加參數:

1.2.5 通路 eureka 伺服器,檢視注冊資訊

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試
Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

1.2.6 eureka用戶端注冊時,向兩個伺服器注冊

修改以下微服務

sp02-itemservice

sp03-userservice

sp04-orderservice

#yml
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
           

當一個 eureka 服務當機時,仍可以連接配接另一個 eureka 服務

2.ribbon 服務消費者

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

ribbon 提供了負載均衡和重試功能, 它底層是使用 RestTemplate 進行 Rest api 調用

2.1 RestTemplate

RestTemplate 是SpringBoot提供的一個Rest遠端調用工具

它的常用方法:

getForObject() - 執行get請求
postForObject() - 執行post請求
           

2.1.1 之前的系統結構是浏覽器直接通路背景服務

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

2.2.2 後面我們通過一個Demo項目示範 Spring Cloud 遠端調用

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

2.3 下面我們先不使用ribbon, 單獨使用RestTemplate來執行遠端調用

2.3.1 建立 sp06-ribbon 項目

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

2.3.2 添加依賴

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

2.3.3 pom.xml

eureka-client 中已經包含 ribbon 依賴

需要添加 sp01-commons 依賴

<?xml version="1.0" encoding="UTF-8"?>
<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.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>sp06-ribbon</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sp06-ribbon</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR10</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>sp01-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

           

2.3.4 application.yml

spring:
  application:
    name: ribbon
    
server:
  port: 3001
  
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
           

2.3.5 java源檔案

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

2.3.6 主程式

建立 RestTemplate 執行個體

RestTemplate 是用來調用其他微服務的工具類,封裝了遠端調用代碼,提供了一組用于遠端調用的模闆方法,例如:getForObject()、postForObject() 等
package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {
	
	//建立 RestTemplate 執行個體,并存入 spring 容器
	@Bean
	public RestTemplate getRestTemplate() {
		return new RestTemplate();
	}

	public static void main(String[] args) {
		SpringApplication.run(Sp06RibbonApplication.class, args);
	}

}
           

2.3.7 RibbonController

package cn.tedu.sp06.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;

@RestController
public class RibbonController {
	@Autowired
	private RestTemplate rt;
	
	@GetMapping("/item-service/{orderId}")
	public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
	    //向指定微服務位址發送 get 請求,并獲得該服務的傳回結果 
	    //{1} 占位符,用 orderId 填充
		return rt.getForObject("http://localhost:8001/{1}", JsonResult.class, orderId);
	}

	@PostMapping("/item-service/decreaseNumber")
	public JsonResult decreaseNumber(@RequestBody List<Item> items) {
	    //發送 post 請求
		return rt.postForObject("http://localhost:8001/decreaseNumber", items, JsonResult.class);
	}

	/
	
	@GetMapping("/user-service/{userId}")
	public JsonResult<User> getUser(@PathVariable Integer userId) {
		return rt.getForObject("http://localhost:8101/{1}", JsonResult.class, userId);
	}

	@GetMapping("/user-service/{userId}/score") 
	public JsonResult addScore(
			@PathVariable Integer userId, Integer score) {
		return rt.getForObject("http://localhost:8101/{1}/score?score={2}", JsonResult.class, userId, score);
	}
	
	/
	
	@GetMapping("/order-service/{orderId}")
	public JsonResult<Order> getOrder(@PathVariable String orderId) {
		return rt.getForObject("http://localhost:8201/{1}", JsonResult.class, orderId);
	}

	@GetMapping("/order-service")
	public JsonResult addOrder() {
		return rt.getForObject("http://localhost:8201/", JsonResult.class);
	}
}

           

2.3.8 啟動服務,并通路測試

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試
http://eureka1:2001

http://localhost:3001/item-service/35
http://localhost:3001/item-service/decreaseNumber
使用postman,POST發送以下格式資料:
[{"id":1, "name":"abc", "number":23},{"id":2, "name":"def", "number":11}]

http://localhost:3001/user-service/7
http://localhost:3001/user-service/7/score?score=100

http://localhost:3001/order-service/123abc
http://localhost:3001/order-service/
           

3. ribbon 負載均衡和重試

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試
Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

3.1 Ribbon 負載均衡

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

3.2 修改 sp06-ribbon 項目

3.2.1添加 ribbon 起步依賴(可選)

eureka 依賴中已經包含了 ribbon

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
           

3.3 RestTemplate 設定 @LoadBalanced

package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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;

@SpringBootApplication
public class Sp06RibbonApplication {
	
	@LoadBalanced //負載均衡注解
	@Bean
	public RestTemplate getRestTemplate() {
		return new RestTemplate();
	}

	public static void main(String[] args) {
		SpringApplication.run(Sp06RibbonApplication.class, args);
	}

}
           

3.4 通路路徑設定為服務id

package cn.tedu.sp06.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;

@RestController
public class RibbonController {
	@Autowired
	private RestTemplate rt;
	
	@GetMapping("/item-service/{orderId}")
	public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
	    //這裡伺服器路徑用 service-id 代替,ribbon 會向服務的多台叢集伺服器分發請求
		return rt.getForObject("http://item-service/{1}", JsonResult.class, orderId);
	}

	@PostMapping("/item-service/decreaseNumber")
	public JsonResult decreaseNumber(@RequestBody List<Item> items) {
		return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
	}

	/
	
	@GetMapping("/user-service/{userId}")
	public JsonResult<User> getUser(@PathVariable Integer userId) {
		return rt.getForObject("http://user-service/{1}", JsonResult.class, userId);
	}

	@GetMapping("/user-service/{userId}/score") 
	public JsonResult addScore(
			@PathVariable Integer userId, Integer score) {
		return rt.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class, userId, score);
	}
	
	/
	
	@GetMapping("/order-service/{orderId}")
	public JsonResult<Order> getOrder(@PathVariable String orderId) {
		return rt.getForObject("http://order-service/{1}", JsonResult.class, orderId);
	}

	@GetMapping("/order-service")
	public JsonResult addOrder() {
		return rt.getForObject("http://order-service/", JsonResult.class);
	}
}

           

3.5 通路測試

通路測試,ribbon 會把請求分發到 8001 和 8002 兩個服務端口上

3.5.1 便于觀看端口

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試
Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試
Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

4. ribbon 重試

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試

4.1 pom.xml 添加 spring-retry 依賴

複制代碼到 pom.xml

<dependency>
	<groupId>org.springframework.retry</groupId>
	<artifactId>spring-retry</artifactId>
</dependency>
           

4.1.1 application.yml 配置 ribbon 重試

spring:
  application:
    name: ribbon
    
server:
  port: 3001
  
eureka:
  client:    
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
      
ribbon:
  MaxAutoRetriesNextServer: 2
  MaxAutoRetries: 1
  OkToRetryOnAllOperations: true

           
ConnectionTimeout
ReadTimeout
OkToRetryOnAllOperations=true
           

預設隻對GET請求重試, 當設定為true時, 對POST等所有類型請求都重試

MaxAutoRetriesNextServer
           

更換執行個體的次數

MaxAutoRetries
           

目前執行個體重試次數,嘗試失敗會更換下一個執行個體

4.2 主程式設定 RestTemplate 的請求工廠的逾時屬性

package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class Sp06RibbonApplication {

	@LoadBalanced
	@Bean
	public RestTemplate getRestTemplate() {
		SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
		f.setConnectTimeout(1000);
		f.setReadTimeout(1000);
		return new RestTemplate(f);
		
		//RestTemplate 中預設的 Factory 執行個體中,兩個逾時屬性預設是 -1,
		//未啟用逾時,也不會觸發重試
		//return new RestTemplate();
	}

	public static void main(String[] args) {
		SpringApplication.run(Sp06RibbonApplication.class, args);
	}

}

           

4.3 item-service 的 ItemController 添加延遲代碼,以便測試 ribbon 的重試機制

package cn.tedu.sp02.item.controller;

import java.util.List;
import java.util.Random;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.service.ItemService;
import cn.tedu.web.util.JsonResult;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
public class ItemController {
	@Autowired
	private ItemService itemService;
	
	@Value("${server.port}")
	private int port;
	
	@GetMapping("/{orderId}")
	public JsonResult<List<Item>> getItems(@PathVariable String orderId) throws Exception {
		log.info("server.port="+port+", orderId="+orderId);

        ///--設定随機延遲
		if(Math.random()<0.6) { 
			long t = new Random().nextInt(5000);
			log.info("item-service-"+port+" - 暫停 "+t);
			Thread.sleep(t);
		}
		///~~
		
		List<Item> items = itemService.getItems(orderId);
		return JsonResult.ok(items).msg("port="+port);
	}
	
	@PostMapping("/decreaseNumber")
	public JsonResult decreaseNumber(@RequestBody List<Item> items) {
		itemService.decreaseNumbers(items);
		return JsonResult.ok();
	}
}

           

4.4 通路,測試 ribbon 重試機制

通過 ribbon 通路 item-service,當逾時,ribbon 會重試請求叢集中其他伺服器

Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試
Spring Cloud Day021.eureka 和 “服務提供者”的高可用2.ribbon 服務消費者3. ribbon 負載均衡和重試4. ribbon 重試
ribbon的重試機制,在 feign 和 zuul 中進一步進行了封裝,後續可以使用feign或zuul的重試機制