Kubernetes 开发 SpringCloud (三)、使用 SpringCloud Feign 进行 SrpingCloud 服务间的通信
文章目录
!版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。
相关博文:
- Kubernetes 开发 SpringCloud (一)、使用SpringCloud Kubernetes组件进行服务发现
- Kubernetes 开发 SpringCloud (二)、使用 SpringCloud Kubernetes 组件进行动态配置
- Kubernetes 开发 SpringCloud (三)、使用 SpringCloud Feign 进行 SrpingCloud 服务间的通信
- Kubernetes 开发 SpringCloud (四)、Kubnernetes 部署 Zipkin 搭配 Kafka+ElasticSearch 实现链路追踪
系统环境:
- Kubernetes 版本:1.14.0
- SpringCloud Feign 版本:2.1.2.RELEASE
- SpringCloud Kubernetes 版本:1.0.2.RELEASE
- 示例部署文件 Github 地址:https://github.com/my-dlq/blog-example/tree/master/springcloud/springcloud-kubernetes/springcloud-kubernetes-feign-demo
一、介绍
Feign 简介
Feign 是一个声明式的 Web Service 客户端,可以帮助我们更快捷、优雅地调用HTTP API。它的出现使开发 Web Service 客户端变得很简单,使用 Feign 只需要创建一个接口加上对应的注解就可以完成服务间的调用。
Spring Cloud Feign 帮助我们定义和实现依赖服务接口的定义。在Spring Cloud feign 的实现下,只需要创建一个接口并用注解方式配置它,即可完成服务提供方的接口绑定,简化了在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。
Feign 功能:
- 可插拔的注解支持,包括Feign注解和JAX-RS注解。
- 支持可插拔的HTTP编码器和解码器(Gson,Jackson,Sax,JAXB,JAX-RS,SOAP)。
- 支持Hystrix和它的Fallback。
- 支持Ribbon的负载均衡。
- 支持HTTP请求和响应的压缩。
- 灵活的配置:基于 name 粒度进行配置
- 支持多种客户端:JDK URLConnection、apache httpclient、okhttp,ribbon)
- 支持日志
- 支持错误重试
- url支持占位符
- 可以不依赖注册中心独立运行
Hystrix 简介
在分布式环境中,许多服务依赖项中的一些必然会失败。Hystrix 是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix 通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。
Hystrix 功能:
- 服务降级:优先核心服务,非核心服务不可用或弱可用
- 依赖隔离:依赖隔离其实就是资源隔离,把对依赖使用的资源隔离起来,统一控制和调度。
- 监控(Dashboard)状态:hystrix 提供了一套监控组件Dashboard,用于查看最近熔断器的情况。
- 服务熔断:类似现实世界中的"保险丝",当某个异常条件被触发,直接熔断整个服务,进行服务的降级快速返回"错误"的响应信息而不是一直等到此服务超时。当检测到该节点微服务响应正常后恢复调用链路。
二、Kubernetes 中使用 Feign 进行服务间通信
在 Kubernetes 环境中使用 SpringCloud 框架开发服务跟本地开发一样,服务间调用也是可用利用 Feign
完成服务间通信工作,保持和本地开发 SpringCloud 模式不变,这样也能减少学习成本。如果使用 SpringCloud Feign
还能配合 SpringCloud Sleuth
生成链路日志,然后配合 Zipkin
配合完成服务间链路追踪工作。
在 Kubernetes 环境下使用 Feign
还有一个问题是,现在已经将 Eureka
等注册中心去掉了,且在 Kubernetes 中所有的 Service
信息都会通过 Kubernetes 存入 Etcd
中,且每个服务都会通过 CoreDNS
分配一个以 服务名称
命名的不会重复域名,所以我们可以在开发项目过程中,可以通过配置 Feign
使用 域名
+ 端口号
方式完成服务间的调用,如下图所示:
三、Kubernetes 中项目使用 Feign 进行服务间通信示例
这里写三个项目,分别是 提供者
项目 与 消费者
项目 与两个服务都引用的 接口类
项目,开发模式跟在本地使用 SpringCloud 框架开发服务一样,消费者
调用 提供者
,且两者都引用 接口项目
,其中提供者实现 接口项目
的 interface
的实现,消费者调通过 Feign
引入 interface
接口。这两个服务创建完成后将其部署到 Kubernetes 环境下,通过 NodePort
方式暴露端口供外部访问,然就外部调用 消费者
服务来测试是否能成功调用 提供者
服务提供的接口。
1、Feign 接口项目示例代码
(1)、Maven 引入相关依赖
1<?xml version="1.0" encoding="UTF-8"?>
2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4 <modelVersion>4.0.0</modelVersion>
5
6 <groupId>club.mydlq</groupId>
7 <artifactId>service-feign-interface</artifactId>
8 <version>0.0.1</version>
9 <name>service-feign-interface</name>
10 <description>service feign interface</description>
11
12 <dependencies>
13 <dependency>
14 <groupId>org.springframework.boot</groupId>
15 <artifactId>spring-boot-starter-web</artifactId>
16 <version>2.1.6.RELEASE</version>
17 </dependency>
18 </dependencies>
19
20</project>
(2)、Interface 接口类定义
1import org.springframework.web.bind.annotation.*;
2
3public interface UserInterface {
4
5 /**
6 * 获取测试信息
7 * @return
8 */
9 @GetMapping("/")
10 public String getInfo();
11
12}
2、服务提供者示例代码
(1)、Maven 引入相关依赖
1<?xml version="1.0" encoding="UTF-8"?>
2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4 <modelVersion>4.0.0</modelVersion>
5
6 <parent>
7 <groupId>org.springframework.boot</groupId>
8 <artifactId>spring-boot-starter-parent</artifactId>
9 <version>2.1.6.RELEASE</version>
10 <relativePath/>
11 </parent>
12
13 <groupId>club.mydlq</groupId>
14 <artifactId>service-provider</artifactId>
15 <version>0.0.1</version>
16 <name>service-provider</name>
17 <description>service provider</description>
18
19 <properties>
20 <java.version>1.8</java.version>
21 </properties>
22
23 <dependencies>
24 <!--SpringBoot Web-->
25 <dependency>
26 <groupId>org.springframework.boot</groupId>
27 <artifactId>spring-boot-starter-web</artifactId>
28 </dependency>
29 <!--SpringBoot Actuator-->
30 <dependency>
31 <groupId>org.springframework.boot</groupId>
32 <artifactId>spring-boot-starter-actuator</artifactId>
33 </dependency>
34 <!--Service Interface API-->
35 <dependency>
36 <groupId>club.mydlq</groupId>
37 <artifactId>service-feign-interface</artifactId>
38 <version>0.0.1</version>
39 </dependency>
40 </dependencies>
41
42 <build>
43 <plugins>
44 <plugin>
45 <groupId>org.springframework.boot</groupId>
46 <artifactId>spring-boot-maven-plugin</artifactId>
47 </plugin>
48 </plugins>
49 </build>
50
51</project>
(2)、实现 UserInterface 接口
1import club.mydlq.feign.TestInterface;
2import org.springframework.web.bind.annotation.RestController;
3
4@RestController
5public class UserController implements TestInterface {
6
7 @Override
8 public String getInfo() {
9 return "Hello World!";
10 }
11
12}
(3)、Application 配置文件
1spring:
2 application:
3 name: service-provider
4
5server:
6 port: 8080
7
8management:
9 server:
10 port: 8081
11 endpoints:
12 web:
13 exposure:
14 include: "*"
(4)、启动类
1import org.springframework.boot.SpringApplication;
2import org.springframework.boot.autoconfigure.SpringBootApplication;
3
4@SpringBootApplication
5public class Application {
6
7 public static void main(String[] args) {
8 SpringApplication.run(Application.class, args);
9 }
10
11}
3、服务消费者示例代码
(1)、Maven 引入相关依赖
1<?xml version="1.0" encoding="UTF-8"?>
2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4 <modelVersion>4.0.0</modelVersion>
5
6 <parent>
7 <groupId>org.springframework.boot</groupId>
8 <artifactId>spring-boot-starter-parent</artifactId>
9 <version>2.1.6.RELEASE</version>
10 <relativePath/>
11 </parent>
12
13 <groupId>club.mydlq</groupId>
14 <artifactId>service-customer</artifactId>
15 <version>0.0.1</version>
16 <name>service-customer</name>
17 <description>service customer</description>
18
19 <properties>
20 <java.version>1.8</java.version>
21 </properties>
22
23 <dependencies>
24 <!--SpringBoot Web-->
25 <dependency>
26 <groupId>org.springframework.boot</groupId>
27 <artifactId>spring-boot-starter-web</artifactId>
28 </dependency>
29 <!--SpringBoot Actuator-->
30 <dependency>
31 <groupId>org.springframework.boot</groupId>
32 <artifactId>spring-boot-starter-actuator</artifactId>
33 </dependency>
34 <!--Service Interface API-->
35 <dependency>
36 <groupId>club.mydlq</groupId>
37 <artifactId>service-feign-interface</artifactId>
38 <version>0.0.1</version>
39 </dependency>
40 <!--Feign-->
41 <dependency>
42 <groupId>org.springframework.cloud</groupId>
43 <artifactId>spring-cloud-starter-openfeign</artifactId>
44 <version>2.1.2.RELEASE</version>
45 </dependency>
46 </dependencies>
47
48 <build>
49 <plugins>
50 <plugin>
51 <groupId>org.springframework.boot</groupId>
52 <artifactId>spring-boot-maven-plugin</artifactId>
53 </plugin>
54 </plugins>
55 </build>
56
57</project>
(2)、Feign Service类
这里我们写一个 Feign 接口类,通过 Feign 调用服务提供者的接口,需要注意的是,在使用 @FeignClient
注解中需要指定 name
和 url
两个属性,并且使两个值保持一致,内容设置为要访问的 Kubernetes 下的 Service 名称
及其对应的 端口号
,而且一般来说这个值是配置在 application 配置文件当中,需要通过引入变量方式将其引入,这里为了简单直接将值填在这里。
1import club.mydlq.feign.TestInterface;
2import org.springframework.cloud.openfeign.FeignClient;
3
4@FeignClient(name = "http://service-provider:8080", url = "http://service-provider:8080", fallback = TestFallback.class)
5public interface TestService extends TestInterface {
6
7}
(3)、Feign Fallback 类
1import org.springframework.stereotype.Component;
2
3@Component
4public class TestFallback implements TestService {
5
6 @Override
7 public String getInfo() {
8 return "fallback!";
9 }
10
11}
(4)、TestController 类
1import club.mydlq.k8s.feign.TestService;
2import org.springframework.beans.factory.annotation.Autowired;
3import org.springframework.web.bind.annotation.*;
4
5@RestController
6public class TestController {
7
8 @Autowired
9 private TestService testService;
10
11 @GetMapping("/")
12 public String getTestInfo(){
13 return testService.getInfo();
14 }
15
16}
(5)、Application 配置文件
1spring:
2 application:
3 name: service-customer
4
5server:
6 port: 8080
7
8management:
9 server:
10 port: 8081
11 endpoints:
12 web:
13 exposure:
14 include: "*"
15
16#开启 Hystrix
17feign:
18 hystrix:
19 enabled: true
(6)、启动类
1import org.springframework.boot.SpringApplication;
2import org.springframework.boot.autoconfigure.SpringBootApplication;
3import org.springframework.cloud.openfeign.EnableFeignClients;
4
5@SpringBootApplication
6@EnableFeignClients
7public class Application {
8
9 public static void main(String[] args) {
10 SpringApplication.run(Application.class, args);
11 }
12
13}
4、构建 Docker 镜像
(1)、执行 Maven 编译
1#Manve 编译 service-feign-interface 项目
2$ cd service-feign-interface
3$ mvn clean install
4
5#Manve 编译 service-provider 项目
6$ cd service-provider
7$ mvn clean install
8
9#Manve 编译 service-customer 项目
10$ cd service-customer
11$ mvn clean install
(2)、准备 Dockerfile
Dockerfile
1FROM openjdk:8u212-b04-jre-slim
2VOLUME /tmp
3ADD target/*.jar app.jar
4RUN sh -c 'touch /app.jar'
5ENV JVM_OPTS="-Xss256k -XX:MaxRAMPercentage=80.0 -Duser.timezone=Asia/Shanghai -Djava.security.egd=file:/dev/./urandom"
6ENV JAVA_OPTS=""
7ENV APP_OPTS=""
8ENTRYPOINT [ "sh", "-c", "java $JVM_OPTS $JAVA_OPTS -jar /app.jar $APP_OPTS" ]
(3)、执行 Docker 构建镜像
1#构建 service-provider 镜像
2$ docker build -t mydlqclub/service-provider:0.0.1 .
3
4#构建 service-customer 镜像
5$ docker build -t mydlqclub/service-customer:0.0.1 .
5、准备 Kubernetes 部署文件
service-provider.yaml
1apiVersion: v1
2kind: Service
3metadata:
4 name: service-provider
5spec:
6 type: NodePort
7 ports:
8 - name: server
9 nodePort: 31001
10 port: 8080
11 targetPort: 8080
12 - name: management
13 nodePort: 31002
14 port: 8081
15 targetPort: 8081
16 selector:
17 app: service-provider
18---
19apiVersion: apps/v1
20kind: Deployment
21metadata:
22 name: service-provider
23 labels:
24 app: service-provider
25spec:
26 replicas: 1
27 selector:
28 matchLabels:
29 app: service-provider
30 template:
31 metadata:
32 name: service-provider
33 labels:
34 app: service-provider
35 spec:
36 restartPolicy: Always
37 containers:
38 - name: service-provider
39 image: registry.cn-beijing.aliyuncs.com/mydlq/service-provider:0.0.1
40 imagePullPolicy: IfNotPresent
41 ports:
42 - containerPort: 8080
43 name: server
44 - containerPort: 8081
45 name: management
46 resources:
47 limits:
48 memory: 1000Mi
49 cpu: 1000m
50 requests:
51 memory: 500Mi
52 cpu: 500m
service-customer.yaml
1apiVersion: v1
2kind: Service
3metadata:
4 name: service-customer
5spec:
6 type: NodePort
7 ports:
8 - name: server
9 nodePort: 31003
10 port: 8080
11 targetPort: 8080
12 - name: management
13 nodePort: 31004
14 port: 8081
15 targetPort: 8081
16 selector:
17 app: service-customer
18---
19apiVersion: apps/v1
20kind: Deployment
21metadata:
22 name: service-customer
23 labels:
24 app: service-customer
25spec:
26 replicas: 1
27 selector:
28 matchLabels:
29 app: service-customer
30 template:
31 metadata:
32 name: service-customer
33 labels:
34 app: service-customer
35 spec:
36 restartPolicy: Always
37 containers:
38 - name: service-customer
39 image: registry.cn-beijing.aliyuncs.com/mydlq/service-customer:0.0.1
40 imagePullPolicy: IfNotPresent
41 ports:
42 - containerPort: 8080
43 name: server
44 - containerPort: 8081
45 name: management
46 resources:
47 limits:
48 memory: 1000Mi
49 cpu: 1000m
50 requests:
51 memory: 500Mi
52 cpu: 500m
6、将项目推送到 Kubernetes
执行 Kubectl 部署命将项目部署到 Kubernetes。
1#创建service-customer
2$ kubectl apply -f service-customer.yaml -n mydlqcloud
3
4#创建service-provider
5$ kubectl apply -f service-provider.yaml -n mydlqcloud
7、测试接口
输入地址:http://192.168.2.11:31003 测试 service-customer
服务是否能正常通过 Feign
调用 service-provider
服务。
结果:
1Hello World!
将 Kubernetes 中 service-provider 关掉来测试 fallback,再次输入地址:http://192.168.2.11:31003
结果:
1fallback!
由上面结果可知,在 Kubernetes 环境下通过 SpringCloud Feign 根据 服务名称
+ 端口号
这种方式,完成服务间的通信是可行的。
四、可配置参数
1、Feign 配置
Feign Client 全局默认配置
1feign:
2 client:
3 config:
4 default:
5 connectTimeout: 5000 #默认连接超时时间
6 readTimeout: 5000 #默认读取超时时间
7 loggerLevel: basic #默认日志级别
Feign Client 类单独配置
1feign:
2 client:
3 config:
4 <feignClassName>:
5 connectTimeout: 5000 #连接超时时间
6 readTimeout: 5000 #读取超时时间
7 loggerLevel: full #日志等级
Feign 请求/响应压缩配置
1# 请求开启压缩
2feign.compression.request.enabled=true
3# 响应开启压缩
4feign.compression.response.enabled=true
5# 什么数据类型才进行压缩操作
6feign.compression.request.mine-types=text/xml,application/xml,application/json
7# 请求压缩的大小下线
8feign.compression.request.min-request-size=2048
Feign 日志配置
在 Feign 中项目中会为每个 Feign 客户端创建一个记录器,默认情况下记录器的名称是接口的完整类名(包名+类名),并且 Feign 仅会记录 Debug 级别的日志。
(1)、Feign 记录器配置
配置 Feign 客户端记录器,且 Feign 仅仅记录 Debug 级别日志。
1#配置格式:logging.level.<包路径>.<类名>:DEBUG
2#例如本项目设置Feign 客户端日志如下:
3logging:
4 level:
5 club.mydlq.k8s.feign.HelloService: DEBUG
(2)、Feign 日志级别配置
日志配置器有四种日志级别,分别记录不同的日志信息,需要设置一个配置类配合配置文件来完成日志设置。
1@Configuration
2public class FeignConfig {
3
4 @Bean
5 Logger.Level feignLoggerLevel() {
6 return Logger.Level.FULL;
7 }
8
9}
- NONE:不记录日志(DEFAULT)。
- BASIC:仅记录请求方法和 URL 以及响应状态代码和执行时间。
- HEADERS:记录基本信息以及请求和响应 Headers 信息。
- FULL:记录请求和响应的 Headers、Body 和 Metadata 数据。
2、Hystrix 配置
开启 Hystrix
1#在Feign中开启Hystrix
2feign:
3 hystrix:
4 enabled: true
---END---
!版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。