Spring Cloud中使用Ribbon

一 准备工作
搭建Eureka服务器
服务提供者
服务调用者
二 新建项目spring-lb-server,充当Eureka服务器
1 启动spring-lb-server
三 新建项目spring-lb-provider
1 新建启动类
package org.crazyit.cloud;

import java.util.Scanner;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class ProviderApp {

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String port = scan.nextLine();
        new SpringApplicationBuilder(ProviderApp.class).properties("server.port=" + port).run(args);
    }

}
2 新建实体类
package org.crazyit.cloud;
public class Police {
      private Integer id;
      private String name;
      private String message;
      public Integer getId() {
            return id;
      }
      public void setId(Integer id) {
            this.id = id;
      }
      public String getName() {
            return name;
      }
      public void setName(String name) {
            this.name = name;
      }
      public String getMessage() {
            return message;
      }
      public void setMessage(String message) {
            this.message = message;
      }
      
}
3 新建控制类
package org.crazyit.cloud;

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PoliceController {

    @RequestMapping(value = "/call/{id}", method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Police call(@PathVariable Integer id, HttpServletRequest request) {
        Police p = new Police();
        p.setId(id);
        p.setName("angus");
        p.setMessage(request.getRequestURL().toString());
        return p;
    }
}
4 启动8080和8081两个实例
四 新建项目spring-lb-invoker
1 新建一个启动类
package org.crazyit.cloud;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class InvokerApp {

    public static void main(String[] args) {
        new SpringApplicationBuilder(InvokerApp.class).web(true).run(args);
    }

}
2 新建控制类
package org.crazyit.cloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.netflix.loadbalancer.ZoneAwareLoadBalancer;

@RestController
@Configuration
public class TestController {
    
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    @GetMapping("/router")
    @ResponseBody
    public String router() {
        RestTemplate tpl = getRestTemplate();
        String json = tpl.getForObject("http://spring-lb-provider/call/1", String.class);
        return json;
    }
    
}
3 启动服务并测试
浏览器反复输入 http://localhost:9000/router
在下面两个结果轮询
{"id":1,"name":"angus","message":"http://DESKTOP-5SDKDG4:8080/call/1"}
{"id":1,"name":"angus","message":"http://DESKTOP-5SDKDG4:8081/call/1"}
说明实现了轮询。
4 新建自定义规则,让大部分选择8080端口
package org.crazyit.cloud;

import java.util.List;
import java.util.Random;

import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;

public class MyRule implements IRule {
    
    private ILoadBalancer lb;

    public Server choose(Object key) {
        System.out.println("这是自定义的规则类");
        Random r = new Random();
        int randomNum = r.nextInt(10);
        List<Server> servers = lb.getAllServers();
        if(randomNum > 7) {
            Server s = getServerByPort(servers, 8081);
            return s;
        }
        return getServerByPort(servers, 8080);
    }
    
    private Server getServerByPort(List<Server> servers, int port) {
        for(Server s : servers) {
            if(s.getPort() == port) {
                return s;
            }
        }
        return null;
    }

    public void setLoadBalancer(ILoadBalancer lb) {
        this.lb = lb;
    }

    public ILoadBalancer getLoadBalancer() {
        return lb;
    }

}
5 新建配置类
package org.crazyit.cloud;

import org.springframework.context.annotation.Bean;

import com.netflix.loadbalancer.IRule;

public class MyConfig {

    @Bean
    public IRule getRule() {
        return new MyRule();
    }
}
6 新建MyClient类,和配置类关联
package org.crazyit.cloud;
importorg.springframework.cloud.netflix.ribbon.RibbonClient;
@RibbonClient(name = "spring-lb-provider", configuration = MyConfig.class)
public class MyClient {
}
说明:上面代码的name之所以为spring-lb-provider,是因为要和getForObject中的spring-lb-provider保持一致,否则无法关联到MyClient类,更不会和MyConfig配置发生关联
      @GetMapping("/router")
      @ResponseBody
      public String router() {
            RestTemplate tpl = getRestTemplate();
            String json = tpl.getForObject("http://spring-lb-provider/call/1", String.class);
            return json;
      }
7 测试自定义的规则
输出结果大部分如下:
{"id":1,"name":"angus","message":"http://DESKTOP-5SDKDG4:8080/call/1"}
再看控制台输出:
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
说明自定义规则生效。
8 通过配置文件中的NFLoadBalancerRuleClassName进行配置,去掉@RibbonClient(name = "spring-lb-provider", configuration = MyConfig.class)注解,在配置文件中加入下面一段,也能起到同样的效果
spring-lb-provider:
  ribbon:
    NFLoadBalancerRuleClassName: org.crazyit.cloud.MyRule
9 再次测试自定义规则,效果一样
输出结果大部分如下:
{"id":1,"name":"angus","message":"http://DESKTOP-5SDKDG4:8080/call/1"}
再看控制台输出:
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
10 LoadBalancerClient的应用
在控制类中加入以下代码
      @Autowired
      private LoadBalancerClient client;
      @RequestMapping(value = "/lb", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
      public ServiceInstance lb() {
            ServiceInstance si = client.choose("spring-lb-provider");
            return si;
      }
11 测试
大部分输出下面内容
{"serviceId":"spring-lb-provider","server":{"host":"DESKTOP-5SDKDG4","port":8080,"id":"DESKTOP-5SDKDG4:8080","zone":"defaultZone","readyToServe":true,"instanceInfo":{"instanceId":"DESKTOP-5SDKDG4:spring-lb-provider:8080","app":"SPRING-LB-PROVIDER","appGroupName":null,"ipAddr":"192.168.0.106","sid":"na","homePageUrl":"http://DESKTOP-5SDKDG4:8080/","statusPageUrl":"http://DESKTOP-5SDKDG4:8080/info","healthCheckUrl":"http://DESKTOP-5SDKDG4:8080/health","secureHealthCheckUrl":null,"vipAddress":"spring-lb-provider","secureVipAddress":"spring-lb-provider","countryId":1,"dataCenterInfo":{"@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo","name":"MyOwn"},"hostName":"DESKTOP-5SDKDG4","status":"UP","leaseInfo":{"renewalIntervalInSecs":30,"durationInSecs":90,"registrationTimestamp":1531641405889,"lastRenewalTimestamp":1531643144502,"evictionTimestamp":0,"serviceUpTimestamp":1531641403608},"isCoordinatingDiscoveryServer":false,"metadata":{},"lastUpdatedTimestamp":1531641405889,"lastDirtyTimestamp":1531641341331,"actionType":"ADDED","asgName":null,"overriddenStatus":"UNKNOWN"},"metaInfo":{"serverGroup":null,"instanceId":"DESKTOP-5SDKDG4:spring-lb-provider:8080","appName":"SPRING-LB-PROVIDER","serviceIdForDiscovery":"spring-lb-provider"},"alive":true,"hostPort":"DESKTOP-5SDKDG4:8080"},"secure":false,"metadata":{},"host":"DESKTOP-5SDKDG4","port":8080,"uri":"http://DESKTOP-5SDKDG4:8080"}
再观察控制台:
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
说明当访问微服务spring-lb-provider时,我们的配置生效了。
12 输出客户端的配置
在控制类中加入以下代码
    @Autowired
    private SpringClientFactory factory;
    
    @RequestMapping(value = "/fa", method = RequestMethod.GET)
    public String factory() {
        ZoneAwareLoadBalancer lb = (ZoneAwareLoadBalancer)factory.getLoadBalancer("default");
        System.out.println(lb.getRule().getClass().getName());
        
        ZoneAwareLoadBalancer lb2 = (ZoneAwareLoadBalancer)factory.getLoadBalancer("spring-lb-provider");
        System.out.println(lb2.getRule().getClass().getName());
        return "";
    }
13 测试
输出
com.netflix.loadbalancer.ZoneAvoidanceRule
org.crazyit.cloud.MyRule
再看看配置文件
spring-lb-provider:
  ribbon:
    NFLoadBalancerRuleClassName: org.crazyit.cloud.MyRule
当选择default时,采用的规则是com.netflix.loadbalancer.ZoneAvoidanceRule
当选择spring-lb-provider时,采用的规则是org.crazyit.cloud.MyRule




猜你喜欢

转载自blog.csdn.net/chengqiuming/article/details/81054684