首页 > 技术文章 > spring cloud中feign的使用

scuury 2019-03-11 16:54 原文

  我们在进行微服务项目的开发的时候,经常会遇到一个问题,比如A服务是一个针对用户的服务,里面有用户的增删改查的接口和方法,而现在我有一个针对产品的服务B服务中有一个查找用户的需求,这个时候我们可以在B服务里再写一个查找用户的接口,可是就为了一个接口就得从控制层到持久层都写一遍怎么看都不值当,最关键的是这个接口在别的服务里面还有,这就更不应该做了,所以springCloud提供了服务调用的方法——feign。

  由于之前写的都是springboot的例子,所以一直都只有一个服务,这次既然是服务调用,必然有一个服务提供者和消费者,如果要让这两个服务联系起来,就得用到springCloud的那一套东西,所以意味着我们还得要一个服务注册中心eureka,先把eureka注册中心的代码贴一下。

启动类EurekaApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

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

相比之前的springboot启动类,这里多了一个@EnableEurekaServer注解,此注解的作用就是启用一个服务注册中心以提供给其他服务进行对话。

配置文件application.properties

#服务注册中心实例的主机名
eureka.instance.hostname=localhost
#是否向服务注册中心注册自己
eureka.client.register-with-eureka=false
#是否检索服务
eureka.client.fetch-registry=false
#服务注册中心的配置内容,指定服务注册中心的位置
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
#服务注册中心端口号
server.port=8763

每个配置项上面注释都解释了,其中第二个注解如果选择true的话,在注册中心启动后便会存在一个服务即本身,但大部分情况都是写的fasle

依赖pom.xml

<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.fh.msDemo</groupId>
  <artifactId>service-eureka</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

把以上三个文件在相应位置建好之后,把服务一起输入“主机名”+“端口”即可访问注册中心,按照我上面的配置输入的是localhost:8763或者127.0.0.1:8763

效果如下:

因为我默认了不注册自己,所以目前没有实例。

好了,再来看一下两个业务服务,首先是服务提供者用户服务service-user,这里为了简单起见,只在controller中模拟查询的业务场景

启动类UserApplication.java

package com.fc.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

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

 可以看到和springboot项目不同的是这个服务多了一个注解@EnableEurekaClient,该注解的作用就是以eureka作为服务注册中心将服务注册上去。

userController.java

package com.fc.demo.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fc.demo.pojo.User;
@RestController
public class UserController {
    
    @RequestMapping("/user/list")
    public List<User> getUser() {
      List<User> users = new ArrayList<>();
      users.add(new User(1, "小明", 13));
      users.add(new User(2, "小华", 15));
      return users;
    }
}

配置文件application.properties

#服务注册中心的地址
eureka.client.service-url.defaultZone=http://localhost:8763/eureka/
#服务端口号
server.port=8768
#服务名称
spring.application.name=user

最后是服务消费者产品服务service-product

启动类ProductApplication.java

package com.fc.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignClient;

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

与上一个服务相比,此服务又多了一个注解@EnableFeignClients,该注解的作用是:告诉框架扫描所有通过注解@Feignclient定义的feign客户端,也就是说如果不加这个注解,框架将找不到所有的feign客户端。

UserFeign.java

package com.fc.demo.feign;

import java.util.List;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.feign.FeignClientsConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import com.fc.demo.pojo.User;

@FeignClient(value="user",configuration =
{FeignClientsConfiguration.class})
public interface UserFeign {

  @RequestMapping("/user/list")
  public List<User> getAllUser();
}

只要@FeignClient注解中的value值和服务提供者的服务名保持一致即可。

ProductController.java

package com.fc.demo.controller;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fc.demo.feign.UserFeign;
import com.fc.demo.pojo.User;
@RestController
public class ProductController {

  @Autowired
  private UserFeign userFeign;
  @RequestMapping("user/list")
  public List<User> showUser() {
    return userFeign.getAllUser();
  }
}

前面也提到了@EnableFeignClients注解将所有的feign客户端注入了容器中,直接使用自动装配的注解的便可使用feign客户端

配置文件application.properties

#服务注册中心的地址
eureka.client.service-url.defaultZone=http://localhost:8763/eureka/
#服务端口号
server.port=8767
#服务名称
spring.application.name=product

把三个服务同时跑起来,注册中心截图如下

显示两个服务已经注册成功了。接下来在浏览器中输入http://localhost:8767/user/list,成功返回json串

此外,feign还具有负载均衡的功能,修改user服务的controller,新增一个返回端口号的方法

package com.fc.demo.controller;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fc.demo.pojo.User;
@RestController
public class UserController {
  
    @Value("${server.port}")
    private String port;    //获取配置文件中的端口号
    
    /**获取用户列表*/
    @RequestMapping("/user/list")
    public List<User> getUser() {
      List<User> users = new ArrayList<>();
      users.add(new User(1, "小明", 13));
      users.add(new User(2, "小华", 15));
      return users;
    }
    /**获取服务端口号*/
     @RequestMapping("/user/port")
     public String getPort() {
       return port;
     }
}

在product服务里面的UserFeign接口和controller中增加相应的方法

package com.fc.demo.feign;

import java.util.List;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.feign.FeignClientsConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import com.fc.demo.pojo.User;

@FeignClient(value="user",configuration =
{FeignClientsConfiguration.class})
public interface UserFeign {
  /**获取用户列表*/
  @RequestMapping("user/list")
  public List<User> getAllUser();
  
  /**获取服务端口号*/
  @RequestMapping("user/port")
  public String getPort() ;
}
package com.fc.demo.controller;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fc.demo.feign.UserFeign;
import com.fc.demo.pojo.User;
@RestController
public class ProductController {

  @Autowired
  private UserFeign userFeign;

  /**获取用户列表*/
  @RequestMapping("user/list")
  public List<User> showUser() {
    return userFeign.getAllUser();
  }
  /**获取服务端口号*/
  @RequestMapping("/user/port")
  public String getPort() {
    return "调用的服务端口号为"+userFeign.getPort();
  }
}

把服务跑起来以后,修改user服务配置文件中的端口号改为8769,再次跑一遍,这时eureka上显示user服务有两个实例,端口号分别为8767和8769,多次刷新浏览器

 效果基本就是这样,采用轮询机制调用两个user实例。

总结

feign的基本用法就是这样,目前本人对于springcloud和feign的了解也仅限于此,只知道基本的用法,对于其原理机制一概不知,所以还得继续深究下去,若有收获则会完善该系列后续的博客。

推荐阅读