声明式服务调用Feign+熔断机制Hystrix

Posted MyAzhe0ci3

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了声明式服务调用Feign+熔断机制Hystrix相关的知识,希望对你有一定的参考价值。

  • 在以往的单工程项目,所有代码都融合在一起,业务相互调用只需要引入共有的工具类或者对应模块的service。
  • 但是到了微服务时代,已然不能使用这种方式,我们需要寻找新的解决方案。
  • 若每个模块都把需要调用模块的service拷贝一份,那会令代码非常冗余,影响整个工程的健壮性。
  • 稍大一些的系统,会分成多个库,比如用户库、订单库分开,订单服务想要取到用户的相关信息,由于不能连接到用户的库,所以无法直接新建数据库查询以达到目的。
  • 这个时候,远程调用方案出现,订单服务只需调用用户服务的API,就可以获取所需信息,非常方便。

我们下面来学习,如何使用SpringCloud的Feign来进行微服务远程调用。

创建blade_blog表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for blade_blog
-- ----------------------------

DROP TABLE IF EXISTS `blade_blog`;
CREATE TABLE `blade_blog`  (
  `id` bigint(64) NULL DEFAULT NULL,
  `blog_title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `blog_content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `is_deleted` int(2) NULL DEFAULT 0,
  `blog_date` datetime NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of blade_blog
-- ----------------------------
INSERT INTO `blade_blog` VALUES (1, '测试1', '内容1', 0, '2021-07-30 16:03:15');
INSERT INTO `blade_blog` VALUES (1421018570775953409, '标题测试', '内容测试', 0, '2021-07-30 16:03:23');

SET FOREIGN_KEY_CHECKS = 1;

创建实体类

package org.springblade.demo.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.io.Serializable;
import java.sql.Date;
@Data
@TableName("blade_blog")
public class Blog implements Serializable {
	private static final long serialVersionUID = 1L;
	/**
	 * 主键
	 */
	@TableId(value = "id", type = IdType.ID_WORKER)
	private Long id;
	/**
	 * 标题
	 */
	private String blogTitle;
	/**
	 * 内容
	 */
	private String blogContent;
	/**
	 * 时间
	 */
	@JsonFormat(shape=JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
	private Date blogDate;
	/**
	 * 是否已删除
	 */
	@TableLogic
	private Integer isDeleted;
}

blogDate变量使用了字符串类型,接口接收参数后将该字符串转为日期时,发生错误,项目中使用fastjson来处理json数据。

此问题解决方法

在接收参数实体类的createtime字段添加JsonFormat注解

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")

使用的持久层框架是mabatisplus,默认你们会使用,就不把步骤列出来了

创建DemoController

package org.springblade.demo.controller;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.demo.entity.Blog;
import org.springblade.demo.service.BlogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * DemoController
 */
@Slf4j //日志
@RestController//@responseBody+@Controller的组合
@RequestMapping("api")//请求路径
@AllArgsConstructor//labmda表达式简洁写法 使用该注解不用注入自动创建
public class DemoController {

	private BlogService blogService;

	/**
	 * 详情
	 */
	@GetMapping("/detail")
	public R<Blog> detail(String id) {
		Blog detail = blogService.getById(id);
		return R.data(detail);
	}
}

创建BlogClient接口

package org.springblade.demo.feign;
import org.springblade.common.constant.CommonConstant;
import org.springblade.core.tool.api.R;
import org.springblade.demo.entity.Blog;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(
	//定义Feign指向的service-id  也就是指向了demoApplication微服务模块的id
	value = "blade-demo"
)
public interface BlogClient{
	/**
	 * 接口前缀
	 */
	String API_PREFIX = "/api/blog";
	/**
	 * 获取详情
	 *
	 * @param id 主键
	 * @return
	 */
	@GetMapping(API_PREFIX + "/detail")
	R<Blog> detail(@RequestParam("id") Integer id);
}

如果@FeignClient指向错误会报

com.netflix.zuul.exception.ZuulException: Forwarding error Caused by:
com.netflix.client.ClientException: Load balancer does not have
available server for client: service-xxx异常

创建feign的实现类BlogClientImpl

package org.springblade.demo.feign;

import lombok.AllArgsConstructor;
import org.springblade.core.tool.api.R;
import org.springblade.demo.entity.Blog;
import org.springblade.demo.service.BlogService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@AllArgsConstructor
public class BlogClientImpl   implements  BlogClient{

	private BlogService service;

	@Override
	@GetMapping(API_PREFIX+"/detail")
	public R<Blog> detail(Integer id) {
		return R.data(service.getById(id));
	}
}


什么是熔断机制

  • hystrix对应的中文名字是“豪猪”,豪猪周身长满了刺,能保护自己不受天敌的伤害,代表了一种防御机制。
  • 这与hystrix本身的功能不谋而合,因此Netflix团队将该框架命名为Hystrix,并使用了对应的卡通形象做作为logo。
  • 在一个分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,如何能够保证在一个依赖出问题的情况下,不会导致整体服务失败,这个就是Hystrix需要做的事情。
  • Hystrix提供了熔断、隔离、Fallback、cache、监控等功能,能够在一个、或多个依赖同时出现问题时保证系统依然可用。

创建Hystrix类BlogClientFallback
同样实现 BlogClient 接口,只是此时只需返回对应数据即可,不需要再定义为 Controller

package org.springblade.demo.feign;

import org.springblade.core.tool.api.R;
import org.springblade.demo.entity.Blog;

import java.time.LocalDateTime;

public class BlogClientFallback implements BlogClient {
	@Override
	public R<Blog> detail(Integer id) {
		Blog blog = new Blog();
		blog.setBlogTitle("Hystrix");
		blog.setBlogContent("FallBack Success");
		blog.setBlogDate(LocalDateTime.now());
		blog.setIsDeleted(0);
		return R.data(blog);
	}
}

修改BlogClient,增加Hystrix配置

package org.springblade.demo.feign;
import org.springblade.common.constant.CommonConstant;
import org.springblade.core.tool.api.R;
import org.springblade.demo.entity.Blog;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(
	//定义Feign指向的service-id
	value = "blade-demo",
	//定义hystrix配置类
	fallback = BlogClientFallback.class
)
public interface BlogClient{
	/**
	 * 接口前缀
	 */
	String API_PREFIX = "/api/blog";
	/**
	 * 获取详情
	 *
	 * @param id 主键
	 * @return
	 */
	@GetMapping(API_PREFIX + "/detail")
	R<Blog> detail(@RequestParam("id") Integer id);
}

增加 FallBack 自动配置(不新建配置,直接在BlogClientFallback类上加@Component注解也可以)

package org.springblade.demo.config;

import org.springblade.demo.feign.BlogClientFallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * feign失败配置
 *
 */
@Configuration
public class BlogFeignConfiguration {

	@Bean
	public BlogClientFallback blogClientFallback(){
		return new BlogClientFallback();
	}
}

 这时我们去 blade-demo 的 BlogClientImpl 模拟异常
@RestController
@AllArgsConstructor
public class BlogClientImpl implements BlogClient {
private BlogService service;
@Override
@GetMapping(API_PREFIX + "/detail")
public R<Blog> detail(Integer id) {
int cnt = 100 / 0;
return R.data(service.getById(id));
}
}


OK打完收工

以上是关于声明式服务调用Feign+熔断机制Hystrix的主要内容,如果未能解决你的问题,请参考以下文章

声明式服务调用Feign+熔断机制Hystrix

Spring Cloud中HystrixRibbon及Feign的熔断关系

SpringBoot如何使用Feign实现远程接口调用

SpringCloud系列之二---Feign实现服务间调用,集成Hystrix熔断器Hystrix-Dashboard仪表盘

010 Hystrix的使用

springcloud 服务调用feign熔断hystrix网关gateway