Spring Cloud(4):Feign的使用

基于上一篇文章:https://www.cnblogs.com/xuyiqing/p/10867739.html

使用Ribbon实现了订单服务调用商品服务的Demo

下面介绍如何使用Feign实现这个Demo

 

Feign:伪RPC客户端,底层基于HTTP

在订单服务的POM中加入依赖

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

并在启动类中加入注解,这里已经加入的Ribbon可以保留

package org.dreamtech.orderservice;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;

import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {

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

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Feign接口

package org.dreamtech.orderservice.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "product-service")
public interface ProductClient {
    @RequestMapping("/api/product/find")
    String findById(@RequestParam(value = "id")int id);
}

使用

package org.dreamtech.orderservice.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import org.dreamtech.orderservice.domain.ProductOrder;
import org.dreamtech.orderservice.service.ProductClient;
import org.dreamtech.orderservice.service.ProductOrderService;
import org.dreamtech.orderservice.utils.JsonUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.Map;
import java.util.UUID;

@Service
public class ProductOrderServiceImpl implements ProductOrderService {

    private final ProductClient productClient;

    @Autowired
    public ProductOrderServiceImpl(ProductClient productClient) {
        this.productClient = productClient;
    }

    @Override
    public ProductOrder save(int userId, int productId) {

        String response = productClient.findById(productId);
        JsonNode jsonNode = JsonUtils.str2JsonNode(response);

        ProductOrder productOrder = new ProductOrder();
        productOrder.setCreateTime(new Date());
        productOrder.setUserId(userId);
        productOrder.setTradeNo(UUID.randomUUID().toString());

        productOrder.setProductName(jsonNode.get("name").toString());
        productOrder.setPrice(Integer.parseInt(jsonNode.get("price").toString()));

        return productOrder;
    }
}

工具类

package org.dreamtech.orderservice.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class JsonUtils {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    public static JsonNode str2JsonNode(String str) {
        try {
            return objectMapper.readTree(str);
        } catch (IOException e) {
            return null;
        }
    }
}

 

重启Eureka Server和多个product-service:

访问http://localhost:8781/api/order/save?user_id=1&product_id=1,成功

 

使用注意:

1.Feign的路由和服务的路由必须一致(如这里的/api/product/find)

2.注意服务名的一致

3.参数必须加入@RequestParam确保参数名和服务的一致

4.如果服务的参数加入了@RequestBody,Feign客户端也要加入@RequestBody并修改为POST方式

5.这里获取到JSON直接解析,实际情况可以抽取公共实体类为JAR包引入返回实体类

 

FeignClient原理:

 

查看Clien类可以发现,Feign的实现是通过HTTP形式:

        HttpURLConnection convertAndSend(Request request, Options options) throws IOException {
            HttpURLConnection connection = (HttpURLConnection)(new URL(request.url())).openConnection();
            if (connection instanceof HttpsURLConnection) {
                HttpsURLConnection sslCon = (HttpsURLConnection)connection;
                if (this.sslContextFactory != null) {
                    sslCon.setSSLSocketFactory(this.sslContextFactory);
                }

                if (this.hostnameVerifier != null) {
                    sslCon.setHostnameVerifier(this.hostnameVerifier);
                }
            }

 

Client的实现类中:

发现Feign调用了Ribbon做负载均衡

    public Response execute(Request request, Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request, uriWithoutHost);
            IClientConfig requestConfig = this.getClientConfig(options, clientName);
            return ((RibbonResponse)this.lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse();
        } catch (ClientException var8) {
            IOException io = this.findIOException(var8);
            if (io != null) {
                throw io;
            } else {
                throw new RuntimeException(var8);
            }
        }
    }

 

处理超时问题:实际情况服务和调用者之间的通信可能较慢,这时候Feign会报错

如下配置Feign超时实际为10秒

feign:
  client:
    config:
      default:
        connectTimeout: 10000
        readTimeout: 10000

Feign默认超时为1秒

 

Feign和Ribbon的对比:

1.Feign里面包含Ribbon,Feign的负载均衡由Ribbon实现

2.Feign更方便,可以和Hystrix结合

posted @ 2019-05-16 11:33  4ra1n  阅读(828)  评论(0编辑  收藏  举报