1. 程式人生 > 實用技巧 >SpringCloud07:Fegin負載均衡(基於服務端)

SpringCloud07:Fegin負載均衡(基於服務端)

1、Feign簡介

Feign是宣告式Web Service客戶端,它讓微服務之間的呼叫變得更簡單,類似controller呼叫service。SpringCloud集成了Ribbon和Eureka,可以使用Feigin提供負載均衡的http客戶端

只需要建立一個介面,然後添加註解即可~

Feign,主要是社群版,大家都習慣面向介面程式設計。這個是很多開發人員的規範。呼叫微服務訪問兩種方法

  1. 微服務名字 【ribbon】
  2. 介面和註解 【feign】

Feign能幹什麼?

  • Feign旨在使編寫Java Http客戶端變得更容易
  • 前面在使用Ribbon + RestTemplate時,利用RestTemplate
    對Http請求的封裝處理,形成了一套模板化的呼叫方法。但是在實際開發中,由於對服務依賴的呼叫可能不止一處,往往一個介面會被多處呼叫,所以通常都會針對每個微服務自行封裝一個客戶端類來包裝這些依賴服務的呼叫。所以,Feign在此基礎上做了進一步的封裝,由他來幫助我們定義和實現依賴服務介面的定義,在Feign的實現下,我們只需要建立一個介面並使用註解的方式來配置它 (類似以前Dao介面上標註Mapper註解,現在是一個微服務介面上面標註一個Feign註解),即可完成對服務提供方的介面繫結,簡化了使用Spring Cloud Ribbon 時,自動封裝服務呼叫客戶端的開發量。

Feign預設集成了Ribbon

  • 利用Ribbon維護了MicroServiceCloud-Dept的服務列表資訊,並且通過輪詢實現了客戶端的負載均衡,而與Ribbon不同的是,通過Feign只需要定義服務繫結介面且以宣告式的方法,優雅而簡單的實現了服務呼叫。

2、Feign的使用步驟

2.1 依據springcloud-consumer-ribbon-80模組複製,建立springcloud-consumer-feign-80模組,修改依賴

<dependencies>
    <dependency>
        <groupId>com.godfrey</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--Feign-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!--Eureka: Ribbon需要從Eureka服務中心獲取要拿什麼-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>com.godfrey</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

Feign實現消費者模組呼叫服務提供者模組的原理和原來的Dubbo+Zookeeper類似,即需要使用註解實現遠端注入,所以我們直接在springcould-api模組先新增Feign依賴:

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

2.2 在service包下新建一個介面DeptClientService,

這個介面中的方法定義自己隨意,只是方法上面要像controller一樣寫上@RequestMapping或者它的變體@GetMapping、@PostMapping等,但是這個介面上面不需要使用註解@Controller或@RestController

這個介面上面需要使用註解@FeignClient(value = “服務叢集在註冊中心中的名稱”)和註解@Component或者它的變體@Service;其中註解@FeignClient+value屬性用於指定註冊中心中哪一個服務中的

@Component
@FeignClient(value = "SPRINGCOULD-PROVIDER-DEPT")
public interface DeptClientService {

    @PostMapping("/dept/add")
    boolean addDept(Dept dept);
    
    @GetMapping("/dept/queryById/{id}")
    Dept queryById(@PathVariable("id") Long id);

    @GetMapping("/dept/queryList")
    List<Dept> queryAll();
}

注意:還需要在這個介面上加上註解@Component/@Service,否則這個介面不會被spring託管/裝配到spring容器中,那麼我們在消費者model中使用這個類的時候就不能使用註解@Autowired實現物件自動注入到消費者的controller中(注意:我們是在springcould-api模組中定義的這個介面,並在這個介面上面加上了註解@Component,這個註解不是要在springcould-api模組中起作用,而是要在使用springcould-api模組的其他模組中起作用,即哪個模組匯入了springcould-api模組,那麼在這個model啟動的時候,有註解@Component的這個介面就會被裝配到spring容器中去,然後我們只需要在當前的這個模組中使用註解@Autowired就可以獲取到這個介面在spring容器中的例項,就可以呼叫它內部的方法實現對應的功能)

通過Ribbon實現:—原來的controller:DeptConsumerController.java

@RestController
public class DeptConsumerController {

    private RestTemplate restTemplate;

    //Ribbon:我們這裡的地址,應該是一個變數,通過服務名來訪問
    //private static final String REST_URL_PREFIX = "http://localhost:8001";
    private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

    @Autowired
    DeptConsumerController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @PostMapping("/consumer/dept/add")
    public boolean addDept(Dept dept) {
        return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add/", dept, Boolean.class);
    }

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept queryById(@PathVariable("id") Long id) {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
    }

    @RequestMapping("/consumer/dept/list")
    public List<Dept> queryAll() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
    }
}

使用ribbon的時候,我們是直接將服務叢集在註冊中心中的服務名直接寫死在程式碼中(private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";),通過拼接實現的消費者呼叫服務提供者對應的服務,拼接的後半段是服務對應功能在controller中暴露的API(/dept/add/,/dept/get/,/dept/get/)

通過Feign實現:—改造後controller:DeptConsumerController.java

@RestController
public class DeptConsumerController {

    private DeptClientService service;

    @Autowired
    public DeptConsumerController(@Qualifier("SPRINGCOULD-PROVIDER-DEPT") DeptClientService service) {
        this.service = service;
    }

    @PostMapping("/consumer/dept/add")
    public boolean addDept(Dept dept) {
        return service.addDept(dept);
    }

    @GetMapping("/consume/dept/queryById/{id}")
    public Dept queryById(@PathVariable("id") Long id) {
        return service.queryById(id);
    }

    @RequestMapping("/consumer/dept/queryAll")
    public List<Dept> queryAll() {
        return service.queryAll();
    }
}

使用Feign的時候,我們只需要在消費者的controller中組合進剛剛在springcloud-api中編寫的介面方法即可,並使用註解@Autowired實現依賴注入,下面的方法就可以呼叫介面中的方法了,使用Feign之後,消費者的controller中方法的實現更加符合controller調service層提供服務的情形,並目整個程式碼結構更加的清晰了,就像真的就是在呼叫消費者模組中的service提供的服務—樣,但是我們要是到,消費者模組根本就沒有service層的程式碼

最後還要在spring boot專案的入口程式處加上啟動Feign的註解@EnableFeignClients(basePackages = “我們定義的service所在的包路徑”),注意:這裡不是實際存在於springcould-consumer-dept-feign的model下的包路徑,而是我們匯入的springcould-api的包路徑

2.3 啟動Feign模組測試效果

從上面的測試結果可以發現,Feign預設使用的也是輪詢演算法

3、小結

  • 注意:通過上面使用Feign實現負載均衡我們可以發現,Feign做的事情就是在原生代碼中,通過在springcould-api定義一個專門用於通過Feign實現負載均衡的介面,並在介面上寫上註解@FeignClient以及這個註解的name/value屬性,用於繫結註冊中心中指定名稱的服務,就像上面的例子中

  • 繫結之後,介面中的方法和註冊中心已經註冊的服務中的功能提供的API通過註解@RequestMapping或其變體進行繫結

  • 然後再將springcould-api模組匯入其他的模組中進行復用,在需要使用服務者功能的消費者的controller中,通過註解@Autowired實現上面定義的介面的依賴注入,並在消費者的controller中直接像controller層呼叫service層一樣,呼叫這個介面中的方法,實現消費者對於服務者提供的服務的消費

  • 最後在使用這個介面的model的入口程式/主啟動類上添加註解@EnableFeignClients(basePackages = “springcould-api中介面所在的包路徑”)

所以Feign的核心就是將服務者提供的服務API進行本地化,存入消費者model中,然後再通過註解@EnableFeignClients和註解@FeignClient實現通過呼叫消費者模組中的本地化的服務API,呼叫到註冊中心中真正服務提供者的API的作用(所以介面上的API對映需要和服務提供者的API保持一致),只需要4步就實現了Feign實現負載均衡

4、Feign和Ribbon如何選擇?

根據個人習慣而定,如果喜歡REST風格使用Ribbon;如果喜歡社群版的面向介面風格使用Feign.

Feign 本質上也是實現了 Ribbon,只不過後者是在呼叫方式上,為了滿足一些開發者習慣的介面呼叫習慣!

下面我們關閉springcloud-consumer-dept-80 這個服務消費方,換用springcloud-consumer-dept-feign(埠還是80) 來代替:(依然可以正常訪問,就是呼叫方式相比於Ribbon變化了)