SpringCloud:搭建一個簡單Spring Cloud項目


最近在學習springcloud微服務框架,看了很多博客和文章之后,自己模擬一個簡單的業務場景搭建了一個springcloud項目。本次練習包括對springcloud核心組件:eureka、ribbon、hystrix的使用,以及feign和Gateway的簡單嘗試!

模擬業務場景介紹:

​ 假設現在有個訂單服務(order-service)r,要實現訂單支付功能,流程如下:

  • 調用訂單服務的支付接口
  • 訂單服務調用商品服務的扣除庫存接口
  • 訂單服務調用積分服務的增加積分接口

一、項目搭建

1.1、創建一個maven父項目

​ 新建一個空白的maven project

New Project -> Maven -> Next

​ 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.xct</groupId>
    <artifactId>springcloud_2</artifactId>
    <version>1.0-SNAPSHOT</version>

</project>

這個maven 是一個工程包,打包用。

1.2、服務的注冊和發現(Eureka)

eureka說明:

這里借用一下別人描述:https://www.jianshu.com/p/31dfb595170c

咱們來考慮第一個問題:訂單服務想要調用庫存服務、倉儲服務,或者是積分服務,怎么調用?

  • 訂單服務壓根兒就不知道人家庫存服務在哪台機器上啊!他就算想要發起一個請求,都不知道發送給誰,有心無力!
  • 這時候,就輪到Spring Cloud Eureka出場了。Eureka是微服務架構中的注冊中心,專門負責服務的注冊與發現。

咱們來看看下面的這張圖,結合圖來仔細剖析一下整個流程:

img

如上圖所示,庫存服務、倉儲服務、積分服務中都有一個Eureka Client組件,這個組件專門負責將這個服務的信息注冊到Eureka Server中。說白了,就是告訴Eureka Server,自己在哪台機器上,監聽着哪個端口。而Eureka Server是一個注冊中心,里面有一個注冊表,保存了各服務所在的機器和端口號

訂單服務里也有一個Eureka Client組件,這個Eureka Client組件會找Eureka Server問一下:庫存服務在哪台機器啊?監聽着哪個端口啊?倉儲服務呢?積分服務呢?然后就可以把這些相關信息從Eureka Server的注冊表中拉取到自己本地緩存起來。

這時如果訂單服務想要調用庫存服務,不就可以找自己本地的Eureka Client問一下庫存服務在哪台機器?監聽哪個端口嗎?收到響應后,緊接着就可以發送一個請求過去,調用庫存服務扣減庫存的那個接口!同理,如果訂單服務要調用倉儲服務、積分服務,也是如法炮制。

總結一下:

  • Eureka Client:負責將這個服務的信息注冊到Eureka Server中
  • Eureka Server:注冊中心,里面有一個注冊表,保存了各個服務所在的機器和端口號

創建一個module:

在這里插入圖片描述

在這里插入圖片描述
2020-11-19 補充:注意:這里不要使用默認的新發布的springboot 2.4.0,否則啟動項目會報錯。
在這里插入圖片描述
可以選擇springboot 2.3.6
在這里插入圖片描述
又或者創建好項目之后在pom.xml里面把版本號手動改為:<version>2.3.5.RELEASE</version>
在這里插入圖片描述

配置文件:application.yml

spring:
  application:
    name: registry-service
server:
  port: 8761
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false #該實例是否向Eureka Server注冊自己。因為自身為Eureka Server,所以false
    fetch-registry: false #該實例是否向 Eureka 服務器獲取所有的注冊信息表  同上
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

最后在項目啟動類上加上注解

@EnableEurekaServer //eureka服務注冊中心

