基于Spring Cloud集成OpenFeign应用feign客户端调用微服务

记录:387

场景:在基于Spring Cloud微服务架构,使用nacos实现微服务注册和配置,使用OpenFeign实现微服务之间Restful接口调用。

官网https://spring.io/projects/spring-cloud-openfeign

源码https://github.com/OpenFeign/feign

技术文档https://docs.spring.io/spring-cloud-openfeign/docs/

1.初始化准备

1.1准备nacos

Nacos版本:Nacos 2.1.1。

(1)启动和登录nacos

启动命令:sh startup.sh -m standalone

地址:http://127.0.0.1:8848/nacos

用户名/口令:nacos/nacos

(2)创建命名空间

命名空间ID:aa3eebb6-daa2-4db8-9a29-03dd8a17db15

命名空间名:hub

1.2创建Maven工程

使用IntelliJ IDEA创建Maven工程。

(1)微服务名称

名称:hub-example-302-feign

(2)微服务groupId和artifactId

groupId: com.hub

artifactId: hub-example-302-feign

(3)微服务核心模块版本

spring-boot 2.6.3
spring-cloud 2021.0.1
spring-cloud-alibaba 2021.0.1.0

2.修改pom.xml

修改pom.xml,引入项目依赖Jar和管理Jar包。

2.1修改pom.xml文件

(1)spring-cloud-openfeign依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

解析:引入sentinel,本例使用sentinel作为微服务熔断回调。

(2)nacos依赖

<!--nacos注册中心-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>2021.0.1.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!--nacos配置中心-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>2021.0.1.0</version>
    <exclusions>
      <exclusion>
          <groupId>com.alibaba.nacos</groupId>
          <artifactId>nacos-client</artifactId>
      </exclusion>
    </exclusions>
</dependency>

2.2全量pom.xml文件

全量pom.xml文件请参考附录:2.11.1全量pom.xml文件

3.创建bootstrap.yml文件和配置nacos

3.1创建bootstrap.yml

server:
  port: 18302
  servlet:
    context-path: /hub-302-feign
spring:
  application:
    name: hub-example-302-feign
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:18848
        username: nacos
        password: nacos
        namespace: aa3eebb6-daa2-4db8-9a29-03dd8a17db15
        group: DEFAULT_GROUP
      config:
        server-addr: 127.0.0.1:18848
        username: nacos
        password: nacos
        namespace: aa3eebb6-daa2-4db8-9a29-03dd8a17db15
        group: DEFAULT_GROUP
        file-extension: yaml
        shared-configs:
          - dataId: hub-example01-config.yml
            group: DEFAULT_GROUP
            refresh: true

3.2配置nacos

Data ID:hub-example01-config.yml

Group:DEFAULT_GROUP

配置格式:YAML

配置内容:

spring:
  main:
    allow-bean-definition-overriding: true
  jackson:
    time-zone: GMT+8
server:
    max-http-header-size: 51200
feign:
  client:
    config:
      default:
        connectTimeout: 20000
        readTimeout: 90000
  compression:
    request:
      enabled: true
    response:
      enabled: true
  sentinel:
    enabled: true
  httpclient:
    enabled: false
  okhttp:
    enabled: true

解析:feign.sentinel.enabled,必须为true,否则feign的FallbackFactory实现类不会生效。

4.创建启动类

4.1创建包

com.hub.example.domain:微服务使用到的DTO等实体类。

com.hub.example.controller:Controller类,发布Restful接口。

4.2启动类

包名:com.hub.example。

启动类:HubExampleFeignApplication。

(1)内容

@SpringBootApplication
@ComponentScan(basePackages = "com.hub.example.*")
@EnableFeignClients(basePackages = "com.hub.example.*")
public class HubExampleFeignApplication {
  public static void main(String[] args) {
    SpringApplication.run(HubExampleFeignApplication.class, args);
  }
}

(2)解析

@SpringBootApplication,SpringBoot标记启动类的注解。

@ComponentScan,扫描指定的包,将组件加载到IOC容器中。

@EnableFeignClients,扫描指定Feign接口。

5.编写Controller代码

5.1Controller代码

@RestController
@RequestMapping("/hub/example/city")
@RefreshScope
@Slf4j
public class CityController {
    @Autowired
    private CityService cityService;
    @PostMapping("/queryCityByCityId")
    public ResultObj<CityDTO> queryCityByCityId(String cityId) {
        return cityService.queryCityByCityId(cityId);
    }
}

6.编写Service代码

6.1接口

public interface CityService {
    ResultObj<CityDTO> queryCityByCityId(String cityId);
}

6.2实现类

@Service
public class CityServiceImpl implements CityService {
  @Autowired
  private CityFeignService cityFeignService;
  @Override
  public ResultObj<CityDTO> queryCityByCityId(String cityId) {
    return cityFeignService.queryCityByCityId(cityId);
  }
}

7.编写feign接口和回调类以及配置类代码

7.1编写feign接口

(1)代码

@FeignClient(contextId = "cityFeignService",
    value = "hub-example-301-nacos",
    fallbackFactory = CityFeignServiceFallbackFactory.class,
    configuration={FeignConfiguration.class})
public interface CityFeignService {
    @PostMapping("/hub-301-nacos/hub/example/city/queryCityByCityId")
    ResultObj<CityDTO> queryCityByCityId(String cityId);
}

(2)解析

@FeignClient,标记是Feign接口。contextId,指定接口唯一标识。value,指定使用Feign接口调用的微服务名称,必须在nacos已注册。fallbackFactory,指定Feign接口调用报错时的回调类。这个类在使用熔断和降级机制时,都会回调。configuration,指定当前微服务需要传递到下一个微服务的配置信息,比如请求头信息传递到下一个微服务。

@PostMapping("/hub-301-nacos/hub/example/city/queryCityByCityId"),指定调用目标微服务的全路径。

7.2编写回调类FallbackFactory

回调类继承feign的FallbackFactory接口,并实现方法。

@Slf4j
@Component
public class CityFeignServiceFallbackFactory implements FallbackFactory<CityFeignService> {
  @Override
  public CityFeignService create(Throwable cause) {
    log.info("调用FallbackFactory的create方法.");
    CityFeignServiceFallbackImpl impl = new CityFeignServiceFallbackImpl();
    impl.setThrowable(cause);
    return impl;
  }
}

7.3编写回调类实现类

回调实现类,实际就是实现feign接口,方法也是给回调类是使用。

@Slf4j
@Component
public class CityFeignServiceFallbackImpl implements CityFeignService {
  private Throwable throwable;
  @Override
  public ResultObj<CityDTO> queryCityByCityId(String cityId) {
    log.error("CityFeignService调用微服务失败.");
    this.throwable.printStackTrace();
    return ResultObj.fail("CityFeignService调用微服务失败.");
  }
  public void setThrowable(Throwable throwable) {
    this.throwable = throwable;
  }
}

7.4编写配置类

(1)RequestInterceptor接口实现代码

@Configuration
public class FeignConfiguration implements RequestInterceptor {
  @Override
  public void apply(RequestTemplate requestTemplate) {
    RequestAttributes reqAttributes = RequestContextHolder.currentRequestAttributes();
    HttpServletRequest request = ((ServletRequestAttributes) reqAttributes).getRequest();
    String cityCode = request.getHeader("cityCode");
    requestTemplate.header("cityCode", cityCode);
  }
}

(2)RequestInterceptor功能

编写配置类,就是实现feign的feign.RequestInterceptor接口。RequestInterceptor实际上就是一个拦截器。

功能就是使用feign接口调用微服务前,可以在请求对象中添加配置,比如添加请求头信息,传递到下一个微服务。

(3)RequestInterceptor功能和使用场景

场景:浏览器->微服务A->微服务B,调用路径中,请求头cityCode需传递到微服务A和微服务B。

默认情况下,从浏览器到微服务A的请求头信息,在微服务A调用微服务B时,请求头信息不会被传递微服务B,如需传递,就必须实现feign.RequestInterceptor接口。

8.支撑对象

8.1CityDTO

@Data
public class CityDTO implements Serializable {
  private Long cityId;
  private String cityName;
  private Double landArea;
  private Long population;
  private Double gross;
  private String cityDescribe;
  private String dataYear;
  @JsonFormat(
      pattern = "yyyy-MM-dd HH:mm:ss"
  )
  private Date updateTime;
}

8.2ResultObj

@Data
public class ResultObj<T> implements Serializable {
  private int code;
  private boolean success;
  private String msg;
  private T data;
  private ResultObj(int code, T data, String msg) {
    this.code = code;
    this.data = data;
this.msg = msg;
this.success = code == 200;
  }
  public static <T> ResultObj<T> data(int code, T data, String msg) {
    return new ResultObj<>(code, data, msg);
  }
}

9.使用Postman工具测试

使用Postman工具测试。

测试地址:http://127.0.0.1:18301/hub-301-nacos/hub/example/city/queryCityByCityId

设置请求头:cityCode=310001

测试入参:cityId=20230322

返回结果:

{
  "code": 200,
  "success": true,
  "msg": "执行成功",
  "data": {
    "cityId": 20230322,
    "cityName": "杭州",
    "landArea": null,
    "population": null,
    "gross": null,
    "cityDescribe": null,
    "dataYear": null,
    "updateTime": "2023-03-22 21:21:55"
  }
}

10.在微服务A和微服B中分别获取请求头方式

10.1注入HttpServletRequest

在Controller中注入HttpServletRequest对象。

@Autowired
private HttpServletRequest request;

10.2在方法中获取请求头

log.info("cityCode=" + request.getHeader("cityCode"));

11.附录

11.1全量pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
    <groupId>com.hub.example</groupId>
    <artifactId>hub-example-302-feign</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
    </parent>
    <description>集成feign框架应用</description>
    <packaging>jar</packaging>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring.boot.maven.plugin.version>2.6.3</spring.boot.maven.plugin.version>
        <spring.boot.version>2.6.3</spring.boot.version>
        <spring.cloud.version>2021.0.1</spring.cloud.version>
        <spring.cloud.alibaba.version>2021.0.1.0</spring.cloud.alibaba.version>
        <nacos.client.version>2.1.1</nacos.client.version>
        <lombok.version>1.18.24</lombok.version>
        <guava.version>30.1-jre</guava.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring.cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
        <!--nacos注册中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--nacos配置中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.alibaba.nacos</groupId>
                    <artifactId>nacos-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>${nacos.client.version}</version>
        </dependency>
        <!--feign配置-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>${guava.version}</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.boot.maven.plugin.version}</version>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

以上,感谢。

2023年3月22日

猜你喜欢

转载自blog.csdn.net/zhangbeizhen18/article/details/129720373