Feign、Ribbon、Hystrix(铁三角)以及三者超时时间配置 Feign 如何设置超时时间(connectionTimeout、readTimout

这篇具有很好参考价值的文章主要介绍了Feign、Ribbon、Hystrix(铁三角)以及三者超时时间配置 Feign 如何设置超时时间(connectionTimeout、readTimout。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在微服务架构中很多功能都需要调用多个服务才能完成某一项功能,一个成熟的微服务集群,内部调用必然依赖一个好的 RPC 框架,比如:基于 Http 协议的 Feign,基于私有 tcp 协议的 Dubbo 

1. Feign 是什么
Feign 是Spring Cloud Netflix组件中的轻量级Restful的 HTTP 服务客户端,实现了负载均衡和 Rest 调用的开源框架,封装了Ribbon和RestTemplate, 实现了WebService的面向接口编程,进一步降低了项目的耦合度。

Feign 通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,封装了 Http 调用流程。
 

Feign、Ribbon、Hystrix(铁三角)以及三者超时时间配置
Feign 如何设置超时时间(connectionTimeout、readTimout

1、Robbin
(1)客户端的软负载

Robbin是springcloud的LB调用组件,提供客户端的软件负载均衡。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。

Robbin 提供的客户端软负载,是SpringCloud微服务的典型特征之一。

Feign、Ribbon、Hystrix(铁三角)以及三者超时时间配置
Feign 如何设置超时时间(connectionTimeout、readTimout

2)负载均衡策略

  • 轮询(RoundRobin),以轮询的方式依次将请求调度不同的服务器,即每次调度执行i = (i + 1) mod n,并选出第i台服务器。
  • 随机(Random),随机选择状态为UP的Server。
  • 加权响应时间(WeightedResponseTime),根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。
  • 区域感知轮询(ZoneAvoidanceRule),复合判断server所在区域的性能和server的可用性选择server。

(3)核心组件

Ribbon的核心组件(均为接口类型)有以下几个,

  • ServerList,用于获取地址列表。它既可以是静态的(提供一组固定的地址),也可以是动态的(从注册中心中定期查询地址列表)。
  • ServerListFilter,仅当使用动态ServerList时使用,用于在原始的服务列表中使用一定策略过虑掉一部分地址。
  • IRule,选择一个最终的服务地址作为LB结果。选择策略有轮询、根据响应时间加权、断路器(当Hystrix可用时)等。

Ribbon在工作时首选会通过ServerList来获取所有可用的服务列表,然后通过ServerListFilter过虑掉一部分地址,最后在剩下的地址中通过IRule选择出一台服务器作为最终结果。
 

2、Fegin

Fegin是一个声明式Http端调用,集成了Robbin的负载均衡功能,同时声明式调用更加方便(只需要简单的注解即可)。简单的可以理解为:Spring Cloud Feign 的出现使得Eureka和Ribbon的使用更加简单。

(1)Fegin接口示例

a、启动类@EnableFeignClients 注解

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients // 启用fegin声明式调用
public class FeginComsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeginComsumerApplication.class, args);
    }

}

b、声明一个调用的Feign接口,

@Service
@FeignClient(name = "name-service")
public interface NameService {

    @RequestMapping(value = "/getName", method = RequestMethod.GET)
    public String getName();

}

c、服务端提供接口实现

@RequestMapping(value = "/getName", method = RequestMethod.GET)
public String getName(){
    return "hello world";
}

d、fegin声明是调用

@Autowired
private NameServiceClient feginNameServiceClient;

@RequestMapping(value = "/getName", method= RequestMethod.GET)
public String getName(){
    return feginNameServiceClient.getName();
}

