SpringCloud声明式服务调用Feign

org.springframework.cloud

spring-cloud-starter-feign

org.springframework.boot

spring-boot-starter-web

2.在主类上使用@EnableFeignClients注解开启SpringCloudFeign的支持功能

@SpringBootApplication

@EnableDiscoveryClient

@EnableFeignClients

public class FeignApplication {

public static void main(String[] args) {

SpringApplication.run(FeignApplication.class, args);

}

}

3.接口定义:我们这里调用USER-SERVICE服务,在该服务中创建一个查询所有用户的接口,然后在feign-consumer中定义。

USER-SERVICE

@RestController

public class UserFeignController {

@Autowired

private UserRepository userRepository;

@GetMapping(“/feign/user/list”)

public List findAllUser(){

return  userRepository.findAll();

}

}

feign-consumer

@FeignClient(value = “USER-SERVICE”)

public interface UserService {

@GetMapping(“/feign/user/list”)

List findAll();

}

使用@FeignClient注解指定服务名来绑定服务,如果不指定服务名,启动项目将会报错。然后创建一个接口与调用普通的service一样调用UserService。

@RestController

public class FeignConsumerController {

@Autowired

private UserService userService;

@GetMapping(value = “/feign/find”)

public List findAllUser(){

return userService.findAll();

}

}

最后修改配置文件

spring:

application:

name: feign-consumer

server:

port: 50000

eureka:

client:

service-url:

defaultZone: http://localhost:8888/eureka/

这里使用的User对象与前面ARTICLE-SERVICE的User对象一样。依次启动服务注册中心、服务提供方、服务消费方。然后调用/feign/find接口,可以正常返回数据。

参数绑定

在实际开发中,像上面那种不带参数的接口可能少之又少。Feign提供了多种参数绑定的方式。

在服务提供的UserFeignController中添加以下三个接口:

/**

  • 根据id查询用户,将参数包含在Request参数

*/

@GetMapping(“/feign/userById”)

public User finUserById(@RequestParam Long id){

logger.info(“>>>>>>>>>>>id:{}<<<<<<<<<<<<<”,id);

return userRepository.findOne(id);

}

/**

  • 带有Header信息的请求,需要注意的是,使用请求头传递参数,如果参数是中文会出现乱码

  • 所以需要使用 URLEncoder.encode(name,“UTF-8”) 先编码

  • 后解码  URLDecoder.decode(name,“UTF-8”);

*/

@GetMapping(“/feign/header/user”)

public User findUserHeader(@RequestHeader String name,@RequestHeader Long id,@RequestHeader Integer age) throws UnsupportedEncodingException {

User user = new User();

user.setId(id);

user.setUsername( URLDecoder.decode(name,“UTF-8”));

user.setAge(age);

logger.info(“>>>>>>>>>>>findUserHeader{}<<<<<<<<<<<<<”,user);

return user;

}

/***

  • 带有RequestBody以及请求相应体是一个对象的请求

*/

@PostMapping(“/feign/insert”)

public User insertUser(@RequestBody User user){

userRepository.save(user);

return userRepository.findOne(user.getId());

}

直接将上面添加的接口复制到消费方的Service接口中,删除方法体。需要注意的是:在SpringMVC中@RequestParam和@RequestHeader注解,如果我们不指定value,则默认采用参数的名字作为其value,但是在Feign中,这个value必须明确指定,否则会报错。

/**

  • 根据id查询用户,将参数包含在Request参数

*/

@GetMapping(“/feign/userById”)

User finUserById(@RequestParam(“id”) Long id);

/**

  • 带有Header信息的请求

*/

@GetMapping(“/feign/header/user”)

User findUserHeader(@RequestHeader(“name”) String name, @RequestHeader(“id”) Long id,@RequestHeader(“age”) Integer age);

/**

  • 带有RequestBody以及请求相应体是一个对象的请求

*/

@PostMapping(“/feign/insert”)

User insertUser(@RequestBody User user);

测试接口:

@GetMapping(“/testFeign”)

public void testFeign() throws UnsupportedEncodingException {

User user = userService.finUserById(2L);

logger.info(“>>>>>>>>>>>>Request参数:{}>>>>>>>>>>>>>”,user);

User user2 = userService.findUserHeader(URLEncoder.encode(“呜呜呜呜”,“UTF-8”), 3L,1000);

logger.info(“>>>>>>>>>>>>Header:{}>>>>>>>>>>>>>”,user2);

User save_user = new User(5L,“嘻嘻嘻”,56);

User users = userService.insertUser(save_user);

logger.info(“>>>>>>>>>>>>RequestBody:{}>>>>>>>>>>>>>”,users);

}

