源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all

一、feign 简介

在上一个用例中,我们使用ribbon+restTemplate 实现服务之间的远程调用,实际上每一个调用都是模板化的内容,所以spring cloud Feign 在此基础上进行了进一步的封装。我们只需要定义一个接口并使用feign注解的方式来进行配置,同时采用springMvc 注解进行参数绑定就可以完成服务的调用。feign同时还内置实现了负载均衡、服务容错等功能。

二、项目结构

  • common: 公共的接口和实体类;
  • consumer: 服务的消费者,采用feign调用产品服务;
  • producer:服务的提供者;
  • eureka: 注册中心。

三、服务提供者的实现

3.1 产品服务由ProductService提供,并通过ProducerController将服务暴露给外部调用。

ProductService.java:

/**
 * @author : heibaiying
 * @description : 产品提供接口实现类
 */
@Service
public class ProductService implements IProductService, ApplicationListener<WebServerInitializedEvent> {

    private static List<Product> productList = new ArrayList<>();

    public Product queryProductById(int id) {
        return productList.stream().filter(p->p.getId()==id).collect(Collectors.toList()).get(0);
    }

    public List<Product> queryAllProducts() {
        return productList;
    }

    @Override
    public void saveProduct(Product product) {
        productList.add(product);
    }

    @Override
    public void onApplicationEvent(WebServerInitializedEvent event) {
        int port = event.getWebServer().getPort();
        for (long i = 0; i < 20; i++) {
            productList.add(new Product(i, port + "产品" + i, i / 2 == 0, new Date(), 66.66f * i));
        }
    }
}

ProducerController.java:

@RestController
public class ProducerController implements ProductFeign {

    @Autowired
    private IProductService productService;

    @GetMapping("products")
    public List<Product> productList() {
        return productService.queryAllProducts();
    }

    @GetMapping("product/{id}")
    public Product productDetail(@PathVariable int id) {
        return productService.queryProductById(id);
    }

    @PostMapping("product")
    public void save(@RequestBody Product product) {
        productService.saveProduct(product);
    }
}

3.2 指定注册中心地址,并在启动类上开启自动注册@EnableDiscoveryClient

server:
  port: 8020
# 指定服务命名
spring:
  application:
    name: producer
# 指定注册中心地址
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8010/eureka/
@SpringBootApplication
@EnableDiscoveryClient
public class ProducerApplication {

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

}

四、服务消费者的实现

4.1 导入openfeign依赖

<!-- feign 依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

4.2 指定注册中心地址,并在启动类上添加注解@EnableDiscoveryClient和@EnableFeignClients

@EnableFeignClients 会去扫描工程中所有用 @FeignClient 声明的 feign 客户端。

server:
  port: 8080
# 指定服务命名
spring:
  application:
    name: consumer
# 指定注册中心地址
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8010/eureka/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {

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

}

4.3 创建服务调用公共接口

/**
 * @author : heibaiying
 * @description : 声明式服务调用
 */
public interface ProductFeign {

    @GetMapping("products")
    List<Product> productList();

    /**
     * 这是需要强调的是使用feign时候@PathVariable一定要用value指明参数,
     * 不然会抛出.IllegalStateException: PathVariable annotation was empty on param 异常
     */
    @GetMapping("product/{id}")
    Product productDetail(@PathVariable(value = "id") int id);

    @PostMapping("product")
    void save(@RequestBody Product product);
}

按照官方对于服务最佳化的推荐,这里我们的服务调用接口放在公共模块中,因为在实际的开发中,同一个服务调用接口可能被多个模块所使用。

4.4 继承公共接口,创建CProductFeign, 用@FeignClient声明为feign客户端

/**
 * @author : heibaiying
 * @description : 声明式接口调用
 */
@FeignClient(value = "producer",configuration = FeignConfig.class)
public interface CProductFeign extends ProductFeign {

}

4.5 注入使用 feign 服务调用接口

@Controller
@RequestMapping("sell")
public class SellController {

