Spring Cloud 学习记录(二) Netflix-Ribbon
Ribbon 简介
Ribbon 是 Netflix 下的负载均衡项目,它在集群中为各个客户端的通信提供了支持,它
主要实现中间层应用程序的负载均衡。Ribbon 提供以下特性:
- 负载均衡器,可支持插拔式的负载均衡规则。
- 对多种协议提供支持,例如 HTTP、TCP、UDP。
- 集成了负载均衡功能的客户端。
同为 Netflix 项目,Ribbon 可以与 Eureka 整合使用,Ribbon 同样被集成到 Spring Cloud
中,作为 spring-cloud-netflix 项目中的子模块。Spring Cloud 将 Ribbon 的 API 进行了封装,
使用者可以使用封装后的 API 来实现负载均衡,也可以直接使用 Ribbon 的原生 API。
Ribbon 子模块
Ribbon 主要有以下三大子模块:
- ribbon-core:该项目为Ribbon项目的核心,主要包括负载均衡接口定义、客户端接口定义、内置负载均衡实现等API。
- ribbon-eureka:为Eureka提供的负载均衡实现类。
- ribbon-httpclient:对Apache的HttpClient进行封装,该模块提供了含有负载均衡功能的REST客户端。
负载均衡器组件
Ribbon的负载均衡器主要与集群中的各个服务器进行通信,负载均衡器需要提供以下功能:
- 维护服务的IP、DNS名称等信息
- 根据特定的逻辑在服务器列表中循环。
为了实现负载均衡的基础功能,Ribbon 的负载均衡器有以下三大子模块: - Rule:一个逻辑组件,这些逻辑将会决定,从服务器中返回哪个服务器实例
- Ping:该组件主要使用定时器,来确保服务器网络可以连接
- ServerList:服务器列表,可以通过静态的配置确定负载的服务器,也可以动态指定服务器列表。如果动态指定服务器列表,则会有后台线程来刷新该列表
本次关于Ribbon的学习,主要围绕负载均衡器组件进行,下面搞个demo:
Ribbon程序
本程序直接使用Ribbon原生API,结构图如下:
首先创建一个springboot项目,添加web依赖,用作服务器端,并以不同的端口启动,修改入口类函数,代码如下:
import java.util.Scanner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@SpringBootApplication
public class FirstRibbonServerApplication {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String port = scan.nextLine();
new SpringApplicationBuilder(FirstRibbonServerApplication.class).properties("server.port="+port).run(args);
//SpringApplication.run(FirstRibbonServerApplication.class, args);
}
}
并随意创建一个controller,编写一个方法,用于返回Json/字符串,之后:
分别用8080/8081端口启动程序,之后使用Ribbon编写客户端调用两个服务,代码如下:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.netflix.client.ClientFactory;
import com.netflix.client.http.HttpRequest;
import com.netflix.client.http.HttpResponse;
import com.netflix.config.ConfigurationManager;
import com.netflix.niws.client.http.RestClient;
@SpringBootApplication
public class FirstRibbonClientApplication {
public static void main(String[] args) throws Exception {
//SpringApplication.run(FirstRibbonClientApplication.class, args);
ConfigurationManager.getConfigInstance().setProperty("my-client.ribbon.listOfServers", "localhost:8080,localhost:8081");
@SuppressWarnings("deprecation")
RestClient client=(RestClient) ClientFactory.getNamedClient("my-client");
HttpRequest request = HttpRequest.newBuilder().uri("/hello/jackshen").build();
for (int i = 0; i < 6; i++) {
HttpResponse response = client.executeWithLoadBalancer(request);
String result = response.getEntity(String.class);
System.out.println("+++++++++++++++++++++++++++++++++++"+result);
}
}
}
最终在客户端会返回如下信息:
http://localhost:8080/hello/jackshen
http://localhost:8081/hello/jackshen
http://localhost:8080/hello/jackshen
http://localhost:8081/hello/jackshen
http://localhost:8080/hello/jackshen
http://localhost:8081/hello/jackshen
以上是:Ribbon原生API使用方式
Ribbon配置
在编写客户端时,使用了 ConfigurationManager 来设置配置项,除了在代码中指定配
置 项 外 , 还 可 以 将 配 置 放 到 “ .properties ” 文 件 中 , ConfigurationManager 的
loadPropertiesFromResources 方法可以指定 properties 文件的位置,配置格式如下:
<client>.<nameSpace>.<property>=<value>
其中为客户的名称,声明该配置属于哪一个客户端,在使用 ClientFactory 时可
传入客户端的名称,即可返回对应的“请求客户端”实例。为该配置的命名
空间,默认为“ribbon”,为属性名,为属性值。如果想对全部的客户端
生效,可以将客户端名称去掉,直接以“.”的格式进行配置。以
下的配置,为客户端指定服务器列表:
my-client.ribbon.listOfServers=localhost:8080,localhost:8081
Ribbon 的配置,同样可以使用在 Spring Cloud 的配置文件(即 application.yml)中使
用。
spring 的application.yml 中配置自定义规则和ping类,配置内容如下:
cloud-provider:
ribbon:
NFLoadBalancerRuleClassName: org.crazyit.cloud.MyRule
NFLoadBalancerPingClassName: org.crazyit.cloud.MyPing
listOfServers: http://localhost:8080/,http://localhost:8081/
springboot类配置方法如下:
package com.panku.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.panku.domain.MyPing;
import com.panku.domain.MyRule;
@Configuration
public class MyConf {
@Bean
public IRule getRule() {
return new MyRule();
}
@Bean
public IPing getPing() {
return new MyPing();
}
}
package com.panku.conf;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
@RibbonClient(name="cloud-provider",configuration=MyConf.class)
public class CloudProviderConfig {
}
调用方法如下:
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Configuration
public class InvokerController {
@Bean
@LoadBalanced
public RestTemplate getRestTpl() {
return new RestTemplate();
}
@RequestMapping("/hello/{name}")
public String hello(@PathVariable("name")String name) {
RestTemplate restTpl = getRestTpl();
String json = restTpl.getForObject("http://cloud-provider/hello/"+name, String.class);
return json;
}
}
RestTemplate 负载均衡原理
@LoadBalanced 注解概述
RestTemplate 本是 spring-web 项目中的一个 REST 客户端访问类,它遵循 REST 的
设计原则,提供简单的 API 让调用去访问 HTTP 服务器。RestTemplate 本身不具有负载均
衡的功能,该类也与 Spring Cloud 没有关系,但为何加入@LoadBalanced 注解后,一个
RestTemplate 实例就具有负载均衡的功能呢?实际上这要得益于 RestTemplate 的拦截器
功能。
在 Spring Cloud 中,使用@LoadBalanced 修饰的 RestTemplate,在 Spring 容器启动
时,会为这些被修饰过的 RestTemplate 添加拦截器,拦截器中使用了 LoadBalancerClient
来处理请求,LoadBalancerClient 本来就是 Spring 封装的负载均衡客户端,通过这样间接
处理,使得 RestTemplate 就拥有了负载均衡的功能
REST 客户端 Feign 介绍
Feign 是一个 Github 上一个开源项目,目的是为了简化 Web Service 客户端的开发。
在使用 Feign 时,可以使用注解来修饰接口,被注解修饰的接口具有访问 Web Service 的
能力,这些注解中既包括了 Feign 自带的注解,也支持使用第三方的注解。除此之外,Feign
还支持插件式的编码器和解码器,使用者可以通过该特性,对请求和响应进行不同的封装与
解析。
Spring Cloud 将 Feign 集成到 netflix 项目中,当与 Eureka、Ribbon 集成时,Feign 就
具有负载均衡的功能。Feign 本身在使用上的简便性,加上与 Spring Cloud 的高度整合,使
用该框架在 Spring Cloud 中调用集群服务,将会大大降低开发的工作量
调用方法:
1.在入口类添加注解:@EnableFeignClients
2.编写接口类:内容如下:
package com.panku.icloud;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient("cloud-provider")
public interface HelloClient {
@RequestMapping("/hello/{name}")
String hello(@PathVariable("name")String name);
}
3.编写调用控制器,内容如下:
package com.panku.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.panku.icloud.HelloClient;
@RestController
public class InvokerController {
@Autowired
private HelloClient helloClient;
@RequestMapping("/simhello/{name}")
public String simHello(@PathVariable("name")String name) {
return helloClient.hello(name);
}
}
注意:springboot2.0之后所feign的starter名称变为:spring-cloud-starter-openfeign