openfeign+hystrix

openfeign+hystrix

1、openfeign

openfeign概念

  • Openfeign是spring cloud家族的一个成员,是一个声明式的客户端,使用feign能够让web service客户端更加简单。
  • 使用方法就是在客户端即消费端定义一个服务接口然后在上面加上注解@FeignClient,也支持可拔插式的编码和解码器,spring cloud对feign进行了封装,使其支持了spring MVC标准注解和httpMessageConverters,可以与Ribbon和hystrix实现负载均衡和服务降级。
  • 之前我们使用 Ribbon+RestTemplate来进行对服务的调用,但是由于对服务的调用不止一处,即一个服务可能被多处调用,所以我们就通过一个接口包含服务端的所有接口,这个可以作为客户端,通过该openfeign来实现远程服务端调用。简化的Ribbon+RestTemplate远程调用。
  • 集成了Ribbon即存在负载均衡机制。
  • 官网:春云开放菲恩 (spring.io)

feign和openfeign的区别:

feignopenfeign
Feign是一个spring cloud组件中一个轻量级RESTFul的HTTP服务客户端,feign内置了Ribbon,用于客户端的负载均衡。openfeign是springcloud在feign的基础上支持了springMVC的注解,如@RequestMapping,openfeign的@FeignClient可以解析SpringMVC的RequestMapping下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
基本上不会再使用该方法了 org.springframework.cloud spring-cloud-starter-openfeign 3.1.1

openfeign的日志打印:

  • **NONE:**默认的,不显示打印日志。
  • **BASIC:**仅记录请求方法、URL、响应状态码及执行时间。
  • **HEADERS:**除了BASIC中定义的以外,还有请求响应头信息。
  • **FULL:**除了HEADERS之外,还有请求和响应的正文即元数据。

FULL显示示例:

在这里插入图片描述

2、openfeign的基本使用

注意这里是的的注册中心为eureka,且为了很好的进行包的管理,这里创建一个父工程

2.1 父工程pom.xml
<dependencyManagement>   <!--在父工程中才存在该标签,用于管理子工程的相关包-->
        <dependencies>
            <!--spring boot的包依赖下面子工程的boot版本都为这个版本-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.6.5</version>
                <type>pom</type>
                <!--表明后续不需在导入spring boot依赖-->
                <scope>import</scope>
            </dependency>
             <!--spring cloud的包依赖下面子工程的cloud版本都为这个版本-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.1</version>
                <type>pom</type>
                 <!--表明后续不需在导入spring cloud依赖-->
                <scope>import</scope>
            </dependency>
            <!--spring cloud alibaba的包依赖下面子工程的cloud alibaba版本都为这个版本-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2021.0.1.0</version>
                <type>pom</type>
                 <!--表明后续不需在导入spring cloud alibaba依赖-->
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
2.2 创建eureka注册中心服务

在这里插入图片描述

  • 改pom.xml
<dependencies>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--图形化依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
    <!--一个自定义包用于公共的pojo等,解耦,可以不用-->
    <dependency>
        <groupId>com.qiu</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--eureka-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <!--test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
</project>
  • 改yaml
server:
  port: 7001

eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/  #注册中心地址
  • 主启动类
package com.qiumin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer   //开启eureka服务
public class eurekaServer7001 {
    public static void main(String[] args) {
        SpringApplication.run(eurekaServer7001.class,args);
    }
}

  • 启动后访问 http://localhost:7001/

在这里插入图片描述

2.3 创建服务提供者 provider

在这里插入图片描述

  • 改pom.xml
<dependencies>
    <!--eureka客户端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--自己定义的一个工程-->
    <dependency>
        <groupId>com.qiu</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--图形化处理-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
        <version>2.6.5</version>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>
    <!--druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.10</version>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
        <version>2.2.2.RELEASE</version>
    </dependency>
    <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <version>2.6.5</version>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
  • 改yaml
server:
  port: 8001

spring:
  application:
    name: cloud-provide-payment   #服务名称
  datasource:   #数据源
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/cloud?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

