在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端。Feign就是Spring Cloud提供的一种声明式REST客户端。可以通过Feign访问调用远端微服务提供的REST接口。现在我们就用Feign来调用SERVICE-HELLOWORLD暴露的REST接口,以获取到“Hello World”信息。在使用Feign时,Spring Cloud集成了Ribbon和Eureka来提供HTTP客户端的负载均衡。

下面我们就采用Feign的方式来调用服务集群。

1. 创建Maven工程,加入spring-cloud-starter-feign依赖

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

完整的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>
<parent>
<groupId>com.pupeiyuan.springcloud</groupId>
<artifactId>spring-Cloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>springcloud-moveServer</artifactId> <dependencies>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- 支持springWEB web支持: 1、web mvc; 2、restful; 3、jackjson支持; 4、aop ........ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- AOP依赖模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.7</version>
</dependency>
<!-- jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency> <!-- Mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!-- 通用Mapper -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>RELEASE</version>
</dependency>
<!-- 分页助手 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency> <!-- mysql 数据库驱动. -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<!-- jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- JSP相关 -->
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope>
</dependency> <!-- spring cloud -->
<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.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies> <build>
<plugins>
<!-- java编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 这是spring boot devtool plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--fork : 如果没有该项配置,肯呢个devtools不会起作用,即应用不会restart -->
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
</project>

2. 创建启动类,需呀加上@EnableFeignClients注解以使用Feign, 使用@EnableDiscoveryClient开启服务自动发现

mainApplication.java

package com.pupeiyuan.config;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.client.RestTemplate; import feign.Logger; @Configuration
//扫描bean
@ComponentScan(basePackages = "com.pupeiyuan.*")
//不用自动配置数据源
@EnableDiscoveryClient
@SpringBootApplication(exclude=DataSourceAutoConfiguration.class)
@EnableFeignClients(basePackages="com.pupeiyuan.feignClient")
public class MainApplication extends SpringBootServletInitializer { @Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
//相当于xml中的bean标签 用于调用当前方法获取到指定的对象
@Bean(name="remoteRestTemplate")
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(MainApplication.class);
}
}

3. 添加配置文件application-test.properties, 使用端口8085, 名字定义为springcloud-moveServer, 并注册到eureka服务中心

application-test.properties

server.port=8085
server.servlet-path=/ #log4j
#logging.level.org.springframework=DEBUG #SpringMVC
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp #mybatis && Mapper
mybatis.configuration.mapUnderscoreToCamelCase=true
mapper.mappers=com.karle.tk.TkMapper
mapper.identity=MYSQL #banner
banner.charset= UTF-8 #jsp
server.jsp-servlet.init-parameters.development=true # pagehelper properties
pagehelper.offsetAsPageNum=true
pagehelper.rowBoundsWithCount=true
pagehelper.pageSizeZero=true
pagehelper.reasonable=false
pagehelper.params=pageNum=pageHelperStart;pageSize=pageHelperRows;
pagehelper.supportMethodsArguments=false #mq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest management.security.enabled=false
logging.level.com.pupeiyuan.feignClient.FeignClient2=DEBUG

bootstrap.properties

spring.application.name=moveServer
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=pringcloud-configServer
spring.cloud.config.profile=test
spring.cloud.config.label=master eureka.client.serviceUrl.defaultZone=http://root:123456@peer1:8000/eureka
eureka.instance.prefer-ip-address=true

4. 定义Feign:一个用@FeignClient注解的接口类,UserFeignClient.java

package com.pupeiyuan.feignClient;

import java.util.List;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import com.pupeiyuan.bean.NhReportStatusHistory; @FeignClient("MULTIPLE")
public interface UserFeignClient { @RequestMapping(value = "/getDate/{id}", method = RequestMethod.GET)
public List<NhReportStatusHistory> dataList(@PathVariable("id") Long id); // 两个坑:1. @GetMapping不支持 2. @PathVariable得设置value /*@RequestMapping(value = "/user", method = RequestMethod.POST)
public User postUser(@RequestBody User user); // 该请求不会成功,只要参数是复杂对象,即使指定了是GET方法,feign依然会以POST方法进行发送请求。可能是我没找到相应的注解或使用方法错误。
// 如勘误,请@lilizhou2008 eacdy0000@126.com
@RequestMapping(value = "/get-user", method = RequestMethod.GET)
public User getUser(User user);*/
}

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

