@TOC
Ribbon是什么?美国的一个人名而已。《当幸福来敲门》中Gardner在投资公司接到的第一个大客户就叫Ribbon。而在SpringCloud中,他是一个重要的负载均衡组件,从一堆服务节点中选择一个节点发起请求。
一、使用方法
Demo参见git项目。
1、最简单的用法:
public static void main(String[] args) {
ILoadBalancer lb = new BaseLoadBalancer();
List<Server> servers = new ArrayList<Server>();
servers.add(new Server("localhost",8080));
servers.add(new Server("localhost",8081));
servers.add(new Server("localhost",8082));
servers.add(new Server("localhost",8083));
lb.addServers(servers);
for(int i = 0 ; i < 10 ; i ++) {
Server server = lb.chooseServer(null);
System.out.println(server);
}
}
复制代码
2、带配置信息的用法:
通过配置信息加载服务节点
public static void main(String[] args) {
//设置服务器列表。和配置到properties里一样
ConfigurationManager.getConfigInstance().setProperty("my-client.ribbon.listOfServers","localhost:8081,localhost:8082,localhost:8083,localhost:8084");
//该实例是ZoneAwareLoadBalancer 实例
BaseLoadBalancer lb = (BaseLoadBalancer)ClientFactory.getNamedLoadBalancer("my-client");
IRule rule = new RoundRobinRule();
lb.setRule(rule);
for(int i = 0 ; i < 10 ; i ++) {
Server server = lb.chooseServer(null);
System.out.println(server);
}
}
复制代码
这种方式涉及到了ribbon的属性配置,
Ribbon相关的配置有两部分,一部分是Ribbon的通用配置。以ribbon.开头,配置ribbon的一些通用组件
另一部分是Ribbon的实例配置。每个ribbon实例可以有不用的一些负载均衡相关策略。这部分配置的格式是.ribbon.=value。其中instance就是ribbon的实例名。
实现类接口 | 配置参数 |
---|---|
ILoadBalancer | .ribbon.NFLoadBalancerClassName |
IRule | .ribbon.NFLoadBalancerRuleClassName |
IPing | .ribbon.NFLoadBalancerPingClassName |
ServerList | .ribbon.NIWSServerListClassName |
ServerListFilter | .ribbon.NIWSServerListFilterClassName |
手动配置服务节点 | .ribbon.listOfServers |
### 3、自定义扩展组件 | |
Ribbon中有几个重要的接口,可以通过这些接口,扩展自己的负载均衡策略。例如可以通过自己实现IRule接口,实现自己的服务节点选择策略。 |
4、配合Eureka使用
以上的节点列表都是在程序中指定的,而在SpringCloud中,这些节点信息是放在Eureka中维护的。
首先,在SpringCloud的配置文件中先配置Eureka地址。上面示例代码中的server节点,都需要从Eureka上获取。之前有记录,这里就不介绍了。
然后,增加Ribbon相关的配置。主要是配置ribbon的通用部分配置。
最后,关于Ribbon的使用方法,以下代码总结了四种常用方法。看注释就知道了。
private String instanceId = "EurekaDemo";//服务端的应用名
/**
* 1、通过instance,ribbon会利用负载均衡策略选出一个服务地址进行调用。
*/
@Resource
private LoadBalancerClient loadBalancerClient;
@RequestMapping(value="/logInstance",method = RequestMethod.GET)
@ResponseBody
public Object logInstance() {
ServiceInstance serviceInstance = loadBalancerClient.choose(instanceId);
logger.info("serviceId: " + serviceInstance.getServiceId() + "; service host:" + serviceInstance.getHost()
+ ";service port : " + serviceInstance.getPort());
return serviceInstance;
}
/**
* 2、启动项上要添加注释@EnableEurekaClient
*/
@Resource
private DiscoveryClient discoveryClient;
@ResponseBody
@GetMapping(value="/getServerList")
public Object getServerList() {
List<ServiceInstance> instances = discoveryClient.getInstances(instanceId);
for(ServiceInstance instance:instances) {
logger.info(JSON.toJSONString(instance));
}
return instances;
}
/**
* 3、restTemplate做负载均衡核心就是会在RestTemplate中添加个interceptor,从而将instanceId的访问地址转换成负载均衡选出的服务器地址
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Resource
private RestTemplate restTemplate;
@RequestMapping(value = "/getSomeUsers", method = RequestMethod.GET)
@ResponseBody
public Object getSomeUsers(@RequestParam int countNum) {
logger.info("Ribbon controller test i = " + 1);
List<User> users = new ArrayList<User>();
for (int i = 0; i < countNum; i++) {
User user = new User();
user.setUserId("" + i);
users.add(user);
}
String url = "http://"+instanceId+"/user/getSomeUsers";
List<?> userRes = restTemplate.postForObject(url, users, ArrayList.class);
return userRes;
}
@RequestMapping(value="/getOneUsers/{id}",method=RequestMethod.GET)
@ResponseBody
public Object getOneUsers(@PathParam("id")String userId) {
logger.info("Ribbon controller getOneUsers id = " + userId);
User user = new User();
user.setUserId(userId);
String url = "http://"+instanceId+"/testServe/getOneUsers";
User userInfo = restTemplate.postForObject(url, user, User.class);
return userInfo;
}
/**
* 4、使用SpringClientFactory构建负载均衡器。
*/
@Autowired
private SpringClientFactory factory;
@Bean
//注入一个自定义rule
public IRule getRule() {
return new MyRule();
}
@RequestMapping(value = "/fa", method = RequestMethod.GET)
public String factory() {
ZoneAwareLoadBalancer lb = (ZoneAwareLoadBalancer)factory.getLoadBalancer("default");
//加载的是IOC容器里的RULE
String rn1 = lb.getRule().getClass().getName();
logger.info("rn1 : "+rn1);
Server ser1 = lb.chooseServer();
logger.info("ser1 : "+ser1);
ZoneAwareLoadBalancer lb2 = (ZoneAwareLoadBalancer)factory.getLoadBalancer(instanceId);
//加载的是IOC容器里的RULE
String rn2 = lb2.getRule().getClass().getName();
logger.info("rn2 : "+rn2);
Server ser2 = lb2.chooseServer();
logger.info("ser2 : "+ser2);
return "please see the console log";
}
复制代码
二、Ribbon与RestTemplate
这其中有意思一点的就是Ribbon结合RestTemplate一起使用。
给RestTemplate加上@LoadBalanced注解后,这个RestTemplate就具备了负载均衡的功能。其实现原理主要是RestTemplate继承的InterceptingHttpAccessor,而这个InterceptingHttpAccessor可以设置一个拦截器列表。这样,在对RestTemplate进行初始化后,可以根据@LoadBalanced注解,给他加上一个Ribbon实例作为拦截器,这样,就可以将 "http://"+instanceId+"/user/getSomeUsers" 这样的请求,转发到instanceId对应的一个服务节点上。例如:"http://localhost:8081/user/getSomeUsers"
三、问题
其实看完SpringCloud之后一直还有个问题没有解决。当使用微服务后,如何在一些非Spring应用中调用微服务呢?例如曾经遇到一个问题,如何在一个Spark计算程序中调用微服务获取外部数据?
这在现在Apache孵化中的Dubbo里,已经非常简单了,但是SpringCloud,还没有弄明白,虽然大致思路还是明白,需要把SpringCloud内部相关的一系列AOP加载过程全部抽取出来,但是一直无法成功。也希望有大牛能给个示例代码。