[SpringCloud学习笔记3]SpringCloud服务调用(ribbon,openFeign)
SpringCloud服务调用
这次学习的服务调用主要是ribbon和openFeign的应用
一、ribbon的负载均衡和RestTemplate
1.负载均衡
在调用eurake的时候,其pom文件默认帮我们调用了ribbon,所以能实现默认的轮询方式的负载均衡功能.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-ribbon</artifactId>
<version>2.2.1.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
查询ribbon源码,其调用规则是IRule
package com.netflix.loadbalancer;
public interface IRule {
Server choose(Object var1);
void setLoadBalancer(ILoadBalancer var1);
ILoadBalancer getLoadBalancer();
}
查看其源码可以发现其实现类
-
com.netflix.loadbalancer.RoundRobinRule 实现轮询选择
-
com.netflix.loadbalancer.RandomRule 实现随机选择
-
com.netflix.loadbalancer.RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试
-
com.netflix.loadbalancer.WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
-
com.netflix.loadbalancer.BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
-
com.netflix.loadbalancer.AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
-
com.netflix.loadbalancer.ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
默认调用的就是轮询选择
2.RestTemplate的方法区别
模板获取对象方法有以下几种
getForObject方法/getForEntity方法
postForObject/postForEntity方法
其实区别不大,就是object的返回值是json串,entitity返回的是一个带有很多头信息等等信息的对象
3.rule切换
1.新建子包
默认规则是轮询,现在可以把它改成随机,根据官网所述,不能将新的rule放入到@componetScan注解能够扫描到的地方,所以先新建一个包.
2.新建一个Myrule类
@Component
public class MyRule {
@Bean
public IRule randomRule() {
return new RandomRule();
}
}
3.在主方法上声明调用
//必须指定name和configuration
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MyRule.class)
4.轮询算法源码探究
轮询算法底层还是挺简单的,就是用CAS和一个循环.
源码分析如下:
public class RoundRobinRule extends AbstractLoadBalancerRule {
//声明一个AtomicInteger类
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);
//初始化AtomicInteger参数为0
public RoundRobinRule() {
this.nextServerCyclicCounter = new AtomicInteger(0);
}
//初始化ILoadBalancer
public RoundRobinRule(ILoadBalancer lb) {
this();
this.setLoadBalancer(lb);
}
public Server choose(ILoadBalancer lb, Object key) {
//如果为空,那就提示没有ILoadBalancer
if (lb == null) {
log.warn("no load balancer");
return null;
} else {
//如果ILoadBalancer非空,就开始计数
Server server = null;
int count = 0;
//开启一个循环
while(true) {
//默认是10次为上限,每次重启都会重置count
if (server == null && count++ < 10) {
//获取所有可用的服务器
List<Server> reachableServers = lb.getReachableServers();
//获取所有服务器
List<Server> allServers = lb.getAllServers();
//获取所有可用的服务器数量
int upCount = reachableServers.size();
//获取所有服务器数量
int serverCount = allServers.size();
if (upCount != 0 && serverCount != 0) {
//调用下面的incrementAndGetModulo函数来选择几号服务器
int nextServerIndex = this.incrementAndGetModulo(serverCount);
//调用对应编号服务器
server = (Server)allServers.get(nextServerIndex);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive() && server.isReadyToServe()) {
//如果可以用就返回
return server;
}
server = null;
}
continue;
}
log.warn("No up servers available from load balancer: " + lb);
return null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: " + lb);
}
return server;
}
}
}
//核心算法
private int incrementAndGetModulo(int modulo) {
//初始化参数,传入modulo为总数
int current;
int next;
//每次用一次就+1%总数
do {
current = this.nextServerCyclicCounter.get();
next = (current + 1) % modulo;
} while(!this.nextServerCyclicCounter.compareAndSet(current, next));
//返回服务器编号
return next;
}
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
二、openFeign的入门
Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations
Feign是一个声明式的web服务客户端,让编写web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可
1.openFeign服务调用
1.修改pom
<!--openfeign里面引入了ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.编写appliciation.yml
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/
3.编写主方法
@SpringBootApplication
//开启openfeign
@EnableFeignClients
public class FeignOrderMain80 {
public static void main(String[] args) {
SpringApplication.run(FeignOrderMain80.class,args);
}
}
4.开启feign注解的接口服务
@Component
@FeignClient(name = "CLOUD-PAYMENT-SERVICE")
public interface FeignOrderService {
@GetMapping("/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) ;
}
5.控制层编写
@RestController
@Slf4j
public class FeignPaymentController {
@Resource
FeignOrderService feignOrderService;
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
return feignOrderService.getPaymentById(id);
}
}
2.openFeign的超时控制
默认情况下,如果业务不在一秒内完成就会报出timeout的错误
There was an unexpected error (type=Internal Server Error, status=500).
Read timed out executing GET http://CLOUD-PAYMENT-SERVICE/payment/timeout
feign.RetryableException: Read timed out executing GET http://CLOUD-PAYMENT-SERVICE/payment/timeout
由于整合了ribbon,可以直接在yaml文件中进行修改
ribbon:
ReadTimeout: 5000
ConnectTimeout: 5000
3.日志增强
1.新建配置类
@Configuration
public class MyFeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
2.改写Yml文件
logging:
level:
#以什么级别监视哪个接口
com.xiaoxiao.springcloud.service.FeignOrderService: debug
上一篇: 3D游戏(5)——与游戏世界交互
下一篇: unity实现简单巡逻兵
推荐阅读
-
SpringCloud 学习笔记(3)Eureka
-
SpringCloud学习笔记(3):使用Feign实现声明式服务调用
-
SpringCloud学习系列之二 ----- 服务消费者(Feign)和负载均衡(Ribbon)
-
SpringCloud学习笔记(一)服务注册
-
【微服务】Springcloud学习笔记(一) —— Eureka
-
SpringCloud 学习笔记01 服务注册与发现
-
SpringCloud-2.0-周阳(3. SpringCloud 环境搭建)学习笔记
-
[SpringCloud学习笔记3]SpringCloud服务调用(ribbon,openFeign)
-
SpringCloud 2.x学习笔记:3、Hystrix(Greenwich版本)
-
SpringCloud学习笔记(二):服务注册与发现Eureka