Spring Cloud 教程第五章 Feign 的声明式服务调用与负载均衡

JAVA herman 413浏览 0评论

网上很少有人把 Feign 单独作为一章来写,但我认为 Feign 也很重要,使用好 Feign 能更好的理解 RESTFUL 接口设计规范。如果想要用一篇文章来写好 Feign 我认为是很难的,我尽量用一篇文章来描述清楚。

Feign 是一个声明式 Web Service 客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud 对 Feign 进行了封装,使其支持了 Spring MVC 标准注解和 HttpMessageConverters。Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡。

在 Spring Cloud 中 Feign 是用作服务调用和客户端负载均衡的。Feign 可以配合 Spring Cloud 使用,也可以单独使用。

在不使用 Feign 之前,我们调用其它服务需要用到 httpclient 或者 okhttp 这样相对较重的框架,而且需要编写大量的代码。虽然 spring 中 RestTemplate 也可以灵活的使用,但是它不具备负载均衡的功能,需要我们自己实现;另外 RestTemplate 对配置化,支持的还不够友好,所以 Feign 应运而生。

我们先来看看 Feign 的单独使用。首先,需要导入 Maven 的相关配置:

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.sourceEncoding>UTF-8</project.reporting.sourceEncoding>
    <java.version>1.7</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.8.2</version>
    </dependency>

    <dependency>
        <groupId>com.netflix.feign</groupId>
        <artifactId>feign-core</artifactId>
        <version>8.18.0</version>
    </dependency>

    <dependency>
        <groupId>com.netflix.feign</groupId>
        <artifactId>feign-gson</artifactId>
        <version>8.18.0</version>
    </dependency>
</dependencies>

然后自定义一个远程调用接口,代码如下:

public interface GitHub {
    @RequestLine("GET /repos/{owner}/{repo}/contributors")
    List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}

上面的 Contributor 类代码如下:

public class Contributor {
    String login;
    int contributions;
}

然后对 Feign 的远程调用功能进行测试,代码如下:

public class FeignTest {
    public static void main(String... args) {
        //www.xttblog.com
        GitHub github = Feign.builder()
                .decoder(new GsonDecoder())
                .target(GitHub.class, "https://api.github.com");

        // Fetch and print a list of the contributors to this library.
        List<Contributor> contributors = github.contributors("OpenFeign", "feign");
        for (Contributor contributor : contributors) {
            System.out.println(contributor.login + " (" + contributor.contributions + ")");
        }
    }
}

运行后,输出的结果我就不写了,大家可以自行的去测试。相关代码我已上传到 https://github.com/xmt1139057136/xttblog-cloud/xttblog-feign-github

上面的案例,我们主要是对 Feign 有了一个简单的认识,在 Spring Cloud 中并不会这样的去使用。下面我们来看看在 Spring Cloud 中如何使用 Feign。

首先创建一个 xttblog-cloud-producer 项目。pom.xml 配置如下:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.RELEASE</version>
    </parent>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Edgware.SR4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>

application.yml 配置文件内容如下:

server:
  port: 8888

eureka:
  instance:
    hostname: localhost
  client:
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:8761/eureka/

spring:
  application:
    name: xttblog-cloud-producer

然后编写一个服务提供者的 Controller,代码如下:

@RestController
@RequestMapping("/cal")
public class CalController {
    @RequestMapping("/add")
    public Integer add(@RequestParam Integer a, @RequestParam Integer b){
        return a + b;
    }
}

启动类的代码没有大的改动,具体如下:

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

至此,服务提供者我们已经写完了,下面我们来编写服务调用着代码。同样的先新建一个 xttblog-cloud-consumer 工程。pom.xml 代码如下:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.RELEASE</version>
    </parent>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Edgware.SR4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

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

接着是 application.yml 文件的代码:

server:
  port: 7777

eureka:
  instance:
    hostname: localhost
  client:
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:8761/eureka/

spring:
  application:
    name: xttblog-cloud-consumer

再接着是消费者的 Controller 代码:

@RestController
@RequestMapping("/test")
public class ConsumerController {

    @Autowired
    private CalService calService;

    @Autowired
    private CalRemoteService calRemoteService;

    @RequestMapping("/add")
    public String add(@RequestParam Integer a, @RequestParam Integer b){
        return a + "+" + b + "的RestTemplate调用结果为:" + calService.add(a, b);
    }

    @RequestMapping("/radd")
    public String rAdd(@RequestParam Integer a, @RequestParam Integer b){
        return a + "+" + b + "的Cloud调用结果为:" + calRemoteService.add(a, b);
    }
}

然后是 Feign 的远程调用的代码:

@FeignClient(name= "xttblog-cloud-producer")
public interface CalRemoteService {
    @RequestMapping(value = "/cal/add")
    public String add(@RequestParam(value="a") Integer a, @RequestParam("b") Integer b);
}
public interface CalService {
    public String add(Integer a, Integer b);
}
@Service
public class CalServiceImpl implements CalService{

    @Autowired
    private RestTemplate restTemplate;

    @Bean
    //@LoadBalanced//启用ribbon负载均衡调用服务
    public RestTemplate cteateRestTemplate(){
        return new RestTemplate();
    }

    @Override
    public String add(Integer a, Integer b) {
        String url = "http://localhost:8888/cal/add?a=" + a + "&b=" + b;
        ResponseEntity responseEntity = restTemplate.getForEntity(url,String.class);
        return responseEntity.getBody().toString();
    }
}

我这里有两个 Service 类,一个是使用 RestTemplate 调用服务,一个是 Feign 来调用;让大家比较一下两者的调用方式。

在使用 @FeignClient 注解后,必须在主启动类上加上 @EnableFeignClients 注解,否则调用不会成功。

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

另外需要注意的是,在服务消费方必须引入 Feign 的依赖,否则启动时会报错。

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

那有人会问了,为什么我引入注解时不报错,而是在运行时才报错呢?那是因为我们都是面向接口编程的。引入注解,并没有引入 Feign 的实现,所以会报错。

上面两个服务,我们都注册到了 Eureka,用的是 Spring Cloud 教程系列第二章的服务注册工程《Spring Cloud 教程第二章 服务的注册、发现、治理之 Eureka 实战》,具体可以到我的 github 上查看源码:https://github.com/xmt1139057136/xttblog-cloud/tree/master/xttblog-eureka-server

写完上面的代码后,我们依次启动,xttblog-eureka-server、xttblog-cloud-producer、xttblog-cloud-consumer。然后浏览器地址栏里输入 http://localhost:7777/test/add?a=1&b=2 和 http://localhost:7777/test/radd?a=1&b=3 即可看到运行效果。

最后本文涉及的相关源码都已上传到:https://github.com/xmt1139057136/xttblog-cloud,喜欢的可以给个 star。

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加QQ1群:135430763(2000人群已满),QQ2群:454796847,QQ3群:187424846。QQ群进群密码:xttblog,想加微信群的朋友,可以微信搜索:xmtxtt,备注:“xttblog”,添加助理微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

本文原文出处:业余草: » Spring Cloud 教程第五章 Feign 的声明式服务调用与负载均衡