基於上一篇文章: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結合