    @Autowired
    private CProductFeign cproductFeign;

    @GetMapping("products")
    public String productList(Model model) {
        List<Product> products = cproductFeign.productList();
        model.addAttribute("products", products);
        return "products";
    }

    @GetMapping("product/{id}")
    public String productDetail(@PathVariable int id, Model model) {
        Product product = cproductFeign.productDetail(id);
        model.addAttribute("product", product);
        return "product";
    }

    @PostMapping("product")
    public String save(@RequestParam String productName) {
        long id = Math.round(Math.random() * 100);
        Product product = new Product(id, productName, false, new Date(), 88);
        cproductFeign.save(product);
        return "redirect:products";
    }
}

五、启动测试

5.1 启动一个Eureka服务、三个producer服务(注意区分端口)、和一个消费者服务

feign 的依赖中导入了spring-cloud-starter-netflix-ribbon依赖,并且在内部实现了基于ribbon的客户端负载均衡,所以我们这里启动三个producer实例来观察负载均衡的情况。

服务注册中心:

5.2 访问http://localhost:8080/sell/products 查看负载均衡的调用结果

六、 feign 的服务容错

6.1 feign 的依赖中默认导入了hystrix 的相关依赖,我们不需要额外导入,只需要开启相关配置即可

6.2 在application.yml 中开启hystrix

feign:
  hystrix:
    # 如果为true,则OpenFign客户端将使用Hystrix断路器进行封装 默认为false
    enabled: true

6.3 创建CProductFeignImpl,继承feign接口(CProductFeign),定义熔断时候的回退处理

/**
 * @author : heibaiying
 * @description : 定义发生熔断时候的回退处理。除了继承自CProductFeign,还需要用@Component声明为spring的组件
 */
@Component
public class CProductFeignImpl implements CProductFeign {

    // 发生熔断时候,返回空集合,前端页面会做容错显示
    @Override
    public List<Product> productList() {
        return new ArrayList<>();
    }

    @Override
    public Product productDetail(int id) {
        return null;
    }

    @Override
    public void save(Product product) {

    }
}

页面的简单容错处理:

<!doctype html>
<html lang="en">
<head>
    <title>产品列表</title>
</head>
<body>
<h3>产品列表:点击查看详情</h3>
<form action="/sell/product" method="post">
    <input type="text" name="productName">
    <input type="submit" value="新增产品">
</form>
<ul>
    <#if (products?size>0) >
        <#list products as product>
            <li>
                <a href="/sell/product/${product.id}">${product.name}</a>
            </li>
        </#list>
    <#else>
        <h4 style="color: red">当前排队人数过多,请之后再购买!</h4>
    </#if>
</ul>
</body>
</html>

6.4 在 @FeignClient 注解中,用fallback参数指定熔断时候的回退处理

/**
 * @author : heibaiying
 * @description : 声明式接口调用
 */
@FeignClient(value = "producer",configuration = FeignConfig.class,fallback = CProductFeignImpl.class)
public interface CProductFeign extends ProductFeign {

}

6.5 测试熔断处理

hystrix 默认调用超时时间为2s ,这里我们使用线程休眠的方式来模拟超时熔断。

public List<Product> queryAllProducts() {
    /*用于测试 hystrix 超时熔断
    try {
        int i = new Random().nextInt(2500);
        Thread.sleep(i);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }*/
    return productList;
}

测试结果:

源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all

