一、Ribbon與OpenFeign關系
說到 OpenFeign就不得不提 Ribbon,OpenFeign默認將Ribbon作為負載均衡器,直接內置了 Ribbon。在導入OpenFeign 依賴后無需專門導入Ribbon 依賴。
Ribbon 是 Netflix 公司的一個開源的負載均衡項目,一個客戶端負載均衡器,運行在消費者端。簡單來說就是在消費者端配置對提供者的負載均衡器。這點與 Dubbo略有不同,Dubbo 在消費者端與提供者端均可配置負載均衡器。
二、聲明式Rest客戶端OpenFeign案例
(一)消費端配置
1、添加openfeign依賴
注意, 這里使用的是 spring-cloud-starter-openfeign 依賴,而非 spring-cloud-starter-feign依賴。
<!--feign依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.2.3.RELEASE</version> </dependency>
2、定義feign接口
使用@FeignClient設置需要調用的項目名稱,同時使用RequestMapping進行調用,調用地址要和服務提供者的地址保持一致。
@FeignClient(value = "provider01-depart")//注解作用:聲明當前為Feign客戶端接口 @RequestMapping("/provider/depart")// 參數為要調用的提供者相應的uri,抽取所有方法的公有uri地址 public interface DepartService {//更加符合面向接口api調用習慣 @PostMapping("/save") boolean saveDepart(@RequestBody Depart depart); @DeleteMapping("/del/{id}") boolean removeDepartById(@PathVariable("id") int id); @PutMapping("/update") boolean modifyDepart(@RequestBody Depart depart); @GetMapping("/get/{id}") Depart getDepartById(@PathVariable("id") int id); @GetMapping("/list") List<Depart> listAllDeparts(); }
3、處理器注入Feign客戶端對象
@RestController @RequestMapping("/feign/consumer/depart") public class DepartFeignController { @Autowired private DepartService departService;//跨服務根據id查詢 @GetMapping("/get/{id}") public Depart getHandle(@PathVariable("id") int id) { return departService.getDepartById(id); } }
4、修改啟動類
使用@EnableFeignClients來打開對Feign客戶端的支持,同時設置客戶端掃描的包。
由於直接使用了Feign Client,因此就不再使用RestTemplate進行處理了。
@SpringBootApplication @EnableFeignClients(basePackages = "com.lcl.cloud.alibaba.consumer02.service")//開啟當前服務支持Feign客戶端,作用掃描所有客戶端接口 public class Consumer02Application { public static void main(String[] args) { SpringApplication.run(Consumer02Application.class, args); } // @Bean // @LoadBalanced // public RestTemplate restTemplate() { // return new RestTemplate(); // } }
5、需要注意的一點,就是Feign的服務名稱不能使用下划線和橫線,否則會報 Service id not legal hostname (provider02_nacosconfig) 的錯誤。
(二)超時配置
1、客戶端的超時配置
feign: client: config: default: #連接超時時間 connectTimeout: 5000 #數據讀取超時是時間 readTimeout: 5000
2、服務端模擬超時
public Depart getDepartById(int id) { try { TimeUnit.SECONDS.sleep(6); } catch (InterruptedException e) { e.printStackTrace(); } if(repository.existsById(id)) { return repository.getOne(id); } Depart depart = new Depart(); depart.setName(departName); return depart; }
3、驗證結果
(三)Gzip壓縮設置
Feign 支持對請求和響應進行 Gzip 壓縮以提高通信效率。注意,這里的請求是指 Feign向提供者所提交的請求,響應是指 Feign 向客戶端作出的響應。
配置在官網中可以查看:https://docs.spring.io/spring-cloud-openfeign/docs/2.2.5.RELEASE/reference/html/#feign-requestresponse-compression
參數如下所示:
feign.compression.request.enabled=true feign.compression.response.enabled=true
三、更換負載均衡策略
(一)更換內置策略
若要更換負載均衡策略,則首先要了解負載均衡策略的定義接口 IRule。Ribbon 默認采用的是 RoundRobinRule,即輪詢策略。但通過修改消費者工程的配置文件,或修改消費者的啟動類或 JavaConfig 類可以實現更換負載均衡策略的目的。
1、修改配置文件
修改配置文件,在其中添加如下內容,指定要使用的負載均衡策略 <clientName>. <clientConfigNameSpace>.NFLoadBalancerRuleClassName。
該方式的好處是,可以為不同的微服務指定相應的負載均衡策略。
provider02Nacosconfig:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
2、修改JavaConfig類
在 JavaConfig 類中添加負載 Bean 方法。全局所有feign對應服務都可以生效。
@Configuration public class FeignConfiguration { /** * 配置隨機的負載均衡策略 * 特點:對所有的服務都生效 */ @Bean public IRule loadBalancedRule() { return new RandomRule(); } }
(二)自定義負載均衡策略
1、定義一個負載均衡策略
該負載均衡策略的思路是:從所有可用的 provider 中排除掉指定端口號的provider,剩余 provider 進行隨機選擇。
/** * 自定義負載均衡算法 */ public class CustomRule implements IRule { private ILoadBalancer lb; private List<Integer> excludePorts; public CustomRule() { } public CustomRule(List<Integer> excludePorts) { this.excludePorts = excludePorts; } @Override public void setLoadBalancer(ILoadBalancer lb) { this.lb = lb; } @Override public ILoadBalancer getLoadBalancer() { return lb; } /** * 目標:自定義負載均衡策略:從所有可用的provider中排除掉指定端口號的provider,剩余provider進行隨機選擇 * 實現步驟: * 1.獲取到所有Server * 2.從所有Server中排除掉指定端口的Server后,剩余的Server * 3.從剩余Server中隨機選擇一個Server */ @Override public Server choose(Object key) { // 1.獲取到所有Server List<Server> servers = lb.getReachableServers(); // 2.從所有Server中排除掉指定端口的Server后,剩余的Server List<Server> availableServers = this.getAvailableServers(servers); // 3.從剩余Server中隨機選擇一個Server return this.getAvailableRandomServers(availableServers); } private List<Server> getAvailableServers(List<Server> servers) { // 若沒有指定要排除的port,則返回所有Server if(excludePorts == null || excludePorts.size() == 0) { return servers; } List<Server> aservers = servers.stream() // filter() // noneMatch() 只有當流中所有元素都沒有匹配上時,才返回true,只要有一個匹配上了,則返回false .filter(server -> excludePorts.stream().noneMatch(port -> server.getPort() == port)) .collect(Collectors.toList()); return aservers; } private Server getAvailableRandomServers(List<Server> availableServers) { // 獲取一個[0,availableServers.size())的隨機數 int index = new Random().nextInt(availableServers.size()); return availableServers.get(index); } }
2、修改JavaConfig類,使用自定義的負載均衡策略
@Configuration public class FeignConfiguration { @Bean public IRule loadBalancedRule() { List<Integer> list = new ArrayList<>(); list.add(8081);//排除訪問端口 return new CustomRule(list); } }
(三)Ribbon內置負載均衡算法
1、 RoundRobinRule
輪詢策略:Ribbon 默認采用的策略。若經過一輪輪詢沒有找到可用的provider,其最多輪詢 10 輪(代碼中寫死的,不能修改)。若還未找到,則返回 null。
2、RandomRule
隨機策略:從所有可用的 provider 中隨機選擇一個。
3、RetryRule
重試策略:先按照 RoundRobinRule 策略獲取 server,若獲取失敗,則在指定的時限內重試。默認的時限為 500 毫秒。
4、BestAvailableRule
最可用策略:選擇並發量最小的 provider,即連接的消費者數量最少的provider。其會遍歷服務列表中的每一個server,選擇當前連接數量minimalConcurrentConnections 最小的server。
5、AvailabilityFilteringRule
可用過濾算法:該算法規則是過濾掉處於熔斷狀態的 server 與已經超過連接極限的server,對剩余 server 采用輪詢策略。
四、負載均衡器SpringCloudLoadBalancer
由於 Netflix 對於 Ribbon 的維護已經暫停,所以 Spring Cloud 對於負載均衡建議使用由其自己定義的 Spring Cloud LoadBalancer。對於Spring Cloud LoadBalancer 的使用非常簡單。
1、關閉Ribbon的負載均衡器
spring: application: name: consumer01-depart cloud: loadbalancer: # 關閉Ribbon的負載均衡器 ribbon: enabled: false
2、pom中添加LoadBalancer依賴
<!--spring cloud loadbalancer 依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> <version>2.2.3.RELEASE</version> </dependency>