package com.xct.registry;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class RegistryApplication {

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

測試:

啟動服務,訪問localhost:8761

在這里插入圖片描述

到此,eureka注冊中心就配置成功了。

1.3 創建服務

概念:服務提供者和服務消費者

服務提供者:服務的被調用方,即為其他服務提供服務的服務。

服務消費者:服務的調用方,即依賴其他服務的服務。

這個概念網上都是這么說的,但是兩者代碼沒有什么區別。網上查詢資料和思考,這里它們還真的只是概念。它沒有調用其他服務,只提供服務它就是提供者;它調了其他服務,它就是消費者,它要是兩者都是也行。

創建一個module:user-service

依賴:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

application.yml配置:

spring:
  application:
    name: user-service
server:
  port: 8090
eureka:
  client:
    service-url:
      deaultZone: http://localhost:8761/eureka/

在啟動類上添加注解:

@EnableDiscoveryClient或者@EnableEurekaClient兩個都是注冊。

@EnableDiscoveryClient與@EnableEurekaClient區別

1,@EnableDiscoveryClient注解是基於spring-cloud-commons依賴,並且在classpath中實現;

2,@EnableEurekaClient注解是基於spring-cloud-netflix依賴,只能為eureka作用;

簡單來說就是注冊中心是eureka使用@EnableEurekaClient這個只為Eureka服務,其他的注冊中心使用

@EnableDiscoveryClient,包括Eureka。所以這里咱們使用@EnableDiscoveryClient

package com.xct.orderservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

創建另外一個module:order-service

依賴:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

application.yml配置:

spring:
  application:
    name: order-service
server:
  port: 8081
eureka:
  client:
    service-url:
      deaultZone: http://localhost:8761/eureka/

在啟動類上添加注解:

@EnableDiscoveryClient

測試:

至此,兩個項目就已經搭建完成,分別啟動兩個項目,然后訪問: http://localhost:8761/
在這里插入圖片描述

可以看到我們創建的兩個項目都已經被注冊到注冊中心了。

1.4 服務間的通信及負載均衡

1.4.1使用Feign實現服務間的通信

Spring Cloud核心組件:Feign描述

看一下官方的解釋:Feign 是一個聲明式 WebService 客戶端。使用 Feign 能讓編寫的 WebService 客戶端更加簡潔,它的使用方法式定義一個接口,然后在上面添加注解。Spring Cloud 對 Feign 進行了封裝,使其支持了 Spring MVC 標准注解和 HttpMessageConverters。Feign 可以與 Eureka 和 Ribbon 組合使用以支持負載均衡。

原文描述:https://www.jianshu.com/p/31dfb595170c

Feign客戶端是一個web聲明式http遠程調用工具,提供了接口和注解方式進行調用(用來調用其他服務)

Feign的一個關鍵機制就是使用了動態代理。

  1. 首先,如果你對某個接口定義了@FeignClient注解,Feign就會針對這個接口創建一個動態代理

  2. 接着你要是調用那個接口,本質就是會調用 Feign創建的動態代理,這是核心中的核心

  3. Feign的動態代理會根據你在接口上的@RequestMapping等注解,來動態構造出你要請求的服務的地址

  4. 最后針對這個地址,發起請求、解析響應

使用:

首先,我們在user-service中寫一個接口供order-service調用:

package com.xct.userservice.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Xct1194542884
 * @title: xct
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 11:00
 */
@RestController
@RequestMapping("user")
public class UserController {
    @Value("${spring.application.name}")
    private String serverName;

    @Value("${server.port}")
    private String port;

    @RequestMapping("/getUser")
    public String getUser(){
        return "服務名:"+serverName+",端口:"+port;
    }
}

在order-service中添加Feign依賴:

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

在啟動類上加上注解:@EnableFeignClients

package com.xct.orderservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {

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

創建一個接口:UserApi

package com.xct.orderservice.api;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author Xct1194542884
 * @title: xct
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 11:04
 */
@FeignClient(value = "user-service")
@Service
public interface UserApi {

    @RequestMapping("user/getUser")
    String getUser();
}

@FeignClient(value = "user-service") //告訴RPC訪問那個服務
@RequestMapping("user/getUser") //調用服務哪個接口

控制器:

package com.xct.orderservice.controller;

import com.xct.orderservice.api.UserApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Xct1194542884
 * @title: xct
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 11:05
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    UserApi userApi;

    @RequestMapping("/orderGetUser")
    public String orderGetUser(){
        return userApi.getUser();
    }
}

最后,啟動項目,訪問 http://localhost:8081/order/orderGetUser

在這里插入圖片描述

到此,我們創建的兩個服務就能進行通信了!

1.4.3使用Ribbon+Feign實現負載均衡

Spring Cloud核心組件:Ribbon描述

該處借用別人的描述:https://www.jianshu.com/p/31dfb595170c

現在我們假設用戶服務部署在三台服務器上面,如下圖。

  • 192.168.169:9000
  • 192.168.170:9000
  • 192.168.171:9000
  • 192.168.172:9000
  • 192.168.173:9000

那么Feign怎么知道該請求哪台機器呢?

這時Spring Cloud Ribbon就派上用場了。Ribbon就是專門解決這個問題的。它的作用是負載均衡,會幫你在每次請求時選擇一台機器,均勻的把請求分發到各個機器上

Ribbon的負載均衡默認使用的最經典的Round Robin輪詢算法。這是啥?簡單來說,就是如果訂單服務對庫存服務發起10次請求,那就先讓你請求第1台機器、然后是第2台機器、第3台機器、第4台機器、第5台機器,接着再來—個循環,第1台機器、第2台機器。。。以此類推。

此外,Ribbon是和Feign以及Eureka緊密協作,完成工作的,具體如下:

  • 首先Ribbon會從 Eureka Client里獲取到對應的服務注冊表,也就知道了所有的服務都部署在了哪些機器上,在監聽哪些端口號。
  • 然后Ribbon就可以使用默認的Round Robin算法,從中選擇一台機器
  • Feign就會針對這台機器,構造並發起請求。
使用:

我們創建多個user-service服務,這些服務除了端口不一樣,其他都相同

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在user-service中加入依賴:

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

注意:此處可以導入ribbon依賴,亦可以導入Feign依賴:

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

啟動所有服務,然后訪問 http://localhost:8081/order/orderGetUser

第一次訪問的時候端口是8090

在這里插入圖片描述

F5刷新一下,變成了8091:

在這里插入圖片描述

再刷新,變為8092:

在這里插入圖片描述

到這,負載均衡也成功了!

1.5 Hystrix

簡單模擬業務:

讓我們回到最開始模擬的業務場景:

創建一個goods-service服務,該服務提供扣減商品庫存的接口

導入依賴:

<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-openfeign</artifactId>
</dependency>

在啟動類上添加注解:@EnableDiscoveryClient

package com.xct.goodsservice;

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

@SpringBootApplication
@EnableDiscoveryClient
public class GoodsServiceApplication {

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

application.yml配置:

spring:
  application:
    name: goods-service
server:
  port: 9001
eureka:
  client:
    service-url:
      deaultZone: http://localhost:8761/eureka/

控制層:模擬扣減庫存業務

package com.xct.goodsservice.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Xct1194542884
 * @title: xct
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 15:02
 */
@RestController
@RequestMapping("goods")
public class GoodsController {
    private static final Logger LOGGER= LoggerFactory.getLogger(GoodsController.class);

    @RequestMapping("/reduceStock")
    public Integer reduceStock(){
        LOGGER.info("商品服務被調用,扣減庫存成功!");
        return 1;
    }
}

創建一個integral-service服務,該服務提供增加積分的接口

導入依賴:

<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-openfeign</artifactId>
</dependency>

在啟動類上添加注解:@EnableDiscoveryClient

package com.xct.integralservice;

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

@SpringBootApplication
@EnableDiscoveryClient
public class IntegralServiceApplication {

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

}

application.yml配置:

spring:
  application:
    name: integral-service
server:
  port: 9005
eureka:
  client:
    service-url:
      deaultZone: http://localhost:8761/eureka/

控制層:模擬增加積分業務

package com.xct.integralservice.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Xct1194542884
 * @title: xct
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 15:19
 */
@RestController
@RequestMapping("integral")
public class IntegralController {
    private static final Logger LOGGER= LoggerFactory.getLogger(IntegralController.class);
    @RequestMapping("/addIntegral")
    public Integer addIntegral(){
        LOGGER.info("調用積分服務,增加積分成功!");
        return 1;
    }
}
使用order-service服務分別調用goods-service的扣減庫存接口和integral-service增加積分接口:

在order-service中新建兩個接口:

GoodsApi:

package com.xct.orderservice.api;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author Xct1194542884
 * @title: xct
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 15:06
 */
@FeignClient(value = "goods-service")
public interface GoodsApi {

    @RequestMapping("/goods/reduceStock")
    public Integer reduceStock();
}

IntegralApi:

package com.xct.orderservice.api;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author Xct1194542884
 * @title: xct
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 15:22
 */
@FeignClient(value = "integral-service")
public interface IntegralApi {

    @RequestMapping("/integral/addIntegral")
    public Integer addIntegral();
}

控制層模擬訂單支付業務:

package com.xct.orderservice.controller;

import com.xct.orderservice.api.GoodsApi;
import com.xct.orderservice.api.IntegralApi;
import com.xct.orderservice.api.UserApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Xct1194542884
 * @title: xct
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 11:05
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    UserApi userApi;

    @Autowired
    GoodsApi GoodsApi;

    @Autowired
    IntegralApi integralApi;

    @RequestMapping("/orderGetUser")
    public String orderGetUser(){
        return userApi.getUser();
    }

    @RequestMapping("/orderPayment")
    public Integer orderPayment(){
        Integer result1 = GoodsApi.reduceStock();
        Integer result2 = integralApi.addIntegral();
        return result1+result2;
    }
}

啟動服務,訪問 http://localhost:8081/order/orderPayment

如果所有服務都正常,則

頁面顯示:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-wZhzzFXs-1605173237283)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112154016098.png)]

