1.准备

在spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端。我们可以使用JDK原生的URLConnection、Apache的Http Client、Netty的异步HTTP Client, Spring的RestTemplate。但是,用起来最方便、最优雅的还是要属Feign了。

Feign是一个声明式的Web Service客户端,它使得编写Web Serivce客户端变得更加简单。我们只需要使用Feign来创建一个接口并用注解来配置它既可完成。它具备可插拔的注解支持,包括Feign注解和JAX-RS注解。

Feign也支持可插拔的编码器和解码器。Spring Cloud为Feign增加了对Spring
MVC注解的支持,还整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现。

2.使用Feign实现客户端负载均衡的消费者

1.通过 http://start.spring.io/ 使用SPRING INITIALIZR 创建一个基础的Spring Boot工程,

  • Dependencies :Eureka Discovery + Feign
  • Group : com.alanzh.platform
  • Artifact : wish.feign.server
  • Spring boot 版本: 1.5.4
<?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.alanzh.platform</groupId>
    <artifactId>wish.feign.server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>wish.feign.server</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </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>Dalston.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <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>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</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>
</project>

@EnableFeignClients注解开启Feign功能

@EnableDiscoveryClient注解来添加发现服务能力。

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
定义wish.user.service服务的接口,具体如下:
@FeignClient(name="wish.user.service")
public interface UserFeignService {
    @RequestMapping(value="/user/{userEntryID}", method=RequestMethod.GET) 
    String getUserEntry(@PathVariable("userEntryID") Long userEntryID);

}

@FeignClient用于通知Feign组件对该接口进行代理(不需要编写接口实现),使用者可直接通过@Autowired注入。

value="/user/{userEntryID}"用来接收参数并传递参数到URI.

@PathVariable("userEntryID") Long userEntryID 请不要忽略这里的@PathVariable("userEntryID")

@FeignClient(name="wish.user.service")
注解来绑定该接口对应wish.user.service服务

创建Controller层调用wish.user.service服务的接口来消费wish.user.service服务.
@RestController
public class UserFeignAction {

    @Autowired
    UserFeignService userFeignService;

    @RequestMapping(value="/user/{userEntryID}", method=RequestMethod.GET) 
    public String getUserEntry(@PathVariable Long userEntryID) {
        // 通过HTTP调用远程服务
        return userFeignService.getUserEntry(userEntryID);
    }
}

application.properties中配置eureka服务注册中心

server.port=4000
spring.application.name=@project.name@
eureka.client.serviceUrl.defaultZone=http://localhost:1000/eureka/

启动应用,访问:http://localhost:4000/user/1

wish.user.service的两个服务提供方,分别输出了类似下面的日志内容:

2017-04-28 02:02:12.664  INFO 12912 --- [nio-2000-exec-4] c.a.p.controller.HelloWorldController    : /hello, host:ALAN-HOME, service_id:ms-hello-world-service,Hello name

到这里,我们已经通过Feign在客户端已经实现了对服务调用的均衡负载。

Spring Cloud应用在启动时,Feign会扫描标有@FeignClient注解的接口,生成代理,并注册到Spring容器中。生成代理时Feign会为每个接口方法创建一个RequetTemplate对象,该对象封装了HTTP请求需要的全部信息,请求参数名、请求方法等信息都是在这个过程中确定的,Feign的模板化就体现在这里。

齐活.