(2)Fegin 的类加载流程

  • 通过主类上的EnableFeignClients 注解开启FeignClient;
  • 根据Feign 的规则实现接口,并加上FeignClient注解,供调用的地方注入调用;
  • 程序启动后,会扫描所有FeignClient 注解的类,并将这些信息注入到IOC 容器中;
  • 当b中接口被调用时,通过jdk代理,以及反射(Spring处理注解的方式),来生成具体的RequestTemplate
  • RequestTemplate 生成Reqest
  • Request 交给httpclient处理,这里的httpclient 可以是OkHttp,也可以是HttpUrlConnection 或者HttpClient
  • 最后Client被封装到LoadBalanceClient类,这个类结合Ribbon 实现负载均衡。
     

(3)Fegin的原理

Feign、Ribbon、Hystrix(铁三角)以及三者超时时间配置
Feign 如何设置超时时间(connectionTimeout、readTimout

3、Hystrix

Hystrix 是springcloud生态的断路器(隔离、限流、降级),主要是用来预防服务雪崩的现象,剔除掉分布式系统中某些挂掉或请求过慢的服务节点。Hystrix是一个帮助解决分布式系统中超时处理和容错的类库, 拥有保护系统的能力。

(1)隔离、限流、降级

Hystrix断路器有两种隔离策略:信号量隔离(默认)和线程池隔离。

信号量模式从始至终都只有请求线程自身,是同步调用模式,不支持超时调用,不支持直接熔断,由于没有线程的切换,开销非常小。
线程池模式可以支持异步调用,支持超时调用,支持直接熔断,存在线程切换,开销大。

Feign、Ribbon、Hystrix(铁三角)以及三者超时时间配置
Feign 如何设置超时时间(connectionTimeout、readTimout

信号量隔离:常用于获取共享资源的场景中,比如计算机连接了两个打印机,那么初始的信号量就是2,被某个进程或线程获取后减1,信号量为0后,需要获取的线程或进程进入资源等待状态。Hystrix的处理有些不同,其不等待,直接返回失败。

线程池隔离:采用的就是jdk的线程池,其默认选用不使用阻塞队列的线程池,例如线程池大小为10,如果某时刻10个线程均被使用,那么新的请求将不会进入等待队列,而是直接返回失败,起到限流的作用。

此外,其还引入了一个断路器机制,当断路器处于打开状态时,直接返回失败或进入降级流程。断路器打开和关闭的触发流程为:当总的请求数达到可阈值HystrixCommandProperties.circuitBreakerRequestVolumeThreshold(),或总的请求失败百分比达到了阈值HystrixCommandProperties.circuitBreakerErrorThresholdPercentage(),这时将断路器的状态由关闭设置为打开。当断路器打开时,所有的请求均被短路,在经过指定休眠时间窗口后,让下一个请求通过(断路器被认为是半开状态)。如果请求失败,断路器进入打开状态,并进入新的休眠窗口;否则进入关闭状态。
Feign、Ribbon、Hystrix(铁三角)以及三者超时时间配置
Feign 如何设置超时时间(connectionTimeout、readTimout

 流程如上图所示,Hystrix框架通过命令模式来实现方法粒度上的服务保障,主要涉及HystrixCommand和HystrixObservableCommand类,前者提供同步的execute和异步的queue方法,后者提供立即执行observe和延迟执行toObservable的回调方法。此外,实际项目中通常不会使用Hystrix集成的本地缓存。
 

配置问题

Feign 如何设置超时时间(connectionTimeout、readTimout

对于这个问题网上有很多相关资料,大体上有两种方案,一种是通过设置 ribbon 的超时时间(因为 Feign 是基于 ribbon 来实现的,所以通过 ribbon 的超时时间设置也能达到目的),一种是直接设置 Feign 的超时时间,我将会在下边的篇幅里分别说一下如何通过application.yml 配置文件来设置超时时间

1、Ribbon

对于 ribbon 又分为全局配置和指定服务配置:

  • 全局配置
    对所有的服务该配置都生效
    ribbon:  
        ReadTimeout: 30000 #单位毫秒
        ConnectTimeout: 30000 #单位毫秒
  • 指定服务配置
    下边代码中的 jettopro-basic 是服务的名称,意思是该配置只针对名为 annoroad-beta 的服务有效,根据实际的需要替换成你自己的服务名
    jettopro-basic:
      ribbon:
        ReadTimeout: 30000 #单位毫秒
        ConnectTimeout: 30000 #单位毫秒

    2、Feign

    与 Ribbon 一样,Feign 也分为全局配置和指定服务配置:

  • 全局配置
    下边代码中使用的 feign.client.config.default ,意思是所有服务都采用该配置
feign:
  client:
    config:
      default:
        connectTimeout: 10000 #单位毫秒
        readTimeout: 10000 #单位毫秒
  • 指定服务配置
    下边代码中使用的 feign.client.config.jettopro-basic,意思是该配置只针对名为 jettopro-basic 的服务有效,可以根据实际的需要替换成你自己的服务名
feign:
  client:
    config:
      jettopro-basic:
        connectTimeout: 10000 #单位毫秒
        readTimeout: 10000 #单位毫秒

3、总结
如果同时配置了Ribbon、Feign,那么 Feign 的配置将生效
Ribbon 的配置要想生效必须满足微服务相互调用的时候通过注册中心,如果你是在本地通过 @FeignClient 注解的 url 参数进行服务相互调用的测试,此时 ribbon 设置的超时时间将会失效,但是通过 Feign 设置的超时时间不会受到影响(仍然会生效)
综上所述建议使用 Feign 的来设置超时时间
 

基本介绍
1. Feign 是什么
Feign 是Spring Cloud Netflix组件中的轻量级Restful的 HTTP 服务客户端,实现了负载均衡和 Rest 调用的开源框架,封装了Ribbon和RestTemplate, 实现了WebService的面向接口编程,进一步降低了项目的耦合度。

Feign 通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,封装了 Http 调用流程。

2. 为什么要使用 Feign
如果不使用 RPC 框架,那么调用服务需要走 Http 的话,配置请求 head、body,然后才能发起请求。获得响应体后,还需解析等操作,十分繁琐。

Feign 旨在使编写 JAVA HTTP 客户端变得更加简单,Feign 简化了RestTemplate代码,实现了Ribbon负载均衡,使代码变得更加简洁,也少了客户端调用的代码,使用 Feign 实现负载均衡是首选方案,只需要你创建一个接口,然后在上面添加注解即可。

Feign 是声明式服务调用组件,其核心就是:像调用本地方法一样调用远程方法,无感知远程 HTTP 请求。让开发者调用远程接口就跟调用本地方法一样的体验,开发者完全无感知这是远程方法,无需关注与远程的交互细节,更无需关注分布式环境开发。

3. OpenFeign
Feign 内置了Ribbon,用来做客户端负载均衡调用服务注册中心的服务。
Feign 支持的注解和用法参考官方文档:https://github.com/OpenFeign/feign官方文档,使用 Feign 的注解定义接口,然后调用这个接口,就可以调用服务注册中心的服务。

Feign本身并不支持Spring MVC的注解,它有一套自己的注解,为了更方便的使用Spring Cloud孵化了OpenFeign。并且支持了Spring MVC的注解,如@RequestMapping,@PathVariable等等。
OpenFeign的@FeignClient可以解析Spring MVC的@RequestMapping注解下的接口,并通过动态代理方式产生实现类,实现类中做负载均衡调用服务。

  • Feign 采用的是基于接口的注解
  • Feign 整合了 Ribbon,具有负载均衡的能力
  • 整合了 Hystrix,具有熔断的能力

一、Feign 和 Ribbon

1. 设置 OpenFeign 的超时时间

我们首先来看一下 OpenFeign 自己的请求超时配置,直接在 yml 文件中配置:

eign:
  # 设置 feign 超时时间
  client:
    config:
      # default 设置的全局超时时间,指定服务名称可以设置单个服务的超时时间
      default:
        connectTimeout: 5000
        readTimeout: 5000

default 默认是全局的,将 default 换成某个服务的名称可以设置单个服务的超时时间

2. 设置 Ribbon 的超时时间

ribbon:
  # 建立链接所用的时间,适用于网络状况正常的情况下, 两端链接所用的时间
  ReadTimeout: 5000
  # 指的是建立链接后从服务器读取可用资源所用的时间
  ConectTimeout: 5000

注意这两个参数设置的时候没有智能提示

ConnectTimeout:

指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间。
在java中,网络状况正常的情况下,例如使用 HttpClient 或者 HttpURLConnetion 连接时设置参数 connectTimeout=5000 即5秒,如果连接用时超过5秒就是抛出 java.net.SocketException: connetct time out 的异常。
 

ReadTimeout:

指的是建立连接后从服务器读取到可用资源所用的时间。
在这里我们可以这样理解ReadTimeout:正常情况下,当我们发出请求时可以收到请求的结果,也就是页面上展示的内容,但是当网络状况很差的时候,就会出现页面上无法展示出内容的情况。另外当我们使用爬虫或者其他全自动的程序时,无法判断当前的网络状况是否良好,此时就有了ReadTimeout的用武之地了,通过设置ReadTimeout参数,例:ReadTimeout=5000,超过5秒没有读取到内容时,就认为此次读取不到内容并抛出Java.net.SocketException: read time out的异常。
 

源码分析

可以发现 OpenFeign 的默认的 connectTimeout 是 10 秒,readTimeout 是 60 秒

此时,我们是要验证 OpenFeign 的默认超时时间,所以在 application.yml 中 feign 和 ribbon 的超时时间都没有设置。报错了,连接超时,可是我们代码里睡 5 秒,明明还在超时时间范围内,怎么就连接超时了呐?其实 OpenFeign 集成了 RibbonRibbon 的默认超时连接时间、读超时时间都是 1 秒

如果 OpenFeign 没有设置对应得超时时间,那么将会采用 Ribbon 的默认超时时间

  • 设置 OpenFeign 超时时间
    feign:
      client:
        config:
          default:
            connectTimeout: 8000
            readTimeout: 8000
  • 设置 Ribbon 超时时间
  • ribbon:
      ReadTimeout: 7000
      ConectTimeout: 7000

重复上面步骤,断点进去一看 ??? 怎么还是 8000

原因是 OpenFeign 和 Ribbon 的超时时间只会有一个生效两者是二选一的,且 OpenFeign 优先

注掉 OpenFeign 超时时间配置之后,就变成了使用设置的 Ribbon 的超时时间

4. 结论
Feign 和 Ribbon 的超时时间只会有一个生效,规则:如果没有设置过 feign 超时,也就是等于默认值的时候,就会读取 ribbon 的配置,使用 ribbon 的超时时间和重试设置。否则使用 feign 自身的设置。两者是二选一的,且 feign 优先。


二、Ribbon 和 Hystrix

1. Hystrix 设置超时时间


# 设置 hystrix 超时时间
feign:
  hystrix:
    enabled: true
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000

@FeignClient(contextId = "remoteUserService", value = "cloud-system", fallbackFactory = RemoteUserFallbackFactory.class)

 注意:如果没有配置 fallback,那么 hystrix 的超时就不会生效,而是由 ribbon 来控制。

hystrix 的默认超时时间是 1s,这个配置在 HystrixCommandProperties 类

设置 hystrix 超时时间比 ribbon 大(OpenFign 的超时时间注掉)

ribbon:
  ReadTimeout: 2000
  ConectTimeout: 2000

访问地址 http://localhost:9203/test/getUserInfo?userId=2 发现请求 2s 左右就返回了,这个值刚好是 ribbon.ReadTimeout 的时间。表示此时 ribbon 超时触发了。然后进入了 hystrix 的熔断过程。

2. 结论:

  • 如果请求时间超过 ribbon 的超时配置,会触发重试;
  • 在配置 fallback 的情况下,如果请求的时间(包括 ribbon 的重试时间),超出了 ribbon 的超时限制,或者 hystrix 的超时限制,那么就会熔断。

一般来说,会设置 ribbon 的超时时间 < hystrix, 这是因为 ribbon 有重试机制。(这里说的 ribbon 超时时间是包括重试在内的,即,最好要让 ribbon 的重试全部执行,直到 ribbon 超时被触发)。
 

由于 connectionTime 一般比较短,可以忽略。那么,设置的超时时间应该满足:

(1 + MaxAutoRetries) * (1 + MaxAutoRetriesNextServer)* ReadTimeOut < hystrix 的 *timeoutInMillisecond

微服务系列:服务调用 Spring Cloud 之 OpenFeign 详细入门_程序猿秃头之路的博客-CSDN博客

微服务系列:服务调用 Spring Cloud 之 OpenFeign 性能优化_程序猿秃头之路的博客-CSDN博客

微服务系列:Spring Cloud 之 Feign、Ribbon、Hystrix 三者超时时间配置_feign和ribbon超时时间设置_程序猿秃头之路的博客-CSDN博客

错误1:springcloud异常:timed-out and no fallback available,failed and no fallback available的问题解决 

timed-out and no fallback available:

这个错误基本是出现在Hystrix熔断器,熔断器的作用是判断该服务能不能通,如果通了就不管了,调用在指定时间内超时时,就会通过熔断器进行错误返回。

一般设置如下配置的其中一个即可:

1、把时间设长

这里设置5秒

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000  

2、把超时发生异常属性关闭

hystrix.command.default.execution.timeout.enabled=false  

3、禁用feign的hystrix 

feign.hystrix.enabled: false  

failed and no fallback available:

而通过上面设置只是针对熔断器的错误关闭,并不能解决根本问题,比如Feign客户端调用远程服务时,默认为8秒超时时间,如果在规定时间内没有返回,同样会跳转到熔断器进行处理。即使关闭了熔断器的错误,但是总的错误处理还会是有这个问题出现。

那么要解决根本问题,就要从请求超时时间入手,因为有些服务可能存在调用时间长的问题,所以直接配置:

ribbon.ReadTimeout=60000

ribbon.ConnectTimeout=60000

这些才是真正解决请求超时的问题,如果不设置这个,被调用接口很慢时,会出现Read Timeout on Request。

而针对调用失败重试的次数也可以设置:

ribbon.maxAutoRetries=0

参考:

  1. https://github.com/spring-cloud/spring-cloud-netflix/issues/321

  2. https://stackoverflow.com/questions/38080283/how-to-solve-timeout-feignclient

  3. http://www.itmuch.com/spring-cloud-feign-ribbon-first-request-fail/

  4. https://github.com/spring-cloud/spring-cloud-netflix/issues/696

  5. http://www.jianshu.com/p/0eb13fd033a8

  6. http://blog.csdn.net/qwlzxx/article/details/77163268

  7. http://blog.csdn.net/clementad/article/details/54315805文章来源地址https://www.toymoban.com/news/detail-504423.html

到了这里,关于Feign、Ribbon、Hystrix(铁三角)以及三者超时时间配置 Feign 如何设置超时时间(connectionTimeout、readTimout的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微服务springcloud 06.feign框架,配合ribbon 负载均衡和重试,配合hystrix 降级,监控和熔断测试

    feign是ribbon +hystrix 的整合 01.新建 sp09-feign 项目 第一步: 第二步:选择依赖: pom.xml 需要添加 sp01-commons 依赖: 第三步:修改sp09-feign项目的application.yml 第四步:sp09-feign的主程序添加 @EnableDiscoveryClient 和 @EnableFeignClients 02.feign 声明式客户端 第一步:声明三个代理接口 这里的

    2024年02月10日
    浏览(52)
  • 【java】Spring Cloud --Feign Client超时时间配置以及单独给某接口设置超时时间方法

    FeignClient面对服务级有三种超时时间配置 feign配置是在ribbon配置的基础上做了扩展,可以支持服务级超时时间配置,所以,feign配置和ribbon配置的效果应该是一样的。 SpringCloud对这两种配置的优先级顺序如下: Feign局部配置 Feign全局配置 Ribbon局部配置 Ribbon全局配置 在feign-co

    2024年02月12日
    浏览(45)
  • 十二、Feign客户端整合Hystrix服务保护

    目录 1、项目pom文件中引入feign客户端依赖 2、编写feign客户端接口,并配置fallback回调方法的类 3、编写controller,使用feign客户端进行RPC远程过程调用

    2024年02月05日
    浏览(38)
  • 服务调用---------Ribbon和Feign

    目录 ​​​​​​​ 1、Ribbon 1.1 Ribbon简介 1.2 Ribbon负载均衡 负载均衡原理 负载均衡策略 Ribbon和Nginx的区别 1.3 服务调用和Ribbon负载均衡实现 2、FeignopenFeign 3、Feign支持的配置 日志功能  连接池  feign-api远程包 1.1 Ribbon简介 Ribbon是一个用于 客户端负载均衡的组件 ,它是Netfl

    2024年02月14日
    浏览(32)
  • springCloud的ribbon和feign

    就是将原来的具体地址,改为了通过服务名去调用。注册中心中有多个服务,相同服务名,就会算作可以调用的服务。 首先得有一个注册中心,然后各种服务注册进去,然后利用ribbon或者feign去调用。   ribbon是直接用地址拼接,然后调用。 feign通过对外编写接口,然后实现

    2024年01月22日
    浏览(36)
  • 服务调用Ribbon,LoadBalance,Feign

    服务调用Ribbon、Fegin Ribbon实现负载均衡的原理 1:LoadBalancerAutoConfiguration这个类,这个类主要做的就是把LoadBalancer拦截器封装到RestTemplte拦截器集合里面去。 2:然后在代码里面调用restTemplate.getForObject或者其他方法的时候,就会调用到这个拦截器。 3:在LoadBalancer拦截器类中,

    2024年01月22日
    浏览(41)
  • springboot整合feign实现RPC调用,并通过Hystrix实现服务降级

    feign/openfeign和dubbo是常用的微服务RPC框架,由于feigin内部已经集成ribbon,自带了负载均衡的功能,当有多个同名的服务注册到注册中心时,会根据ribbon默认的负载均衡算法将请求分配到不同的服务。这篇文章就简单介绍一下怎么使用feign来调用远程的服务。 首先,需要有一个

    2024年02月16日
    浏览(47)
  • Spring Cloud OpenFeign:基于Ribbon和Hystrix的声明式服务调用

    💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! Spring Cloud OpenFeign是一个声明式的服务调用框架,基于Feign并整合了Ribbon和Hystrix;目标是简化分布式系统中编写服务间调用的代码,并提供一种更加优雅和便捷的方式来进行服务之间的通信 依赖导入 application.yml配置 启

    2024年02月05日
    浏览(50)
  • 微服务 springcloud 08.zuul框架,API网关,整合ribbon和hystrix框架

    01.zuul是作为springcloud中的一个网关框架来使用的。 zuul API 网关,为微服务应用提供统一的对外访问接口。 zuul 还提供过滤器,对所有微服务提供统一的请求校验。 在项目中的位置: 02.新建 sp11-zuul 项目 03.pom.xml 需要添加 sp01-commons 依赖 04.application.yml zuul 路由配置可以省略,

    2024年02月09日
    浏览(38)
  • Spring Cloud Feign实战来袭:工程中配置断路器Hystrix报错NoClassDefFoundError:HystrixCommandAspect解决方案

    在Spring Cloud Feign工程中配置断路器Hystrix的时候,pom.xml文件已经加入Hystrix相关的jar: Application.java: 可以看出来是找不到HystrixCommandAspec.java这个类,于是在github上找到这个源文件: https://github.com/dmgcodevil/Hystrix/blob/958ec5d7b4bb967be077a4c2bbcdc71e7a7f5248/hystrix-contrib/hystrix-javanica/src/mai

    2024年02月16日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包