您当前的位置:首页 > 电脑百科 > 程序开发 > 架构

SpringCloud Alibaba实战(使用OpenFeign服务调用)

时间:2021-06-29 09:55:15  来源:今日头条  作者:深夜敲代码

1、Feign简介

Feign是一种声明式、模板化的HTTP客户端。使用Feign,可以做到声明式调用。

尽管Feign目前已经不再迭代,处于维护状态,但是Feign仍然是目前使用最广泛的远程调用框架之一。

在SpringCloud Alibaba的生态体系内,有另一个应用广泛的远程服务调用框架Dubbo,在后面我们会接触到。

Feign是在RestTemplate 和 Ribbon的基础上进一步封装,使用RestTemplate实现Http调用,使用Ribbon实现负载均衡。

SpringCloud Alibaba实战(使用OpenFeign服务调用)

 

接下来,我们开始学习Feign的使用,非常简单!

2、Feign使用

2.1、引入OpenFeign

在前面的章节里,我们已经引入了SpringCloud,现在我们只需要在需要引入的子模块中添加依赖:

复制代码

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

2.2、Feign远程调用

我们现在来完成一个业务:添加商品

SpringCloud Alibaba实战(使用OpenFeign服务调用)

 

这个业务涉及两个子服务,添加商品的时候同时要添加库存,查询商品的时候,同时要查询库存。商品服务作为消费者,库存服务作为生产者。

2.2.1、服务提供者

作为服务提供者的库存服务很简单,提供两个接口添加库存、根据商品ID获取库存量。

  • 控制层

复制代码

@RestController
@RequestMApping("/shop-stock/api")
@Slf4j
@Api(value = "商品服务对外接口", tags = "商品服务对外接口")
public class ShopStockApiController {
    @Autowired
    private IShopStockService shopStockService;

    @PostMapping(value = "/add")
    @ApiOperation("添加库存")
    public Integer addStock(@RequestBody StockAddDTO stockAddDTO) {
        log.info("client call add stock interface,param:{}", stockAddDTO);
        return this.shopStockService.addStockApi(stockAddDTO);
    }

    @GetMapping(value = "/account/get")
    @ApiOperation("根据商品ID获取库存量")
    public Integer getAccountById(@RequestParam Integer goodsId) {
        return this.shopStockService.getAccountById(goodsId);
    }
}

注意看,为了演示出本地调用类似的效果,这两个接口和普通的前后端接口不同。

SpringCloud Alibaba实战(使用OpenFeign服务调用)

 

我们没有返回之前定下的统一返回结果CommonResult,而是直接返回了数据。

  • 业务层普通的增、查而已

复制代码

    /**
     * 添加库存-直接返回主键
     *
     * @param stockAddDTO
     * @return
     */
    public Integer addStockApi(StockAddDTO stockAddDTO) {
        ShopStock stock = new ShopStock();
        stock.setGoodsId(stockAddDTO.getGoodsId());
        stock.setInventory(stockAddDTO.getAccount());
        log.info("准备添加库存,参数:{}", stock.toString());
        this.baseMapper.insert(stock);
        Integer stockId =stock.getStockId();
        log.info("添加库存成功,stockId:{}", stockId);
        return stockId;
    }

    /**
     * 根据商品ID获取商品库存
     *
     * @param goodsId
     * @return
     */
    public Integer getAccountById(Integer goodsId) {
        ShopStock stock = this.getOne(Wrappers.<ShopStock>lambdaQuery().eq(ShopStock::getGoodsId, goodsId));
        Integer account = stock.getInventory();
        return account;
    }

  • 添加库存实体类

复制代码

@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "库存添加", description = "")
public class StockAddDTO implements Serializable {
    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "商品主键")
    private Integer goodsId;

    @ApiModelProperty(value = "数量")
    private Integer account;
}

至此,我们的服务提供者的相关开发到此完成,打开地址
http://localhost:8050/doc.html ,可以看到我们开发的接口:

SpringCloud Alibaba实战(使用OpenFeign服务调用)

 

2.2.2、服务消费者

好了,接下里要开始我们的服务消费者,也就是商品服务的开发。

  • 远程调用Feign客户端

声明式调用——看一下Feign客户端的代码,你就知道什么是声明式调用:

