欢迎关注我的个人博客,关注最新动态: http://www.mydlq.club
相关博文:
系统环境:
Feign 是一个声明式的 Web Service 客户端,可以帮助我们更快捷、优雅地调用HTTP API。它的出现使开发 Web Service 客户端变得很简单,使用 Feign 只需要创建一个接口加上对应的注解就可以完成服务间的调用。
Spring Cloud Feign 帮助我们定义和实现依赖服务接口的定义。在Spring Cloud feign 的实现下,只需要创建一个接口并用注解方式配置它,即可完成服务提供方的接口绑定,简化了在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。
Feign 功能:
在分布式环境中,许多服务依赖项中的一些必然会失败。Hystrix 是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix 通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。
Hystrix 功能:
在 Kubernetes 环境中使用 SpringCloud 框架开发服务跟本地开发一样,服务间调用也是可用利用 Feign
完成服务间通信工作,保持和本地开发 SpringCloud 模式不变,这样也能减少学习成本。如果使用 SpringCloud Feign
还能配合 SpringCloud Sleuth
生成链路日志,然后配合 Zipkin
配合完成服务间链路追踪工作。
在 Kubernetes 环境下使用 Feign
还有一个问题是,现在已经将 Eureka
等注册中心去掉了,且在 Kubernetes 中所有的 Service
信息都会通过 Kubernetes 存入 Etcd
中,且每个服务都会通过 CoreDNS
分配一个以 服务名称
命名的不会重复域名,所以我们可以在开发项目过程中,可以通过配置 Feign
使用 域名
+ 端口号
方式完成服务间的调用,如下图所示:
这里写三个项目,分别是 提供者
项目 与 消费者
项目 与两个服务都引用的 接口类
项目,开发模式跟在本地使用 SpringCloud 框架开发服务一样,消费者
调用 提供者
,且两者都引用 接口项目
,其中提供者实现 接口项目
的 interface
的实现,消费者调通过 Feign
引入 interface
接口。这两个服务创建完成后将其部署到 Kubernetes 环境下,通过 NodePort
方式暴露端口供外部访问,然就外部调用 消费者
服务来测试是否能成功调用 提供者
服务提供的接口。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>club.mydlqgroupId>
<artifactId>service-feign-interfaceartifactId>
<version>0.0.1version>
<name>service-feign-interfacename>
<description>service feign interfacedescription>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>2.1.6.RELEASEversion>
dependency>
dependencies>
project>
import org.springframework.web.bind.annotation.*;
public interface UserInterface {
/**
* 获取测试信息
* @return
*/
@GetMapping()
public String getInfo();
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.6.RELEASEversion>
<relativePath/>
parent>
<groupId>club.mydlqgroupId>
<artifactId>service-providerartifactId>
<version>0.0.1version>
<name>service-providername>
<description>service providerdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>club.mydlqgroupId>
<artifactId>service-feign-interfaceartifactId>
<version>0.0.1version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
import club.mydlq.feign.TestInterface;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController implements TestInterface {
@Override
public String getInfo() {
return "Hello World!";
}
}
spring:
application:
name: service-provider
server:
port: 8080
management:
server:
port: 8081
endpoints:
web:
exposure:
include: "*"
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.6.RELEASEversion>
<relativePath/>
parent>
<groupId>club.mydlqgroupId>
<artifactId>service-customerartifactId>
<version>0.0.1version>
<name>service-customername>
<description>service customerdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>club.mydlqgroupId>
<artifactId>service-feign-interfaceartifactId>
<version>0.0.1version>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
<version>2.1.2.RELEASEversion>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
import club.mydlq.feign.TestInterface;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(name = "http://service-provider:8080", url = "http://service-provider:8080", fallback = TestFallback.class)
public interface TestService extends TestInterface {
}
import org.springframework.stereotype.Component;
@Component
public class TestFallback implements TestService {
@Override
public String getInfo() {
return "fallback!";
}
}
import club.mydlq.k8s.feign.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
public class TestController {
@Autowired
private TestService testService;
@GetMapping("/")
public String getTestInfo(){
return testService.getInfo();
}
}
spring:
application:
name: service-customer
server:
port: 8080
management:
server:
port: 8081
endpoints:
web:
exposure:
include: "*"
#开启 Hystrix
feign:
hystrix:
enabled: true
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#Manve 编译 service-feign-interface 项目
$ cd service-feign-interface
$ mvn clean install
#Manve 编译 service-provider 项目
$ cd service-provider
$ mvn clean install
#Manve 编译 service-customer 项目
$ cd service-customer
$ mvn clean install
Dockerfile
FROM openjdk:8u212-b04-jre-slim
VOLUME /tmp
ADD target/*.jar app.jar
RUN sh -c 'touch /app.jar'
ENV JAVA_OPTS="-Duser.timezone=Asia/Shanghai"
ENV APP_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ]
#构建 service-provider 镜像
$ docker build -t mydlqclub/service-provider:0.0.1 .
#构建 service-customer 镜像
$ docker build -t mydlqclub/service-customer:0.0.1 .
service-provider.yaml
apiVersion: v1
kind: Service
metadata:
name: service-provider
spec:
type: NodePort
ports:
- name: server
nodePort: 31001
port: 8080
targetPort: 8080
- name: management
nodePort: 31002
port: 8081
targetPort: 8081
selector:
app: service-provider
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-provider
labels:
app: service-provider
spec:
replicas: 1
selector:
matchLabels:
app: service-provider
template:
metadata:
name: service-provider
labels:
app: service-provider
spec:
restartPolicy: Always
containers:
- name: service-provider
image: registry.cn-beijing.aliyuncs.com/mydlq/service-provider:0.0.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: server
- containerPort: 8081
name: management
resources:
limits:
memory: 1000Mi
cpu: 1000m
requests:
memory: 500Mi
cpu: 500m
service-customer.yaml
apiVersion: v1
kind: Service
metadata:
name: service-customer
spec:
type: NodePort
ports:
- name: server
nodePort: 31003
port: 8080
targetPort: 8080
- name: management
nodePort: 31004
port: 8081
targetPort: 8081
selector:
app: service-customer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-customer
labels:
app: service-customer
spec:
replicas: 1
selector:
matchLabels:
app: service-customer
template:
metadata:
name: service-customer
labels:
app: service-customer
spec:
restartPolicy: Always
containers:
- name: service-customer
image: registry.cn-beijing.aliyuncs.com/mydlq/service-customer:0.0.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: server
- containerPort: 8081
name: management
resources:
limits:
memory: 1000Mi
cpu: 1000m
requests:
memory: 500Mi
cpu: 500m
执行 Kubectl 部署命将项目部署到 Kubernetes。
#创建RBAC
$ kubectl apply -f service-rbac.yaml -n mydlqcloud
#创建service-customer
$ kubectl apply -f service-customer.yaml -n mydlqcloud
#创建service-provider
$ kubectl apply -f service-provider.yaml -n mydlqcloud
输入地址:http://192.168.2.11:31003 测试 service-customer
服务是否能正常通过 Feign
调用 service-provider
服务。
结果:
Hello World!
将 Kubernetes 中 service-provider 关掉来测试 fallback,再次输入地址:http://192.168.2.11:31003
结果:
fallback!
由上面结果可知,在 Kubernetes 环境下通过 SpringCloud Feign 根据 服务名称
+ 端口号
这种方式,完成服务间的通信是可行的。
feign:
client:
config:
default:
connectTimeout: 5000 #默认连接超时时间
readTimeout: 5000 #默认读取超时时间
loggerLevel: basic #默认日志级别
feign:
client:
config:
:
connectTimeout: 5000 #连接超时时间
readTimeout: 5000 #读取超时时间
loggerLevel: full #日志等级
# 请求开启压缩
feign.compression.request.enabled=true
# 响应开启压缩
feign.compression.response.enabled=true
# 什么数据类型才进行压缩操作
feign.compression.request.mine-types=text/xml,application/xml,application/json
# 请求压缩的大小下线
feign.compression.request.min-request-size=2048
在 Feign 中项目中会为每个 Feign 客户端创建一个记录器,默认情况下记录器的名称是接口的完整类名(包名+类名),并且 Feign 仅会记录 Debug 级别的日志。
(1)、Feign 记录器配置
配置 Feign 客户端记录器,且 Feign 仅仅记录 Debug 级别日志。
#配置格式:logging.level.<包路径>.<类名>:DEBUG
#例如本项目设置Feign 客户端日志如下:
logging:
level:
club.mydlq.k8s.feign.HelloService: DEBUG
(2)、Feign 日志级别配置
日志配置器有四种日志级别,分别记录不同的日志信息,需要设置一个配置类配合配置文件来完成日志设置。
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
#在Feign中开启Hystrix
feign:
hystrix:
enabled: true
—END—
欢迎关注我的个人博客,关注最新动态: http://www.mydlq.club