springcloud负载均衡组件openfeign与ribbon单独使用

喜欢ヅ旅行 2021-12-05 03:57 665阅读 0赞

在springcloud中,openfeign是取代了feign作为负载均衡组件的,feign最早是netflix提供的,他是一个轻量级的支持RESTful的http服务调用框架,内置了ribbon,而ribbon可以提供负载均衡机制,因此feign可以作为一个负载均衡的远程服务调用框架使用。feign后来不升级了,被github的openfeign取代,openfeign在feign的基础上,又支持springmvc注解的功能。

我们说openfeign其实也可以通过ribbon实现负载均衡,但是他们都可以单独使用,不一定是在springcloud大基础下,下面给出一个实例,来说明ribbon和openfeign单独使用时,如何负载均衡,并且实现远程服务调用。

首先我们需要利用springboot构建一个RESTfull的应用,提供一个接口index(),返回一个字符串。然后我们利用不同的配置文件,开启两个实例。以便我们模拟负载均衡。应用的结构如下:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaW5pZmk_size_16_color_FFFFFF_t_70

springboot主要内容WebApplication.java:

  1. package com.xxx.webapp;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. @SpringBootApplication
  8. @RestController
  9. public class WebApplication {
  10. @Value("${webapp.message}")
  11. private String message;
  12. @Value("${server.port}")
  13. private int port;
  14. public static void main( String[] args ){
  15. SpringApplication.run(WebApplication.class, args);
  16. }
  17. @GetMapping("/index")
  18. public String index(){
  19. return "{\"id\':101,\"name\":\"xxx\",\"message\":\""+message+"\",\"port\":\""+port+"\"}";
  20. }
  21. }

application.yml

  1. server:
  2. port: 8081
  3. webapp:
  4. message: springboot

application-dev.yml

  1. server:
  2. port: 8082
  3. webapp:
  4. message: springboot2

这里就是我们准备要远程调用的服务。

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

我们新建一个maven工程,单独使用openfeign和ribbon,我们需要引入如下依赖:

  1. <dependency>
  2. <groupId>com.netflix.ribbon</groupId>
  3. <artifactId>ribbon-loadbalancer</artifactId>
  4. <version>2.2.5</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.netflix.ribbon</groupId>
  8. <artifactId>ribbon-httpclient</artifactId>
  9. <version>2.2.5</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.slf4j</groupId>
  13. <artifactId>slf4j-log4j12</artifactId>
  14. <version>1.7.25</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>io.github.openfeign</groupId>
  18. <artifactId>feign-core</artifactId>
  19. <version>9.5.1</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>io.github.openfeign</groupId>
  23. <artifactId>feign-jackson</artifactId>
  24. <version>9.5.1</version>
  25. </dependency>
  26. <dependency>
  27. <groupId>io.github.openfeign</groupId>
  28. <artifactId>feign-ribbon</artifactId>
  29. <version>9.5.1</version>
  30. </dependency>

先来看看负载均衡算法:

  1. package com.xxx.ribbon;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import com.netflix.loadbalancer.BaseLoadBalancer;
  5. import com.netflix.loadbalancer.ILoadBalancer;
  6. import com.netflix.loadbalancer.Server;
  7. public class BalancerApplication {
  8. public static void main( String[] args ){
  9. ILoadBalancer balancer = new BaseLoadBalancer();
  10. List<Server> servers = new ArrayList<Server>();
  11. servers.add(new Server("localhost", 8081));
  12. servers.add(new Server("localhost", 8082));
  13. balancer.addServers(servers);
  14. for(int i=0;i<10;i++){
  15. Server choosedServer = balancer.chooseServer(null);
  16. System.out.println(choosedServer);
  17. }
  18. }
  19. }

这个测试程序和前面准备的两个springboot服务没有关系,只是说明ribbon的负载均衡,默认BaseLoadBalancer类的负载均衡算法是round robin(循环),这里有两个服务地址,按照预期,我们会看到打印结果应该是两个地址交替打印。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaW5pZmk_size_16_color_FFFFFF_t_70 1

打印结果,符合我们的预期,我们可以看看BaseLoadBalancer的源代码,看看默认的负载均衡算法:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaW5pZmk_size_16_color_FFFFFF_t_70 2

如果我们需要定制自己的规则,可以通过实现IRule接口,这里给出一个自定义的规则:所有http请求60%到8081,剩余的到8082, 按照思路,我们的代码可以这么来实现。

  1. package com.xxx.ribbon;
  2. import java.util.List;
  3. import java.util.Random;
  4. import com.netflix.loadbalancer.BaseLoadBalancer;
  5. import com.netflix.loadbalancer.ILoadBalancer;
  6. import com.netflix.loadbalancer.IRule;
  7. import com.netflix.loadbalancer.Server;
  8. public class CustomerBalancerRule implements IRule {
  9. private ILoadBalancer balancer = new BaseLoadBalancer();
  10. @Override
  11. public Server choose(Object key) {
  12. List<Server> servers = balancer.getAllServers();
  13. Random random = new Random();
  14. final int number = random.nextInt(10);
  15. if(number<7){
  16. return servers.get(0);
  17. }
  18. return servers.get(1);
  19. }
  20. @Override
  21. public void setLoadBalancer(ILoadBalancer lb) {
  22. this.balancer = lb;
  23. }
  24. @Override
  25. public ILoadBalancer getLoadBalancer() {
  26. return this.balancer;
  27. }
  28. }

