spring cloud alibaba -远程调用(openFeign)
使用feignClient远程调用案例
/mvc注解的两套使用逻辑
- 放在controller上 是接受这样的请求
- 放在FeignClient上 是发送这样的请求`
package com.nie.order;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.annotation.NacosConfig;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@EnableFeignClients//开启Feign远程调用功能
@SpringBootApplication
@EnableDiscoveryClient
public class OrderMainApplication {
public static void main(String[] args) {
SpringApplication.run(OrderMainApplication.class,args);
}
@Bean
ApplicationRunner applicationRunner(NacosConfigManager nacosConfigManager) {
return args -> {
ConfigService configService = nacosConfigManager.getConfigService();
configService.addListener("service-order.properties",
"DEFAULT_GROUP", new Listener() {
@Override
public Executor getExecutor() {
return Executors.newFixedThreadPool(4);
}
@Override
public void receiveConfigInfo(String configInfo) {
System.out.println("变化的配置信息: " + configInfo);
//模拟一下
System.out.println("进行邮件通知");
}
});
System.out.println("=======");
};
}
}
package com.nie.order.feign;
import com.nie.product.bean.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
@FeignClient(value = "service-product") //feign客户端
public interface ProductFeignClient {
//mvc注解的两套使用逻辑
//放在controller上 是接受这样的请求
//放在FeignClient上 是发送这样的请求
@GetMapping("/product/{id}")
Product getPoductFeign(@PathVariable("id") Long id);
}
package com.nie.order.service.Impl;
import com.nie.order.bean.Order;
import com.nie.order.feign.ProductFeignClient;
import com.nie.order.service.OrderService;
import com.nie.product.bean.Product;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private ProductFeignClient productFeignClient;
public Order createOrder(Long productId , Long userId) {
Product product= productFeignClient.getPoductFeign(productId);
Order order = new Order();
order.setId(1L);
//TODO 总金额
order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum())));
order.setUserId(userId);
order.setNickName("zhangsan");
order.setAddress("小聂");
//TODO 远程查询商品列表
order.setProductList(Arrays.asList(product));
return order;
}
private Product getProduct(Long productId) {
List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
ServiceInstance serviceInstance = instances.get(0);
String url="http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/product/"+productId;
Product product = restTemplate.getForObject(url, Product.class);
return product;
}
private Product getProduct1(Long productId) {
String url="http://service-product/product/"+productId;
Product product = restTemplate.getForObject(url, Product.class);
return product;
}
}
运行结果为:
说明他是调用成功的
注意
使用FeignClient是负载均衡的
日志
如果我们要查看远程调用的日志的话我们只需要做两步
第一:写入日志级别
logging:
level:
com.nie.order.feign: debug
第二:
@Configuration
public class OrderConfig {
@Bean
Logger.Level feignLogLevel(){
return Logger.Level.FULL;
}
}
这样就可以了 日志输出了get方式的 请求路径
超时控制
default为默认设置 如果自己没设置则采用默认设置
spring:
cloud:
openfeign:
client:
config:
default:
logger-level: full
connect-timeout: 3000
read-timeout: 5000
service-product:
logger-level: full
connect-timeout: 3000
read-timeout: 5000
重试机制
远程调用超时失败后,还可以进行多次尝试,如果某次成功返回ok,如果多次依然失败则结束调用,返回错误
如果不传参数 采用默认的
@Configuration
public class OrderConfig {
@Bean
Retryer retryer(){
return new Retryer.Default();
}
}
拦截器
package com.nie.order.inertceptor;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
public class XTokenRequestInterceptor implements RequestInterceptor {
/**
* 请求拦截器
* @param requestTemplate
*/
@Override
public void apply(RequestTemplate requestTemplate) {
System.out.println("XTokenRequestInterceptor .....................");
requestTemplate.header("X-Token", UUID.randomUUID().toString());
}
}
当请求后会拦截 生成一个X-Token
兜底返回(Fallback)
兜底返回的目的是在远程调用失败时 能拿到一个数据 (可能是假数据) 让业务接着运行
package com.nie.order.feign;
import com.nie.order.feign.fallback.ProductFeignClientFallback;
import com.nie.product.bean.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
@FeignClient(value = "service-product",fallback = ProductFeignClientFallback.class ) //feign客户端
public interface ProductFeignClient {
//mvc注解的两套使用逻辑
//放在controller上 是接受这样的请求
//放在FeignClient上 是发送这样的请求
@GetMapping("/product/{id}")
Product getPoductFeign(@PathVariable("id") Long id);
}
package com.nie.order.feign.fallback;
import com.nie.order.feign.ProductFeignClient;
import com.nie.product.bean.Product;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
@Component
public class ProductFeignClientFallback implements ProductFeignClient {
@Override
public Product getPoductFeign(Long id) {
System.out.println("兜底回调.......");
Product product = new Product();
product.setId(id);
product.setNum(1);
product.setProductName("苹果");
product.setPrice(new BigDecimal(3));
return product;
}
}
导入sentinel依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
feign:
sentinel:
enabled: true
如果远程调用访问错误的时候 那么他会传入我自己构造的Product对象的数据