继承特性

在上面的例子中,在服务消费方声明接口时都是将服务提供方的Controller复制过来。这么做会出现很多重复代码。在SpringCloudFeign中提供了继承特性来帮助我们解决这些复制操作。

1. 创建建一个基础的Maven工程,命名service-api,以复用DTO与接口定义。这里需要用到SpringMVC的注解,所以需要引入依赖:

org.springframework.boot

spring-boot-starter-web

将上面的User对象复制到api中,并创建UserService

@Data

@AllArgsConstructor

@NoArgsConstructor

public class User implements Serializable {

private Long id;

private String username;

private int age;

}

@RequestMapping(“/rafactor”)

public interface UserService {

@GetMapping(“/feign/user/list”)

List findAll();

/**

  • 根据id查询用户,将参数包含在Request参数

*/

@GetMapping(“/feign/userById”)

User finUserById(@RequestParam(“id”) Long id);

/**

  • 带有Header信息的请求

*/

@GetMapping(“/feign/header/user”)

User findUserHeader(@RequestHeader(“name”) String name, @RequestHeader(“id”) Long id, @RequestHeader(“age”) Integer age);

/**

  • 带有RequestBody以及请求相应体是一个对象的请求

*/

@PostMapping(“/feign/insert”)

User insertUser(@RequestBody User user);

}

重构USER-SERVICE,在pom文件中新增service-api;并创建UserRafactorController类实现service-api的UserService类;

com.wqh

sevice-api

0.0.1-SNAPSHOT

@RestController

public class UserRafactorController implements UserService{

private final Logger logger = LoggerFactory.getLogger(UserRafactorController.class);

@Autowired

private UserRepository userRepository;

@Override

public List findAll() {

return null;

}

@Override

public User finUserById(Long id) {

logger.info(“>>>>>>>>>>>Rafactor id:{}<<<<<<<<<<<<<”,id);

com.wqh.user.entity.User one = userRepository.findOne(id);

User user = new User(one.getId(),one.getUsername(),one.getAge());

return user;

}

@Override

public User findUserHeader(@RequestHeader(“name”)String name, @RequestHeader(“id”)Long id,@RequestHeader(“age”) Integer age) {

User user = new User();

user.setId(id);

try {

user.setUsername( URLDecoder.decode(name,“UTF-8”));

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

}

user.setAge(age);

logger.info(“>>>>>>>>>>>Rafactor findUserHeader{}<<<<<<<<<<<<<”,user);

return user;

}

@Override

public User insertUser(@RequestBody User user) {

logger.info(“>>>>>>>>>>>Rafactor RequestBody{}<<<<<<<<<<<<<”,user);

return user;

}

}

该类不需要使用@RequestMapping注解来定义请求映射,参数注解需要添加,并且在类上添加@RestController注解。

4. 重构feign-consumer,添加service-api的依赖

com.wqh

sevice-api

0.0.1-SNAPSHOT

创建UserRafactorService接口继承UserService接口

@FeignClient(value = “USER-SERVICE”)

public interface UserRafactorService extends UserService {

}

测试接口

@GetMapping(“/testRafactorService”)

public void testRafactorService() throws UnsupportedEncodingException {

com.wqh.api.dto.User user = userRafactorService.finUserById(2L);

logger.info(“>>>>>>>>>>>>Rafactor Request参数:{}>>>>>>>>>>>>>”,user);

com.wqh.api.dto.User user2 = userRafactorService.findUserHeader(URLEncoder.encode(“呜呜呜呜”,“UTF-8”), 3L,1000);

logger.info(“>>>>>>>>>>>>Rafactor Header:{}>>>>>>>>>>>>>”,user2);

com.wqh.api.dto.User save_user = new com.wqh.api.dto.User(5L,“嘻嘻嘻”,56);

com.wqh.api.dto.User users = userRafactorService.insertUser(save_user);

logger.info(“>>>>>>>>>>>>Rafactor RequestBody:{}>>>>>>>>>>>>>”,users);

}

注意:这里对于对象之间的处理是存在问题,就不详细的修改了,主要是为了Feign的继承特性。

Feign配置详解

Ribbon配置