控制台分別打印:

在這里插入圖片描述

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-LdV7WrvT-1605173237289)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112154151462.png)]

此時我們將積分服務給關閉,再次訪問http://localhost:8081/order/orderPayment ,則會發現

頁面會顯示錯誤:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NmbDKm3X-1605173237294)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112154325698.png)]

order-service服務控制台會拋出異常:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-HlNOCiJd-1605173237297)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112160133373.png)]

此處借用別人的描述:https://www.jianshu.com/p/31dfb595170c

在微服務架構里,一個系統會有很多的服務。以本文的業務場景為例:訂單服務在一個業務流程里需要調用三個服務。現在假設訂單服務自己最多只有100個線程可以處理請求,然后呢,積分服務不幸的掛了,每次訂單服務調用積分服務的時候,都會卡住幾秒鍾,然后拋出—個超時異常。

咱們一起來分析一下,這樣會導致什么問題?

  • 如果系統處於高並發的場景下,大量請求涌過來的時候,訂單服務的100個線程都會卡在請求積分服務這塊。導致訂單服務沒有一個線程可以處理請求
  • 然后就會導致別人請求訂單服務的時候,發現訂單服務也掛了,不響應任何請求了

上面這個,就是微服務架構中恐怖的服務雪崩問題,如下圖所示:

