基于Feign实现服务调用
Feign是什么
feign是Spring Cloud提供的一个声明式的伪Http客户端,它使得调用远程服务就像调用本地服务一样。
nacos很好地兼容了feign,feign默认集成了ribbon。所以在nacos下使用feign默认就实现了负载均衡的效果。
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果
Feign 采用的是基于接口的注解
Feign 整合了ribbon,具有负载均衡的能力
整合了Hystrix(SpringCloud官方那一套),具有熔断的能力
Feign的使用
首先,在orders中引入feign依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>xxx</version>
</dependency>
/// 高版本需要添加依赖 loadbalance
<!--解决:Did you forget to include spring-cloud-starter-loadbalancer?-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<version>xxx</version>
</dependency>
然后,在主启动类上添加@EnableFeignClients注解
@SpringBootApplication
@MapperScan("com.example.orders.mapper")
@EnableDiscoveryClient
@EnableFeignClients
public class OrdersAppliaction {
public static void main(String[] args) {
SpringApplication.run(OrdersAppliaction.class,args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
接着,在order这一方,创建一个IProductService接口,并使用Feign实现微服务调用
@FeignClient("micro-product") //此处名称必须为提供者的实例名称
/// 这里若是没有集成springcloud 也可以写 固定的地址
/// 如 @FeignClient(name = "order" , url = "localhost:8081/product")
public interface IProductService {
@GetMapping("/product/{pid}")
ResultVO findByPid(@PathVariable("pid") Integer pid);
@GetMapping("/product/testLoadBalance")
ResultVO testLoadBalance();
}
最后,修改OrderController代码,并测试。注意,这里使用IProductService替换掉了RestTemplate和DiscoveryClient。
RestController
@RequestMapping("/orders")
@Slf4j
public class OrdersController {
@Resource
private IProductService productService;
@Resource
private IOrdersService ordersService;
@GetMapping("/{pid}/{count}")
public ResultVO addOrder(@PathVariable Integer pid,@PathVariable Integer count){
ResultVO resultVO = productService.findByPid(pid);
Map map = (Map)resultVO.getE();
Orders orders = new Orders();
BeanUtil.fillBeanWithMap(map, orders, true);
orders.setCount(count);
orders.setUname("向来痴");
log.info("填充完毕{}", orders);
ResultVO<Orders> resultOrderVO = null;
if(ordersService.save(orders))
resultOrderVO = new ResultVO<Orders>(Constants.OPEN_SUCCESS, "订单插入成功",orders);
else
resultOrderVO = new ResultVO<Orders>(Constants.OPEN_FAILURE, "订单插入失败",orders);
return resultOrderVO;
}
@GetMapping("/testLoadBalance")
public ResultVO testLoadBalance() {
ResultVO resultVO = productService.testLoadBalance();
return resultVO;
}
}
访问地址:
http://127.0.0.1:9000/orders/testLoadBalance
http://127.0.0.1:9000/orders/1/5
坑:
Request method 'POST' not supported
如果feign代理的是get请求,则每个参数必须带上注解,否则会报post not support!
1.首先保障Client接口和控制层接口的请求方式一致
2.如果有参数,必须指定@RequestParam
3:版本降级
Pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
提供者
@GetMapping("/findById")
public Userinfo findById(@RequestBody Integer uid){
return userinfoMapper.selectByPrimaryKey(uid);
}
消费者
@GetMapping("/findById")
public Userinfo findById(Integer uid){
System.out.println("into "+uid);
return userinfoService.findById(uid);
}
feign接口
@FeignClient("mircoprovider")
public interface UserinfoService {
@GetMapping("findById")
public Userinfo findById(@RequestBody Integer uid);
}
打完收工
OpenFeign请求传递对象
OpenFeign在传递参数为对象类型是为空
解决的方法很简单,在参数前面加上@RequestBody即可
回顾 @RequestBody
@RequestBody注解常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json或者是application/xml等。一般情况下来说常用其来处理application/json类型。
消费者
@GetMapping("/save")
public String save(Userinfo info){
System.out.println("into userinfo is "+info);
return userinfoService.save(info);
}
feign接口
@PostMapping("/save")
public String save(@RequestBody Userinfo info);
提供者
@PostMapping("/save")
public String save(@RequestBody Userinfo info){
System.out.println("插入对象"+info);
userinfoMapper.insert(info);
return "ok";
}
当然,还有其它几种姿势
feign接口
@RequestMapping(value = "/user", method = RequestMethod.POST, consumes = "application/json")
String getUserId(@RequestBody User user);
1、consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
2、produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
3、或者 = MediaType.APPLICATION_JSON_VALUE
4、正常来说,在FeignClient的接口中,也不需要在参数上注解@RequestBody ,只需要在实现类上添加@RequestBody 注解即可。
提供者
@PostMapping("/save")
public String save(@RequestBody Userinfo info){
System.out.println("插入对象"+info);
userinfoMapper.insert(info);
return "ok";
}