OpenFeign内部调用上
我们的代码已经搬运完毕了
但是我们的服务之间是无法相互调用的
我们可以使用OpenFeign进行远程调用
一个http调用客户端
提供了更方便的方式让你远程调用其他的服务
Nacos注册中心获取服务的调用地址
如果没有实现OpenFeign
也能实现跨服务的调用 写死成url也行 从Nacos调用
但是OpenFeign可以直接把服务扔给他 就行
1...梳理服务的调用关系 确定哪些服务(接口)需要给内部调用
用户服务 : 没有其他的依赖
题目服务 : userservice
判题服务 : questionService 和 questionSubmitService
2...确定要提供哪些服务
我们找一下我们的公共接口客户端
3...编写service-client部分的代码
就是在这个包里面
编写client部分的代码
由于这个request没有涉及数据库中的操作
为了防止数据因为没有序列化的问题而丢失了
我们会把这个方法写成默认方法 default
将方法定义为 default
是为了在接口中提供一个默认实现,允许实现该接口的类在不重写此方法的情况下,直接使用它。这种设计有助于减少代码重复,提高可维护性,同时允许实现类根据需要覆盖此方法以实现特定的逻辑。
将方法定义为 default
的原因主要包括以下几点:
-
接口演进:
default
方法允许在接口中添加新方法而不破坏现有实现。这使得可以在不影响已有代码的情况下扩展接口功能。 -
代码重用:通过提供默认实现,可以在多个实现类中重用相同的逻辑,减少代码重复。
-
灵活性:实现类可以选择重写
default
方法以提供特定实现,或者直接使用默认实现,增强了灵活性。 -
简化实现:对于一些常用的方法,提供默认实现可以减少实现类的负担,让开发者专注于独特的功能。
这些优势使得 default
方法在现代 Java 编程中变得非常有用。
/**
* 获取当前登录用户
*
* @param request
* @return
*/
default User getLoginUser(HttpServletRequest request) {
// 先判断是否已登录
Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
User currentUser = (User) userObj;
if (currentUser == null || currentUser.getId() == null) {
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
}
// 可以考虑在这里做全局权限校验
return currentUser;
}
这边也是
我们写成默认的方法
也没有必要远程调用
/**
* 是否为管理员
*
* @param user
* @return
*/
default boolean isAdmin(User user) {
return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
}
/**
* 获取脱敏的用户信息
*
* @param user
* @return
*/
default UserVO getUserVO(User user) {
if (user == null) {
return null;
}
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
return userVO;
}
接下来
我们要实现客户端接口
要开启openfeign支持 把我们的接口暴露出去 作为API 给其他服务调用
服务注册到注册中心上去
其他服务从注册中心寻找
怎么实现呢 我们依靠的是一个注解@FeignClient
可以把name的内容理解成客户端要到nacos上去找到的具体服务的名称
@FeignClient(name = "yuoj-backend-user-service", path = "/api/user/inner")
我们的每个服务都有这么一行配置
这段配置主要用于定义一个服务器的基本参数,具体说明如下:
-
address: 0.0.0.0:表示服务器将绑定到所有可用的网络接口上,允许外部访问。
-
port: 8102:服务器将监听8102端口,接收请求。
-
servlet:与服务器处理HTTP请求相关的配置。
-
context-path: /api/user:这是应用的上下文路径,所有访问这个服务器的请求都会以
/api/user
开头。
-
-
session:
-
cookie
:用于配置会话的相关属性。
-
max-age: 2592000:设置会话cookie的最大生存时间,这里是2592000秒(即30天)。
-
path: /api:指定cookie的有效路径,表示只有在以
/api
开头的请求中,服务器才会发送这个cookie。
-
-
简单来说,这段配置用于设定一个API服务器的基本连接参数和会话管理。
需要的是修改每一个服务提供者的context-path 全局请求路径
统一一下 便于我们书写接口路径
服务提供者:理解为接口的实现类 实际提供接口的模块
服务消费者:理解为接口的调用方 需要去找到服务提供者 然后调用
还要指定具体服务的地址
/**
* 根据 id 获取用户
* @param userId
* @return
*/
@GetMapping("/get/id")
User getById(@RequestParam("userId") long userId);
/**
* 根据 id 获取用户列表
* @param idList
* @return
*/
@GetMapping("/get/ids")
List<User> listByIds(@RequestParam("idList") Collection<Long> idList);
定义接口方法时 要打上请求注解 注意区分 Get 和 Post
@RequestParam 注解 指定字段
防止数据传输不过去
对于服务 有一些不利于远程调用参数传递 可以不用指定字段
我们默认注册的服务
是写在模块的配置文件里面的
对应@FeignClient注解里面的name
举例
spring:
application:
name: yuoj-backend-user-service
这些路径实际上应该写在文档里面 接口文档
题目模块
package com.yupi.yuojbackendserviceclient.service;
import com.yupi.yuojbackendmodel.model.entity.Question;
import com.yupi.yuojbackendmodel.model.entity.QuestionSubmit;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @description 针对表【question(题目)】的数据库操作Service
* @createDate 2023-08-07 20:58:00
*/
@FeignClient(name = "yuoj-backend-question-service", path = "/api/question/inner")
public interface QuestionFeignClient {
@GetMapping("/get/id")
Question getQuestionById(@RequestParam("questionId") long questionId);
@GetMapping("/question_submit/get/id")
QuestionSubmit getQuestionSubmitById(@RequestParam("questionId") long questionSubmitId);
@PostMapping("/question_submit/update")
boolean updateQuestionSubmitById(@RequestBody QuestionSubmit questionSubmit);
}
判题模块
这是之前写在代码沙箱里面的
package com.yupi.yuojbackendserviceclient.service;
import com.yupi.yuojbackendmodel.model.entity.QuestionSubmit;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 判题服务
*/
@FeignClient(name = "yuoj-backend-judge-service", path = "/api/judge/inner")
public interface JudgeFeignClient {
/**
* 判题
* @param questionSubmitId
* @return
*/
@PostMapping("/do")
QuestionSubmit doJudge(@RequestParam("questionSubmitId") long questionSubmitId);
}
注意的是
@RequestParam
注解用于处理 HTTP 请求参数,主要在 Spring MVC 中使用。它可以将请求中的参数绑定到方法的参数上,方便获取用户输入的值
1.要给接口的每个方法打上请求注解 注意区分Get 和 Post
2.要给请求参数打上注解 比如说RequestParam RequestBody
3.FeignClient定义的请求路径一定要和服务提供方实际的请求路径保持一致
路由划分
用springboot的context-path统一修改个项目的接口前缀
比如 用户服务
/api/user
/api/user/inner(内部调用 网关层面要做限制)
比如 题目服务
/api/question(也包含题目提交信息)
/api/question/inner(内部调用 网关层面要做限制)
比如 判题服务
/api/judge
/api/judge/inner(内部调用 网关层面要做限制)
最终效果
注入客户端对象
实现远程调用