复制代码

/**
 * @Author: 三分恶
 * @Date: 2021/5/26
 * @Description: 库存服务feign客户端
 **/
@FeignClient(value = "stock-service")
public interface StockClientFeign {

    /**
     * 调用添加库存接口
     *
     * @param stockAddDTO
     * @return
     */
    @PostMapping(value = "/shop-stock/api/add")
    Integer addStock(@RequestBody StockAddDTO stockAddDTO);

    /**
     * 调用根据商品ID获取库存量接口
     *
     * @param goodsId
     * @return
     */
    @GetMapping(value = "/shop-stock/api/account/get")
    Integer getAccountById(@RequestParam(value = "goodsId") Integer goodsId);
}
  • 定义完成之后,我们还要在启动类上加上注解@EnableFeignClients去扫描Feign客户端。

复制代码

@SpringBootApplication
@MapperScan("cn.fighter3.mapper")
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "cn.fighter3.client")
public class EshopGoodsApplication {

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

使用Feign客户端也很简单,直接在需要使用的地方注入就行了。

复制代码

@Autowired
private StockClientFeign stockClientFeign;
  • 商品服务控制层

复制代码

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author 三分恶
 * @since 2021-05-18
 */
@RestController
@RequestMapping("/shop-goods")
@Api(value = "商品管理接口", tags = "商品接口")
@Slf4j
public class ShopGoodsController {

    @Autowired
    private IShopGoodsService goodsService;

    @PostMapping(value = "/add")
    @ApiOperation(value = "添加商品")
    public CommonResult addGoods(@RequestBody GoodsAddDTO goodsAddDTO) {
        return this.goodsService.addGoods(goodsAddDTO);
    }

    @GetMapping(value = "/get/by-id")
    @ApiOperation(value = "根据ID获取商品")
    public CommonResult<GoodsVO> getGoodsById(@RequestParam Integer goodsId) {
        return this.goodsService.getGoodsById(goodsId);
    }

}
  • 服务层

在服务层除了对商品库的操作之外,还通过Feign客户端远程调用库存服务的接口。

复制代码

@Service
@Slf4j
public class ShopGoodsServiceImpl extends ServiceImpl<ShopGoodsMapper, ShopGoods> implements IShopGoodsService {

    @Autowired
    private StockClientFeign stockClientFeign;

    /**
     * 添加商品
     *
     * @param goodsAddDTO
     * @return
     */
    public CommonResult addGoods(GoodsAddDTO goodsAddDTO) {
        ShopGoods shopGoods = new ShopGoods();
        BeanUtils.copyProperties(goodsAddDTO, shopGoods);
        this.baseMapper.insert(shopGoods);
        log.info("添加商品,商品主键:{}", shopGoods.getGoodsId());
        log.info(shopGoods.toString());
        StockAddDTO stockAddDTO = StockAddDTO.builder().goodsId(shopGoods.getGoodsId()).account(goodsAddDTO.getAccount()).build();
        log.info("准备添加库存,参数:{}", stockAddDTO.toString());
        Integer stockId = this.stockClientFeign.addStock(stockAddDTO);
        log.info("添加库存结束,库存主键:{}", stockId);
        return CommonResult.ok();
    }

    /**
     * 获取商品
     *
     * @param goodsId
     * @return
     */
    public CommonResult<GoodsVO> getGoodsById(Integer goodsId) {
        GoodsVO goodsVO = new GoodsVO();
        //获取商品基本信息
        ShopGoods shopGoods = this.baseMapper.selectById(goodsId);
        BeanUtils.copyProperties(shopGoods, goodsVO);
        //获取商品库存数量
        Integer account = this.stockClientFeign.getAccountById(goodsId);
        log.info("商品数量:{}", account);
        goodsVO.setAccount(account);
        return CommonResult.ok(goodsVO);
    }
}
  • 实体类添加库存实体类和库存服务相同,略过,商品展示实体类

复制代码

@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "商品", description = "")
public class GoodsVO implements Serializable {
    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "商品主键")
    private Integer goodsId;

    @ApiModelProperty(value = "商品名称")
    private String goodsName;

    @ApiModelProperty(value = "价格")
    private BigDecimal price;

    @ApiModelProperty(value = "商品介绍")
    private String description;

    @ApiModelProperty(value = "数量")
    private Integer account;
}

