SpringCloud的学习,Consul服务注册与发现、分布式配置,以及 服务调用和负载均衡
介绍
Consul 是一套开源的分布式服务发现和配置管理系统,由 HashiCorp 公司用 Go 语言开发。
提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。它具有很多优点。包括: 基于 raft 协议,比较简洁; 支持健康检查, 同时支持 HTTP 和 DNS 协议 支持跨数据中心的 WAN 集群 提供图形界面 跨平台,支持 Linux、Mac、Windows
简言之,Consul的作用是服务发现和配置管理
Consul的作用
- 服务发现:提供HTTP和DNS两种发现方式。
- 健康检测:支持多种方式,HTTP、TCP、Docker、Shell脚本定制化监控
- kv存储:Key、Value的存储方式
- 多数据中心:Consul支持多数据中心
- 可视化Web界面
服务注册与发现
步骤:
- pom文件中引入consul服务注册与发现的依赖
<!--SpringCloud consul discovery --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-consul-discovery</artifactId><exclusions><exclusion><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId></exclusion></exclusions></dependency>
- yaml文件中做好consul的相关配置,例:
spring:application:name: cloud-consumer-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}
- 在启动类上加上@EnableDiscoveryClient
- 在RestTemplateConfig配置类中,创建RestTemplate组件的方法上,加上负载均衡支持的注解
@Configuration
public class RestTemplateConfig
{@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}
}
- 在要引用服务的地址的时候,就可以将原来的 ip地址:端口号 改为对应的服务名
public static final String PaymentSrv_URL = "<http://cloud-payment-service>";
分布式配置
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服。务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。
服务配置:
配置通用全局配置信息,直接注册进Consul服务器,从Consul获取配置信息:
- pom中加入依赖:
<!--SpringCloud consul config-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
-
关于bootstrap.yaml文件:
applicaiton.yml是用户级的资源配置项
bootstrap.yml是系统级的,优先级更加高
Spring Cloud会创建一个“Bootstrap Context”,作为Spring应用的
Application Context
的父上下文。初始化的时候,Bootstrap Context
负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的Environment
。Bootstrap
属性有高优先级,默认情况下,它们不会被本地配置覆盖。Bootstrap context
和Application Context
有着不同的约定,所以新增了一个bootstrap.yml
文件,保证Bootstrap Context
和Application Context
配置的分离。application.yml文件改为bootstrap.yml,这是很关键的或者两者共存,一般推荐两者共存。
因为bootstrap.yml是比application.yml先加载的。bootstrap.yml优先级高于application.yml
例:
spring:application:name: cloud-payment-service####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:service-name: ${spring.application.name}config:profile-separator: '-' # default value is ",",we update '-'format: YAML
接着在consul中配置好kv配置信息,要读取配置信息,也是和读取普通配置文件中的信息一样,使用@Value注解获取。
kv信息的配置格式为config/服务名-运行环境/data,最终的配置信息在data中配置,可以使用yaml的格式。
及时动态刷新
在consul中的配置信息修改后,立即访问时候,不会生效,要等待一会才会刷新。
解决步骤:
-
添加@RefreshScope注解到主启动类上
-
修改watch的wait-time(不建议)
spring:application:name: cloud-payment-service####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:service-name: ${spring.application.name}config:profile-separator: '-' # default value is ",",we update '-'format: YAMLwatch:wait-time: 1
LoadBanlancer负载均衡调用
LB负载均衡(Load Balance):
简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用),常见的负载均衡有软件Nginx,LVS,硬件 F5等。
spring-cloud-starter-loadbalancer组件是什么:
Spring Cloud LoadBalancer是由SpringCloud官方提供的一个开源的、简单易用的客户端负载均衡器,它包含在SpringCloud-commons中用它来替换了以前的Ribbon组件。相比较于Ribbon,SpringCloud LoadBalancer不仅能够支持RestTemplate,还支持WebClient(WeClient是Spring Web Flux中提供的功能,可以实现响应式异步请求)
在负责转发请求的rest服务客户端,做负载均衡。导入以下依赖,并在RestTemplateConfig的组件配置中,加上@LoadBalanced注解
<!--loadbalancer-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
原理
LoadBalance在工作时分为两步:
- 第一步,先选择ConsulServer从服务端查询服务列表,默认轮询调用谁都可以正常执行。
- 第二步,按照指定的负载均衡策略从server取到的服务注册中由客户端自己选择一个地址,所以LoadBalancer是一个客户端的负载均衡器
负载均衡轮询算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ,每次服务重启动后rest接口计数从1开始。
默认是两种算法,除了轮询算法,还有一种是随机算法。如果要使用随机算法,RestTemplateConfig配置如下:
@Configuration
@LoadBalancerClient(//下面的value值大小写一定要和consul里面的名字一样,必须一样value = "cloud-payment-service",configuration = RestTemplateConfig.class)
public class RestTemplateConfig
{@Bean@LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力public RestTemplate restTemplate(){return new RestTemplate();}@BeanReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}
}
一般还是建议使用默认的轮询算法。
OpenFeign服务接口调用
openFeign是一个声明式的服务客户端。也支持并包含了loadBalanced。
官方介绍文:
它使编写web服务客户端变得更容易。使用Feign创建一个接口并对其进行注释。它具有可插入的注释支持,包括Feign注释和JAX-RS注释。Feign还支持可插拔编码器和解码器。Spring Cloud添加了对Spring MVC注释的支持,以及对使用Spring Web中默认使用的HttpMessageConverter的支持。Spring Cloud集成了Eureka、Spring Cloud CircuitBreaker以及Spring Cloud LoadBalancer,以便在使用Feign时提供负载平衡的http客户端。
由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每一个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以OpenFeign在此基础上做了进一步封装,帮助我们定义和实现依赖服务接口的定义。
可以在使用OpenFeign式提供http客户端的负载均衡,也可以集成阿里巴巴的Sentinel来提供熔断降级等功能。通过OpenFeign只需要服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用
使用步骤:
- 建好服务客户端模块后,引入依赖:
<!--openfeign-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- yaml文件:
server:port: 80spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}
- 主启动类上加上consul的支持注册服务的注解和启用feign客户端的注解
@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用consul为注册中心时注册服务
@EnableFeignClients//启用feign客户端,定义服务+绑定接口,以声明式的方法优雅而简单的实现服务调用
public class MainOpenFeign80 {public static void main(String[] args) {SpringApplication.run(MainOpenFeign80.class,args);}
}
- 在通用模块commons中引入依赖(如上)后,新建服务接口PayFeignApi,在接口上配置@FeignClient注解,并定义好抽象方法,抽象方法的定义要对应的服务的controller类中的controller方法。相应的mapping注解也要对应好。
@FeignClient("cloud-payment-service")
public interface PayFeignApi {@PostMapping(value = "/pay/add")public ResultData<String> addPay(@RequestBody Pay pay);@DeleteMapping("pay/del/{id}")public ResultData<Integer> deletePay(@PathVariable("id") Integer id);@PutMapping("/pay/update")public ResultData<String> updatePay(@RequestBody PayDTO payDto);@GetMapping("/pay/get/{id}")public ResultData<Pay> getById(@PathVariable("id") Integer id);@GetMapping("/pay/getAll")public ResultData<List<Pay>> getAll();
}
- 在服务客户端的controller中调用payFeignApi
@RestController
public class OrderController {@Resourceprivate PayFeignApi payFeignApi;@PostMapping("/feign/pay/add")public ResultData addOrder(@RequestBody Pay pay){ResultData<String> resultData = payFeignApi.addPay(pay);return resultData;}@GetMapping("/feign/pay/get/{id}")public ResultData getPayInfo(@PathVariable("id") Integer id){System.out.println("-------支付微服务远程调用,按照id查询订单支付流水信息");ResultData resultData = payFeignApi.getById(id);return resultData;}
}
OpenFeign高级特性
超时控制
在SpringCloud微服务架构中,大部分公司都是利用OpenFeign进行服务间的调用,如果服务要进行比较繁杂的业务计算,那后台很有可能会出现Read Timeout这个异常,因此定制化配置超时时间就非常有必要了。
openFeign默认等待六十秒,超时后会报错。
关于超时控制的配置项:
- connectTimeOut:连接超时时间
- readTimeOut:请求处理超时时间
配置分为两种:
全局配置
直接在application.yaml文件中进行配置:
server:port: 80spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}openfeign:client:config:default:#连接超时时间connectTimeout: 3000#读取超时时间readTimeout: 3000
指定配置
就是config的下一级属性的属性名改为要指定的服务的服务名,default的同级
server:port: 80spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}openfeign:client:config:default:#连接超时时间connectTimeout: 3000#读取超时时间readTimeout: 3000cloud-provider-payment:#连接超时时间connectTimeout: 5000#读取超时时间readTimeout: 5000
服务的超时配置先看指定的,再看全局的。
重试机制
重试是指当请求失败后,会重新再次发送该请求。在OpenFeign中默认是关闭重试的,如果要开启重试,在配置类FeignConfig中设置:
@Configuration
public class FeignConfig {@Beanpublic Retryer myRetryer(){//return Retryer.NEVER_RETRY; //Feign默认配置是不走重试策略的//最大请求次数为3(1+2),初始间隔时间为100ms,重试间最大间隔时间为1sreturn new Retryer.Default(100,1,3);}
}
HttpClient
OpenFeign中http client如果不做特殊配置,默认使用jdk自带的HttpURLConnection发送HTTP请求,由于默认HttpURLConnection没有连接池、性能和效率低,如果采用默认,性能上欠佳。
建议使用阿帕奇的httpclient5。
步骤:
-
pom引入依赖
<!-- httpclient5--> <dependency><groupId>org.apache.httpcomponents.client5</groupId><artifactId>httpclient5</artifactId><version>5.3</version> </dependency> <!-- feign-hc5--> <dependency><groupId>io.github.openfeign</groupId><artifactId>feign-hc5</artifactId><version>13.1</version> </dependency>
-
开启httpclient5的配置,application.yaml文件:
server:port: 80spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}openfeign:client:config:default:connect-timeout: 3000read-timeout: 3000cloud-provider-payment:#连接超时时间connectTimeout: 5000#读取超时时间readTimeout: 5000# 配置httpclient5httpclient:hc5:enabled: true
请求响应压缩
Spring Cloud OpenFeign支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。还可以对请求压缩做一些更细致的设置,比如指定压缩的请求数据类型并设置了请求压缩的大小下限。
application.yaml配置文件:
server:port: 80spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}openfeign:client:config:default:connect-timeout: 3000read-timeout: 3000cloud-provider-payment:#连接超时时间connectTimeout: 5000#读取超时时间readTimeout: 5000httpclient:hc5:enabled: truecompression:request:enabled: truemin-request-size: 2048 #最小触发压缩的大小,单位 字节mime-types: text/xml,application/xml,application/json #触发压缩数据类型response:enabled: true
日志打印
Feign提供了日志打印功能,可以通过配置来调整日志级别,从而了解feign中http请求的细节,也就是对Feign接口的调用情况进行监控和输出。
日志级别
NONE:默认的,不显示任何日志;
BASIC:仅记录请求方法、URL、响应状态码及执行时间;
HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;
FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。
配置方式:
- 在FeignConfig中,配置:
@Configuration
public class FeignConfig {@Beanpublic Retryer myRetryer(){//return Retryer.NEVER_RETRY; //Feign默认配置是不走重试策略的//最大请求次数为3(1+2),初始间隔时间为100ms,重试间最大间隔时间为1sreturn new Retryer.Default(100,1,3);}//配置日志级别@BeanLogger.Level feignLoggerLevel() {return Logger.Level.FULL;}
}
- 在yaml文件中配置,配置公式是:logging.level + 含有@FeignClient注解的完整带包名的接口名+debug,例如:
server:port: 80spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}openfeign:client:config:default:connect-timeout: 3000read-timeout: 3000cloud-provider-payment:#连接超时时间connectTimeout: 5000#读取超时时间readTimeout: 5000httpclient:hc5:enabled: truecompression:request:enabled: truemin-request-size: 2048 #最小触发压缩的大小mime-types: text/xml,application/xml,application/json #触发压缩数据类型response:enabled: true
#日志配置
logging:level:com:ergou:cloud:apis:PayFeignApi: debug