SpringCloud分布式微服务云架构-SpringCloud feign

feigin是一种模板化,声明式的http客户端,feign可以通过注解绑定到接口上来简化Http请求访问。了解springcloud架构可以加求求:三五三六二四七二五九,当然我们也可以在创建Feign对象时定制自定义解码器(xml或者json等格式解析)和错误处理。
一、添加SpringCloud对feign的支持
gradle配置:

compile('org.springframework.cloud:spring-cloud-starter-feign')

feigin最基本使用方法:

interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}

static class Contributor {
  String login;
  int contributions;
}

public static void main(String... args) {
  GitHub github = Feign.builder()
                       .decoder(new GsonDecoder())
                       .target(GitHub.class, "https://api.github.com");

  // Fetch and print a list of the contributors to this library.
  List<Contributor> contributors = github.contributors("OpenFeign", "feign");
  for (Contributor contributor : contributors) {
    System.out.println(contributor.login + " (" + contributor.contributions + ")");
  }
}

feign发送json与xml的格式的http请求:

interface LoginClient {

  @RequestLine("POST /")
  @Headers("Content-Type: application/xml")
  @Body("<login \"user_name\"=\"{user_name}\" \"password\"=\"{password}\"/>")
  void xml(@Param("user_name") String user, @Param("password") String password);

  @RequestLine("POST /")
  @Headers("Content-Type: application/json")
  // json curly braces must be escaped!
  @Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
  void json(@Param("user_name") String user, @Param("password") String password);
}

注意示例中需要添加对gson的支持

feign发送https信任所有证书的代码:

注意示例中需要添加对gson的支持

feign发送https信任所有证书的代码:

二、在SpringCloud中使用Feign
比如说注册中心有如下服务:
在这里插入图片描述
1)application.yml的配置:

spring:
  application:
    name: demo-consumer
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8080/eureka,http://localhost:8081/eureka
server:
  port: 8090

2)创建接口

package com.bdqn.lyrk.consumer.demo.api;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient("demo")
public interface DemoConfigService {

    @RequestMapping("/demo.do")
    String demoService();
}

注意在接口上加上注解:@FeignClient(“demo”) 注解里的参数是在eureka注册的服务名

3)编写启动类:

package com.bdqn.lyrk.consumer.demo;

import com.bdqn.lyrk.consumer.demo.api.DemoConfigService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.ConfigurableApplicationContext;

@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class DemoConsumerProvider {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoConsumerProvider.class, args);
        DemoConfigService demoConfigService = applicationContext.getBean(DemoConfigService.class);
        System.out.println(demoConfigService.demoService());
    }
}

注意在启动类上加上@EnableFeignClients来开启Feign功能

运行后输出:
在这里插入图片描述
此时我们可以发现使用feign客户端我们访问服务的代码简洁了好多

  1. feign多参数设置方法
service-api模块的接口定义:

public interface IBillService {

    @PostMapping("/queryBill")
    List<BillDTO> queryOrders(@RequestBody  BillsVO billsVO);
}

相关服务实现类:


@RestController
public class BillServiceImpl implements IBillService {

    @Autowired
    private BillMapper billMapper;

    @PostMapping("/queryBill")
    @Override
    public List<BillDTO> queryOrders(@RequestBody BillsVO billsVO) {
        return billMapper.query(BeanMap.create(billsVO));
    }
}

注意 需要在接口定义与实现类的参数上加@RequestBody注解

三、feign中的使用Hystrix
  1) 在@FeignClient中有两个属性我们值得关注一下,它们分别是fallBack和fallBackFactory,当然我们系统里要添加Hystrix支持并且在属性文件里设置:

feign.hystrix.enabled=true

同样我们要在启动类里加上@EnableCircuitBreaker注解打开Hystrix保护

2) fallBack属性很简单,用来设置降级方法,当feign请求服务失败时所调用的方法, 这里我给出接口的例子:

首先定义一个接口:IOrderService