2.2.3、效果演示

接下来启动nacos-server,商品服务,库存服务。

访问地址
http://127.0.0.1:8848/nacos/index.html ,登录之后,可以在服务列表里看到我们注册的两个服务:

SpringCloud Alibaba实战(使用OpenFeign服务调用)

 

访问商品服务Knife4j地址:
http://localhost:8020/doc.html ,可以看到添加商品和根据商品ID查找商品的接口,分别调试调用:

  • 添加商品
SpringCloud Alibaba实战(使用OpenFeign服务调用)

 

  • 根据ID获取商品
SpringCloud Alibaba实战(使用OpenFeign服务调用)

 

可以看到各自对应的数据库也有数据生成:

SpringCloud Alibaba实战(使用OpenFeign服务调用)

 

整体的远程调用示意图大概如下:

SpringCloud Alibaba实战(使用OpenFeign服务调用)

 

2.3、Ribbon负载均衡

关于负载均衡,这里偷个懒,就不再演示了。

感兴趣的可以把库存服务打包,以不同的端口启动,然后添加商品,通过日志查看商品服务调用的负载情况。

Feign负载均衡是通过Ribbon实现,Ribbon是一种客户端的负载均衡——也就是从注册中心获取服务列表,由客户端自己决定调用哪一个远程服务。

SpringCloud Alibaba实战(使用OpenFeign服务调用)

 

Ribbon的主要负载均衡策略有以下几种:

规则名称

特点

AvailabilityFilteringRule

过滤掉一直连接失败的被标记为circuit tripped的后端Server,并 过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate 来包含过滤server的逻辑,其实就是检查status里记录的各个server 的运行状态

BestAvailableRule

选择一个最小的并发请求的server,逐个考察server, 如果Server被tripped了,则跳过

RandomRule

随机选择一个Server

ResponseTimeWeightedRule

已废弃,作用同WeightedResponseTimeRule

WeightedResponseTimeRule

根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低

RetryRule

对选定的负载均衡策略加上重试机制,在一个配置时间段内当 选择Server不成功,则一直尝试使用subRule的方式选择一个 可用的Server

RoundRobinRule

轮询选择,轮询index,选择index对应位置的Server

ZoneAvoidanceRule

默认的负载均衡策略,即复合判断Server所在区域的性能和Server的可用性 选择Server,在没有区域的环境下,类似于轮询(RandomRule)

这里就不再展开讲了,感兴趣的自行了解。

3、意外状况

  • 发现远程调用的时候出现读取响应结果超时的情况:
JAVA.net.SocketTimeoutException: Read timed out

修改Ribbon超时配置就行了:

# ribbon超时时间
ribbon:
  ReadTimeout: 30000
  ConnectTimeout: 30000
  • Feign接口中,使用@RequestParam报错

发现报错:

Caused by: java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0

Feign声明里需要加上value:

Integer getAccountById(@RequestParam(value = "goodsId") Integer goodsId);