spring cloud 系列第4篇 —— feign 声明式服务调用 (F版本)的更多相关文章

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

    目录 一.Feign是什么? 二.Feign的快速搭建 三.Feign的几种姿态 参数绑定 继承特性 四.其他配置 Ribbon 配置 Hystrix 配置 一.Feign是什么? ​ 通过对前面Sp ...

  2. Spring Cloud Feign声明式服务调用(转载)+遇到的问题

    转载:原文 总结: 1.pom添加依赖 2.application中填写正确的eureka配置 3.启动项中增加注解 @EnableFeignClients 4.填写正确的调用接口 通过原文使用Fei ...

  3. 笔记:Spring Cloud Feign 声明式服务调用

    在实际开发中,对于服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以我们通常会针对各个微服务自行封装一些客户端类来包装这些依赖服务的调用,Spring Cloud Feign 在此基础上做了进 ...

  4. SpringCloud实战-Feign声明式服务调用

    在前面的文章中可以发现当我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率 ...

  5. Feign声明式服务调用

    Feign是一种声明式.模板化的HTTP客户端(仅在Application Client中使用).声明式调用是指,就像调用本地方法一样调用远程方法,无需感知操作远程http请求. Spring Clo ...

  6. SpringCloud无废话入门03:Feign声明式服务调用

    1.Feign概述 在上一篇的HelloService这个类中,我们有这样一行代码: return restTemplate.getForObject("http://hello-servi ...

  7. 声明式服务调用:Spring Cloud Feign

    最近在学习Spring Cloud的知识,现将声明式服务调用:Spring Cloud Feign 的相关知识笔记整理如下.[采用 oneNote格式排版]

  8. Spring Cloud 2-Feign 声明式服务调用(三)

    Spring Cloud Feign  1. pom.xml 2. application.yml 3. Application.java 4. Client.java 简化RestTemplate调 ...

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

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

随机推荐

  1. 【OC语法要闻速览】一、方法调用

    调用方法 [object method]; [object methodWithInput:input]; output = [object methodWithOutput]; output = [ ...

  2. 史上最全最强SpringMVC详细示例实战教程【good】

    1)Spring MVC 在调用处理方法之前,在请求线程中自动的创建一个隐含的模型对象. 2)调用所有方法级的 标注了 @ModelAttribute 的方法,并将方法返回值添加到隐含的模型对象中. ...

  3. 使用Python破解验证码

    Keywords: python captcha Most people don’t know this but my honours thesis was about using a compute ...

  4. 【Linux计划】XSI IPC

    三种IPC这就是所谓的XSI IPC,每间: 消息队列 信号量 共享存储器 以下分别介绍三种IPC的使用方法. 1.消息队列 消息队列是消息的链接表,具有例如以下函数接口: msgget:创建一个新队 ...

  5. 用callgraph生成的函数调用关系图

    Wu Zhangjin 创作于 2015/04/05 评论打赏 By Falcon of TinyLab.org 2015/04/03 1 故事缘由 源码分析是程序员离不开的话题.无论是研究开源项目, ...

  6. 关于 Swift 2.0 - 语言新特性与革新

    随着刚刚结束的 WWDC 2015 苹果发布了一系列更新,这其中就包括了令人振奋的 Swift 2.0. 这是对之前语言特性的一次大幅的更新,加入了很多实用和方便的元素,下面我们就一起来看看这次更新都 ...

  7. WPF事件(一)内置路由事件

    原文:WPF事件(一)内置路由事件 Windows是消息驱动的操作系统,运行其上的程序也遵照这个机制运行,随着面向对象开发平台日趋成熟,微软把消息机制封装成了更容易让人理解的事件模型,一个事件包含3个 ...

  8. 数据绑定(七)使用ObjectDataProvider对象作为Binding的Source

    原文:数据绑定(七)使用ObjectDataProvider对象作为Binding的Source ObjectDataProvider就是把对象作为数据源提供给Binding,类似的还有XmlData ...

  9. 【C#】list 去重

    原文:[C#]list 去重 Enumerable.Distinct 方法 是常用的LINQ扩展方法,属于System.Linq的Enumerable方法,可用于去除数组.集合中的重复元素,还可以自定 ...

  10. 增加收入的 6 种方式(很多公司的模型是:一份时间卖多次。比如网易、腾讯。个人赚取收入的本质是:出售时间)good

    个人赚取收入的本质是:出售时间.从这个角度出发,下面的公式可以描述个人收入: 个人收入 = 每天可售时间数量 * 单位时间价格 * 单位时间出售次数 在这个公式里,有三个要素: 每天可出售的时间数量 ...