Spring Cloud(4):Feign的使用

基於上一篇文章:http://www.javashuo.com/article/p-ulatwvht-gb.htmlhtml

使用Ribbon實現了訂單服務調用商品服務的Demojava

下面介紹如何使用Feign實現這個Demoweb

 

Feign:僞RPC客戶端,底層基於HTTPspring

在訂單服務的POM中加入依賴json

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

並在啓動類中加入註解,這裏已經加入的Ribbon能夠保留api

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接口app

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;
    }
}

工具類dom

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:ide

訪問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結合

相關文章
相關標籤/搜索