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的区别:

feign openfeign
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显示示例:

openfeign+hystrix_第1张图片

2、openfeign的基本使用

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

2.1 父工程pom.xml

<dependencyManagement>   
        <dependencies>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-dependenciesartifactId>
                <version>2.6.5version>
                <type>pomtype>
                
                <scope>importscope>
            dependency>
             
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>2021.0.1version>
                <type>pomtype>
                 
                <scope>importscope>
            dependency>
            
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-alibaba-dependenciesartifactId>
                <version>2021.0.1.0version>
                <type>pomtype>
                 
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

2.2 创建eureka注册中心服务

openfeign+hystrix_第2张图片

  • 改pom.xml
<dependencies>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-actuatorartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-devtoolsartifactId>
    dependency>
    
    <dependency>
        <groupId>com.qiugroupId>
        <artifactId>cloud-api-commonsartifactId>
        <version>1.0-SNAPSHOTversion>
    dependency>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
    dependency>
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    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/

openfeign+hystrix_第3张图片

2.3 创建服务提供者 provider

openfeign+hystrix_第4张图片

  • 改pom.xml
<dependencies>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    dependency>
    
    <dependency>
        <groupId>com.qiugroupId>
        <artifactId>cloud-api-commonsartifactId>
        <version>1.0-SNAPSHOTversion>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-actuatorartifactId>
        <version>2.6.5version>
    dependency>
    
    <dependency>
        <groupId>org.mybatis.spring.bootgroupId>
        <artifactId>mybatis-spring-boot-starterartifactId>
        <version>2.2.2version>
    dependency>
    
    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>druid-spring-boot-starterartifactId>
        <version>1.1.10version>
    dependency>
    
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>8.0.28version>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-jdbcartifactId>
        <version>2.2.2.RELEASEversion>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-devtoolsartifactId>
        <version>2.6.5version>
        <scope>runtimescope>
        <optional>trueoptional>
    dependency>
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <optional>trueoptional>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    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 创建客户端即消费端

openfeign+hystrix_第5张图片

  • 改pom.xml
<dependencies>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-openfeignartifactId>
    dependency>
    
    
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    dependency>
    
    <dependency>
        <groupId>com.qiugroupId>
        <artifactId>cloud-api-commonsartifactId>
        <version>1.0-SNAPSHOTversion>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-actuatorartifactId>
        <version>2.6.5version>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-devtoolsartifactId>
        <version>2.6.5version>
        <scope>runtimescope>
        <optional>trueoptional>
    dependency>
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <optional>trueoptional>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    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+hystrix_第6张图片

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

3、openfeign整合hystrix

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

openfeign+hystrix_第7张图片

  • 改pom.xml
<dependencies>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
        <version>2.2.10.RELEASEversion>
    dependency>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-openfeignartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    dependency>
    
    <dependency>
        <groupId>com.qiugroupId>
        <artifactId>cloud-api-commonsartifactId>
        <version>1.0-SNAPSHOTversion>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-actuatorartifactId>
        <version>2.6.5version>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-devtoolsartifactId>
        <version>2.6.5version>
        <scope>runtimescope>
        <optional>trueoptional>
    dependency>
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <optional>trueoptional>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    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);
    }
}
  • 启动测试

    • 成功测试

openfeign+hystrix_第8张图片

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

openfeign+hystrix_第9张图片

3.2 注意

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

qiumin

你可能感兴趣的:(微服务框架,注册中心,spring-boot,java,spring,maven,微服务)