mybatis: #mybatis相关配置
  mapper-locations: classpath:mybatis/mapper/*.xml
  type-aliases-package: com.qiumin.pojo

eureka:
  client:
    register-with-eureka: true   #注册到注册中心
    service-url: 
      defaultZone: http://localhost:7001/eureka     #注册中心地址
  instance:
    instance-id: payment8001    #处理在eureka中的显示
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2     #
    lease-expiration-duration-in-seconds: 4
  • 主启动类
package com.qiumin;

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

@SpringBootApplication
@EnableDiscoveryClient   //开启服务发现客户端
public class paymentApplication {
    public static void main(String[] args) {
        SpringApplication.run(paymentApplication.class, args);
    }
}

  • 业务类编写 【service】
package com.qiumin.service;

import com.qiumin.mapper.PaymentDao;
import com.qiumin.pojo.Payment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PaymentServiceImpl implements PaymentService{

    @Autowired
    PaymentDao paymentDao;

    @Override
    public Payment query(int id) {
        return  paymentDao.query(id);
    }

    @Override
    public int insert(Payment payment) {
        return paymentDao.insert(payment);
    }
}

  • 业务类编写 【controller】
package com.qiumin.controller;

import com.qiumin.pojo.CommonResult;
import com.qiumin.pojo.Payment;
import com.qiumin.service.PaymentServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@Slf4j
public class PaymentController {

    @Autowired
    PaymentServiceImpl paymentService;
    @Value("${server.port}")
    String serverPort;

    @RequestMapping("/payment/query/{id}")
    @ResponseBody
    public CommonResult<Payment> query(@PathVariable("id") int id){
        Payment query = paymentService.query(id);
//        int age=10/0;  用于监测是否服务降级而设置
        log.info("======查询结果"+query+"=====");

        if (query!=null){
            return new CommonResult(200,"查询数据库成功=== serverPort为  "+serverPort,query);
        }else {
            return new CommonResult(444,"查询数据库失败=== serverPort为  "+serverPort,null);
        }
    }

    @RequestMapping("/payment/create")
    @ResponseBody
    public CommonResult<Payment> create(Payment payment){
        int result = paymentService.insert(payment);
        log.info("======插入结果"+payment+"=====");

        if (result>0){
            return new CommonResult(200,"插入数据库成功=== serverPort为  "+serverPort,payment);
        }else {
            return new CommonResult(444,"插入数据库失败=== serverPort为  "+serverPort,null);
        }
    }
}

其他的mapper、pojo、mapper.xml自动补全这里只是演示openfeign远程调用

  • 启动,就会注册到eureka注册中心
2.4 创建客户端即消费端

在这里插入图片描述

  • 改pom.xml
<dependencies>
    <!--开启openfeign-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
    
    <!--eureka客户端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--自定义包-->
    <dependency>
        <groupId>com.qiu</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--图形化处理-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
        <version>2.6.5</version>
    </dependency>
    <!--devtools-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <version>2.6.5</version>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
  • 改yaml
server:
  port: 8888

spring:
  application:
    name: cloud-consumer-order  #服务名

eureka:
  client:
    register-with-eureka: true  #注册到注册中心
    service-url:
      defaultZone: http://localhost:7001/eureka   #注册中心地址

ribbon:
  #建立连接所用时间
  ReadTimeout: 5000
  #服务读取数据可用时间
  ConnectTimeout: 5000   #负载均衡超时配置

logging:
  level: 
    com.qiumin.client.paymentClient: debug   #eureka日志打印
  • 编写客户端
package com.qiumin.client;

import com.qiumin.pojo.CommonResult;
import com.qiumin.pojo.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Component
@FeignClient(value = "CLOUD-PROVIDE-PAYMENT")  //指定注册到注册中心的该服务名下的接口方法

public interface paymentClient {
    //服务端即服务提供者的接口方法
    @RequestMapping("/payment/query/{id}")   //服务端的访问地址
    CommonResult<Payment> query(@PathVariable("id") int id);
    
    //服务端即服务提供者的接口方法
    @RequestMapping("/payment/create")
    CommonResult<Payment> insert(Payment payment);
}
  • 配置eureka日志
package com.qiumin.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class FeignConfig {

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;  //日志全打印
    }
}
  • 业务类编写
package com.qiumin.controller;

import com.qiumin.client.paymentClient;
import com.qiumin.pojo.CommonResult;
import com.qiumin.pojo.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class OrderController {

    @Autowired
    paymentClient paymentClient;

    @RequestMapping("/consumer/payment/query/{id}")  //调用该路径实际是通过openfeign调用远程服务
    public CommonResult<Payment> query(@PathVariable("id") int id){
        log.info("进入了该方法!!");
        return paymentClient.query(id);
    }

    @RequestMapping("/consumer/payment/create")
    public CommonResult<Payment> insert(Payment payment){
       return paymentClient.insert(payment);
    }

    @RequestMapping("/test")
    public String test(){
        return "成功";
    }

}
  • 主启动类
package com.qiumin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient //开启eureka客户端
@EnableFeignClients  //开启openfeign远程调用
public class consumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(consumerApplication.class, args);
    }
}

  • 启动测试

在这里插入图片描述

  • 使用openfeign远程服务调用成功。

3、openfeign整合hystrix

3.1 创建带服务降级的客户端

在这里插入图片描述

  • 改pom.xml
<dependencies>
    <!--hystrix-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        <version>2.2.10.RELEASE</version>
    </dependency>
    <!--开启openfeign-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!--eureka客户端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--自定义的jar包-->
    <dependency>
        <groupId>com.qiu</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--web依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--图形化处理依赖,与后面的图形化监控可用-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
        <version>2.6.5</version>
    </dependency>
    <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <version>2.6.5</version>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <!--lombok依赖-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!--test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

  • 改yaml
server:
  port: 8888

spring:
  application:
    name: cloud-consumer-order

eureka:
  client:
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:7001/eureka

feign:
  circuitbreaker:
    enabled: true    //开启服务降级
  • Client
package com.qiumin.client;

import com.qiumin.pojo.CommonResult;
import com.qiumin.pojo.Payment;
import com.qiumin.service.FallbackService;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Component
@FeignClient(value = "CLOUD-PROVIDE-PAYMENT",fallback = FallbackService.class) //对应一个服务降级的类,见下
public interface paymentClient {

    @RequestMapping("/payment/query/{id}")
    CommonResult<Payment> query(@PathVariable("id") int id);

    @RequestMapping("/payment/create")
    CommonResult<Payment> create(Payment payment);
}
  • 服务降级处理类

该类实现client接口,即接口中的所有方法

package com.qiumin.service;

import com.qiumin.client.paymentClient;
import com.qiumin.pojo.CommonResult;
import com.qiumin.pojo.Payment;
import org.springframework.stereotype.Component;

@Component
public class FallbackService implements paymentClient {
    @Override
    public CommonResult<Payment> query(int id) {
        return new CommonResult(444,"请求获取数据超时或错误,进行了服务降级操作!!!",null);
    }

    @Override
    public CommonResult<Payment> create(Payment payment) {
        return new CommonResult(444,"请求获取数据超时或错误,进行了服务降级操作!!!",null);
    }
}
  • controller
package com.qiumin.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.qiumin.client.paymentClient;
import com.qiumin.pojo.CommonResult;
import com.qiumin.pojo.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
@Slf4j
public class OrderController {

    @Autowired
    paymentClient paymentClient;

    @RequestMapping("/consumer/payment/query/{id}")
    @ResponseBody
    //注意使用该方法时不需加  @HystrixCommand注解
    public CommonResult<Payment> query(@PathVariable("id") int id){
        log.info("进入了该方法!!");

        return paymentClient.query(id);
    }

    @RequestMapping("/consumer/payment/create")
    @ResponseBody
    public CommonResult<Payment> create(Payment payment){
        return paymentClient.create(payment);
    }

    @RequestMapping("/test")
    @ResponseBody
    public String test(){
        return "成功";
    }

}
  • 主启动类
package com.qiumin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient     //eureka
@EnableFeignClients     //开启openfeign远程调用
@EnableCircuitBreaker  //开启服务降级
public class consumerHOApplication {
    public static void main(String[] args) {
        SpringApplication.run(consumerHOApplication.class, args);
    }
}
  • 启动测试

    • 成功测试

在这里插入图片描述

  • 服务降级测试【将服务的提供方关闭即宕机】

在这里插入图片描述

3.2 注意
  • 在客户端的controller有错误的代码【如:age=10/0】时必须单独创建一个服务降级方法兜底,否则会报错。
  • 服务端可有错误代码,因为client接口中的所有方法都有兜底的服务降级方法。
  • hystrix相关后续更新,详细介绍。

qiumin

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值