我们通过实际的服务调用来看看,从调用结果上来检验这个负载策略。

  1. package com.xxx.ribbon;
  2. import com.netflix.client.ClientFactory;
  3. import com.netflix.client.http.HttpRequest;
  4. import com.netflix.client.http.HttpResponse;
  5. import com.netflix.config.ConfigurationManager;
  6. import com.netflix.loadbalancer.RoundRobinRule;
  7. import com.netflix.niws.client.http.RestClient;
  8. @SuppressWarnings("deprecation")
  9. public class CustomerBalancerRuleApplication {
  10. public static void main(String[] args) throws Exception{
  11. ConfigurationManager.getConfigInstance().setProperty("providers.ribbon.listOfServers",
  12. "localhost:8081,localhost:8082");
  13. ConfigurationManager.getConfigInstance().setProperty("providers.ribbon.NFLoadBalancerRuleClassName",
  14. RoundRobinRule.class.getName());
  15. RestClient client = (RestClient) ClientFactory.getNamedClient("providers");
  16. HttpRequest request = HttpRequest.newBuilder().uri("/index").build();
  17. for(int i=0;i<10;i++){
  18. HttpResponse response = client.executeWithLoadBalancer(request);
  19. System.out.println("loop "+i+" run result -> "+response.getEntity(String.class));
  20. }
  21. }
  22. }

我们通过配置providers.ribbon.listOfServers属性来指定providers服务的两个地址,然后通过 providers.ribbon

.NFLoadBalancerRuleClassName属性来指定我们的负载均衡策略。providers是自定义的服务名,关于ribbon的属性配置,都需要冠以这个前缀,后面我们通过providers会获取client,然后拼接我们的uri,就构成了具体的请求地址。

下面运行这个程序,我们查看打印信息:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaW5pZmk_size_16_color_FFFFFF_t_70 3

打印结果是不固定的,因为我们的规则本来就是计算的是一个概率,因此这条请求最终会落到那台服务器上,只能说60%的可能会在8081上。这里打印的10次正好有6次确实是落在了8081上,也有可能是7次甚至8次都会落在8081上。我们注释掉代码中设置负载规则的部分,再次运行程序,又回到了默认的循环规则打印的结果:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaW5pZmk_size_16_color_FFFFFF_t_70 4

以上示例,说明了ribbon单独作为一个远程调用服务框架,是具有负载均衡功能的。下面我们使用openfeign提供的api,并且使用ribbon作为client,来实现远程服务调用。

需要先定义一个接口UserService.java:

  1. package com.xxx.openfeign;
  2. import feign.RequestLine;
  3. public interface UserService {
  4. @RequestLine("GET /index")
  5. String index();
  6. }

这里,我们使用从配置文件读取配置的方法,准备一个配置文件providers.properties,文件名和我们对应的服务名providers对应:

  1. providers.ribbon.MaxAutoRetries=1
  2. providers.ribbon.MaxAutoRetriesNextServer=1
  3. providers.ribbon.OkToRetryOnAllOperations=true
  4. providers.ribbon.ServerListRefreshInterval=2000
  5. providers.ribbon.ConnectTimeout=3000
  6. providers.ribbon.ReadTimeout=3000
  7. providers.ribbon.listOfServers=localhost:8081,localhost:8082
  8. providers.ribbon.EnablePrimeConnections=true

最主要的配置是需要指定providers.ribbon.listOfServers属性。

启动类:

  1. package com.xxx.openfeign;
  2. import java.io.IOException;
  3. import com.netflix.config.ConfigurationManager;
  4. import feign.Feign;
  5. import feign.ribbon.RibbonClient;
  6. public class FeignApplication {
  7. public static void main(String[] args) throws IOException {
  8. //加载配置文件
  9. ConfigurationManager.loadPropertiesFromResources("providers.properties");
  10. //UserService
  11. UserService userService = Feign.builder().client(RibbonClient.create())
  12. //.encoder(new JacksonEncoder())
  13. //.decoder(new JacksonDecoder())
  14. .target(UserService.class,"http://providers");
  15. for(int i=0;i<10;i++){
  16. String result = userService.index();
  17. System.out.println("loop "+i+",result -> "+result);
  18. }
  19. }
  20. }

运行程序,打印信息如下:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaW5pZmk_size_16_color_FFFFFF_t_70 5

打印结果与ribbon使用默认负载均衡规则的效果是一样的。

openfeign和ribbon都可以单独使用,能够实现负载均衡的远程服务调用。

发表评论

表情:
评论列表 (有 0 条评论,665人围观)

还没有评论,来说两句吧...

相关阅读