前提回顧
【SpringCloud技術專題】「原生態Fegin」開啟Fegin之RPC技術的開端,你會使用原生態的Fegin嗎?(中)
【SpringCloud技術專題】「原生態Fegin」開啟Fegin之RPC技術的開端,你會使用原生態的Fegin嗎?(上)
內容簡介
在專案開發中,除了考慮正常的呼叫之外,負載均衡和故障轉移也是關注的重點,這也是feign + ribbon的優勢所在,基於上面兩篇文章的基礎,接下來我們開展最後一篇原生態fegin結合ribbon服務進行服務遠端呼叫且實現負載均衡機制,也幫助大家學習ribbon奠定基礎。
maven依賴
<dependencies>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
<version>8.18.0</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-jackson</artifactId>
<version>8.18.0</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-ribbon</artifactId>
<version>8.18.0</version>
</dependency>
<dependency>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-core</artifactId>
</dependency>
</dependencies>
其中feign-core和feign-ribbon是必須的,如果需要在服務消費端和服務生產端之間進行物件互動,建議使用feign-jackson
配置讀取
import com.netflix.config.ConfigurationManager;
import feign.Feign;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import feign.ribbon.RibbonClient;
public class AppRun {
public static void main(String[] args) throws Exception {
User param = new User();
param.setUsername("test");
RemoteService service = Feign.builder().client(RibbonClient.create())
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.options(new Options(1000, 3500))
.retryer(new Retryer.Default(5000, 5000, 3))
.target(RemoteService.class, "http://remote-client/gradle-web");
/**
* 呼叫測試
*/
for (int i = 1; i <= 10; i++) {
User result = service.getOwner(param);
System.out.println(result.getId() + "," + result.getUsername());
}
}
}
-
宣告瞭一個User型別的物件param,該物件將作為引數被髮送至服務生產端。
-
重點在於通過RibbonClient.create()使得Feign物件獲得了Ribbon的特性。之後通過encoder,decoder設定編碼器與解碼器,並通過target方法將之前定義的介面RemoteService與一個URL地址http://remote-client/gradle-web進行了繫結。
現在來看remote-client.properties中的配置項,主要多是RemoteClient的配置機制
remote-client.ribbon.MaxAutoRetries=1
remote-client.ribbon.MaxAutoRetriesNextServer=1
remote-client.ribbon.OkToRetryOnAllOperations=true
remote-client.ribbon.ServerListRefreshInterval=2000
remote-client.ribbon.ConnectTimeout=3000
remote-client.ribbon.ReadTimeout=3000
remote-client.ribbon.listOfServers=127.0.0.1:8080,127.0.0.1:8085
remote-client.ribbon.EnablePrimeConnections=false
所有的key都以remote-client開頭,表明這些配置項作用於名為remote-client的服務。其實就是與之前繫結RemoteService介面的URL地址的schema相對應。
重點看remote-client.ribbon.listOfServers配置項,該配置項指定了服務生產端的真實地址。
之前與RemoteService介面繫結的URL地址是 : http://remote-client/gradle-web
在呼叫時會被替換為:
@RequestLine指定的地址進行拼接,得到最終請求地址。本例中最終請求地址為:
由於使用的ribbon,所以feign不再需要配置超時時長,重試策略。ribbon提供了更為完善的策略實現。
本例中,服務生產端是一個簡單的springMvc,實現如下:
@RestController
@RequestMapping(value="users")
public class UserController {
@RequestMapping(value="/list",method={RequestMethod.GET,RequestMethod.POST,RequestMethod.PUT})
public User list(@RequestBody User user) throws InterruptedException{
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
user.setId(new Long(request.getLocalPort()));
user.setUsername(user.getUsername().toUpperCase());
return user;
}
}
故障轉移是通過remote-client.properties中的配置項進行配置。
- 首先利用archaius專案的com.netflix.config.ConfigurationManager讀取配置檔案remote-client.properties,該檔案位於src/main/resources下。
負載均衡的策略又是如何設定呢?
import com.netflix.client.ClientFactory;
import com.netflix.client.config.IClientConfig;
import com.netflix.config.ConfigurationManager;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.RandomRule;
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
import feign.Feign;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import feign.ribbon.LBClient;
import feign.ribbon.LBClientFactory;
import feign.ribbon.RibbonClient;
public class AppRun {
public static void main(String[] args) throws Exception {
ConfigurationManager.loadPropertiesFromResources("remote-client.properties");
User param = new User();
param.setUsername("test");
RibbonClient client = RibbonClient.builder().lbClientFactory(new LBClientFactory() {
@Override
public LBClient create(String clientName) {
IClientConfig config = ClientFactory.getNamedConfig(clientName);
ILoadBalancer lb = ClientFactory.getNamedLoadBalancer(clientName);
ZoneAwareLoadBalancer zb = (ZoneAwareLoadBalancer) lb;
zb.setRule(new RandomRule());
return LBClient.create(lb, config);
}
}).build();
RemoteService service = Feign.builder().client(client).encoder(new JacksonEncoder())
.decoder(new JacksonDecoder()).options(new Options(1000, 3500))
.retryer(new Retryer.Default(5000, 5000, 3)).target(RemoteService.class, "http://remote-client/gradle-web");
/**
* 呼叫測試
*/
for (int i = 1; i <= 10; i++) {
User result = service.getOwner(param);
System.out.println(result.getId() + "," + result.getUsername());
}
}
}
其他負載均衡策略
/**
* Ribbon負載均衡策略實現
* 使用ZoneAvoidancePredicate和AvailabilityPredicate來判斷是否選擇某個server,前一個判斷判定一個zone的執行效能是否可用,
* 剔除不可用的zone(的所有server),AvailabilityPredicate用於過濾掉連線數過多的Server。
* @return
*/
private IRule zoneAvoidanceRule() {
return new ZoneAvoidanceRule();
}
/**
* Ribbon負載均衡策略實現
* 隨機選擇一個server。
* @return
*/
private IRule randomRule() {
return new RandomRule();
}
不再使用RibbonClient.create()來建立預設的RibbonClient,而是通過RibbonClient.builder()獲得feign.ribbon.Builder,進而設定LBClientFactory的實現來定製LBClient,在建立LBClient的過程中即可指定負載策略的具體實現。