一、服务端
1.1、定义rpc服务代理注解
用于标识需要作为远程调用的接口,应用于类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RpcService {
}
1.2、定义handler处理器
这里只列出接收数据关键处理代码,rpcServices为所有需要rpc调用的实例类对象
private Map<String, Object> rpcServices;
public RpcServerInboundHandler(Map<String, Object> rpcServices) {
this.rpcServices = rpcServices;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
RpcRequest rpcRequest = (RpcRequest) msg;
RpcResponse response = new RpcResponse();
response.setRequestType(rpcRequest.getRequestType());
try {
if(0 == rpcRequest.getRequestType()){
log.info("接收到客户端请求, 请求接口 -> {}, 请求方法 -> {}", rpcRequest.getClassName(), rpcRequest.getMethodName());
response.setRequestId(rpcRequest.getRequestId());
this.handleRequest(rpcRequest, response);
}
else{
log.info("服务器收到心跳");
}
} catch (Exception e) {
e.printStackTrace();
response.setSuccess(false);
response.setErrorMessage(e.getMessage());
}
log.info("服务器响应 -> {}", JSON.toJSONString(response));
ctx.channel().writeAndFlush(response);
}
private void handleRequest(RpcRequest rpcRequest, RpcResponse response) throws Exception {
Object bean = rpcServices.get(rpcRequest.getClassName());
if (bean == null) {
throw new RuntimeException("未找到对应的服务: " + rpcRequest.getClassName());
}
Method method = bean.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes());
method.setAccessible(true);
response.setReturnType(method.getReturnType());
response.setResult(method.invoke(bean, rpcRequest.getParameters()));
}
1.3、定义编码解码器
解码器
public class JsonDecoder extends LengthFieldBasedFrameDecoder {
public JsonDecoder() {
super(Integer.MAX_VALUE, 0, 4, 0, 4);
}
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
ByteBuf msg = (ByteBuf) super.decode(ctx, in);
byte[] bytes = new byte[msg.readableBytes()];
msg.readBytes(bytes);
RpcRequest rpcRequest = JSON.parseObject(bytes, RpcRequest.class);
// 类型转换
if(null != rpcRequest.getParameters() && rpcRequest.getParameters().length > 0){
for(int n =0; n < rpcRequest.getParameters().length; n++){
if(null == rpcRequest.getParameters()[n]){
continue;
}
rpcRequest.getParameters()[n] = ((JSONObject) rpcRequest.getParameters()[n]).toJavaObject(rpcRequest.getParameterTypes()[n]);
}
}
return rpcRequest;
}
}
编码器
public class JsonEncoder extends MessageToByteEncoder<RpcResponse> {
@Override
protected void encode(ChannelHandlerContext ctx, RpcResponse rpcResponse, ByteBuf out) {
byte[] bytes = JSON.toJSONBytes(rpcResponse);
// 将消息体的长度写入消息头部
out.writeInt(bytes.length);
// 写入消息体
out.writeBytes(bytes);
}
}
1.4、定义消息结构体
RpcRequest
/**
* 0 正常消息,1 心跳检查
*/
private Integer requestType;
/**
* 请求ID 用来标识本次请求以匹配RPC服务器的响应
*/
private String requestId;
/**
* 调用的类(接口)权限定名称
*/
private String className;
/**
* 调用的方法名
*/
private String methodName;
/**
* 方法参类型列表
*/
private Class<?>[] parameterTypes;
/**
* 方法参数
*/
private Object[] parameters;
RpcResponse
/**
* 0 正常消息,1 心跳检查
*/
private Integer requestType;
/**
* 响应对应的请求ID
*/
private String requestId;
/**
* 调用是否成功的标识
*/
private boolean success = true;
/**
* 调用错误信息
*/
private String errorMessage;
/**
* 返回类型
*/
private Class<?> returnType;
/**
* 调用结果
*/
private Object result;
1.5、启动rpc服务
启动netty服务,监听指定端口
@Component
@Slf4j
public class RpcServiceConfig {
@Value("${rpc.server.port}")
private int port;
@Bean
public CustomRpcServer serverStart(){
CustomRpcServer customRpcServer = new CustomRpcServer();
customRpcServer.setPort(port);
log.info("port={}", port);
return customRpcServer;
}
}
获取spring上下文容器中带有rpcservice注解的对象
@Slf4j
public class CustomRpcServer implements ApplicationContextAware, InitializingBean {
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
private Map<String, Object> rpcServices = new HashMap<>(5);
private int port;
public void setPort(int port) {
this.port = port;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Object> services = applicationContext.getBeansWithAnnotation(RpcService.class);
for (Map.Entry<String, Object> entry : services.entrySet()) {
Object bean = entry.getValue();
Class<?>[] interfaces = bean.getClass().getInterfaces();
for (Class<?> inter : interfaces) {
rpcServices.put(inter.getName(), bean);
}
}
log.info("加载RPC服务数量:{}", rpcServices.size());
}
@Override
public void afterPropertiesSet() {
threadPoolTaskExecutor.submit(() -> {
// 监听连接的 parent channel 的线程组
EventLoopGroup boss = new NioEventLoopGroup(1);
// 负责客户端连接读写的 child channel 线程组
EventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
// 设置reactor 线程
bootstrap.group(boss, worker)
// 装配流水线
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 超时机制
pipeline.addLast(new IdleStateHandler(0, 0, 30));
pipeline.addLast(new JsonDecoder());
pipeline.addLast(new JsonEncoder());
// pipeline管理channel中的Handler
// 在channel队列中添加一个handler来处理业务
pipeline.addLast(new RpcServerInboundHandler(rpcServices));
}
})
// 设置nio类型的channel
.channel(NioServerSocketChannel.class);
// 开始绑定server
// 通过调用sync同步方法阻塞直到绑定成功
ChannelFuture future = bootstrap.bind(port).sync();
log.info("RPC 服务器启动, 监听端口:" + port);
// 监听通道关闭事件
// 应用程序会一直等待,直到channel关闭
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
boss.shutdownGracefully();
worker.shutdownGracefully();
}
});
}
}
二、客户端
代码中复用客户端定义的结构体,其中codec与服务端相反
2.1、定义rpc调用代理注解
当该注解被调用时,导入RpcProxy、RpcBeanDefinitionRegistryPostProcessor
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
@Import({RpcProxy.class, RpcBeanDefinitionRegistryPostProcessor.class})
public @interface RpcCustomClient {
}
2.2、bean注册器
指定包加载类
@Slf4j
public class RpcBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
RpcScanner rpcScanner = new RpcScanner(registry);
// RPC接口所在的包名
rpcScanner.scan(BaseRpcConstant.BASE_RPC_PACKAGES);
}
略...
}
自定义rpc 扫描器
在IOC 容器初始化阶段,执行
public class RpcScanner extends ClassPathBeanDefinitionScanner {
public RpcScanner(BeanDefinitionRegistry registry) {
super(registry);
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 获取指定包下的类
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
// 获取构造函数参数结合,添加指定类型的泛型;
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
// 修改当前类bean的类型名称,当从BeanFactory获取对象时,实际调用RpcFactoryBean的 getObject方法
log.info("doScan -> beanDefinition={}", beanDefinition.getBeanClassName());
beanDefinition.setBeanClassName(RpcFactoryBean.class.getName());
}
return beanDefinitionHolders;
}
略...
}
rpc bean 工厂
在运行时创建一个动态代理类对象
public class RpcFactoryBean<T> implements FactoryBean<T> {
private Class<T> interfaceClass;
@Autowired
private RpcProxy rpcProxy;
public RpcFactoryBean(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
}
@Override
public T getObject() {
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, rpcProxy);
}
@Override
public Class<?> getObjectType() {
return interfaceClass;
}
}
2.3、RPC服务动态代理
public class RpcProxy implements InvocationHandler {
@Autowired
private RpcClient rpcClient;
public void setRpcClient(RpcClient rpcClient) {
this.rpcClient = rpcClient;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setRequestType(0);
rpcRequest.setClassName(method.getDeclaringClass().getName());
rpcRequest.setMethodName(method.getName());
rpcRequest.setParameters(args);
rpcRequest.setParameterTypes(method.getParameterTypes());
log.info("invoke -> rpcRequest={}", JSON.toJSONString(rpcRequest));
RpcResponse rpcResponse = rpcClient.send(rpcRequest);
return rpcResponse.getResult();
}
}
2.4、handler处理器
消息处理核心代码,利用SynchronousQueue的特性,实现接口消息回调
private Map<String, SynchronousQueue<RpcResponse>> results;
public RpcClientInboundHandler(Map<String, SynchronousQueue<RpcResponse>> results) {
this.results = results;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
RpcResponse rpcResponse = (RpcResponse) msg;
log.info("收到服务器响应 -> {}", JSON.toJSONString(rpcResponse));
// 取出结果容器,将response放进queue中
if(null != rpcResponse && 0 == rpcResponse.getRequestType()){
SynchronousQueue<RpcResponse> queue = results.get(rpcResponse.getRequestId());
queue.put(rpcResponse);
}
}
2.5、启动客户端
加载指定注解
@RpcCustomClient
@Slf4j
public class RpcConfig {
@Value("${rpc.remote.hosts}")
private String remoteIpPorts;
@Bean
public RpcClient start(){
RpcClient rpcClient = new RpcClient();
rpcClient.setRemoteIps(remoteIpPorts);
log.info("remoteIpPorts={}", remoteIpPorts);
return rpcClient;
}
}
启动服务,初始化信息
代码中,TcpPools为TCP连接池,管理tcp通道计数和连接地址,解决服务单点问题,此处暂略
@Slf4j
public class RpcClient implements InitializingBean {
private String remoteIpPorts;
private List<Bootstrap> bootstraps = new ArrayList<>();
public void setRemoteIps(String remoteIpPorts) {
this.remoteIpPorts = remoteIpPorts;
}
private final Map<String, SynchronousQueue<RpcResponse>> results = new ConcurrentHashMap<>();
public RpcClient() {
}
@Override
public void afterPropertiesSet() throws Exception {
for (String remoteIpPort : remoteIpPorts.split(";")) {
String[] data = remoteIpPort.split(":");
// 客户端启动类,绑定端口,host
Bootstrap bootstrap = new Bootstrap().remoteAddress(data[0], Integer.valueOf(data[1]));
bootstraps.add(bootstrap);
NioEventLoopGroup worker = new NioEventLoopGroup(1);
bootstrap.group(worker)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
// 超时设定
pipeline.addLast(new IdleStateHandler(0, 0, 10));
pipeline.addLast(new JsonEncoder());
pipeline.addLast(new JsonDecoder());
pipeline.addLast(new RpcClientInboundHandler(results));
}
});
}
}
public RpcResponse send(RpcRequest rpcRequest) {
RpcResponse rpcResponse;
rpcRequest.setRequestId(UUID.randomUUID().toString());
try {
Channel channel = TcpPools.getCannle(bootstraps);
log.info("发送请求 -> {}", JSON.toJSONString(rpcRequest));
channel.writeAndFlush(rpcRequest);
SynchronousQueue<RpcResponse> queue = new SynchronousQueue<>();
results.put(rpcRequest.getRequestId(), queue);
// 阻塞等待获取响应
rpcResponse = queue.poll();
int count = 0;
while (null == rpcResponse && count < 100){
Thread.sleep(100);
rpcResponse = queue.poll();
count ++;
}
if(null == rpcResponse){
channel.close();
throw new RuntimeException("调用rpc超时");
}
if (!rpcResponse.isSuccess()) {
throw new RuntimeException("调用结果异常,异常信息:" + rpcResponse.getErrorMessage());
}
} catch (Exception e) {
log.error("send -> error", e);
throw new RuntimeException(e.getLocalizedMessage());
} finally {
results.remove(rpcRequest.getRequestId());
}
return rpcResponse;
}
}
总结
通过这个rpc示例服务,知道了怎么构建并运行一个rpc客户端及服务器。示例虽然难度不大,但基于netty实现的服务原理都大同小异,只要掌握了基础,就可以基于此实现更为复杂优秀的应用了。