RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC它假定某些协议的存在,例如TPC/UDP等,为通信程序之间携带信息数据。在OSI网络七层模型中,RPC跨越了传输层和应用层,RPC使得开发,包括网络分布式多程序在内的应用程序更加容易。
说通俗一点,就是RPC可以让我们调用远程方法像本地方法一样简单,客户端并不会感知到网络通信等等细节
RPC框架的通信与具体的协议无关,RPC可基于HTTP或者TCP协议,现在市面上比较成熟的Java生态中的RPC实现一般是这两种:
OpenFeign
Netflix Feign 是 Netflix 公司发布的一种实现负载均衡和服务调用的开源组件。Spring Cloud 将其与 Netflix 中的其他开源服务组件(例如 Eureka、Ribbon 以及 Hystrix 等)一起整合进 Spring Cloud Netflix 模块中,整合后全称为 Spring Cloud Netflix Feign。
Feign 是一种声明式服务调用组件,它在 RestTemplate 的基础上做了进一步的封装。通过 Feign,我们只需要声明一个接口并通过注解进行简单的配置(类似于 Dao 接口上面的 Mapper 注解一样)即可实现对 HTTP 接口的绑定。
但是Feign已经停更了
OpenFeign 全称 Spring Cloud OpenFeign,它是 Spring 官方推出的一种声明式服务调用与负载均衡组件,它的出现就是为了替代进入停更维护状态的 Feign
OpenFeign 是 Spring Cloud 对 Feign 的二次封装,它具有 Feign 的所有功能,并在 Feign 的基础上增加了对 Spring MVC 注解的支持,例如 @RequestMapping、@GetMapping 和 @PostMapping 等
我们今天的主角就是它,接下来我们来重点介绍它吧
Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。
Dubbo
比起OpenFegin
有自身的优势:
在分布式系统中,尤其是随着微服务架构的发展,应用的部署、发布、扩缩容变得极为频繁,作为 RPC 消费方,如何动态的发现服务提供方地址成为 RPC 通信的前置条件。Dubbo 提供了自动的地址发现机制,用于应对分布式场景下机器实例动态迁移的问题。如下图所示,通过引入注册中心来协调提供方与消费方的地址,提供者启动之后向注册中心注册自身地址,消费方通过拉取或订阅注册中心特定节点,动态的感知提供方地址列表的变化。
Dubbo3已经开始拥抱HTTP2,解决了Dubbo之前的互通性问题,以下内容源于官网:
Triple 是 Dubbo3 提出的基于 HTTP2 的开放协议,旨在解决 Dubbo2 私有协议带来的互通性问题。相比于原有 Dubbo2 协议,Triple 有以下优势:
- 原生和 gRPC 协议互通。打通 gRPC 生态,降低从 gRPC 至 Dubbo 的迁移成本。
- 增强多语言生态。避免因 CPP/C#/RUST 等语言的 Dubbo SDK 能力不足导致业务难以选型适配的问题。
- 网关友好。网关无需参与序列化,方便用户从传统的 HTTP 转泛化 Dubbo 调用网关升级至开源或云厂商的 Ingress 方案。
- 完善的异步和流式支持。带来从底层协议到上层业务的性能提升,易于构建全链路异步以及严格保证消息顺序的流式服务。
目前 Java 和 Go 的 Dubbo SDK 已全面支持 Triple 协议。 在阿里巴巴,Triple 协议广泛用于跨环境、跨语言、跨生态互通,已有数十万容器生产级使用。
接下来我们来对之前的项目进行改造
2.7.8
org.apache.dubbo
dubbo-spring-boot-starter
${dubbo.version}
复制代码
user-service
里面添加依赖
com.alibaba.spring
spring-context-support
1.0.10
org.apache.dubbo
dubbo-spring-boot-starter
org.hibernate
hibernate-validator
5.2.4.Final
复制代码
先创建实体类
package cuit.epoch.pymjl.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @author Pymjl
* @version 1.0
* @date 2022/8/27 0:35
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private String email;
private String phone;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
复制代码
再创建接口
package cuit.epoch.pymjl.service;
import com.baomidou.mybatisplus.extension.service.IService;
import cuit.epoch.pymjl.entity.User;
/**
* @author Pymjl
* @version 1.0
* @date 2022/8/27 0:56
**/
public interface UserService extends IService {
/**
* 注册
*/
void register();
/**
* 得到用户
*
* @param id id
* @return {@code User}
*/
User get(Long id);
}
复制代码
注意,dubbo用到的实体类以及接口需要写在通用模块common-utils
里面方便消费者调用,而真正的接口实现类是放在服务提供者的包里面的
实际的开发中,Dubbo服务一般分为client和core两个模块,client存放dubbo所用到的实体类以及接口,core存放接口的实现类(真正的业务处理),随后服务提供者需要将client打包让消费者引入,向其暴露接口
注意:所有传输的类都需要序列化(实现序列化接口)
然后编写对应的实现类
/**
* @author Pymjl
* @version 1.0
* @date 2022/8/27 0:50
**/
@DubboService
public class UserServiceImpl extends ServiceImpl implements UserService {
private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(0);
@Override
public void register() {
User user = new User();
user.setUsername("pymjl@" + ATOMIC_INTEGER.incrementAndGet());
user.setPassword("123456");
user.setPhone("1231234214124");
user.setEmail("pymjl@" + ATOMIC_INTEGER.incrementAndGet() + ".com");
baseMapper.insert(user);
}
@Override
public User get(Long id) {
return baseMapper.selectById(id);
}
}
复制代码
一定记得要给主启动类加上@EnableDubbo
注解
最后,编写application.yml
dubbo:
application:
name: ${spring.application.name}
logger: slf4j
protocol:
name: dubbo
port: -1
registry:
address: nacos://192.168.199.128:8848
config-center:
address: nacos://192.168.199.128:8848
metadata-report:
address: nacos://192.168.199.128:8848
consumer:
validation: true
timeout: 3000
check: false
scan:
base-packages: cuit.epoch.pymjl.dubbo
复制代码
一样的,先在消费者的pom里面导入上面的依赖,然后在主启动类上加上@EnableDubbo
注解
application.yml
dubbo:
application:
name: ${spring.application.name}
logger: slf4j
protocol:
name: dubbo
port: -1
registry:
address: nacos://192.168.199.128:8848
config-center:
address: nacos://192.168.199.128:8848
metadata-report:
address: nacos://192.168.199.128:8848
consumer:
validation: true
timeout: 3000
check: false
scan:
base-packages: cuit.epoch.pymjl.controller
复制代码
package cuit.epoch.pymjl.controller;
import cuit.epoch.pymjl.entity.User;
import cuit.epoch.pymjl.result.CommonResult;
import cuit.epoch.pymjl.result.ResultUtils;
import cuit.epoch.pymjl.service.UserService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
/**
* @author Pymjl
* @version 1.0
* @date 2022/8/26 12:17
**/
@RestController
@RequestMapping("/consumer")
public class TestController {
private static final String SERVICE_URL = "http://UserService";
@Resource
RestTemplate restTemplate;
@DubboReference(check = false)
UserService userService;
@GetMapping("/test")
public CommonResult consumerTest() {
return restTemplate.getForObject(SERVICE_URL + "/user/test", CommonResult.class);
}
@GetMapping("/register")
public CommonResult register() {
userService.register();
return ResultUtils.success();
}
@GetMapping("/get/{id}")
public CommonResult get(@PathVariable("id") Long id) {
return ResultUtils.success(userService.get(id));
}
}
复制代码
Nacos
控制台观察后台日志输出
至此,Spring Cloud集成Nacos、Dubbo就演示完毕了
项目源码:gitee github