Feign的使用
使用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包引入返回实体类
查看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结合