目录
5、Feign的通讯优化:HttpClient客户端替换及HTTP连接池
7.3.3.、修改application.properties配置文件信息
9、在 Feign 中使用 Hystrix Dashboard
9.3、添加 Hystrix Dashboard 界面访问配置
1、什么是Feign
Feign是一种声明式、模板化的HTTP客户端(仅在Application Client中使用)。声明式调用是指,就像调用本地方法一样调用远程方法,无需感知操作远程http请求。
Spring Cloud的声明式调用, 可以做到使用 HTTP请求远程服务时能就像调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。Feign的应用,让Spring Cloud微服务调用像Dubbo一样,Application Client直接通过接口方法调用Application Service,而不需要通过常规的RestTemplate构造请求再解析返回数据。它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。
2、使用Feign技术开发时的应用部署结构
- 在使用Feign技术开发Spring Cloud微服务时,需要先抽取要注册发布的服务标准,将这套标准通过接口的形式定义出来。
- 在Application Service端开发中,依赖抽取的服务标准接口工程,并对接口给予实现。
- 在Application Client端开发中,依赖抽取的服务标准接口工程,并应用接口信息和Feign技术,实现远程服务的调用。
在整体微服务开发中,Eureka Server作为注册中心必不可少,注册中心的作用不变,仍旧是注册和发现服务。
3、简单实用
Feign的使用很简单,有以下几步:
1、添加依赖
<!-- 添加springboot fegin依赖,product项目即可以作为生产者,又可以作为消费者--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
2、启动类添加 @EnableFeignClients 注解支持
3、建立Client接口,并在接口中定义需调用的服务方法。
@FeignClient(name ="product-server",path = "/product")
public interface ProductClient {
@RequestMapping("/getProduct")
public Product getProduct();
}
4、使用Client接口。
@RestController
public class ConsumerController {
@Autowired
private ProductClient productClient;
@RequestMapping("/getConsumer")
public String getConsumer(){
Product product = productClient.getProduct();
return product.toString();
}
4、Feign通讯优化:GZIP压缩
4.1、GZIP简介
gzip介绍:gzip是一种数据格式,采用deflate算法压缩数据;gzip是一种流行的数据压缩算法,应用十分广泛,尤其是在Linux平台。
gzip能力:当Gzip压缩到一个纯文本数据时,效果是非常明显的,大约可以减少70%以上的数据大小。
gzip作用:网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是Gzip与搜索引擎的抓取工具有着更好的关系。例如 Google就可以通过直接读取gzip文件来比普通手工抓取更快地检索网页。
4.2、HTTP协议中关于压缩传输的规定
如图所示:
第一:客户端向服务器请求头中带有:Accept-Encoding:gzip, deflate 字段,向服务器表示,客户端支持的压缩格式(gzip或者deflate),如果不发送该消息头,服务器是不会压缩的。
第二:服务端在收到请求之后,如果发现请求头中含有Accept-Encoding字段,并且支持该类型的压缩,就对响应报文压缩之后返回给客户端,并且携带Content-Encoding:gzip消息头,表示响应报文是根据该格式压缩过的。
第三:客户端接收到响应之后,先判断是否有Content-Encoding消息头,如果有,按该格式解压报文。否则按正常报文处理。
4.3、在Feign技术中应用GZIP压缩
在Spring Cloud微服务体系中,一次请求的完整流程如下:
在整体流程中,如果使用GZIP压缩来传输数据,涉及到两次请求-应答。而这两次请求-应答的连接点是Application Client,那么我们需要在Application Client中配置开启GZIP压缩,来实现压缩数据传输。
4.3.1、只配置Feign请求-应答的GZIP压缩
这里只开启Feign请求-应答过程中的GZIP,也就是浏览器-Application Client之间的请求应答不开启GZIP压缩。在全局配置文件中,使用下述配置来实现Feign请求-应答的GZIP压缩:
# feign gzip
# 局部配置。只配置feign技术相关的http请求-应答中的gzip压缩。
# 配置的是application client和application service之间通讯是否使用gzip做数据压缩。
# 和浏览器到application client之间的通讯无关。
# 开启feign请求时的压缩, application client -> application service
feign.compression.request.enabled=true
# 开启feign技术响应时的压缩, application service -> application client
feign.compression.response.enabled=true
# 设置可以压缩的请求/响应的类型。
feign.compression.request.mime-types=text/xml,application/xml,application/json
# 当请求的数据容量达到多少的时候,使用压缩。默认是2048字节。
feign.compression.request.min-request-size=512
4.3.2、配置全局的GZIP压缩
在全局配置文件中配置下述内容,来开启所有请求-应答中的GZIP压缩,这里使用的是Spring Boot中的GZIP技术。在Spring Boot中已经集成了GZIP压缩技术,并对所有的请求-应答实现GZIP数据压缩。Spring Cloud中已经集成了Spring Boot技术,所以在配置文件中可以开启Spring Boot中的GZIP压缩技术,对完整流程中与当前节点(Application Client)所有相关的请求-应答开启GZIP压缩。
# spring boot gzip
# 开启spring boot中的gzip压缩。就是针对和当前应用所有相关的http请求-应答的gzip压缩。
server.compression.enabled=true
# 哪些客户端发出的请求不压缩,默认是不限制
server.compression.excluded-user-agents=gozilla,traviata
# 配置想压缩的请求/应答数据类型,默认是 text/html,text/xml,text/plain
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain
# 执行压缩的阈值,默认为2048
server.compression.min-response-size=512
5、Feign的通讯优化:HttpClient客户端替换及HTTP连接池
两台服务器建立HTTP连接的过程是很复杂的一个过程,涉及到多个数据包的交换,并且也很耗时间。HTTP连接需要的3次握手4次挥手开销很大,这一开销对于大量的比较小的HTTP消息来说更大。
那么如何来优化HTTP连接性能?我们可以采用HTTP连接池来提升性能,连接池可以节约大量的3次握手4次挥手的时间,这样能大大提升吞吐率。
5.1、Feign技术的底层实现
Feign的HTTP客户端支持3种框架,分别是;HttpURLConnection、HttpClient、OKHttp。Feign中默认使用HttpURLConnection。
HttpURLConnection是JDK自带的HTTP客户端技术,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。对于网络请求这种底层相对复杂的操作,如果有可用的其他方案,也没有必要自己去管理连接对象。
Apache提供的HttpClient框架相比传统JDK自带的HttpURLConnection,它封装了访问http的请求头,参数,内容体,响应等等;它不仅使客户端发送HTTP请求变得容易,而且也方便了开发人员测试接口(基于Http协议的),即提高了开发的效率,也方便提高代码的健壮性;另外高并发大量的请求网络的时候,还是用“HTTP连接池”提升吞吐量。
OKHttp是一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献用于替代HttpUrlConnection和Apache HttpClient。OKHttp拥有共享Socket,减少对服务器的请求次数,通过连接池,减少了请求延迟等技术特点。
本案例中,通过替换Feign底层的HTTP客户端实现为HttpClient,来提升Feign的通讯性能。
5.2、Feign中应用HttpClient
资源依赖:Feign技术发起请求的位置是Application Client端,下述依赖只需要在Application Client所在工程中添加即可。
<!-- httpclient相关依赖 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
修改全局配置文件:
# 开启feign技术对底层httpclient的依赖。 切换底层实现技术。
feign.httpclient.enabled=true
提供连接池配置类:
/**
* 配置类型
* 用于提供一个HTTP连接池,并实现连接池管理。
* 主要目的是,提供一个合理的资源回收方式。
*/
@Configuration
public class HttpClientConfiguration {
@Bean
public HttpClient httpClient(){
System.out.println("init feign httpclient configuration " );
// 生成默认请求配置
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
// 超时时间
requestConfigBuilder.setSocketTimeout(5 * 1000);
// 连接时间
requestConfigBuilder.setConnectTimeout(5 * 1000);
RequestConfig defaultRequestConfig = requestConfigBuilder.build();
// 连接池配置
// 长连接保持30秒
final PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.MILLISECONDS);
// 总连接数
pollingConnectionManager.setMaxTotal(5000);
// 同路由的并发数
pollingConnectionManager.setDefaultMaxPerRoute(100);
// httpclient 配置
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
// 保持长连接配置,需要在头添加Keep-Alive
httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
httpClientBuilder.setConnectionManager(pollingConnectionManager);
httpClientBuilder.setDefaultRequestConfig(defaultRequestConfig);
HttpClient client = httpClientBuilder.build();
// 启动定时器,定时回收过期的连接, 最重要。 如果没有定义回收策略。连接池会在运行一段时间后失效。
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
pollingConnectionManager.closeExpiredConnections();
pollingConnectionManager.closeIdleConnections(5, TimeUnit.SECONDS);
}
}, 10 * 1000, 5 * 1000);
System.out.println("===== Apache httpclient 初始化连接池===");
return client;
}
}
使用HttpClient客户端替换HttpURLConnection,仅需修改Application Client,其余无需修改。当使用HttpClient技术作为Feign的底层HTTP技术应用时,使用GET请求方式请求头传递自定义类型对象是可行的,只要在服务标准对象中定义的方法参数上增加注解@RequestBody即可处理。
6、配置Feign中的请求超时(即Ribbon使用)
请求超时/请求时间: 客户端发起请求,服务端响应并成功建立双向链接的时间。
链接超时/链接时间: 双向链接建立成功后,需要多久必须得到服务端的响应结果。
在Feign声明式远程调用中,负载均衡还是使用的Ribbon技术。而Ribbon技术默认的链接超时是1秒,也就是1秒内Application Service没有处理Application Client的请求,且链接请求处理后,1秒之内没有返回响应,Application Client都会抛出超时异常。在商业项目中,部分服务是很难在1秒之内处理链接,并在处理链接后1秒之内返回响应的,所以配置超时信息就很有必要了。
定义超时配置的时候,建议为每个服务定义超时策略。如果有部分服务可以使用同样的超时策略,可以使用全局配置。指定服务配置超时策略,会覆盖全局配置。
超时策略的使用优先级: 指定服务的超时策略 -> 全局配置的超时策略 -> 默认的超时策略。
对Ribbon链接超时的配置都在全局配置文件中定义。具体如下。
6.1、全局服务配置
全局服务配置粒度粗糙。商业项目中不同的服务对响应时长的限制也不同,全局服务配置不推荐应用。
#以下配置全局有效
ribbon.eureka.enabled=true
#请求连接的超时时间 默认的时间为1秒
ribbon.ConnectTimeout=60000
#请求处理的超时时间,5分钟
ribbon.ReadTimeout=60000
#所有操作都重试
ribbon.OkToRetryOnAllOperations=true
#重试发生,更换节点数最大值
ribbon.MaxAutoRetriesNextServer=10
#单个节点重试最大值
ribbon.MaxAutoRetries=1
6.2、指定服务配置
指定服务配置粒度细致,可以为每个具体服务配置不同的超时信息,推荐使用。
大多数情况下,我们对于服务调用的超时时间可能会根据实际服务的特性做一些调整,所以仅仅进行个性化配置的方式与使用Spring Cloud Ribbon时的配置方式是意义的,都采用<client>.ribbon.key=value的格式进行设置。但是,这里就有一个疑问了,<cleint>所指的Ribbon客户端在那里呢?
在定义Feign客户端的时候,我们使用了@FeignClient注解。在初始化过程中,Spring Cloud Feign会根据该注解的name属性或value属性指定的服务名,自动创建一个同名的Ribbon客户端。如下:
#以下配置对服务hello-service-provider有效
hello-service-provider.ribbon.eureka.enabled=true
#建立连接超时时间,原1000
hello-service-provider.ribbon.ConnectTimeout=60000
#请求处理的超时时间,5分钟
hello-service-provider.ribbon.ReadTimeout=60000
#所有操作都重试
hello-service-provider.ribbon.OkToRetryOnAllOperations=true
#重试发生,更换节点数最大值
hello-service-provider.ribbon.MaxAutoRetriesNextServer=10
#单个节点重试最大值
hello-service-provider.ribbon.MaxAutoRetries=1
7、Feign配置
7.1、Ribbon配置
ribbon的配置其实非常简单,直接在application.properties中配置即可,如下:
# 设置连接超时时间
ribbon.ConnectTimeout=600
# 设置读取超时时间
ribbon.ReadTimeout=6000
# 对所有操作请求都进行重试
ribbon.OkToRetryOnAllOperations=true
# 切换实例的重试次数
ribbon.MaxAutoRetriesNextServer=2
# 对当前实例的重试次数
ribbon.MaxAutoRetries=1
7.2、Hystrix配置
Feign中Hystrix的配置和Ribbon有点像,基础配置如下:
# 设置熔断超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
# 关闭Hystrix功能(不要和上面的配置一起使用)
feign.hystrix.enabled=false
# 关闭熔断功能
hystrix.command.default.execution.timeout.enabled=false
这种配置也是全局配置,如果我们想针对某一个接口配置,比如/hello接口,那么可以按照下面这种写法,如下:
# 设置熔断超时时间
hystrix.command.hello.execution.isolation.thread.timeoutInMilliseconds=10000
# 关闭熔断功能
hystrix.command.hello.execution.timeout.enabled=false
但是我们的接口名可能会重复,这个时候同名的接口会共用这一条Hystrix配置。
7.3、feign日志
Feign为每一个FeignClient都提供了一个feign.Logger实例,我们可以在配置中开启日志,开启方式很简单。
7.3.1、编写Feign配置类
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
NONE :不记录任何日志(默认)。
BASIC:仅记录请求方法、URL、响应状态代码以及执行时间。
HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
FULL:记录请求和响应的header,body和元数据。
7.3.2、修改Feign,使用指定配置类
@FeignClient(name = "eureka-client1",configuration = FeignConfig.class)
public interface EurekaFeignClient {
@GetMapping("/hello/{name}")
public String index(@PathVariable String name);
}
7.3.3.、修改application.properties配置文件信息
# 开启日志 格式为logging.level.+Feign客户端路径
logging.level.com.example.feigndemo.feign.FeignConfig=DEBUG
如此之后,控制台就会输出请求的详细日志。
8、Feign Hystrix
因为熔断只是作用在服务调用这一端,因此我们只需要改动consumer项目相关代码就可以。因为,Feign中已经依赖了Hystrix。
服务调用接口
@FeignClient(name ="product-server",path = "/product",fallback = ProductClientFallback.class)
public interface ProductClient {
@RequestMapping("/getProduct")
public Product getProduct();
}
创建熔断处理类
@Component
public class ProductClientFallback implements ProductClient {
@Override
public Product getProduct() {
Product product = new Product();
product.setName("consumer 调用product的getProduct 失败");
return product;
}
}
配置文件
spring:
application:
name: consumer-server
server:
port: 8002
servlet:
context-path: /consumer
#eureka配置
eureka:
# 客户端配置
client:
serviceUrl:
defaultZone: http://localhost:8000/eureka/ #注册中心url
healthcheck:
enabled: true # 开启健康检查(需要spring-boot-starter-actuator依赖)
fetch-registry: true # 客户端是否获取eureka服务器注册表上的注册信息 缓存到本地,默认为true,
registry-fetch-interval-seconds: 5 # 客户端从注册中心获取服务列表的间隔时间,默认为30s,开发时,可以设置为5s
# 服务实例配置
instance:
# hostname: ${spring.cloud.client.ip-address}
# ip-address: 192.168.6.66 # 只有prefer-ip-address=true时才会生效
prefer-ip-address: true # 设置微服务调用地址为IP优先(缺省为false)
# Eureka 首页显示的微服务调用地址,默认是这样的:http://jadyer-pc:2100/info
# 而在设置 prefer-ip-address=true 之后,调用地址会变成:http://10.16.18.95:2100/info
# 这时若再设置 ip-address=192.168.6.66,则调用地址会变成:http://192.168.6.66:2100/info
# instance-id: ${spring.application.name} # 修改显示的微服务名为:应用名称
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 修改显示的微服务名为:IP:端口
status-page-url: http://${spring.cloud.client.ip-address}:${server.port}/doc.html # 服务实例文档
lease-renewal-interval-in-seconds: 5 # 心跳时间,即服务续约间隔时间(默认30秒)
lease-expiration-duration-in-seconds: 10 # 发呆时间,即服务续约到期时间(缺省为90s)
# feign配置
feign:
hystrix:
enabled: true # 设置feign开启hystrix(服务保护)
启动类
@SpringBootApplication
@EnableEurekaClient
//@EnableEurekaClient 和 @EnableDiscoveryClient 都是让eureka发现该服务并注册到eureka上 的注解
//相同点:都能让注册中心Eureka发现,并将该服务注册到注册中心上;
//不同点:@EnableEurekaClient只适用于Eureka作为注册中心,而@EnableDiscoveryClient可以是其他注册中心;
@EnableFeignClients // 开启Fegin客户端
public class SpringcloudStudyConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudStudyConsumerApplication.class, args);
}
@Bean
@LoadBalanced // 提供客户端负载均衡
public RestTemplate initRestTemplate() {
return new RestTemplate();
}
}
9、在 Feign 中使用 Hystrix Dashboard
Hystrix Dashboard 是监控 Hystrix 的熔断器状况 的一个组件,提供了数据监控和友好的图像化展示页面。
9.1、pom依赖
在client项目的 pom.xml 中加上 Actuator、Hystrix 、Hystrix Dashboard的依赖 。pom.xml 代码如下:
<dependencies>
<!-- 主要使用其中的SpringMVC相关技术。将请求的URL和服务的方法耦合到一起。 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>springcloud-study-entity</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- springboot2.0.*对应的eureka client依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 添加springboot fegin依赖,product项目即可以作为生产者,又可以作为消费者-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!-- 监控熔断器的状态-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix-dashboard -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
9.2 、开启 Hystrix Dashboard 功能
在启动类上加上 @EnableEurekaClient开启服务注册与发现,加上@EnableHystrix开启Hystrix 的熔断器功能,加上@EnableHystrixDashboard 开启 Hystrix Dashboard 的功能。代码如下
@SpringBootApplication
@EnableEurekaClient
//@EnableEurekaClient 和 @EnableDiscoveryClient 都是让eureka发现该服务并注册到eureka上 的注解
//相同点:都能让注册中心Eureka发现,并将该服务注册到注册中心上;
//不同点:@EnableEurekaClient只适用于Eureka作为注册中心,而@EnableDiscoveryClient可以是其他注册中心;
//表示开启Fegin客户端
@EnableFeignClients
@EnableHystrix // 开启熔断
@EnableHystrixDashboard // 开启 Hystrix Dashboard 的功能,监控熔断器的状态
public class SpringcloudStudyConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudStudyConsumerApplication.class, args);
}
@Bean
@LoadBalanced // 提供客户端负载均衡
public RestTemplate initRestTemplate() {
return new RestTemplate();
}
}
9.3、添加 Hystrix Dashboard 界面访问配置
访问地址:ip:端口/${servlet.context-path}/hystrix
/**
* 熔断配置
*/
@Configuration
public class HystrixConfiguration {
// 解决http://localhost:8002/consumer/hystrix 访问不了的问题
// 监控熔断器的状态
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/actuator/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
界面如下: