Feign客户端调用工具

SpringCloud客户端,支持两种客户端调用工具

1、rest调用,基本不用

2、Feign客户端工具,以后在实际的开发中用的最多

Feign是一个声明式的Http客户端调用工具,采用接口 + 注解方式实现,易读性比较强。

使用

导入依赖

因为使用到了Feign的注解,所以导入相应的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在启动类开启Feign权限

@SpringBootApplication
@EnableFeignClients   // 开启Feign权限
public class JxOrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(JxOrderApplication.class, args);
    }
}

接口书写

  1. feign书写方式以SpringMVC接口形式书写
  2. 在对应接口上加上@FeignClient注解调用服务接口
    1. name:要调用的服务名称
  3. 想要使用服务相应的方法,只需要将对应方法抽象到接口中即可
@FeignClient(name = "jx-member")
public interface MemberAppFeign {

    /**
     * 使用member服务的getMember方法,只需要将对应的方法去掉方法体放在接口中即可
     */
    @GetMapping("/getMember")
    String getMember();
}

Feign客户端调用重构

目前feign客户端调用的缺点:我们在使用feign去调用对应的其他服务上的方法时,需要在本地写一个接口,里面声明对应的方法,当多个服务需要调用某个服务的方式时就需要写多个这个的接口,会非常麻烦,这就需要我们重构项目

重构项目目录

springcloud2.0-jx-parent依赖信息

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR2</spring-cloud.version>
    </properties>

    <dependencies>
        <!-- web starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- eureka client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!-- feign相关依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!-- lombok依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
依赖信息

springcloud2.0-jx-api-service  --> springcloud2.0-jx-api-service-member 下建立接口信息

注意,实体类要放在接口项目中,但是实现类在对应的接口实现类里面

/**
 * 实体类和定义的接口信息存放在接口项目里面
 * 代码的具体实现存放在接口实现类里面
 */
public interface IMemberService {

    @GetMapping("/getMember")
    User getMember(String name);
}

实体类

import lombok.Data;
@Data
public class User {
    private String name;
    private Integer age;
}

在实现类springcloud2.0-jx-member-impl 里面实现接口方法

import com.jx.aip.entity.User;
import com.jx.aip.service.IMemberService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MemberServiceImpl implements IMemberService {

    @Override
    @GetMapping("/getMember")
    public User getMember(String name) {
        User user = new User();
        user.setName(name);
        user.setAge(20);
        return user;
    }
}

写配置文件

server:
  port: 8001

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8100/eureka
spring:
  application:
    name: jx-member

启动类上标注@EnableEurekaClient@EnableFeignClients注解

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class AppMember {
    public static void main(String[] args) {
        SpringApplication.run(AppMember.class, args);
    }
}

启动项目,访问对应地址:

order项目(springcloud2.0-jx-order-impl)中远程rpc调用member服务

首先准备feign相关的接口,标注上@FeignClient注解

说明:因为在写Member实现类项目之前,建立了一个api项目,里面放的都是接口,而在其子项目中有一个member接口项目,里面写了相关接口,也就是上面的IMemberService,因此我们这个Feign接口只需要继承IMemberService 即可

import com.jx.aip.service.IMemberService;
import org.springframework.cloud.openfeign.FeignClient;

@FeignClient(name = "jx-member")
public interface MemberServiceFeign extends IMemberService {
}

在自动类上标注@EnableFeignClients@EnableEurekaClient注解

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class AppOrder {
    public static void main(String[] args) {
        SpringApplication.run(AppOrder.class, args);
    }
}

配置文件

server:
  port: 8002
spring:
  application:
    name: jx-order
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8100/eureka

最后在接口的实现类中实现远程调用

@Autowired
private MemberServiceFeign memberServiceFeign;


@Override
@RequestMapping(value = "/orderToMember")
public String orderToMember(String name) {
    User member = memberServiceFeign.getMember(name);
    Optional<User> member1 = Optional.ofNullable(member);
    return member1.orElseGet(User::new).toString();
}

当然在这之前要在order的接口项目中定义好相关接口

public interface IOrderService {

    @RequestMapping("/orderToMember")
    String orderToMember(String name);
}

之后访问order相关地址就可以实现远程调用

流程总结

Feign客户端超时时间设置

首先看下这个业务

定义一个common模块,放入服务响应的信息

ResponseBase

import lombok.Data;

@Data
public class ResponseBase {
    private Integer rtnCode;
    private String msg;
    private Object data;

    public ResponseBase() {
    }

    ResponseBase(Integer rtnCode, String msg, Object data) {
        this.rtnCode = rtnCode;
        this.msg = msg;
        this.data = data;
    }
}

BaseApiService

public class BaseApiService {
    public ResponseBase setResultError(Integer code, String msg) {
        return setResult(code, msg, null);
    }

    public ResponseBase setResultError(String msg) {
        return setResult(500, msg, null);
    }

    public ResponseBase setResultSuccess(Object data) {
        return setResult(200, "处理成功", data);
    }

    public ResponseBase setResultSuccess() {
        return setResult(200, "处理成功", null);
    }

    public ResponseBase setResultSuccess(String msg) {
        return setResult(200, msg, null);
    }

    private ResponseBase setResult(Integer code, String msg, Object data) {
        return new ResponseBase(code, msg, data);
    }
}

然后写这样一个逻辑

会员服务有一个方法,getUserInfo(),在这个方法里面首先阻塞1.5秒,然后返回成功信息,之后让订单服务远程调用这个方法

MemberServiceImpl

@RestController
public class MemberServiceImpl extends BaseApiService implements IMemberService {
    @Override
    @RequestMapping("/getUserInfo")
    public ResponseBase getUserInfo() {
        // 是会员服务接口产生1.5秒延迟
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return setResultSuccess("订单服务接口调用会员服务接口成功。。。");
    }
}

OrderServiceImpl

@RestController
public class OrderServiceImpl implements IOrderService {
    @Autowired
    private MemberServiceFeign memberServiceFeign;

    @Override
    @RequestMapping("/orderToMemberUserInfo")
    public ResponseBase orderToMemberUserInfo() {
        return memberServiceFeign.getUserInfo();
    }
}

最后访问

http://127.0.0.1:8002/orderToMemberUserInfo

出现了超时问题

解决问题,配置Feign客户端超时时间

上面的业务逻辑已经很清晰了,就是在订单服务使用Feign调用会员服务的getUserInfo()方法的时候,由于getUserInfo()长时间(1.5秒)未响应,导致报错,解决这一问题的方法就是设置Feign客户端的超时时间,将超时时间设置的长一些就不会报错了

在订单服务(使用了Feign客户端的服务)的application.yml中进行如下配置即可:

 

# 设置Feign客户端超时时间
ribbon:   # Feign客户端默认开启了ribbon的,所以在ribbon中开启即可
  # 表示建立连接之后,服务器读取到可用资源的时间
  ReadTimeout: 5000
  # 指建立连接所用的时间
  ConnectTimeout: 5000

 

设置之后再访问

成功!

posted @ 2018-12-07 12:43  Jin同学  阅读(888)  评论(2)    收藏  举报