SpringCloud_H(服务调用)
1、Rabbion(负载均衡)
-
Spring Cloud Ribbon是基于Netfix Ribbon实现的一套客户端负载均衡工具
-
Ribbon是Nefix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等,就是在配置文件中列出Load Balancer(简称LB)后面所欲的及其,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接)去连接这些机器。可以使用Ribbon实现自定义的负载均衡算法
-
LB负载均衡(Load Balance)
- j将用户的请求平摊的分配到多个服务上,从而达到系统和HA(高可用)
- 常见的负载均衡有软件 Nginx,LVS,硬件F5等
- 负载均衡 + RestTemplate实现RPC远程调用
-
Rabbon本地负载均衡客户端和Nginx服务端负载均衡区别
- Nignx是服务负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的
- Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术
-
集中式LB
- 在服务方和消费方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方
-
进程内LB
- 将LB逻辑继承到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择一个合适的服务器
- Ribbon就属于进程内LB,它只是一个类库,集成于消费方集成,消费方通过它来获取到服务提供方的地址
2、maven依赖
-
Rabbion其实就是一个软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例
-
pom依赖,spring-cloud-starter-netflix-eureka-client依赖中已经包含ribbon
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.1.RELEASE</version>
<scope>compile</scope>
</dependency>
3、RestTemplate的使用
- getForObject(),PostForObject()
- 返回对象为响应体数据转化成的对象,基本上可以理解为JSON
- getForEntity(),PostForEntity()
- 返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等
4、Rabbion自带负载规则
- IRUle:根据特定算法从服务列表中选取一个要访问的服务
- RoundRobinRule: 轮询
- RandomRule: 随机
- RetryRule: 先按照轮询的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用服务
- WeightedResponseTimeRule: 对轮询策略的扩展,响应速度越快的实例选择权重越大,越容易被选择
- BestAvailableRule: 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- AvailabilityFiteringRule: 先过滤故障实例,再选择并发较小的实例
- ZoneAvoidanceRule: 复合判断server所在区域的性能和server的可用选择服务器
5、替换负载规则
-
官方文档明确给出了警告:这个自定义配置类不能放在 @ComponentScan 所扫描的房钱包下以及子包下,否则我们自定义的这个配置类就会被多有的Ribbon客户端所共享,达不到特殊化定制的目的
-
IRule配置类
@Configuration
public class MySelfRule {
@Bean
public IRule myRule(){
return new RandomRule(); //定义为随机策略
}
}
- 启动类添加 @RibbonClient 注解
@SpringBootApplication
@EnableEurekaClient
// 将CLOUD-PAYMENT-SERVICE服务的负载规则修改成自己指定的规则
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}
6、RoundRobinRule(轮询)源码
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) { //没有负载均衡
log.warn("no load balancer");
return null;
} else {
Server server = null;
int count = 0;
while(true) {
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) { //如果有健康的服务器
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;
}
}
}
7、自定义轮询算法
- 取消 @LoadBalanced 注解
@Configuration
public class ApplicationContextConfig {
@Bean
//@LoadBalanced //Rabbion负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- 编写负载均衡接口
public interface LoadBalance {
//传入服务器列表,返回一个服务器
ServiceInstance instance(List<ServiceInstance> serviceInstanceList);
}
- 编写实现类
@Component
@Slf4j
public class MyLB implements LoadBalance{
private AtomicInteger atomicInteger = new AtomicInteger(0);
//自旋锁,第几次访问
public final int getAndIncrement(){
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= 2147483647 ? 0 : current+1;
}while (!this.atomicInteger.compareAndSet(current,next));
return next;
}
@Override
public ServiceInstance instance(List<ServiceInstance> serviceInstanceList) {
//使用访问次数对服务器个数求余实现轮询
int index = getAndIncrement() % serviceInstanceList.size();
return serviceInstanceList.get(index);
}
}
- controller
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
@Autowired
private LoadBalance loadBalance;
@GetMapping("/consumer/paymentLB/{id}")
public CommonResult<Payment> getPaymentLB(@PathVariable("id") long id){
//根据服务名称查看获取所有服务
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
//如果没有服务,返回null
if (instances == null || instances.size() <= 0){
return null;
}
//调用自己编写的方法选择安装轮询的方式选择一个服务器
ServiceInstance serviceInstance = loadBalance.instance(instances);
//得到选择的服务器的URL
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri+"/payment/getPaymentById/"+id,CommonResult.class);
}
}
8、OpenFeign(服务接口调用)
- Feigh是一个声明式WebService客户端。使用Feign能让编写WebService客户端更简单
- 它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Rabbion组合使用以支持负载均衡
- Ribbion + RestTemplate,利用RestTemplate对http请求的封装,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处处理,所以通常都会对每个微服务自行封装一些客户端类来包装一些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Fegin的实现下,我们只需要创建一个接口并使用注解的方式来配置它,即可完成对服务提供方的接口绑定,简化了使用spring cloud ribbon时,自动封装服务调用客户端的开发量。
- Feign集成了Ribbon,利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用
Fegin | OpenFegin |
---|---|
Feign是Spring Cloud组件中的一个轻量级RestFul的HTTP服务客户端,Fegin内置了Ribbon用来做客户端负载均衡,去调用服务注册中心的服务。Fegin的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务 | OpenFegin是Spring Cloud在Fegin的基础上支持了Spring Mvc的注解,如 @RequestMapper 等等。OpenFegin的 @FeignClient 可以解析 SpringMvc的 @RequestMapping 注解下的接口,并通过动态代理的方式产生实现类、实现类中做负载均衡并调用其他服务 |
9、OpenFeign的使用
- 新建服务 consuemr-83
- maven依赖
<dependencies>
<!--OpenFeign-->
<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>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- application.yml
server:
port: 83
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka
- 启动类,添加 EnableFeignClients 注解开启Feign
@SpringBootApplication
@EnableFeignClients //开启Feign
public class OrderFeignMain83 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain83.class,args);
}
}
- service接口
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE") //调用的服务,provider-8001,provider-8002
public interface PaymentService {
@GetMapping("/payment/getPaymentById/{id}")
public CommonResult getPaymentById(@PathVariable("id") long id);
}
- controller
@RestController
@Slf4j
public class OrderFeignController {
@Autowired
private PaymentService paymentService;
@GetMapping("/consumer/getPayment/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") long id){
return paymentService.getPaymentById(id);
}
}
10、OpenFeign超时控制
- 默认Fegin客户端只等待一秒钟,但是服务端处理需要超过一秒钟,导致Feign客户端超时,直接返回报错,为了避免这样的情况,有时我们需要设置Feign客户端的超时控制
- yml文件添加配置
ribbon:
# 指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ReadTimeout: 5000
# 指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
11、OpenFeign日志打印
-
Fegin提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求细节,对Fegin接口的调用情况进行监控和输出
-
日志级别
- NONE: 默认的,不显示任何日志
- BASIC: 仅记录请求方法、URL、响应状态码及执行时间
- HEADERS: 除了BASIC中定义的信息之外,还有请求和响应的头信息
- FULL: 除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据
-
编写config
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLogLevel(){
return Logger.Level.FULL;
}
}
- 编写application.yml
logging:
level:
# feign 日志以什么级别监控那个接口
com.atguigu.springcloud.service.PaymentService: debug
- consuemr-83 调用 CLOUD-PAYMENT-SERVICE 服务,打印的日志
[PaymentService#getPaymentById] ---> GET http://CLOUD-PAYMENT-SERVICE/payment/getPaymentById/1001 HTTP/1.1
[PaymentService#getPaymentById] ---> END HTTP (0-byte body)
[PaymentService#getPaymentById] <--- HTTP/1.1 200 (6ms)
[PaymentService#getPaymentById] connection: keep-alive
[PaymentService#getPaymentById] content-type: application/json
[PaymentService#getPaymentById] date: Wed, 21 Apr 2021 12:13:47 GMT
[PaymentService#getPaymentById] keep-alive: timeout=60
[PaymentService#getPaymentById] transfer-encoding: chunked
[PaymentService#getPaymentById]
[PaymentService#getPaymentById] {"code":200,"message":"查找成功,serverPort:8001","data":{"id":1001,"serial":"youmo"}}
[PaymentService#getPaymentById] <--- END HTTP (89-byte body)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~