Spring Cloud 服务调用方式
Spring Cloud有两种服务调用方式,一种是Ribbon + RestTemplate,另一种是feign。在这一篇文章首先讲解下基于Ribbon + RestTemplate。
Ribbon简介
Ribbon在Spring Cloud中担任着负载均衡的角色,它是一个基于HTTP和TCP的客户端负载均衡工具。Spring Cloud Ribbon只是一个工具类框架,它可以独立部署但是也不需要独立部署,它几乎存在于所有springcloud 构建的微服务和基础设施中。
Ribbon已经实现了负载均衡对象:
/**
*
* RoundRobinRule 轮询
* RandomRule 随机
* AvailabilityFilteringRule 优先过滤因访问故障处于断路器跳闸的服务
* 以及并发连接数超过阈值的服务。然后对剩余的服务做轮询
*
* WeightedResponseTimeRule 根据平均响应时间计算所有服务的权重,响应时间越快的权重越大
* 选择几率越高。
*
* RetryRule 先按照轮询获取服务,如果获取服务失败,
* 在规定时间内会重试获取可用的服务
*
* BestAvailableRule 优先过滤因访问故障处于断路器跳闸的服务,
* 然后选择并发量最小的服务
*
* ZoneAvoidanceRule 默认规则,复合判断服务所在区域的性能,和服务的可用性选择服务器
*
*/
前提
本文是基于SpringCloud 商城系统搭建之eureka
Ribbon集成
1、在supermarker-consume的RestTemplate对象上加上 @LoadBalanced注解 和实列化负载均衡策略
代码修改如下:
package com.zzg.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
@Configuration
public class RestConfig {
@Bean
@LoadBalanced //负载均衡标识
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean // 负载均衡策略实列化
public IRule getIRule(){
// 负载均衡:随机策略
return new RandomRule();
}
}
2、创建两个生产者:supermarker-provider 和supermarker-provider-second
两个生产者的application.properties 大致如下:
server.port=8082
# 生产者应用名称 -
spring.application.name=provider
# 生产者实例名,同一个spring.application.name 名称唯一
eureka.instance.instance-id=provider1
eureka.client.register-with-eureka=true
# 和eureka服务器通讯的URL
eureka.client.service-url.defaultZone=http://localhost:8081/eureka/
# 设置心跳的时间间隔(默认是30秒)
eureka.instance.lease-renewal-interval-in-seconds=5
# eureka server 最后一次收到心跳时等待的时间,超时将会移除client(默认是90秒)
eureka.instance.lease-expiration-duration-in-seconds=3
#h2配置
#启用SQL语句的日志记录
spring.jpa.show-sql=true
#设置ddl模式
spring.jpa.hibernate.ddl-auto=update
##数据库连接设置
#配置h2数据库的连接地址
spring.datasource.url=jdbc:h2:mem:dbtest
spring.datasource.username=sa
spring.datasource.password=sa
spring.datasource.driverClassName=org.h2.Driver
##数据初始化设置
#进行该配置后,每次启动程序,程序都会运行resources/db/schema.sql文件,对数据库的结构进行操作。
spring.datasource.schema=classpath:db/schema.sql
#进行该配置后,每次启动程序,程序都会运行resources/db/data.sql文件,对数据库的数据操作。
spring.datasource.data=classpath:db/data.sql
##h2 web console设置
#表明使用的数据库平台是h2
spring.datasource.platform=h2
# 进行该配置后,h2 web consloe就可以在远程访问了。否则只能在本机访问。
spring.h2.console.settings.web-allow-others=true
#进行该配置,你就可以通过YOUR_URL/h2访问h2 web consloe。YOUR_URL是你程序的访问URl。
spring.h2.console.path=/h2
#进行该配置,程序开启时就会启动h2 web consloe。当然这是默认的,如果你不想在启动程序时启动h2 web consloe,那么就设置为false。
spring.h2.console.enabled=true
#spring-boot-actuator配置
#开放所有的web Endpoints
management.endpoints.web.exposure.include=*
注意:两个消费者除端口和实例id外其他都是一样的,这里就不再粘贴其他功能代码
3、修改两个生产者:supermarker-provider 和supermarker-provider-second 对外提供的接口
supermarker-provider
package com.zzg.controller;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.zzg.dao.UserRepository;
import com.zzg.entity.User;
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;
@Value("${server.port}")
private int port;
@GetMapping("/user/{id}")
public User findById(@PathVariable Integer id) {
System.out.println("---------------provider端口被调用--------------" + port);
return userRepository.findOne(new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
// TODO Auto-generated method stub
List<Predicate> predicates = new ArrayList<Predicate>();
// 用户编号
predicates.add(criteriaBuilder.equal(root.<Integer> get("useId"), id));
return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
}
}).orElse(null);
}
}
supermarker-provider-second
package com.zzg.controller;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.zzg.dao.UserRepository;
import com.zzg.entity.User;
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;
@Value("${server.port}")
private int port;
@GetMapping("/user/{id}")
public User findById(@PathVariable Integer id) {
System.out.println("---------------provider-second端口被调用--------------" + port);
return userRepository.findOne(new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
// TODO Auto-generated method stub
List<Predicate> predicates = new ArrayList<Predicate>();
// 用户编号
predicates.add(criteriaBuilder.equal(root.<Integer> get("useId"), id));
return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
}
}).orElse(null);
}
}
Ribbon 负载均衡功能测试
1、分别启动supermarker-eureka、二个生产者(supermarker-provider和supermarker-provider-second)和消费者(supermarker-consume)
supermarker-eureka启动后,看到如下界面证明二个生产者创建成功,会显示有二个生产者,左边红框内是生产者集群名称,消费者在通过IP和port调用生产者接口时都是通过集群名称调用的,右边红框是实例名称
2、通过消费者调用生产者
消费者调用生产者代码
package com.zzg.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.RestController;
import org.springframework.web.client.RestTemplate;
import com.zzg.entity.User;
@RestController
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user/{id}")
public User findById(@PathVariable Integer id) {
return restTemplate.getForObject("http://provider/user/" + id, User.class);
}
}
消费者启动成功后,通过浏览器访问:http://localhost:8083/user/1 发现两个生产者控制台输入如下信息截图:
说明基于:Ribbon + RestTemplate 负载均衡功能完结,
温馨提示:Ribbon默认采用的负载均衡策略是轮询