服务调用
约 2123 字大约 7 分钟
2025-03-16
1.远程调用
1.1 Feign 和 Dubbo
作用:用于实现每个微服务之间的通信协作,传输数据
实现方式:Feign、Dubbo
Feign 和 Dubbo 的区别?
Feign | Dubbo | |
---|---|---|
定义 | Feign 是一个声明式的 Web 服务客户端,用于简化 HTTP API 的调用。 | Dubbo 是一个分布式服务框架,用于构建面向服务的微服务架构。 |
通信方式 | 基于 HTTP 协议,使用 RESTful 风格的接口进行定义和调用。 | 基于 RPC 协议,支持多种序列化协议如 gRPC、Hessian 等。 |
跨语言性 | 支持跨语言通信,可以使用 HTTP 作为通信协议实现不同语言之间的通信。 | 支持跨语言通信,通过 Dubbo 的 IDL 生成不同语言的客户端和服务端代码。 |
生态系统 | 集成了 Spring Cloud 生态系统,与 Spring Boot 无缝集成。 | 拥有完整的生态系统,包括注册中心、配置中心、监控中心等组件。 |
适用场景 | 适用于构建 RESTful 风格的微服务架构,特别适合基于 HTTP 的微服务调用。 | 适用于构建面向服务的微服务架构,提供更全面的服务治理和容错机制。 |
1.2 rpc 和 grpc
RPC 是远程过程调用(Remote Procedure Call)的缩写。它是一种计算机通信协议,使得程序可以请求另一个进程或者计算机上的服务,就像调用本地的函数一样,从而实现分布式系统之间的交互和通讯。
GRPC 是 Google 开源的一种高性能、通用的远程过程调用(RPC)框架,基于 Protocol Buffers 序列化协议进行数据传输。
RPC 和 GRPC 的区别:
- 通信协议不同:
- gRPC基于HTTP/2协议进行数据传输
- 传统的RPC框架通常使用TCP或UDP等传输层协议
- 序列化方式不同:
- gRPC使用Protocol Buffers作为默认的序列化协议
- 传统的RPC框架则使用JSON、XML等格式
- 自动生成代码:
- gRPC可以根据服务定义文件自动生成客户端和服务器端的代码,大大简化了开发过程
2.Feign
2.1 介绍
介绍:首先,Feign是一个声明式的Web服务客户端,简化了编写服务调用代码的过程。它通过在接口上使用注解的方式,使得调用远程服务就像调用本地对象一样自然。 特点:
- 声明式调用:通过定义接口的方式来描述服务调用,无需手动编写复杂的调用逻辑。
- 统一的错误处理:Feign提供了统一的错误处理机制,可以方便地处理HTTP错误状态。
- Feign默认集成了Ribbon和Eureka,可以实现服务发现和负载均衡。
提提 Feign 和 OpenFeign 的异同?
相同:
Feign和OpenFeign作用一样,都是进行远程调用的组件,里面都内置了Ribbon
都是加在消费端的注解,让消费端可以调用其他生产者的服务。
不同:
- 依赖不同,一个是spring-cloud-starter-feign,一个是spring-cloud-starter-openfeign。
- 使用方式不同,Feign直接加在接口上并且不支持SpringBoot注解(例如 @FeignClient)。OpenFeign支持@RequestMapping("/demo/test")这种注解。
2.2 基本步骤
步骤1:引入 OpenFeign 依赖、在你的 Spring Boot 启动类中,使用 @EnableFeignClients
注解来启用 Feign 客户端功能
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>版本号</version>
</dependency>
步骤2:创建 Feign 客户端
- 定义一个接口,使用@FeignClient注解标注,指定调用的服务名或者URL
- 接口中的方法使用如 @GetMapping、@PostMapping 等注解定义HTTP请求方式和路径
//定义Feign客户端接口
@Feignclient(name="service-name")
public interface MyFeignclient{
@GetMapping("/api/data")
Data getData() ;
}
步骤3:实现该接口方法,具体功能
@RestContro1ler
@RequestMapping
public class DataController{
@GetMapping("/api/data")
public String getData(){
return "Hello from Feign Controller!";
}
}
步骤4:调用 Feign 客户端:在需要使用Feign客户端的地方,通过@Autowired注入定义的Feign客户端接口,然后像调用本地方法一样调用即可。
@service
public class testserviceImpl implements ITestservice{
@Autowired
private MyFeignclient feign;
@override
public string getData(){
String data = feign.getData();
if (stringutils.isNotBlank(data)){
return data;
}
return"数据获取失败";
}
}
2.4 实现原理
声明式接口定义:在使用 OpenFeign 时,首先通过声明式接口(如
@RequestMapping
、@GetMapping
、@PostMapping
等)定义远程调用的接口@FeignClient(name = "user-service") public interface UserClient { @GetMapping("/users/{id}") User getUserById(@PathVariable("id") Long id); }
代理生成:在运行时,OpenFeign 会基于你定义的接口自动生成一个代理对象。当你通过 Spring 容器注入
UserClient
接口时,实际上是获取了一个由 Feign 动态代理生成的实现类。Spring 在启动时会扫描带有
@FeignClient
注解的接口,并为这些接口创建代理对象,这些代理对象会负责发起 HTTP 请求。@Autowired private UserClient userClient;
请求的执行:当你调用
userClient.getUserById(id)
方法时,Feign 会通过以下步骤来处理请求:- 方法拦截:Feign 会拦截方法调用,并使用反射机制提取方法签名、参数等信息。
- 请求构建:根据注解(如
@GetMapping
、@PathVariable
等)信息,Feign 构建 HTTP 请求。这包括确定请求的 HTTP 方法(GET、POST、PUT 等)、请求的路径、请求头、请求体等。 - 请求发送:通过 Feign 的底层 HTTP 客户端(例如
OkHttp
或Apache HttpClient
),将构建好的请求发送到目标服务的 API。
响应的处理:当远程服务响应时,Feign 会执行以下操作:
- 响应接收: 接收到响应数据后,Feign 会解析响应的 HTTP 状态码、响应头和响应体。
- 返回值转换: 根据接口方法的返回类型(如
User
类),Feign 会自动将响应体转换成 Java 对象。如果需要,它会将响应体转换为 JSON,并使用 Jackson 等库将 JSON 反序列化成相应的 Java 对象。 - 返回: 最终,Feign 会将转换后的对象作为方法的返回值返回给调用方。
OpenFeign 的工作原理是通过动态代理生成远程接口的实现,基于注解来自动化构建 HTTP 请求并发起远程调用。它结合了负载均衡、错误处理、重试机制等功能,简化了微服务间的 HTTP 通信。
3.OpenFeign
3.1 介绍
OpenFeign 是一个声明式的 HTTP 客户端,用于在微服务架构中服务之间的通信。
以下是 OpenFeign 的特点:
- 声明式编程:使用注解定义接口和请求,不需要手动编写 HttpClient 请求代码
- 支持 Fallback:通过与 Hystrix 或 Sentinel 集成实现服务降级逻辑
- 支持多种 HTTP 请求:支持 Get 、Post 、Put 、Delete
3.2 基本使用
3.2.1 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</ dependency>
3.2.2 启用 Feign
在 Spring Boot 启动类上加上注解:@EnableFeignClients
3.2.3 定义 Feign 客户端接口
通过注解 @FeignClient 声明客户端调用服务的接口
- name:服务端在服务端注册时的服务名
- url:服务端 url
@FeignClient (name = "user-service", url = "http://localhost:8080/api"')
public interface UserServiceClient {
@GetMapping ("/users/{id}")
User getUserById(@PathVariable("id") Long id);
@PostMapping ("/users")
User createUser (@RequestBody User user);
}
3.2.4 使用
注入 FeignClient 客户端并调用方法
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private UserServiceClient userServiceClient;
@GetMapping ("/{id}")
public String getOrderWithUser(@PathVariable Long id) {
User user = userServiceClient.getUserById(id);
return "Order with User: " + user.getName ();
}
4.项目应用
4.1 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</ dependency>
4.2 启用 Feign
在 Spring Boot 启动类上加上注解:@EnableFeignClients
4.3 客户端添加 UserFeignService
@FeignClient (contextId = "userFeignService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = UserFeignFallbackFactory.class)
public interface UserFeignService {
// 根据用户名获取当前用户信息
@GetMapping ("/system/user/info/{username}")
R<LoginUser> info(@PathVariable("username") String username, @RequestHeader (SecurityConstants.FROM_SOURCE String source);
// 根据 userId 获取用户信息
@GetMapping("/system/user/getInfoByUserId/{userId}")
R<LoginUser> getInfoByUserId(@PathVariable("userId") Long userId, @RequestHeader (SecurityConstants.FROM_SOURCE String source);
// 根据条件获取用户列表
@PostMapping ("/system/user/listOfInner")
R<List<SysUserVO>> ListOfInner (@RequestBody SysUserDTO sysUserDTO, @RequestHeader (SecurityConstants.FROM_SOURCE String source);
}
4.4 新建服务降级 factory
主要用于 Feign 调用失败之后的降级处理措施
@Component
public class UserFeginFallbackFactory implements FallbackFactory<UserFeignService>
private static final Logger log = LoggerFactory-getLogger(UserFeginFallbackFactory.class){
@Override
public UserFeignService create (Throwable throwable){
log.error("用户服务调用失败:{}", throwable getMessage ());
return new UserFeignService()
{
@Override
public R<LoginUser> info(String username, String source) {
return R. fail("根据用户名获取用户失败:"+throwable.getMessage());
}
@Override
public R<LoginUser> getInfoByUserId(Long userId, String source) ‹
return R. fail("根据userId获取用户失败:"+ throwable.getMessage());
}
@Override
public R<List<SysUserVO>> listOfInner (SysUserDTO sysUserDTO, String source) {
return R.fail("根据调教获取用户列表失败:+ throwable.getMessage());
}
}
}
}