5. 定义一个WebController。

注入之前通过@FeignClient定义生成的bean,

package com.pupeiyuan.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.pupeiyuan.bean.NhReportStatusHistory;
import com.pupeiyuan.feignClient.FeignClient2;
import com.pupeiyuan.feignClient.UserFeignClient; @RestController
public class FeignClientController { @Autowired
private UserFeignClient userFeignClient; @GetMapping("/movie/{id}")
public List<NhReportStatusHistory> list(@PathVariable Long id) {
return this.userFeignClient.dataList(id);
}
}

6. 被调用端的WebController--被代理的方法实现

package com.pupeiyuan.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController; import com.github.pagehelper.PageHelper;
import com.pupeiyuan.bean.NhReportStatusHistory;
import com.pupeiyuan.core.DataSourceKey;
import com.pupeiyuan.core.DynamicDataSourceContextHolder;
import com.pupeiyuan.services.NhReportService; @RestController
@RefreshScope
public class ConfigClientController { //services层注入
@Autowired NhReportService nhReportService; @GetMapping("/getDate/{id}")
public List<NhReportStatusHistory> getDate(@PathVariable Long id) {
//参数容器
Map<String, Object> params = new HashMap<String, Object>();
PageHelper.startPage(1, 2);
DynamicDataSourceContextHolder.set(DataSourceKey.DB_SLAVE1);
List<NhReportStatusHistory> findList = nhReportService.findList(params);
return findList;
}
}

7. 启动Feign应用, 访问http://localhost:8085/movie/1, 多次刷新,可以看到和前一章Ribbon里面的应用一样, 两个Hello World服务的输出交替出现。说明通过Feign访问服务, Spring Cloud已经缺省使用了Ribbon负载均衡。

8. 在Feign中使用Apache HTTP Client

Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址gwai会保持一个长连接,即利用HTTP的persistence connection 。我们可以用Apache的HTTP Client替换Feign原始的http client, 从而获取连接池、超时时间等与性能息息相关的控制能力。Spring Cloud从Brixtion.SR5版本开始支持这种替换,首先在项目中声明Apache HTTP Client和feign-httpclient依赖:

  <!-- 使用Apache HttpClient替换Feign原生httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>${feign-httpclient}</version>
</dependency>
然后在application.properties中添加:
feign.httpclient.enabled=true

7. Feign的Encoder、Decoder和ErrorDecoder

Feign将方法签名中方法参数对象序列化为请求参数放到HTTP请求中的过程,是由编码器(Encoder)完成的。同理,将HTTP响应数据反序列化为Java对象是由解码器(Decoder)完成的。默认情况下,Feign会将标有@RequestParam注解的参数转换成字符串添加到URL中,将没有注解的参数通过Jackson转换成json放到请求体中。注意,如果在@RequetMapping中的method将请求方式指定为POST,那么所有未标注解的参数将会被忽略,例如:

@RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET)
void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName, DataObject obj);

此时因为声明的是GET请求没有请求体,所以obj参数就会被忽略。

在Spring Cloud环境下,Feign的Encoder只会用来编码没有添加注解的参数。如果你自定义了Encoder, 那么只有在编码obj参数时才会调用你的Encoder。对于Decoder, 默认会委托给SpringMVC中的MappingJackson2HttpMessageConverter类进行解码。只有当状态码不在200 ~ 300之间时ErrorDecoder才会被调用。ErrorDecoder的作用是可以根据HTTP响应信息返回一个异常,该异常可以在调用Feign接口的地方被捕获到。我们目前就通过ErrorDecoder来使Feign接口抛出业务异常以供调用者处理。

