文章目录
一、自定义注解
使用注解@EasyRpcService
用于声明服务提供者。
二、服务端自动配置类
代码如下,主要属性是一个日志记录工具类Logger
和一个上下文管理类ApplicationContext
。
public class RpcServerAutoConfiguration implements InitializingBean, ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory.getLogger(RpcServerAutoConfiguration.class);
private ApplicationContext applicationContext;
@Override
public void afterPropertiesSet() throws Exception {...}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {...}
private void printBanner() {...}
}
首先RpcServerAutoConfiguration
自动配置类实现了InitializingBean
和ApplicationContextAware
接口。第一个接口的作用是,当Spring装载完Bean后自动调用该类中的afterPropertiesSet()
方法。第二个接口作用为获取容器上下文。
1.afterPropertiesSet()方法
在该方法内进行服务的注册和设置。
@Override
public void afterPropertiesSet() throws Exception {
Server server = null;
Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(EasyRpcService.class);
if (beanMap.size() == 0) {
//说明当前应用内部不需要对外暴露服务
return;
}
printBanner();
long begin = System.currentTimeMillis();
server = new Server();
// 加载配置
server.initServerConfig();
for (String beanName : beanMap.keySet()) {
Object bean = beanMap.get(beanName);
EasyRpcService easyRpcService = bean.getClass().getAnnotation(EasyRpcService.class);
ServiceWrapper dataServiceServiceWrapper = new ServiceWrapper(bean, easyRpcService.group());
dataServiceServiceWrapper.setServiceToken(easyRpcService.serviceToken());
dataServiceServiceWrapper.setLimit(easyRpcService.limit());
dataServiceServiceWrapper.setWeight(easyRpcService.weight());
// 服务注册
server.registyService(dataServiceServiceWrapper);
LOGGER.info(">>>>>>>>>>>>>>> [easy-rpc] {} export success! >>>>>>>>>>>>>>> ", beanName);
}
ServerShutdownHook.registryShutdownHook();
// 启动服务器
server.startServerApplication();
long end = System.currentTimeMillis();
LOGGER.info(" ================== [{}] started success in {}s ================== ",
SERVER_CONFIG.getApplicationName(), ((double) end - (double) begin) / 1000);
}
首先第一步是通过applicationContext
上下文管理工具拿到使用自定义服务端注解修饰的类,如果获取的类集合为0,则代表当前没有服务需要对外暴露。
Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(EasyRpcService.class);
if (beanMap.size() == 0) {
//说明当前应用内部不需要对外暴露服务
return;
}
第二步打印控制台信息,并实例化一个Netty服务端对象,该对象中主要有四个方法,startServerApplication()
、initServerConfig()
、batchExportUrl()
、registyService(ServiceWrapper serviceWrapper)
。
printBanner();
long begin = System.currentTimeMillis();
server = new Server();
第三步根据本地配置文件加载服务器端的配置。
public void initServerConfig() {
SERVER_CONFIG = PropertiesBootstrap.loadServerConfigFromLocal();
}
第四步遍历容器中的实例化对象,找出需要暴露服务的服务端对象,并进行修饰添加额外的属性,最重要的是进行服务的注册(该步骤仅将服务URL添加到PROVIDER_URL_SET
集合中,为后续注册中心进行真正的服务注册做准备)。
for (String beanName : beanMap.keySet()) {
Object bean = beanMap.get(beanName);
EasyRpcService easyRpcService = bean.getClass().getAnnotation(EasyRpcService.class);
ServiceWrapper dataServiceServiceWrapper = new ServiceWrapper(bean, easyRpcService.group());
dataServiceServiceWrapper.setServiceToken(easyRpcService.serviceToken());
dataServiceServiceWrapper.setLimit(easyRpcService.limit());
dataServiceServiceWrapper.setWeight(easyRpcService.weight());
// 服务注册
server.registyService(dataServiceServiceWrapper);
LOGGER.info(">>>>>>>>>>>>>>> [easy-rpc] {} export success! >>>>>>>>>>>>>>> ", beanName);
}
第五步是注册一个服务器关闭的钩子函数,在服务器JVM进程关闭时调用。
ServerShutdownHook.registryShutdownHook();
第六步是启动服务器。
server.startServerApplication();
三、关键函数
1.server.registyService()方法
该方法是将包装好的服务类添加到PROVIDER_URL_SET
集合中,并且在此之前使用SPI的方式根据配置实例化对应的注册中心(本文采用的是Zookeeper)。
public void registyService(ServiceWrapper serviceWrapper) {
// 具体的实现类bean
Object serviceBean = serviceWrapper.getServiceBean();
if (serviceBean.getClass().getInterfaces().length == 0) {
throw new RuntimeException("service must had interfaces!");
}
// 获得所有接口
Class<?>[] classes = serviceBean.getClass().getInterfaces();
if (classes.length > 1) {
throw new RuntimeException("service must only had one interfaces!");
}
// 实例化注册中心
if (REGISTRY_SERVICE == null) {
try {
// 通过SPI的方式默认加载ZookeeperRegister
EXTENSION_LOADER.loadExtension(RegistryService.class);
// EXTENSION_LOADER_CLASS_CACHE中存放接口的实现类,一个接口多个实现类
// key:com.x.x.x.RegistryService value: <zookeeper, xxx.class>, <nacos, xxx.class>
Map<String, Class<?>> registryClassMap = EXTENSION_LOADER_CLASS_CACHE.get(RegistryService.class.getName());
// 根据服务器配置得到具体的注册实现类 zookeeper
Class<?> registryClass = registryClassMap.get(SERVER_CONFIG.getRegisterType());
// 实例化->ZookeeperRegistry
REGISTRY_SERVICE = (AbstractRegister) registryClass.newInstance();
} catch (Exception e) {
throw new RuntimeException("registryServiceType unKnow,error is ", e);
}
}
// 默认选择该对象的第一个实现接口
Class<?> interfaceClass = classes[0];
// 具体提供服务的类放入map中 com.shaogezhu.easy.rpc.interfaces.DataService -> {DataServiceImpl@5435}
PROVIDER_CLASS_MAP.put(interfaceClass.getName(), serviceBean);
URL url = new URL();
url.setServiceName(interfaceClass.getName());
url.setApplicationName(SERVER_CONFIG.getApplicationName());
url.addParameter("host", CommonUtil.getIpAddress());
url.addParameter("port", String.valueOf(SERVER_CONFIG.getPort()));
url.addParameter("group", String.valueOf(serviceWrapper.getGroup()));
url.addParameter("limit", String.valueOf(serviceWrapper.getLimit()));
url.addParameter("weight", String.valueOf(serviceWrapper.getWeight()));
// 服务提供者的ip:port,以及服务提供者的权重,方便后续添加到注册中心
PROVIDER_URL_SET.add(url);
if (serviceWrapper.getLimit() > 0) {
SERVER_SERVICE_SEMAPHORE_MAP.put(interfaceClass.getName(), new ServerServiceSemaphoreWrapper(serviceWrapper.getLimit()));
}
if (CommonUtil.isNotEmpty(serviceWrapper.getServiceToken())) {
PROVIDER_SERVICE_WRAPPER_MAP.put(interfaceClass.getName(), serviceWrapper);
}
}
2.server.startServerApplication()方法
在该方法中,配置了Netty服务端,并对过滤链、监听器和序列化工具进行初始化(同样是采用SPI的机制进行个性化加载实例),这其中需要关注的是SERVER_CHANNEL_DISPATCHER
这个类,在Netty链式处理中的ServerHandler()
会将请求进行封装并添加到SERVER_CHANNEL_DISPATCHER
中的阻塞队列中(详见源码ServerHandler.java
),在SERVER_CHANNEL_DISPATCHER
声明了一个固定大小的线程池和一个阻塞队列,在SERVER_CHANNEL_DISPATCHER
中会执行真正的服务调用以及服务调用结果的发送(详见源码ServerChannelDispatcher.java
)。
public void startServerApplication() throws InterruptedException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup);
bootstrap.channel(NioServerSocketChannel.class);
// 关闭Nagle算法,该算法要求一个TCP连接上最多只能有一个未被确认的小分组,在该小分组的确认到来之前,不能发送其他小分组。
bootstrap.option(ChannelOption.TCP_NODELAY, true);
// backlog 用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度。
// 服务器TCP内核 内维护了两个队列,称为A(未连接队列)和B(已连接队列)
// 如果A+B的长度大于Backlog时,新的连接就会被TCP内核拒绝掉。
bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
// TCP发送缓冲区的容量上限
bootstrap.option(ChannelOption.SO_SNDBUF, 16 * 1024)
// TCP接受缓冲区的容量上限
.option(ChannelOption.SO_RCVBUF, 16 * 1024)
// 当设置为true的时候,TCP会实现监控连接是否有效,当连接处于空闲状态的时候,
// 超过了2个小时,本地的TCP实现会发送一个数据包给远程的 socket,
// 如果远程没有发回响应,TCP会持续尝试11分钟,直到响应为止,如果在12分钟的时候还没响应,TCP尝试关闭socket连接。
.option(ChannelOption.SO_KEEPALIVE, true);
//服务端采用单一长连接的模式,这里所支持的最大连接数和机器本身的性能有关
//连接防护的handler应该绑定在Main-Reactor上
bootstrap.handler(new MaxConnectionLimitHandler(SERVER_CONFIG.getMaxConnections()));
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 分隔符
ByteBuf delimiter = Unpooled.copiedBuffer(DEFAULT_DECODE_CHAR.getBytes());
// 解决半包粘包问题
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(SERVER_CONFIG.getMaxServerRequestData(), delimiter));
ch.pipeline().addLast(new RpcEncoder());
ch.pipeline().addLast(new RpcDecoder());
// 拿到该通道的上下文和协议体添加到分发器中
ch.pipeline().addLast(new ServerHandler());
}
});
//初始化监听器
RpcListenerLoader rpcListenerLoader = new RpcListenerLoader();
rpcListenerLoader.init();
//初始化序列化器
String serverSerialize = SERVER_CONFIG.getServerSerialize();
EXTENSION_LOADER.loadExtension(SerializeFactory.class);
LinkedHashMap<String, Class<?>> serializeMap = EXTENSION_LOADER_CLASS_CACHE.get(SerializeFactory.class.getName());
Class<?> serializeClass = serializeMap.get(serverSerialize);
if (serializeClass == null) {
throw new RuntimeException("no match serializeClass for " + serverSerialize);
}
SERVER_SERIALIZE_FACTORY = (SerializeFactory) serializeClass.newInstance();
//初始化过滤链
ServerBeforeFilterChain serverBeforeFilterChain = new ServerBeforeFilterChain();
ServerAfterFilterChain serverAfterFilterChain = new ServerAfterFilterChain();
EXTENSION_LOADER.loadExtension(ServerFilter.class);
LinkedHashMap<String, Class<?>> filterChainMap = EXTENSION_LOADER_CLASS_CACHE.get(ServerFilter.class.getName());
for (Map.Entry<String, Class<?>> filterChainEntry : filterChainMap.entrySet()) {
String filterChainKey = filterChainEntry.getKey();
Class<?> filterChainImpl = filterChainEntry.getValue();
if (filterChainImpl == null) {
throw new RuntimeException("no match filterChainImpl for " + filterChainKey);
}
SPI spi = filterChainImpl.getDeclaredAnnotation(SPI.class);
if (spi != null && "before".equalsIgnoreCase(spi.value())) {
serverBeforeFilterChain.addServerFilter((ServerFilter) filterChainImpl.newInstance());
} else if (spi != null && "after".equalsIgnoreCase(spi.value())) {
serverAfterFilterChain.addServerFilter((ServerFilter) filterChainImpl.newInstance());
}
}
SERVER_BEFORE_FILTER_CHAIN = serverBeforeFilterChain;
SERVER_AFTER_FILTER_CHAIN = serverAfterFilterChain;
// 初始化请求分发器
SERVER_CHANNEL_DISPATCHER.init(SERVER_CONFIG.getServerQueueSize(), SERVER_CONFIG.getServerBizThreadNums());
// 开始接收客户端消息并启动额外线程进行处理
SERVER_CHANNEL_DISPATCHER.startDataConsume();
// 暴露服务端url
this.batchExportUrl();
bootstrap.bind(SERVER_CONFIG.getPort()).sync();
}
3.server.batchExportUrl()方法
在server.registyService()
方法完成PROVIDER_URL_SET
集合的设置后,调用REGISTRY_SERVICE
注册中心实例化对象进行真正的服务注册。
public void batchExportUrl() {
Thread task = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (URL url : PROVIDER_URL_SET) {
// 进行服务注册
REGISTRY_SERVICE.register(url);
}
}
});
task.start();
}
public void register(URL url) {
if (!zkClient.existNode(ROOT)) {
zkClient.createPersistentData(ROOT, "");
}
//name;interface;ip:port;time;weight;group
//rpc-provider;com.shaogezhu.easy.rpc.interfaces.DataService;192.168.174.1:8010;1702881112921;100;data-group
String urlStr = URL.buildProviderUrlStr(url);
if (zkClient.existNode(getProviderPath(url))) {
zkClient.deleteNode(getProviderPath(url));
}
// path->easy-rpc/name/provider/ip:port, data
zkClient.createTemporaryData(getProviderPath(url), urlStr);
super.register(url);
}
注册成功后的服务在Zookeeper上的结果如下图所示:
其中可以看到节点内所包含的服务配置信息。