img

如上圖,這么多服務互相調用,要是不做任何保護的話,某一個服務掛了,就會引起連鎖反應,導致別的服務也掛。比如積分服務掛了,會導致訂單服務的線程全部卡在請求積分服務這里,沒有一個線程可以工作,瞬間導致訂單服務也掛了,別人請求訂單服務全部會卡住,無法響應。

但是我們思考一下,就算積分服務掛了,訂單服務也可以不用掛啊!為什么?

  • 我們結合業務來看:支付訂單的時候,只要把庫存扣減了,然后通知倉庫發貨就OK了
  • 如果積分服務掛了,大不了等他恢復之后,慢慢人肉手工恢復數據!為啥一定要因為一個積分服務掛了,就直接導致訂單服務也掛了呢?不可以接受!

現在問題分析完了,如何解決?

這時就輪到Hystrix閃亮登場了。Hystrix是隔離、熔斷以及降級的一個框架。啥意思呢?說白了,Hystrix會搞很多個小小的線程池,比如訂單服務請求庫存服務是一個線程池,請求倉儲服務是一個線程池,請求積分服務是一個線程池。每個線程池里的線程就僅僅用於請求那個服務。

打個比方:現在很不幸,積分服務掛了,會咋樣?

當然會導致訂單服務里的那個用來調用積分服務的線程都卡死不能工作了啊!但是由於訂單服務調用庫存服務、倉儲服務的這兩個線程池都是正常工作的,所以這兩個服務不會受到任何影響。

這個時候如果別人請求訂單服務,訂單服務還是可以正常調用庫存服務扣減庫存,調用倉儲服務通知發貨。只不過調用積分服務的時候,每次都會報錯。但是如果積分服務都掛了,每次調用都要去卡住幾秒鍾干啥呢?有意義嗎?當然沒有!所以我們直接對積分服務熔斷不就得了,比如在5分鍾內請求積分服務直接就返回了,不要去走網絡請求卡住幾秒鍾,這個過程,就是所謂的熔斷!

那人家又說,兄弟,積分服務掛了你就熔斷,好歹你干點兒什么啊!別啥都不干就直接返回啊?沒問題,咱們就來個降級:每次調用積分服務,你就在數據庫里記錄一條消息,說給某某用戶增加了多少積分,因為積分服務掛了,導致沒增加成功!這樣等積分服務恢復了,你可以根據這些記錄手工加一下積分。這個過程,就是所謂的降級。