在Feign中配置Ribbon非常简单,直接在application.properties中配置即可,如:

设置连接超时时间

ribbon.ConnectTimeout=500

设置读取超时时间

ribbon.ReadTimeout=5000

对所有操作请求都进行重试

ribbon.OkToRetryOnAllOperations=true

切换实例的重试次数

ribbon.MaxAutoRetriesNextServer=2

对当前实例的重试次数

ribbon.MaxAutoRetries=1

同样也可以指定服务配置,直接在application.properties中采用.ribbon.key=value的格式进行配置,如下:

设置针对user-service服务的连接超时时间

user-service.ribbon.ConnectTimeout=600

设置针对user-service服务的读取超时时间

user-service.ribbon.ReadTimeout=6000

设置针对user-service服务所有操作请求都进行重试

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

ActiveMQ消息中间件面试专题

  • 什么是ActiveMQ?
  • ActiveMQ服务器宕机怎么办?
  • 丢消息怎么办?
  • 持久化消息非常慢怎么办?
  • 消息的不均匀消费怎么办?
  • 死信队列怎么办?
  • ActiveMQ中的消息重发时间间隔和重发次数吗?

ActiveMQ消息中间件面试专题解析拓展:

BAT面试文档:ActiveMQ+redis+Spring+高并发多线程+JVM


redis面试专题及答案

  • 支持一致性哈希的客户端有哪些?
  • Redis与其他key-value存储有什么不同?
  • Redis的内存占用情况怎么样?
  • 都有哪些办法可以降低Redis的内存使用情况呢?
  • 查看Redis使用情况及状态信息用什么命令?
  • Redis的内存用完了会发生什么?
  • Redis是单线程的,如何提高多核CPU的利用率?

BAT面试文档:ActiveMQ+redis+Spring+高并发多线程+JVM


Spring面试专题及答案

  • 谈谈你对 Spring 的理解
  • Spring 有哪些优点?
  • Spring 中的设计模式
  • 怎样开启注解装配以及常用注解
  • 简单介绍下 Spring bean 的生命周期

Spring面试答案解析拓展

BAT面试文档:ActiveMQ+redis+Spring+高并发多线程+JVM


高并发多线程面试专题

  • 现在有线程 T1、T2 和 T3。你如何确保 T2 线程在 T1 之后执行,并且 T3 线程在 T2 之后执行?
  • Java 中新的 Lock 接口相对于同步代码块(synchronized block)有什么优势?如果让你实现一个高性能缓存,支持并发读取和单一写入,你如何保证数据完整性。
  • Java 中 wait 和 sleep 方法有什么区别?
  • 如何在 Java 中实现一个阻塞队列?
  • 如何在 Java 中编写代码解决生产者消费者问题?
  • 写一段死锁代码。你在 Java 中如何解决死锁?

高并发多线程面试解析与拓展

BAT面试文档:ActiveMQ+redis+Spring+高并发多线程+JVM


jvm面试专题与解析

  • JVM 由哪些部分组成?
  • JVM 内存划分?
  • Java 的内存模型?
  • 引用的分类?
  • GC什么时候开始?

JVM面试专题解析与拓展!

BAT面试文档:ActiveMQ+redis+Spring+高并发多线程+JVM

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
ng bean 的生命周期

Spring面试答案解析拓展

[外链图片转存中…(img-o2oj1PSl-1713418557774)]


高并发多线程面试专题

  • 现在有线程 T1、T2 和 T3。你如何确保 T2 线程在 T1 之后执行,并且 T3 线程在 T2 之后执行?
  • Java 中新的 Lock 接口相对于同步代码块(synchronized block)有什么优势?如果让你实现一个高性能缓存,支持并发读取和单一写入,你如何保证数据完整性。
  • Java 中 wait 和 sleep 方法有什么区别?
  • 如何在 Java 中实现一个阻塞队列?
  • 如何在 Java 中编写代码解决生产者消费者问题?
  • 写一段死锁代码。你在 Java 中如何解决死锁?

高并发多线程面试解析与拓展

[外链图片转存中…(img-WfHQj7gI-1713418557774)]


jvm面试专题与解析

  • JVM 由哪些部分组成?
  • JVM 内存划分?
  • Java 的内存模型?
  • 引用的分类?
  • GC什么时候开始?

JVM面试专题解析与拓展!

[外链图片转存中…(img-puG4nOts-1713418557775)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值