spring cloud 声明式rest客户端feign调用远程http服务的更多相关文章

  1. Spring Cloud 入门教程(六): 用声明式REST客户端Feign调用远端HTTP服务

    首先简单解释一下什么是声明式实现? 要做一件事, 需要知道三个要素,where, what, how.即在哪里( where)用什么办法(how)做什么(what).什么时候做(when)我们纳入ho ...

  2. 3.【Spring Cloud Alibaba】声明式HTTP客户端-Feign

    使用Feign实现远程HTTP调用 什么是Feign Feign是Netflix开源的声明式HTTP客户端 GitHub地址:https://github.com/openfeign/feign 实现 ...

  3. 声明式HTTP客户端-Feign 使用入门详解

    什么是 OpenFeign OpenFeign (以下统一简称为 Feign) 是 Netflix 开源的声明式 HTTP 客户端,集成了 Ribbon 的负载均衡.轮询算法和 RestTemplat ...

  4. net core天马行空系列-微服务篇:全声明式http客户端feign快速接入微服务中心nacos

    1.前言 hi,大家好,我是三合,距离上一篇博客已经过去了整整两年,这两年里,博主通关了<人生>这个游戏里的两大关卡,买房和结婚.最近闲了下来,那么当然要继续写博客了,今天这篇博客的主要内 ...

  5. Spring Cloud 声明式服务调用 Feign

    一.简介 在上一篇中,我们介绍注册中心Eureka,但是没有服务注册和服务调用,服务注册和服务调用本来应该在上一章就应该给出例子的,但是我觉得还是和Feign一起讲比较好,因为在实际项目中,都是使用声 ...

  6. SpringCloud学习笔记(9)----Spring Cloud Netflix之声明式 REST客户端 -Feign的使用

    1. 什么是Feign? Feign是一种声明式.模板化的HTTP客户端,在SpringCloud中使用Feign.可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到 ...

  7. SpringCloud学习笔记(10)----Spring Cloud Netflix之声明式 REST客户端 -Feign的高级特性

    1. Feign的默认配置 Feign 的默认配置 Spring Cloud Netflix 提供的默认实现类:FeignClientsConfiguration 解码器:Decoder feignD ...

  8. Spring Cloud声明式调用Feign负载均衡FeignClient详解

    为了深入理解Feign,下面将从源码的角度来讲解Feign.首先来看看FeignClient注解@FeignClient的源码,代码如下: FeignClient注解被@Target(ElementT ...

  9. spring cloud 的hystrix 熔断器 和feign 调用的使用

    1, 添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId&g ...

随机推荐

  1. Django学习手册 - 连接mysql数据库

    版本问题: 首先确认django.msql.python版本是统一支持 当前所用的版本信息: Django setting.py 配置 替换之前的DATABASES 配置: DATABASES = { ...

  2. Spotlight LGWR1 一直告警

    http://www.itpub.net/thread-1181372-1-1.html

  3. ASP.NET Core中使用Unity5

    ⒈添加相关依赖 Install-Package Unity Install-Package Unity.RegistrationByConvention ⒉扫描项目接口实现类 using System ...

  4. k64 datasheet学习笔记10---Signal Multiplexing and Signal Descriptions

    1.前言 本文主要描述多功能引脚相关的内容,包括5个32位的引脚的端口,每个32pin端口被指定一个中断 2.Signal Multiplexing Integration 3. Port contr ...

  5. ARM的Jazelle技术【转】

    转自:https://blog.csdn.net/ken_yjj/article/details/6797290 Come From: http://www.arm.com/zh/products/p ...

  6. Windows PowerShell 入門(3)-スクリプト編

    これまでの記事 Windows PowerShell 入門(1)-基本操作編 Windows PowerShell 入門(2)-基本操作編 2 対象読者 Windows PowerShellでコマンド ...

  7. weblogic和was的巡检报告模板

    weblogic巡检报告模板  https://max.book118.com/html/2017/0710/121553357.shtm 用jrockit How to tell Our WebLo ...

  8. lua io.read()

    io.read(...) 从文件中读取内容,还有另外一种写法就是 file:read() 后面可以跟的读取方式有: (1) "n"  读取一个数字,这是唯一一个返回数字而不是字符串 ...

  9. [JavaScript]使用ArrayBuffer和Blob编辑二进制流

    Blob()构造方法返回一个新的Blob对象. 内容是包含参数array的二进制字节流. 语法 var aBlob = new Blob( array, options ); 参数 array is ...

  10. SP2-0734: 未知的命令开头 "exp wlc/ra..." - 忽略了剩余的行。

    SP2-0734: 未知的命令开头 "exp wlc/ra..." - 忽略了剩余的行. 原来只需要在  $exp wlc/radial_wlc123@ora11g owner=w ...