转载

一起来学SpringCloud之-服务消费者(Feign-上)

上一篇文章,讲述了Ribbon去做负载请求的服务消费者,本章讲述声明性REST客户端:Feign的简单使用方式

- Feign简介

Feign是一个声明式的Web服务客户端。这使得Web服务客户端的写入更加方便 。它具有可插拔注释支持,包括Feign注释和JAX-RS注释。Feign还支持可插拔编码器和解码器。Spring Cloud增加了对Spring MVC注释的支持,并HttpMessageConverters在Spring Web中使用了默认使用的相同方式。Spring Cloud集成了Ribbon和Eureka,在使用Feign时提供负载平衡的http客户端。

官方文档: http://cloud.spring.io/spring-cloud-static/Dalston.SR2/#spring-cloud-feign

- 准备工作

1.启动Consul,所有文章都将以Consul作为服务注册中心

2.创建 battcn-feign-hellobattcn-feign-hi

battcn-feign-hello

- 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.battcn</groupId>
    <artifactId>battcn-feign-hello</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>battcn-feign-hello</name>
    <description>Feign请求</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.SR2</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</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>

- BattcnFeignHelloApplication.java

package com.battcn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class BattcnFeignHelloApplication {

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

创建一个打招呼的的Controller

- HelloController.java

package com.battcn.controller;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/hello")
public class HelloController {

    @ResponseStatus(HttpStatus.OK)
    @GetMapping
    public String findStudentByName(@RequestParam("name") String name) {
        // TODO:不做具体代码实现,只打印Log
        return "挽歌- My Name's" + name;
    }

}

- bootstrap.yml

server:
  port: 8765

spring:
  application:
    name: battcn-feign-hello
  cloud:
    consul:
      host: localhost
      port: 8500
      enabled: true
      discovery:
        enabled: true
        prefer-ip-address: true

访问: http://localhost:8765/hello?name=Levin

结果: 挽歌- My Name's Levin 表示我们第一个服务运行一切正常

battcn-feign-hi

- 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.battcn</groupId>
    <artifactId>battcn-feign-hi</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>battcn-feign-hi</name>
    <description>Feign请求</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.SR2</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</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>

- BattcnFeignHiApplication.java

package com.battcn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"com.battcn.client"})
@SpringBootApplication
public class BattcnFeignHiApplication {

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

- HelloClient.java

创建一个声明式 FeignClient 的接口 HelloClient

package com.battcn.client;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * @author Levin
 * @date 2017-07-28.
 */
@FeignClient(name = "battcn-feign-hello")
public interface HelloClient {

    /** 
     * 在新版本中,Feign 已经可以解析 RestFul 标准的接口API,比如GET POST DELETE PATCH PUT 
     * 旧版中
     * @RequestMapping(method = RequestMethod.GET, value = "/hello")
     * 或者是
     * @RequestMapping(method = RequestMethod.POST, value = "/hello")
     * 
     * 早期文章:http://blog.battcn.com/2017/07/07/springcloud-feign-analysis/
     * /
    @ResponseStatus(HttpStatus.OK)
    @GetMapping("/hello")
    String findStudentByName(@RequestParam("name") String name);
}

- HiController.java

package com.battcn.controller;

import com.battcn.client.HelloClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/hi")
public class HiController {

    @Autowired
    HelloClient helloClient;

    @ResponseStatus(HttpStatus.OK)
    @GetMapping
    public String find(@RequestParam("name") String name) {
        // TODO:只是演示Feign调用的方法
        return "Hi," + helloClient.findStudentByName(name);
    }

}

- bootstrap.yml

server:
  port: 8766

spring:
  application:
    name: battcn-feign-hi
  cloud:
    consul:
      host: localhost
      port: 8500
      enabled: true
      discovery:
        enabled: true
        prefer-ip-address: true

访问: http://localhost:8766/hi?name=Levin

结果: Hi,挽歌- My Name'sLevin 表示第二个服务(hi)通过FeignClient调用服务(hello)一切正常

- 源码分析

org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(Object... args) ,该方法的 args 就是我们传递的请求参数

一起来学SpringCloud之-服务消费者(Feign-上)

feign.Target -> HardCodedTarget 解析出 FeignClient中的 name 形成VIP模式 GET http://battcn-feign-hello/hello?name=Levin HTTP/1.1 然后发送请求

一起来学SpringCloud之-服务消费者(Feign-上)

feign.SynchronousMethodHandler -> this.client.execute(request, this.options);

第一个就是: feign.Request 的一些信息,比如 headermethodbodyurl 等一些基本属性,因为这里是feign的Request所以我们servlet中的请求头是无法传递过来的(下篇会讲述这写小技巧)

第二个就是: connectTimeoutMillisreadTimeoutMillis 很多人肯定不陌生

一起来学SpringCloud之-服务消费者(Feign-上)

通过三面三个步骤,我们不难看出 Feign 就是解析注解然后发送HTTP(阻断器,OKHTTP模式留到下篇),有兴趣的可以自己Debug(如果该处有错误也希望各位指正)

- 说点什么

全文代码: http://git.oschina.net/battcn/battcn-cloud 本章教程过于简单,因此代码未提交GIT(完整代码都在文章里),自行复制即可,如有问题请及时与我联系

个人QQ:1837307557

Spring Cloud中国社区①:415028731

Spring For All 社区⑤:157525002

欢迎一起讨论与交流

转载标明出处,thanks

谢谢你请我吃糖果

一起来学SpringCloud之-服务消费者(Feign-上) 支付宝

一起来学SpringCloud之-服务消费者(Feign-上) 微信

原文  http://blog.battcn.com/2017/07/28/spring-cloud-feign-up/
正文到此结束
Loading...