Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 Netflix Ribbon 实现。 通过 Spring Cloud 的封装, 可以让我们轻松地将面向服务的 REST 模板请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon 虽然只是一个工具类框架,它不像服务注册中心、 配置中心、 API 网关那样需要独立部署, 但是它几乎存在于每一个Spring Cloud 构建的微服务和基础设施中。
服务端负载均衡
负载均衡在系统架构中是一个非常重要,并且是不得不去实施的内容。 因为负载均衡是对系统的高可用、 网络压力的缓解和处理能力扩容的重要手段之一。 我们通常所说的负载均衡都指的是服务端负载均衡, 其中分为硬件负载均衡和软件负载均衡。 硬件负载均衡主要通过在服务器节点之间安装专门用于负载均衡的设备,比如 F5 等;而软件负载均衡则是通过在服务器上安装一些具有均衡负载功能或模块的软件来完成请求分发工作, 比如Nginx等。不论采用硬件负载均衡还是软件负载均衡,只要是服务端负载均衡都能以类似下图的架构:
![Spring Cloud Ribbon](http://p2.ttnews.xyz/loading.gif)
件负载均衡的设备或是软件负载均衡的软件模块都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。当客户端发送请求到负载均衡设备的时候, 该设备按某种算法(比如线性轮询、 按权重负载、 按流量负载等)从维护的可用服务端清单中取出一台服务端的地址, 然后在负载均衡服务器内部进行转发。
客户端负载均衡
客户端负载均衡和服务端负载均衡最大的不同点在千上面所提到的服务清单所存储的位置。 在客户端负载均衡中, 所有客户端节点都维护着自己要访问的服务端清单, 而这些服务端的清单来自于服务注册中心,比如上一章我们介绍的Eureka服务端。同服务端负载均衡的架构类似, 在客户端负载均衡中也需要心跳去维护服务端清单的健康性, 只是这个步骤需要与服务注册中心配合完成。
![Spring Cloud Ribbon](http://p2.ttnews.xyz/loading.gif)
RestTemplate
Get请求
@RequestMapping
(
"queryAll"
)
public
List
<
User
>
queryByPage
(
Integer
pageNow
,
Integer
pageSize
)
{
return
...
}
String
url
=
"http://localhost:9999/queryAll"
;
Map
params
=
new
HashMap
();
params
.
put
(
"pageNow"
,
2
);
params
.
put
(
"pageSize"
,
5
);
User
[]
users
=
restTemplate
.
getForObject
(
url
,
User
[].
class
,
params
);
Post请求
@PostMapping
(
"saveUser"
)
public
void
saveUser
(
@RequestBody
User
user
)
throws
InterruptedException
{
}
String
url
=
"http://localhost:9999/saveUser"
;
User
user
=
new
User
(
"1"
,
"zhangsan"
);
user
.
setBirthDay
(
new
Date
());
restTemplate
.
postForEntity
(
url
,
user
,
null
);
Delete请求
@DeleteMapping
(
"deleteUserByIds"
)
public
void
deleteUserByIds
(
String
[]
ids
)
throws
InterruptedException
{
}
String
url
=
"http://localhost:9999/deleteUserByIds?ids={1}"
;
restTemplate
.
delete
(
url
,
"1,2,3,5"
);
搭建Ribbon测试
引入Ribbon支持
<dependencymanagement>
<dependencies>
<dependency>
<groupid>
org.springframework.cloud
/<groupid>
<artifactid>
spring-cloud-dependencies
/<artifactid>
<version>
Finchley.SR1
/<version>
<type>
pom
/<type>
<scope>
import
/<scope>
/<dependency>
/<dependencies>
/<dependencymanagement>
<dependencies>
<dependency>
<groupid>
org.springframework.cloud
/<groupid>
<artifactid>
spring-cloud-starter-netflix-ribbon
/<artifactid>
/<dependency>
/<dependencies>
添加RestTemplateBean配置
@SpringBootApplication
public
class
WebApplication
{
public
static
void
main
(
String
[]
args
)
{
SpringApplication
.
run
(
WebApplication
.
class
,
args
);
}
@Bean
@LoadBalanced
public
RestTemplate
restTemplate
(){
return
new
RestTemplate
();
}
}
配置application.properties
hello其中 hello-service作用是命名服务,以后Ribbon的负责均衡会根据配置文件的配置的调用列表轮训调用.
-
service
.
ribbon
.
listOfServers
=
localhost
:
7777
,
localhost
:
8888
,
localhost
:
9999
测试ribbon负责均衡
@RunWith测试多次尝试负载均衡效果 : )
(
SpringRunner
.
class
)
@SpringBootTest
(
classes
=
WebApplication
.
class
)
public
class
RestTemplateApplicationTests
{
@Autowired
private
RestTemplate
restTemplate
;
@Test
public
void
testHelloService
(){
String
url
=
"http://hello-service/test"
;
String
forObject
=
restTemplate
.
getForObject
(
url
,
String
.
class
);
System
.
out
.
println
(
forObject
);
}
}
Ribbon和Eureka集成
启动web实例,作为服务提供端
- 依赖清单
<parent>
<groupid>
org.springframework.boot
/<groupid>
<artifactid>
spring-boot-dependencies
/<artifactid>
<version>
2.0.5.RELEASE
/<version>
/<parent>
<dependencymanagement>
<dependencies>
<dependency>
<groupid>
org.springframework.cloud
/<groupid>
<artifactid>
spring-cloud-dependencies
/<artifactid>
<version>
Finchley.SR1
/<version>
<type>
pom
/<type>
<scope>
import
/<scope>
/<dependency>
/<dependencies>
/<dependencymanagement>
<dependencies>
<dependency>
<groupid>
org.springframework.boot
/<groupid>
<artifactid>
spring-boot-starter-web
/<artifactid>
/<dependency>
<dependency>
<groupid>
org.springframework.cloud
/<groupid>
<artifactid>
spring-cloud-starter-netflix-eureka-server
/<artifactid>
/<dependency>
/<dependencies>
<build>
<plugins>
<plugin>
<groupid>
org.springframework.boot
/<groupid>
<artifactid>
spring-boot-maven-plugin
/<artifactid>
/<plugin>
/<plugins>
/<build>
- 代码清单
|-
java
|-
com
.
jiangzz
|-
controller
|-
UserController
.
java
|-
WebIntanceApplication
.
java
|-
resources
|-
application
-
app1
.
properties
|-
application
-
app2
.
properties
- 启动web实例,并且将服务注册到eureka注册中心
UserController.java
package
com
.
jiangzz
.
controller
;
import
org
.
springframework
.
web
.
bind
.
annotation
.
RequestMapping
;
import
org
.
springframework
.
web
.
bind
.
annotation
.
RestController
;
@RestController
public
class
UserController
{
@RequestMapping
(
"hello"
)
public
String
helloService
(){
return
"哈喽!"
;
}
}
application-app1.properites
server
.
port
=
9091
# 配置服务
spring
.
application
.
name
=
user
-
service
eureka
.
instance
.
instance
-
id
=
user
-
server
-
1
eureka
.
instance
.
prefer
-
ip
-
address
=
true
eureka
.
instance
.
lease
-
renewal
-
interval
-
in
-
seconds
=
2
eureka
.
instance
.
lease
-
expiration
-
duration
-
in
-
seconds
=
6
# 注册Eureka
eureka
.
client
.
fetch
-
registry
=
false
eureka
.
client
.
service
-
url
.
defaultZone
=
http
:
//eureka:1111/eureka,http://eureka:2222/eureka,http://eureka:3333/eureka
application-app2.properites
server
.
port
=
9092
# 配置服务
spring
.
application
.
name
=
user
-
service
eureka
.
instance
.
instance
-
id
=
user
-
server
-
2
eureka
.
instance
.
prefer
-
ip
-
address
=
true
eureka
.
instance
.
lease
-
renewal
-
interval
-
in
-
seconds
=
2
eureka
.
instance
.
lease
-
expiration
-
duration
-
in
-
seconds
=
6
# 注册Eureka
eureka
.
client
.
fetch
-
registry
=
false
eureka
.
client
.
service
-
url
.
defaultZone
=
http
:
//eureka:1111/eureka,http://eureka:2222/eureka,http://eureka:3333/eureka
WebIntanceApplication.java
package
com
.
jiangzz
;
import
org
.
springframework
.
boot
.
SpringApplication
;
import
org
.
springframework
.
boot
.
autoconfigure
.
SpringBootApplication
;
import
org
.
springframework
.
cloud
.
netflix
.
eureka
.
EnableEurekaClient
;
@SpringBootApplication
@EnableEurekaClient
public
class
WebInstanceApplication
{
public
static
void
main
(
String
[]
args
)
{
SpringApplication
.
run
(
WebInstanceApplication
.
class
,
args
);
}
}
启动之后服务查看Eureka服务
启动Web消费端,集成Ribbon
- 依赖清单
<parent>
<groupid>
org.springframework.boot
/<groupid>
<artifactid>
spring-boot-dependencies
/<artifactid>
<version>
2.0.5.RELEASE
/<version>
/<parent>
<dependencymanagement>
<dependencies>
<dependency>
<groupid>
org.springframework.cloud
/<groupid>
<artifactid>
spring-cloud-dependencies
/<artifactid>
<version>
Finchley.SR1
/<version>
<type>
pom
/<type>
<scope>
import
/<scope>
/<dependency>
/<dependencies>
/<dependencymanagement>
<dependencies>
<dependency>
<groupid>
org.springframework.boot
/<groupid>
<artifactid>
spring-boot-starter-web
/<artifactid>
/<dependency>
<dependency>
<groupid>
org.springframework.cloud
/<groupid>
<artifactid>
spring-cloud-starter-netflix-eureka-server
/<artifactid>
/<dependency>
<dependency>
<groupid>
org.springframework.cloud
/<groupid>
<artifactid>
spring-cloud-starter-netflix-ribbon
/<artifactid>
/<dependency>
/<dependencies>
<build>
<plugins>
<plugin>
<groupid>
org.springframework.boot
/<groupid>
<artifactid>
spring-boot-maven-plugin
/<artifactid>
/<plugin>
/<plugins>
/<build>
- 代码清单
|-
java
|-
com
.
jiangzz
|-
WebInstanceApplication
.
java
|-
controller
|-
UserRestController
.
java
|-
resources
|-
application
.
properties
WebInstanceApplication.java
package
com
.
jiangzz
;
import
org
.
springframework
.
boot
.
SpringApplication
;
import
org
.
springframework
.
boot
.
autoconfigure
.
SpringBootApplication
;
import
org
.
springframework
.
cloud
.
client
.
discovery
.
EnableDiscoveryClient
;
import
org
.
springframework
.
cloud
.
client
.
loadbalancer
.
LoadBalanced
;
import
org
.
springframework
.
cloud
.
netflix
.
eureka
.
EnableEurekaClient
;
import
org
.
springframework
.
context
.
annotation
.
Bean
;
import
org
.
springframework
.
web
.
client
.
RestTemplate
;
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public
class
WebInstanceApplication
{
public
static
void
main
(
String
[]
args
)
{
SpringApplication
.
run
(
WebInstanceApplication
.
class
,
args
);
}
@Bean
@LoadBalanced
public
RestTemplate
restTemplate
(){
return
new
RestTemplate
();
}
}
UserRestController.java
package
com
.
jiangzz
.
controller
;
import
org
.
springframework
.
beans
.
factory
.
annotation
.
Autowired
;
import
org
.
springframework
.
web
.
bind
.
annotation
.
RequestMapping
;
import
org
.
springframework
.
web
.
bind
.
annotation
.
RestController
;
import
org
.
springframework
.
web
.
client
.
RestTemplate
;
@RestController
public
class
UserRestController
{
@Autowired
private
RestTemplate
restTemplate
;
@RequestMapping
(
"hello"
)
public
String
hello
(){
return
restTemplate
.
getForObject
(
"http://USER-SERVICE/hello"
,
String
.
class
);
}
}
application.properties
# 服务端口
server
.
port
=
9999
# 配置消费者
spring
.
application
.
name
=
user
-
service
-
consumer
eureka
.
instance
.
prefer
-
ip
-
address
=
true
# 注册Eureka
eureka
.
client
.
fetch
-
registry
=
true
eureka
.
client
.
service
-
url
.
defaultZone
=
http
:
//eureka:1111/eureka,http://eureka:22
閱讀更多 Java開發交流 的文章