负载均衡Ribbon(三)
更多内容参见个人技术博客,无广告欢迎关注
1.1 消费者
1.1.1 创建Maven工程
1.1.2 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<parent>
<groupId>com.wood</groupId>
<artifactId>spring-cloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wood</groupId>
<artifactId>consumer-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>consumer-client</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
1.1.3 application.properties
server.port=8091
spring.application.name=consumer-client
eureka.client.serviceUrl.defaultZone=http://localhost:6001/eureka
1.1.4 HelloController.java
RestTemplate是Spring提供的用于访问Rest服务的客户端,
RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。
调用RestTemplate的默认构造函数,RestTemplate对象在底层通过使用java.net包下的实现创建HTTP 请求,
可以通过使用ClientHttpRequestFactory指定不同的HTTP请求方式。
ClientHttpRequestFactory接口主要提供了两种实现方式
1、一种是SimpleClientHttpRequestFactory,使用J2SE提供的方式(既java.net包提供的方式)创建底层的Http请求连接。
2、一种方式是使用HttpComponentsClientHttpRequestFactory方式,底层使用HttpClient访问远程的Http服务,使用HttpClient可以配置连接池和证书等信息。
RestTemplate对象是在RunApp中声明并创建的,用它才可以实现负载均衡,同时注意url中的地址为VIP虚拟IP,为application.yml中配置的application-name。
package com.wood.consumerclient.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class HelloController {
/**
* RestTemplate是Spring提供的用于访问Rest服务的客户端,
*
* RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。
*
* 调用RestTemplate的默认构造函数,RestTemplate对象在底层通过使用java.net包下的实现创建HTTP 请求,
*
* 可以通过使用ClientHttpRequestFactory指定不同的HTTP请求方式。
*
* ClientHttpRequestFactory接口主要提供了两种实现方式
*
* 1、一种是SimpleClientHttpRequestFactory,使用J2SE提供的方式(既java.net包提供的方式)创建底层的Http请求连接。
*
* 2、一种方式是使用HttpComponentsClientHttpRequestFactory方式,底层使用HttpClient访问远程的Http服务,使用HttpClient可以配置连接池和证书等信息。
*
* RestTemplate对象是在RunApp中声明并创建的,用它才可以实现负载均衡,同时注意url中的地址为VIP虚拟IP,为application.yml中配置的application-name。
* */
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello/{name}")
@ResponseBody
public String hello(@PathVariable String name){
String url = "http://localhost:8081/hello" + name; //直接访问
return this.restTemplate.getForObject(url, String.class);
}
}
1.1.5 ConsumerRunApp.java
重点在启动时要初始化RestTemplate对象,同时设置@LoadBalanced负载均衡
package com.wood.consumerclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
public class ConsumerClientApplication {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerClientApplication.class, args);
}
}
1.1.6 测试
package com.wood.consumerclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
public class ConsumerClientApplication {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerClientApplication.class, args);
}
}
执行顺序:
先启动服务端 6001 eureka-server EurekaServerApplication
在启动提供者1 8081 provider-a ProviderAApplication
在启动提供者2 8082 provider-b ProviderBApplication
最后启动消费者 8091 consumer-client ConsumerClientApplication
访问Eureka控制台: http://localhost:6001/
访问请求: http://localhost:8091/hello/wood
1.2 负载均衡Ribbon
1.2.1 问题
之前我们使用的是直接访问的方式,能否实现提供者端负载均衡呢?
public String hello(){
String url = "http://localhost:8091/hello/wood";
return restTemplate.getForObject(url, String.class);
}
这是直接访问提供者,只能写死提供者的端口,并未使用Eureka注册中心,这样当服务宕机,我们也无从知道,只能访问超时。同时也无法“多例”服务进行支撑(负载均衡)。
1.2.2 Ribbon
Feign是netflix开发的声明式、模板化的http客户端,在使用时就像调用本地(服务消费者自己)的方法一般,帮助我们更加优雅的调用服务提供者的API。Feign自身支持springMVC,还整合了Eureka、Ribbon,极大的简化了Feign的使用。就整合Euraka而言,只需和普通的服务配置Eureka server的信息即可。整合Ribbon,就意味着不再需要通过标注@LoadBalanced的实例化后的RestTemplate去调用服务提供者方法了。Feign只需通过简单的定义一个接口即可实现负载均衡。
和nginx不同,它是客户端侧负载均衡。
1.2.3 负载均衡策略
常见提供的负载均衡算法有三种:
l 第一种也是默认为轮询
l 第二种为random随机
l 第三种为WeightedResponseTimeRule,响应时间
1.2.4 导包
无需引入jar包,在spring-cloud-start-euraka已经依赖了ribbon的jar包。
1.3 消费者Ribbon
1.3.1 修改Maven工程内容
新建子工程 comsumer-client-ribbon,内容和consumer-client一样,修改端口为8092
spring-cloud父工程pom.xml
<!--Maven项目可以继承,三个子工程-->
<modules>
<module>eureka-server</module>
<module>provider-a</module>
<module>provider-b</module>
<module>consumer-client</module>
<module>consumer-client-ribbon</module>
</modules>
1.3.2 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<parent>
<groupId>com.wood</groupId>
<artifactId>spring-cloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wood</groupId>
<artifactId>consumer-client-ribbon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>consumer-client-ribbon</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
注意:怎么没有依赖ribbon的jar包呢?
因为eureka中已经含有对ribbon的支持,不需要额外的添加依赖
1.3.3 application.properties
server.port=8092
spring.application.name=consumer-client-ribbon
eureka.client.serviceUrl.defaultZone=http://localhost:6001/eureka
# 定义根目录下日志级别
logging.level.root=INFO
1.3.4 HelloController.java
RestTemplate对象是在RunApp中声明并创建的,用它才可以实现负载均衡,同时注意url中的地址为VIP虚拟IP,为application.yml中配置的application-name。
package
com.wood.consumerclientribbon.controller
;
import
org.springframework.beans.factory.annotation.
Autowired;
import
org.springframework.web.bind.annotation.
GetMapping;
import
org.springframework.web.bind.annotation.
PathVariable;
import
org.springframework.web.bind.annotation.
ResponseBody;
import
org.springframework.web.bind.annotation.
RestController;
import
org.springframework.web.client.RestTemplate
;
@RestController
public class
HelloController {
/**
* RestTemplate是Spring提供的用于访问Rest服务的客户端,
*
* RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。
*
* 调用RestTemplate的默认构造函数,RestTemplate对象在底层通过使用java.net包下的实现创建HTTP 请求,
*
* 可以通过使用ClientHttpRequestFactory指定不同的HTTP请求方式。
*
* ClientHttpRequestFactory接口主要提供了两种实现方式
*
* 1、一种是SimpleClientHttpRequestFactory,使用J2SE提供的方式(既java.net包提供的方式)创建底层的Http请求连接。
*
* 2、一种方式是使用HttpComponentsClientHttpRequestFactory方式,底层使用HttpClient访问远程的Http服务,使用HttpClient可以配置连接池和证书等信息。
*
* RestTemplate对象是在RunApp中声明并创建的,用它才可以实现负载均衡,同时注意url中的地址为VIP虚拟IP,为application.yml中配置的application-name。
* */
@Autowired
private
RestTemplate
restTemplate;
@GetMapping
(
"/hello/{name}"
)
@ResponseBody
public
String
hello
(
@PathVariable
String name){
// VIP虚拟IP,提供者的application-name:provider-user
String url = "http://provider-user/hello/"+name;
return this
.
restTemplate
.getForObject(url
,
String.
class
)
;
}
}
1.3.5 ConsumerClientRibbonApplication.java
重点在启动时要初始化RestTemplate对象,同时设置@LoadBalanced负载均衡
package com.wood.consumerclientribbon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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
public class ConsumerClientRibbonApplication {
@Bean
@LoadBalanced //Ribbon负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerClientRibbonApplication.class, args);
}
}
1.3.6 测试
执行顺序:
先启动服务端 6001 eureka-server EurekaServerApplication
在启动提供者1 8081 provider-user ProviderAApplication
在启动提供者2 8082 provider-user2 ProviderBApplication
最后启动消费者 8092 consumer-client-ribbon ConsumerClientRibbonApplication
访问Eureka控制台: http://localhost:6001/
访问请求: http://localhost:8092/hello/wood
交替出现1:wood和2:wood,说明两个提供者交替执行。这里注意有时可能提供者2还未准备好不能工作,多刷一会就正常了。可以看出Ribbon默认的负载均衡策略是轮询。
服务提供者A:wood
服务提供者B:wood
1.3.7 小结:怎么把普通的消费者改为Ribbon消费者
Controller
@RequestMapping("/hello")
public String hello(){
//provider-user就是Eureka中提供服务
String url = "http://provider-user/hello";
//发起对Eureka中某个服务进行访问,返回值类型和业务返回值类型一致
return restTemplate.getForObject(url, String.class);
}
启动类
@EnableEurekaClient
@SpringBootApplication
public class CustomerClientRunApp {
//初始化RestTemplate对象,Spring就初始化这个beand,就可以在Controller中注入
@Bean
@LoadBalanced //实现负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
1.4 拓展:Ribbon随机负载算法
1.4.1 RibbonRuleConfig.java
自定义规则扩展对象
package com.wood.config;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
/**
* <p>
* RibbonRuleConfig
* </p>
* 自定义Ribbon配置
* 规定:这个类不能再@ComponentScan和@SpringBootApplication本包和子包下,否则引起@RibbonClients扫描冲突
* 注意:随机第一次打断点进入,之后多次刷新就不进入,可能由于本地缓存原因
*/
public class RibbonRuleConfig {
@Bean
public IRule ribbonRule(){
return new RandomRule();
}
}
1.4.2 ConsumerClientRibbonApplication.java
增加一个注解@RibbonClient(name="provider-user", configuration=RibbonRuleConfig.class)
package com.wood.consumerclientribbon;
import com.wood.config.RibbonRuleConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="provider-user", configuration=RibbonRuleConfig.class)
public class ConsumerClientRibbonApplication {
@Bean
@LoadBalanced //Ribbon负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerClientRibbonApplication.class, args);
}
}
附上代码 demo.zip
https://pan.baidu.com/s/1X8GSuqMSMiyMz747aZtQTw