為幫助大家更直觀的理解,接下來用一張圖,梳理一下Hystrix隔離、熔斷和降級的全流程:

img

Hystrix使用:

在order-service的配置文件中添加開啟Hystrix功能配置

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5VpreISN-1605173237308)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112161812860.png)]

為我們剛剛寫的IntegralApi接口編寫一個具體的實現類,重寫接口中的方法實現服務降級。

package com.xct.orderservice.api.impl;

import com.xct.orderservice.api.IntegralApi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * @author Xct1194542884
 * @title: xct
 * @projectName springcloud_2
 * @description: TODO
 * @date 2020-11-12 15:55
 */
@Service
public class IntegralApiImpl implements IntegralApi {
    private static final Logger LOGGER= LoggerFactory.getLogger(IntegralApi.class);
    @Override
    public Integer addIntegral() {
        LOGGER.error("積分服務出現異常,服務熔斷!");
        return 0;
    }
}

然后在IntegralApi接口中,通過@FeignClient注解的failback屬性來指定對應的服務降級實現類。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0pNPtroM-1605173237310)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112162238522.png)]

此時,我們再將項目全部重新啟動,但是將提供增加積分接口的integral-service服務關閉,然后訪問:

http://localhost:8081/order/orderPayment

此時我們發現頁面沒有報錯,控制台也沒有拋出異常:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VfQvAiiJ-1605173237313)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112162435631.png)]

order-service服務控制台打印:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6Rela2X8-1605173237316)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112162504761.png)]

到此,Hystrix也已經整合成功了!

1.6 服務網關Gateway

描述:

Spring Cloud Gateway 是 Spring Cloud 新推出的網關框架,之前是 Netflix Zuul。網關通常在項目中為了簡化

前端的調用邏輯,同時也簡化內部服務之間互相調用的復雜度;具體作用就是轉發服務,接收並轉發所有內外

部的客戶端調用;其他常見的功能還有權限認證,限流控制等等。

Spring Cloud Gateway 功能特征
  • 基於Spring Framework 5, Project Reactor 和 Spring Boot 2.0 進行構建;
  • 動態路由:能夠匹配任何請求屬性;
  • 集成 Spring Cloud 服務發現功能;
  • 可以對路由指定 Predicate(斷言)和 Filter(過濾器);
  • 易於編寫的 Predicate(斷言)和 Filter(過濾器);
  • 集成Hystrix的斷路器功能;
  • 請求限流功能;
  • 支持路徑重寫。

詳情參考:https://blog.csdn.net/qq_38380025/article/details/102968559

使用:

本次只會簡單使用一下網關的基本轉發功能

創建一個module,用於作為Gateway網關:

導入依賴:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

啟動類上添加注解:@EnableDiscoveryClient

package com.xct.gateway;

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

@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {

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

}

application.yml配置:

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #表明gateway開啟服務注冊和發現的功能,
          #並且spring cloud gateway自動根據服務發現為每一個服務創建了一個router,
          # 這個router將以服務名開頭的請求路徑轉發到對應的服務
          lower-case-service-id: true #將請求路徑上的服務名配置為小寫
      routes:
      #我們自定義的路由 ID,保持唯一
      - id: user-service
        #目標服務器地址,表示從微服務注冊中心(如Eureka)訂閱user-service服務,並且進行服務的路由。
        uri: lb://user-service
        #路由條件,接收一個輸入參數,返回一個布爾值結果。
        predicates:
        - Path=/api/user/**
        #過濾規則
        filters:
        - StripPrefix=1
  #- StripPrefix=1表示路由(轉發)時會去掉/user
  #- StripPrefix后面的數字是幾表示去除幾層路徑
  #例如寫2的話就會去除/api/user
  #即訪問/api/user/getUser會變為/user/getUser
  #上面的路由配置表示所有包含/api/user/的url都會被路由(轉發)到user-service服務
  #例如訪問http://localhost:8088/api/user/getUser會轉發到http://localhost:8090/user/getUser
server:
  port: 8088

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/


把我們之前創建的三個user-service服務都給啟動,訪問 http://localhost:8088/api/user/getUser

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-k0mejtJ3-1605173237319)(C:\Users\Xct1194542884\AppData\Roaming\Typora\typora-user-images\image-20201112170405806.png)]

而且當我們刷新頁面后,發現端口也發生了變化,說明Gateway網關可以結合ribbon完成負載均衡的功能:
在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM