SpringCloud07:Fegin負載均衡(基於服務端)
1、Feign簡介
Feign是宣告式Web Service客戶端,它讓微服務之間的呼叫變得更簡單,類似controller呼叫service。SpringCloud集成了Ribbon和Eureka,可以使用Feigin提供負載均衡的http客戶端
只需要建立一個介面,然後添加註解即可~
Feign,主要是社群版,大家都習慣面向介面程式設計。這個是很多開發人員的規範。呼叫微服務訪問兩種方法
- 微服務名字 【ribbon】
- 介面和註解 【feign】
Feign能幹什麼?
- Feign旨在使編寫Java Http客戶端變得更容易
- 前面在使用Ribbon + RestTemplate時,利用RestTemplate
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變化了)