周末在家,闲来无事,就想写一个服务器端自动扫描并注册,客户端自动扫描并代理的 RPC 组件,基于 Spring;
整理了一下思路,这个组件分三个部分:core
、server
和client
;如下:
分三个步骤完成:
- core
:服务定义,数据序列化方式;
- server
:服务类自动扫描并注册、服务暴露;
- client
:客户端接口自动扫描并代理;
第一步 core
- 服务定义:一个
服务
狭义地理解为一个函数,这里面就包含函数的入参数和出参;另外一个层面的问题,怎么样定位服务呢?站在 Java 角度,我使用ServiceNamespace
和ServiceMapping
两个注解,ServiceNamespace
是一些同类服务的命名空间,ServiceMapping
是具体服务入口;默认有一个value
值,即为命名空间的名字和服务的名字; - 数据序列化方式:我这里选择了
ProtoStuff
;
下面贴出相关代码:
package io.http.rpc.core.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceNamespace {
/**
*
* namespace of service
*
*/
String value() default "";
}
package io.http.rpc.core.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceMapping {
/**
*
* name of service
*
*/
String value() default "";
}
package io.http.rpc.core;
public interface CoreServiceInvoker {
/**
* core service.
* @param namespace 命名空间
* @param name 服务名
* @param parameters 参数列表
*
* */
Object invoke(String namespace, String name, Object[] parameters);
}
package io.http.rpc.core.serialize;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtobufIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
/**
* Created by manbu on 7/2/16.
*/
public final class ProtoSerializeUtils {
@SuppressWarnings("unchecked")
public static <T> byte[] encode(T object) {
Schema<T> schema = RuntimeSchema.getSchema((Class<T>) object.getClass());
LinkedBuffer buffer = LinkedBuffer.allocate(512);
return ProtobufIOUtil.toByteArray(object, schema, buffer);
}
@SuppressWarnings("unchecked")
public static <T> T decode(byte[] data, Class<T> clazz) {
Schema<T> schema = RuntimeSchema.getSchema(clazz);
T m = schema.newMessage();
ProtobufIOUtil.mergeFrom(data, m, schema);
return m;
}
}
第二步 server
- 自动扫描:
通过io.http.rpc.server.auto.ServiceScannerConfigurer
的配置basePackage
,扫描逻辑借鉴 mybatis 的 Mapper 扫描机制,扩展org.springframework.context.annotation.ClassPathBeanDefinitionScanner
;
代码:
io.http.rpc.server.auto.ClassPathServiceScanner
- 服务注册:
『服务注册中心』即是一个 Service Holder,当扫描到ServiceNamespace
即注册到『服务注册中心』中;
代码:
io.http.rpc.server.ServiceRegistrationCenter
- 服务暴露:
通过实现
org.springframework.web.HttpRequestHandler
来暴露服务,并在接收到请求后反序列化再从服务注册中心找到服务并执行;
代码:
io.http.rpc.server.HttpServiceExporter
第三步 client
- 自动扫描并代理
通过io.http.rpc.client.auto.ServiceScannerConfigurer
的配置basePackage
,扫描逻辑借鉴 mybatis 的 Mapper 扫描机制,扩展org.springframework.context.annotation.ClassPathBeanDefinitionScanner
;扫描得到一个服务接口后,即通过改变 Spring Bean 的定义,使用ServiceFactoryBean
生产出一个代理类;代理逻辑将请求交给核心层的 ProxyExecutor;
代码:
io.http.rpc.client.auto.ClassPathServiceScanner
io.http.rpc.client.auto.ServiceScannerConfigurer
io.http.rpc.client.auto.ServiceFactoryBean
- ClientBuilder
这里简单实现一个 ClientBuilder;仿照netty-rpc
,后续再进行完善;