SpringCloud-服務間通訊方式

山丘i發表於2020-12-08

接下來在整個微服務架構中,我們比較關心的就是服務間的服務改如何呼叫,有哪些呼叫方式?

總結:在springcloud中服務間呼叫方式主要是使用 http restful方式進行服務間呼叫

1. 基於RestTemplate的服務呼叫

在上面的基礎上,使用的是consul註冊,pom.xml檔案

<?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.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.md</groupId>
    <artifactId>04-products9998</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>04-products9998</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--引入consul依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

        <!-- 這個包是用做健康度監控的-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>



    </dependencies>

    <!--全域性管理springcloud版本,並不會引入具體依賴-->
    <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>

1. 說明

  • spring框架提供的RestTemplate類可用於在應用中呼叫rest服務,它簡化了與http服務的通訊方式,統一了RESTful的標準,封裝了http連結, 我們只需要傳入url及返回值型別即可。相較於之前常用的HttpClient,RestTemplate是一種更優雅的呼叫RESTful服務的方式。

2. RestTemplate 服務呼叫

  1. 建立兩個服務並註冊到consul註冊中心中
  • users 代表使用者服務 埠為 9999
  • products 代表商品服務 埠為 9998
    `注意:這裡服務僅僅用來測試,沒有實際業務意義


3. 在商品服務中提供服務方法

@RestController
@Slf4j
public class ProductController {
    @Value("${server.port}")
    private int port;
    @GetMapping("/product/findAll")
    public Map<String,Object> findAll(){
        log.info("商品服務查詢所有呼叫成功,當前服務埠:[{}]",port);
        Map<String, Object> map = new HashMap<String,Object>();
        map.put("msg","服務呼叫成功,服務提供埠為: "+port);
        map.put("status",true);
        return map;
    }
}

4. 在使用者服務中使用restTemplate進行呼叫

@RestController
@Slf4j
public class UserController {
    @GetMapping("/user/findAll")
    public String findAll(){
        log.info("呼叫使用者服務...");
        //1. 使用restTemplate呼叫商品服務
        RestTemplate restTemplate = new RestTemplate();
        // get請求getxxx,post請求postxxx
        // 引數1:請求路徑,引數2:返回的型別是String的
        String forObject = restTemplate.getForObject("http://localhost:9998/product/findAll", 
                                                     String.class);
        return forObject;
    }
}

5. 啟動服務


6. 測試服務呼叫

7. 總結

  • rest Template是直接基於服務地址呼叫沒有在服務註冊中心獲取服務,也沒有辦法完成服務的負載均衡如果需要實現服務的負載均衡需要自己書寫服務負載均衡策略。
    restTemplate直接呼叫存在問題
  • 1.直接使用restTemplate方式呼叫沒有經過服務註冊中心獲取服務地址,程式碼寫死不利於維護,當服務當機時不能高效剔除
  • 2.呼叫服務時沒有負載均衡需要自己實現負載均衡策略

2. 基於Ribbon的服務呼叫

0. 說明

  • 官方網址: https://github.com/Netflix/ribbon
  • Spring Cloud Ribbon是一個基於HTTP和TCP的客戶端負載均衡工具,它基於Netflix Ribbon實現。通過Spring Cloud的封裝,可以讓我們輕鬆地將面向服務的REST模版請求自動轉換成客戶端負載均衡的服務呼叫。

再建立一個服務類,和上面的服務類一樣,只有埠不同

server.port=9997
spring.application.name=products
spring.cloud.consul.port=8500
spring.cloud.consul.host=localhost

其他一樣

@RestController
@Slf4j
public class ProductController {
    @Value("${server.port}")
    private int port;
    @GetMapping("/product/findAll")
    public Map<String,Object> findAll(){
        log.info("商品服務查詢所有呼叫成功,當前服務埠:[{}]",port);
        Map<String, Object> map = new HashMap<String,Object>();
        map.put("msg","服務呼叫成功,服務提供埠為: "+port);
        map.put("status",true);
        return map;
    }
}

1. Ribbon 服務呼叫

# 1.專案中引入依賴
- 說明: 
	1.如果使用的是eureka client 和 consul client,無須引入依賴,因為在eureka,consul中預設整合了ribbon元件
	2.如果使用的client中沒有ribbon依賴需要顯式引入如下依賴
<!--引入ribbon依賴-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
# 2.檢視consul client中依賴的ribbon

# 3.使用restTemplate + ribbon進行服務呼叫
- 使用discovery client  進行客戶端呼叫
- 使用loadBalanceClient 進行客戶端呼叫
- 使用@loadBalanced     進行客戶端呼叫
# 3.1 使用discovery Client形式呼叫
@Autowired
private DiscoveryClient discoveryClient;

//獲取服務列表,返回全部的,服務id,上面的products
List<ServiceInstance> products = discoveryClient.getInstances("服務ID");
for (ServiceInstance product : products) {
  log.info("服務主機:[{}]",product.getHost());
  log.info("服務埠:[{}]",product.getPort());
  log.info("服務地址:[{}]",product.getUri());
  log.info("====================================");
}
# 3.2 使用loadBalance Client形式呼叫
@Autowired
private LoadBalancerClient loadBalancerClient;
//根據負載均衡策略選取某一個服務呼叫,服務id=products
ServiceInstance product = loadBalancerClient.choose("服務ID");//地址  預設輪詢策略
log.info("服務主機:[{}]",product.getHost());
log.info("服務埠:[{}]",product.getPort());
log.info("服務地址:[{}]",product.getUri());
# 3.3 使用@loadBalanced(常用)
//1.整合restTemplate + ribbon
@Configuration
public class RestTemplateConfig {

    // 在工廠中建立一個restTemplate物件
    @Bean
    // 加上這個註解代表當前的restTemplate物件帶有ribbon負載均衡
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

//2.呼叫controller服務位置注入RestTemplate
// 此時的restTemplate就具有了負載均衡的功能
@Autowired
private RestTemplate restTemplate;

//3.呼叫
@RestController
@Slf4j
public class UserController {
    @Autowired
	private RestTemplate restTemplate;
    
    @GetMapping("/user/findAll")
    public String findAll(){
        // 服務id就是在配置檔案中的當前服務名products
        String forObject = restTemplate.getForObject("http://服務ID/product/findAll", 
                                                     String.class);
        return forObject;
    }
}

預設的輪訓策略,也就是當前訪問的9998,之後是9997,迴圈訪問

2. Ribbon負載均衡策略

ribbon負載均衡演算法

  • RoundRobinRule 輪訓策略 按順序迴圈選擇 Server

  • RandomRule 隨機策略 隨機選擇 Server

  • AvailabilityFilteringRule 可用過濾策略
    `會先過濾由於多次訪問故障而處於斷路器跳閘狀態的服務,還有併發的連線數量超過閾值的服務,然後對剩餘的服務列表按照輪詢策略進行訪問

  • WeightedResponseTimeRule 響應時間加權策略
    `根據平均響應的時間計算所有服務的權重,響應時間越快服務權重越大被選中的概率越高,剛啟動時如果統計資訊不足,則使用RoundRobinRule策略,等統計資訊足夠會切換到

  • RetryRule 重試策略
    `先按照RoundRobinRule的策略獲取服務,如果獲取失敗則在制定時間內進行重試,獲取可用的服務。

  • BestAviableRule 最低併發策略
    `會先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務

3.修改服務的預設負載均衡策略

# 1.修改服務預設隨機策略
- 服務id.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
	`下面的products為服務的唯一標識
products.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

4.Ribbon停止維護

# 1.官方停止維護說明
- https://github.com/Netflix/ribbon

相關文章