SpringCloud之Ribbon

·  阅读 52

@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实例可以有不用的一些负载均衡相关策略。这部分配置的格式是.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加载过程全部抽取出来,但是一直无法成功。也希望有大牛能给个示例代码。

分类:
后端
标签:
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改