Spring Cloud 详解(第二篇:Ribbon负载均衡)
@(Spring Cloud微服务)[java, spring-cloud, eureka,rtibbon]
接上一篇:http://blog.csdn.net/amon1991/article/details/79347660
我们测试了如何通过Eureka组件来构建一个注册中心,这一篇中,我们将使用注册中心Eureka和Ribbon组件实现简单的负载均衡功能。
在本章中,我们将发布两个功能完全一致的service-hi实例,这两个实例在最终的生产环境中应该是无状态的,这样提供服务时,就可以保证无论使用哪个实例都可以提供相同的服务能力。
Eureka-client的基本搭建方法可参考http://blog.csdn.net/amon1991/article/details/79347660,我们在该服务中,在Controller层加入一个简单的测试功能,代码如下:
@Value("${server.port}")
String port;
@RequestMapping("/hi")
public String home(@RequestParam String name) {
return "hi "+name+",i am from port:" +port;
}
具体代码可以参考:https://github.com/amon1991/spring-cloud-demo/tree/master/eureka-client
接着,我们修改application.yml文件中的port参数,分别配置为8762、8763两个端口,并分别打包启动实例,启动后,可以在Eureka Server查看到两个实例已经被正常注册至注册中心:
如图所示,已经有两个service-hi注册到了注册中心,此时/hi这个接口就可以被其它服务使用了。
服务实例集群注册到注册中心后,需要被成功调用才能发挥微服务框架的能力,Spring Cloud框架中提供两种方式实现负载均衡式的调用,分别为Ribbon和Feign。相对而言,Ribbon是对Spring的RestTemple接口的能力扩展,更加底层和通用一些;而Feign是基于Ribbon的又一层封装,层次更加高一些,集成度较高。本小节将说明如何搭建一个简单的Ribbon客户端并使用微服务的方式调用service-hi的服务。
搭建Ribbon客户端的具体步骤如下:
- 第一步:添加Mavan依赖 :加入Spring Boot和Spring Cloud相关依赖,并对应好版本;
- 第二步:注解相关类 :在启用类上加入@EnableDiscoveryClient注解,任意类配置 @LoadBalanced负载均衡注解;
- 第三步:调用Service-hi服务 :使用两种方式调用Service-hi提供的服务。
- 第四步:配置application.yml文件 :修改默认配置,适应生产环境。
完整的Maven依赖如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.siemens.springcloudgroupId>
<artifactId>service-ribbonartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<name>service-ribbonname>
<description>Demo project for Spring Bootdescription>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.8.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Dalston.SR4spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-ribbonartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
首先需要在启用类上做如下配置:
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceRibbonApplication.class, args);
}
}
为了让大家更加区分清楚普通服务请求方式和Ribbon请求方式的区别,本例中构造了两个RestTemplate实例,分别对应于普通实例和Ribbon实例:
@Bean(name = "ribbonRestTemplate")
@LoadBalanced
RestTemplate ribbonRestTemplate() {
return new RestTemplate();
}
@Bean(name = "commonRestTemplate")
RestTemplate commonRestTemplate() {
return new RestTemplate();
}
这两个实例的构造可以放置于任意可被加载进Spring Context环境的配置类中,比如启用类,或者专门的配置类(加@Configuration注解)
我们使用两种方式来调用service-hi的服务,一种是用过IP和端口直连调用的方式,另外一种使用Ribbon的方式。
1、IP+端口直接调用:
@Resource(name = "commonRestTemplate")
RestTemplate commonRestTemplate;
/**
* send restful request by common restTemplate
* @param name
* @return
*/
public String commonHiService(String name) {
return commonRestTemplate.getForObject("http://localhost:8762/hi?name="+name,String.class);
}
2、Ribbon方式调用:
@Resource(name = "ribbonRestTemplate")
RestTemplate ribbonRestTemplate;
/**
* send restful request by ribbon interceptor
* @param name
* @return
*/
public String hiService(String name) {
return ribbonRestTemplate.getForObject("http://service-hi/hi?name="+name,String.class);
}
可以看到Ribbon方式直接使用服务名来访问服务,实际代码底层,会通过eureka提供的注册服务表及相应的负载均衡算法转化为IP和端口,并最终向具体实例发送相关请求。
具体代码可参考:https://github.com/amon1991/spring-cloud-demo/tree/master/service-ribbon
server:
port: 8764
spring:
application:
name: service-ribbon
eureka:
instance:
lease-renewal-interval-in-seconds: 5 # 心跳时间,即服务续约间隔时间(缺省为30s)
lease-expiration-duration-in-seconds: 15 # 发呆时间,即服务续约到期时间(缺省为90s)
client:
registry-fetch-interval-seconds: 10 # 拉取服务注册信息间隔(缺省为30s)
service-url:
defaultZone: http://localhost:8761/eureka/
healthcheck:
enabled: true # 开启健康检查(依赖spring-boot-starter-actuator)
– 1、eureka.instance.lease-renewal-interval-in-seconds:
lease-renewal-interval-in-seconds,表示eureka client发送心跳给server端的频率。如果在leaseExpirationDurationInSeconds后,server端没有收到client的心跳,则将摘除该instance。除此之外,如果该instance实现了HealthCheckCallback,并决定让自己unavailable的话,则该instance也不会接收到流量,默认30秒。
– 2、eureka.instance.lease-expiration-duration-in-seconds:
lease-expiration-duration-in-seconds,表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,该值默认为90秒。
– 3、eureka.client.registry-fetch-interval-seconds:
表示eureka client间隔多久去拉取服务注册信息,默认为30秒,对于api-gateway,如果要迅速获取服务注册状态,可以缩小该值,比如5秒。
配置结束之后,启动Spring-Boot程序,该实例也会注册到Eureka注册中心中,如图:
我们通过访问Ribbon客户端所在web服务来测试其内部是否对service-hi中的服务进行了正确调用。
1、IP+端口直接调用:
如图所示,不断刷新浏览器,可以看到服务返回的端口保持在8762,不会变化,因为代码内部固定了访问的端口信息。
如图所示,不断刷新浏览器,返回端口信息不断在8762和8763之间切换,说明ribbon客户端已经帮我们在负载均衡的访问Service-hi实例,且访问的频率是基本平均的。
至此,我们已经完成了一个简单Ribbon客户端的测试,通过测试我们通过了对eureka-client的负载均衡访问测试。