package com.bdqn.lyrk.service.api;

import com.bdqn.lyrk.service.dto.OrderDTO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;


public interface IOrderService {

    @GetMapping("/orderId/{orderId}")
    OrderDTO getOrderById(@PathVariable("orderId") Integer orderId);

    @GetMapping("/errorOrder")
    OrderDTO getErrorOrder();
}

其次定义Feign的接口OrderServiceClient继承IOrderService

package com.bdqn.lyrk.order.service.consumer.feign;

import com.bdqn.lyrk.service.api.IOrderService;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Primary;

@Primary
@FeignClient(value = "ORDER-SERVER", fallBack="FailedOrderServiceClientImpl.class")
public interface OrderServiceClient extends IOrderService {

}

由于IOrderService不在同一个项目里,而且SpringCloud不推荐服务端和客户端使用同一个接口,所以我采用继承的方式,注意加上@Primary注解以免使用@Autowired时注入失败

在定义实现类:

package com.bdqn.lyrk.order.service.consumer.feign;

import com.bdqn.lyrk.service.dto.OrderDTO;
import org.springframework.stereotype.Component;

@Component
public class FailedOrderServiceClientImpl implements OrderServiceClient {
    @Override
    public OrderDTO getOrderById(Integer orderId) {
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setId(orderId);
        orderDTO.setOrderName("服务中失败的订单,id为:" + orderId);
        return null;
    }

    @Override
    public OrderDTO getErrorOrder() {
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setOrderName("服务中失败的订单");
        orderDTO.setId(-1);
        return orderDTO;
    }
}

最后@FeignClient中设置属性fallBack=“FailedOrderServiceClientImpl.class” 就可以了

3) 当我们需要封装服务端的异常信息时,可以指定fallbackFactory属性,请看下面的例子:

package com.bdqn.lyrk.order.service.consumer.feign;

import com.bdqn.lyrk.service.dto.OrderDTO;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

@Component
public class OrderServiceFallbackFactoryImpl implements FallbackFactory<OrderServiceClient> {
    @Override
    public OrderServiceClient create(Throwable cause) {
       return new OrderServiceClient() {
           @Override
           public OrderDTO getOrderById(Integer orderId) {
               OrderDTO orderDTO = new OrderDTO();
               orderDTO.setOrderName(cause.getMessage());
               return orderDTO;
           }

           @Override
           public OrderDTO getErrorOrder() {
               OrderDTO orderDTO = new OrderDTO();
               orderDTO.setOrderName(cause.getMessage());
               return orderDTO;
           }
       };
    }
}

注意:FallbackFactory的泛型参数一定要指定为@FeignClient修饰的接口,同时不建议fallback与fallbackFactory同时使——了解springcloud架构可以加求求:三五三六二四七二五九

五、 服务端的实现代码:

 package com.bdqn.lyrk.springcloud.order.service;

import com.bdqn.lyrk.service.api.IOrderService;
import com.bdqn.lyrk.service.dto.OrderDTO;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;


@RestController
public class OrderServiceImpl implements IOrderService {

    @HystrixCommand(fallbackMethod = "errorDTO",
            commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")}

    )
    @GetMapping("/orderId/{orderId}")
    @Override
    public OrderDTO getOrderById(@PathVariable("orderId") Integer orderId) {
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setId(orderId);
        orderDTO.setOrderName("订单ID为" + orderId + "的订单");
        try {
            TimeUnit.SECONDS.sleep(orderId);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        return orderDTO;
    }

    @Override
    public OrderDTO getErrorOrder() {
        System.out.println(1 / 0);
        return null;
    }

    public OrderDTO errorDTO(Integer orderId) {
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setId(-1);
        orderDTO.setOrderName("错误的订单,请重试");
        return orderDTO;
    }
}
发布了87 篇原创文章 · 获赞 78 · 访问量 4048

猜你喜欢

转载自blog.csdn.net/weixin_45821812/article/details/104016183