负载均衡
Ribbon(消费)
基础知识
Ribbon有两个特点:
第一是Ribbon可以理解为服务消费者不需要访问服务提供者的地址就可以访问服务提供者,而是用注册中心应用的名称来访问我们消费提供者。
第二是Ribbon提供负载均衡策略。可以这样假想,一个付款业务我们把它放在三台服务器上(三套一模一样的代码,只是访问地址不同),我们可以通过ribbon来控制这个业务在三台服务器上的调用次数。
配置Ribbon
注意(一定要仔细阅读)
- 在配置ribbon之前必须配置好Euraka服务端及其多个客户端(服务提供者),Ribbon是针对于服务消费者的。
- 在配置Ribbon时要注意SpringBoot和SpringCloud版本是否匹配。如果不匹配可能会出现找不到注册中心的服务或者直接不能启动项目。(我就是SpringBoot与SpringCould不匹配找了一个下午的错,想想都亏)
- 官方比对地址:https://start.spring.io/actuator/info 返回数据格式是json需要转义
- 我这里使用版本是SpringBoot version = 2.1.4.RELEASE,SpringCloud Version = Greenwich.SR1
- 服务名称一致是Ribbon的实现必要条件,instance-id来区分服务
导入依赖
由于我们需要去Eureka配置中心读取服务,所以也把Eureka的客户端导入到项目中。
<!-- Ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置文件:
#Eureka 客户端配置
eureka:
client:
register-with-eureka: false # 不向Eureka注册自己
service-url: # 注册中心地址,单为不集群,多个注册中心为集群,写法一样
defaultZone: http://localhost:6998/eureka/,http://eureka6999.com:6999/eureka/,http://eureka7000.com:7000/eureka/
Configuration配置
@Configuration
public class RestTemplateConfig {
//配置负载均衡实现RestTemplate
@Bean
@LoadBalanced //加载负载均衡策略,默认轮询
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
主启动类
开启Eureka客户端
@SpringBootApplication
@EnableEurekaClient
public class SpringcloudConsume80Application {
public static void main(String[] args) {
SpringApplication.run(SpringcloudConsume80Application.class, args);
}
}
Controller
1、 String oldUrl="http://loclhost:8080/"原来的rest请求,看上去刚觉不太友好。
2、使用Riboon后的请求地址为: String URLS=“http://SPRINGCLOUDEPROVIDES”;这里的地址就是你注册在注册中心的地址。也就是服务提供者中配置的应用名称。
@RestController
public class TagController {
@Autowired
RestTemplate restTemplate;
//String oldUrl="http://loclhost:8080/"原来来的rest请求
String URLS="http://SPRINGCLOUDEPROVIDES";//现在的Ribbon请求地址。这里的地址就是你注册在注册中心的地址。
@RequestMapping("getname")
public String getname(String name){
return restTemplate.getForObject(URLS+"/getname?name="+name,String.class);
}
}
如图:
服务提供者
服务名称一致是Ribbon的实现必要条件,instance-id来区分服务
服务提供者需要向注册中心注册自己。创建两个服务提供者,这两个服务提供者除了设置端口、instance-id不一样,其他可以全部一模一样。
如:
端口为7002的服务提供者
server:
port: 7002
spring:
application:
name: springcloudeprovides
eureka:
client:
service-url:
defaultZone: http://localhost:6998/eureka/,http://localhost:6999/eureka/,http://localhost:7000/eureka/
instance:
instance-id: springcloudeprovides:7002
prefer-ip-address: true # true,可以显示服务的IP地址 ~
info:
app.name: 我是ek客户端,端口号为7002
端口为8001的服务提供者
server:
port: 8001
spring:
application:
name: springcloudeprovides # 服务名称一致是Ribbon的实现必要条件
eureka:
client:
service-url:
defaultZone: http://localhost:6998/eureka/,http://localhost:6999/eureka/,http://localhost:7000/eureka/
instance:
instance-id: springcloudeprovides:8001
prefer-ip-address: true # true,可以显示服务的IP地址 ~
info:
app.name: 我是ek客户端,端口号为8001
auth: 作者是姜兴
Controller
两个服务提供者的方法一样
public class TagController {
@Autowired
RestTemplate restTemplate;
String URLS="http://SPRINGCLOUDEPROVIDES";
@RequestMapping("getname")
public String getname(String name){
return restTemplate.getForObject(URLS+"/getname?name="+name,String.class);
}
}
记得服务端开启Eureka
@SpringBootApplication
@EnableEurekaClient
public class SpringcloudConsume80Application {
public static void main(String[] args) {
SpringApplication.run(SpringcloudConsume80Application.class, args);
}
}
测试
启动的配置要求:启动Eureka服务端、启动服务提供者(最少两个,主要是为了看懂Ribbon的负载均衡,一个也可以)、启动服务消费者(我们配置了Ribbon的这个消费者)。
我们可以看到我们的两个服务都注册进eureka注册中心了。
此时访问我们的服务消费者去调用服务提供者:
再一次访问:
这就是Ribbon的负载均衡,简单来说就是通一个业务把他放在不同的服务器上,我们可以使用Ribbon来控制对这几台服务器中业务的请求次数。
Ribbon自定义
文件创建及
Ribbon自定义负载均衡需要在主启动类的,上一个包下创建一个包并创建自定义负载均衡类,其次就是在主启动类上说明那个服务需要自定义负载均衡
如图在主启动类下创建文件
自定义负载类
MyRule类,这里我是直接把轮询 的代码复制过来,不会写算法,所以只能照猫画虎。不过在构造方法中添加了一句话输出,“使用了自定义的负载均衡配置”
public class MyRule extends AbstractLoadBalancerRule {
private AtomicInteger nextServerCyclicCounter;
private static final boolean AVAILABLE_ONLY_SERVERS = true;
private static final boolean ALL_SERVERS = false;
private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);
public MyRule() {
System.out.println("使用了自定义的负载均衡配置");
nextServerCyclicCounter = new AtomicInteger(0);
}
public MyRule(ILoadBalancer lb) {
this();
System.out.println("使用了自定义的负载均衡配置");
setLoadBalancer(lb);
}
.....................剩余代码可以去源码里复制出来...............
}
RestTemplate配置
如果不加 @LoadBalanced就不能实现Ribbon负载均衡。
@Configuration
public class MyResyCon {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
主启动类引用
在微服务启动的时候就能去加载我们自定义Ribbon类 name为我们服务提供者名称,class为我们自定义的负载均衡类
@SpringBootApplication
@EnableEurekaClient
//在微服务启动的时候就能去加载我们自定义Ribbon类 name为我们服务提供者名称,class为我们自定义的负载均衡类
@RibbonClient(name = "SPRINGCLOUDEPROVIDES",configuration = MyRule.class)
public class SpringcloudEkc8002Application {
public static void main(String[] args) {
SpringApplication.run(SpringcloudEkc8002Application.class, args);
}
}
Controller
@RestController
public class TagContrloller {
@RestController
public class TagController {
@Autowired
RestTemplate restTemplate;
String URLS = "http://SPRINGCLOUDEPROVIDES";
@RequestMapping("getname")
public String getname(String name) {
return restTemplate.getForObject(URLS + "/getname?name=" + name, String.class);
}
}
}
测试:连续访问8002服务消费者,我们自定义的策略是轮询,所以他会轮询访问。
第一次请求
第二次请求
项目结构图
OpenFeign(消费)
openfeign也是一种负载均衡的策略,而且他是基于Ribbon的基础上实现的 。ribbon是rest编程也就是http访问远程方法,openfeign是面向接口编程和dubbo的RPC差不多一样。
OpenFeign步骤
公共模块
-
由于openfeign是面向接口编程,那么我们创建一个公共的api来提供服务。
导入依赖
<properties> <java.version>1.8</java.version> <spring-cloud.version>2020.0.0-M5</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </repositories>
-
创建公共模块
1、标明我们的openfeign是针对那服务提供者。@FeignClient(value = “服务提供者”)
2、设置映射;这里的映射就是服务提供者里的映射,而且必须使用@RequestParam约束参数,否则不能传递参数。
@Component @FeignClient(value = "springcloudeprovides") public interface TagService { @RequestMapping("getname") public String getName(@RequestParam("name")String name); }
-
服务消费者
导入openfeign的依赖
<dependencies>
<!--公共模块依赖 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!--openfeign依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</plugins>
</build>
配置文件
#Eureka 客户端配置
eureka:
client:
register-with-eureka: false # 不向Eureka注册自己
service-url: # 注册中心地址
defaultZone: http://localhost:6998/eureka/,http://localhost:6999/eureka/
server:
port: 8002
主启动类
开eureka、开启FeignClients、开启ribbon自定义(可以不写这个注解,不写默认轮询)
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.example.springcloudapi"})
@RibbonClient(name = "SPRINGCLOUDEPROVIDES",configuration = MyRule.class)
public class SpringcloudConsumeFeigin8002Application {
public static void main(String[] args) {
SpringApplication.run(SpringcloudConsumeFeigin8002Application.class, args);
}
}
controller
@RestController
public class TagController {
@Autowired
TagService tagService=null;//获得公共模块
@RequestMapping("getname")//映射路劲
public String getName(@RequestParam("name")String name){
System.out.println(name);
//通过公共模块去访问服务提供者
return tagService.getName(name);
}
}
服务提供者
这里就不写配置,这个服务提供者只需要把自己注册到eureka中就行了。不需要openfeign的依赖和ribbon的依赖
@RestController
public class TagController {
@RequestMapping("getname")
public String getName(@RequestParam("name")String name){
System.out.println("服务提供者controller:"+name);
return name;
}
}
测试:
一个Eureka服务(6998)、两个服务提供者(7003、7004)、一个非消费者(8002);
查看服务是否注册到eureka中
通过访问服务消费者(8002)来调用服务提供(7003、7004)
第一次访问
调用了7003
刷新页面
调用了7004
代码图