Tags:SpringCloud   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
1、Feign简介Feign是一种声明式、模板化的HTTP客户端。使用Feign,可以做到声明式调用。尽管Feign目前已经不再迭代,处于维护状态,但是Feign仍然是目前使用最广泛的远程调用框架...【详细内容】
2021-06-29  Tags: SpringCloud  点击:(113)  评论:(0)  加入收藏
环境:Spring Boot 2.3.9 + Spring Cloud Hoxton.SR8服务发现注册请参考《SpringCloud Zookeeper服务发现及负载均衡 》zookeeper安装配置请参考《Kafka(zookeeper)环境配置超级...【详细内容】
2021-04-06  Tags: SpringCloud  点击:(276)  评论:(0)  加入收藏
本文将详细分析SpringCloud Gateway是如何实现的。架构SpringCloud Gateway(下面简称SG)基于SpringWebFlux,整体架构如下图所示: SG定义了几个概念: 路由(Route):路由是网关的基本...【详细内容】
2020-12-18  Tags: SpringCloud  点击:(582)  评论:(0)  加入收藏
前言当我们的网关Gateway程序开发完成之后,需要部署到生产环境,这个时候你的程序不能是单点运行的,肯定是多节点启动(独立部署或者docker等容器部署),防止单节点故障导致整个服...【详细内容】
2020-10-19  Tags: SpringCloud  点击:(101)  评论:(0)  加入收藏
使用SpringCloud技术栈搭建微服务集群,可以选择的组件比较多,由于有些组件已经闭源或停更,这里主要选用spring-cloud-alibaba作为我们的技术栈。 服务注册与发现: nacos-discove...【详细内容】
2020-08-19  Tags: SpringCloud  点击:(204)  评论:(0)  加入收藏
▌简易百科推荐
为了构建高并发、高可用的系统架构,压测、容量预估必不可少,在发现系统瓶颈后,需要有针对性地扩容、优化。结合楼主的经验和知识,本文做一个简单的总结,欢迎探讨。1、QPS保障目标...【详细内容】
2021-12-27  大数据架构师    Tags:架构   点击:(3)  评论:(0)  加入收藏
前言 单片机开发中,我们往往首先接触裸机系统,然后到RTOS,那么它们的软件架构是什么?这是我们开发人员必须认真考虑的问题。在实际项目中,首先选择软件架构是非常重要的,接下来我...【详细内容】
2021-12-23  正点原子原子哥    Tags:架构   点击:(7)  评论:(0)  加入收藏
现有数据架构难以支撑现代化应用的实现。 随着云计算产业的快速崛起,带动着各行各业开始自己的基于云的业务创新和信息架构现代化,云计算的可靠性、灵活性、按需计费的高性价...【详细内容】
2021-12-22    CSDN  Tags:数据架构   点击:(10)  评论:(0)  加入收藏
▶ 企业级项目结构封装释义 如果你刚毕业,作为Java新手程序员进入一家企业,拿到代码之后,你有什么感觉呢?如果你没有听过多模块、分布式这类的概念,那么多半会傻眼。为什么一个项...【详细内容】
2021-12-20  蜗牛学苑    Tags:微服务   点击:(8)  评论:(0)  加入收藏
我是一名程序员关注我们吧,我们会多多分享技术和资源。进来的朋友,可以多了解下青锋的产品,已开源多个产品的架构版本。Thymeleaf版(开源)1、采用技术: springboot、layui、Thymel...【详细内容】
2021-12-14  青锋爱编程    Tags:后台架构   点击:(20)  评论:(0)  加入收藏
在了解连接池之前,我们需要对长、短链接建立初步认识。我们都知道,网络通信大部分都是基于TCP/IP协议,数据传输之前,双方通过“三次握手”建立连接,当数据传输完成之后,又通过“四次挥手”释放连接,以下是“三次握手”与“四...【详细内容】
2021-12-14  架构即人生    Tags:连接池   点击:(16)  评论:(0)  加入收藏
随着移动互联网技术的快速发展,在新业务、新领域、新场景的驱动下,基于传统大型机的服务部署方式,不仅难以适应快速增长的业务需求,而且持续耗费高昂的成本,从而使得各大生产厂商...【详细内容】
2021-12-08  架构驿站    Tags:分布式系统   点击:(23)  评论:(0)  加入收藏
本系列为 Netty 学习笔记,本篇介绍总结Java NIO 网络编程。Netty 作为一个异步的、事件驱动的网络应用程序框架,也是基于NIO的客户、服务器端的编程框架。其对 Java NIO 底层...【详细内容】
2021-12-07  大数据架构师    Tags:Netty   点击:(16)  评论:(0)  加入收藏
前面谈过很多关于数字化转型,云原生,微服务方面的文章。虽然自己一直做大集团的SOA集成平台咨询规划和建设项目,但是当前传统企业数字化转型,国产化和自主可控,云原生,微服务是不...【详细内容】
2021-12-06  人月聊IT    Tags:架构   点击:(23)  评论:(0)  加入收藏
微服务看似是完美的解决方案。从理论上来说,微服务提高了开发速度,而且还可以单独扩展应用的某个部分。但实际上,微服务带有一定的隐形成本。我认为,没有亲自动手构建微服务的经历,就无法真正了解其复杂性。...【详细内容】
2021-11-26  GreekDataGuy  CSDN  Tags:单体应用   点击:(35)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条