在 SOFARPC 框架中,启动服务器的过程涉及多个步骤,包括服务器配置、初始化、启动服务器以及注册服务等。下面将结合提供的代码片段详细介绍启动服务器的过程。
1. 服务器配置
在启动服务器之前,需要对服务器进行配置,包括设置协议、端口、是否为守护线程等。以下是一个配置示例:
ServerConfig serverConfig = new ServerConfig()
.setProtocol("bolt") // 设置协议,默认bolt
.setPort(12200) // 设置端口,默认12200
.setDaemon(false); // 非守护线程
在上述代码中,创建了一个ServerConfig
对象,并设置了服务器的协议为bolt
,端口为12200
,且不是守护线程。
2. 服务提供者配置
除了服务器配置,还需要配置服务提供者,包括指定接口、实现、服务器和应用信息等。以下是一个服务提供者配置示例:
ApplicationConfig applicationConfig = new ApplicationConfig().setAppName("serverApp");
ProviderConfig<HelloService> providerConfig = new ProviderConfig<HelloService>()
.setInterfaceId(HelloService.class.getName()) // 指定接口
.setRef(new HelloServiceImpl()) // 指定实现
.setServer(serverConfig) // 指定服务端
.setApplication(applicationConfig) // 指定应用配置
.setRegister(true); // 是否注册到注册中心
在上述代码中,创建了一个ApplicationConfig
对象,并设置了应用的名称为serverApp
。然后创建了一个ProviderConfig
对象,指定了服务的接口为HelloService
,实现为HelloServiceImpl
,服务器为之前配置的serverConfig
,应用配置为applicationConfig
,并设置是否注册到注册中心。
3. 初始化服务器
在启动服务器之前,需要对服务器进行初始化。不同的服务器实现类有不同的初始化方式,以下是BoltServer
的初始化示例:
@Extension("bolt")
public class BoltServer implements Server {
@Override
public void init(ServerConfig serverConfig) {
this.serverConfig = serverConfig;
bizExecutor = initExecutor(serverConfig);
if (bizExecutor instanceof ThreadPoolExecutor) {
bizThreadPool = (ThreadPoolExecutor) bizExecutor;
}
boltServerProcessor = new BoltServerProcessor(this);
}
protected Executor initExecutor(ServerConfig serverConfig) {
Executor executor = BusinessPool.initExecutor(
ThreadPoolConstant.BIZ_THREAD_NAME_PREFIX + serverConfig.getPort(), serverConfig);
if (executor instanceof ThreadPoolExecutor) {
configureThreadPoolExecutor((ThreadPoolExecutor) executor, serverConfig);
}
return executor;
}
protected void configureThreadPoolExecutor(ThreadPoolExecutor executor, ServerConfig serverConfig) {
executor.setRejectedExecutionHandler(new SofaRejectedExecutionHandler());
if (serverConfig.isPreStartCore()) { // 初始化核心线程池
executor.prestartAllCoreThreads();
}
}
}
在上述代码中,BoltServer
的init
方法接收一个ServerConfig
对象,并进行了一系列的初始化操作
- 保存服务器配置信息。
- 初始化业务执行器(
Executor
)。 - 如果业务执行器是线程池执行器(
ThreadPoolExecutor
),则将其赋值给bizThreadPool
。 - 创建
BoltServerProcessor
实例。
4. 启动服务器
在初始化服务器之后,可以调用start
方法启动服务器。以下是BoltServer
的启动示例:
@Override
public void start() {
if (started) {
return;
}
// synchronized 关键字对当前对象加锁,
synchronized (this) {
if (started) {
return;
}
// 生成Server对象
remotingServer = initRemotingServer();
try {
// 底层用到netty框架,绑定端口、启动 EventLoopGroup,负责处理网络事件,如连接建立、数据读写、注册处理器
if (remotingServer.start()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Bolt server has been bind to {}:{}", serverConfig.getBoundHost(),
serverConfig.getPort());
}
} else {
throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_START_BOLT_SERVER));
}
started = true;
if (EventBus.isEnable(ServerStartedEvent.class)) {
EventBus.post(new ServerStartedEvent(serverConfig, bizThreadPool));
}
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Exception e) {
throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_START_BOLT_SERVER), e);
}
}
}
protected RemotingServer initRemotingServer() {
// 该方法创建一个 RpcServer 实例,并将其绑定到 ServerConfig 中指定的主机和端口。
RemotingServer remotingServer = new RpcServer(serverConfig.getBoundHost(), serverConfig.getPort());
remotingServer.registerUserProcessor(boltServerProcessor);
return remotingServer;
}
在上述代码中,BoltServer
的start
方法首先检查服务器是否已经启动,如果已经启动则直接返回。然后生成一个RemotingServer
对象,remotingServer.start()
是 BoltServer
启动过程中的核心步骤,负责启动底层的 RpcServer
,使其开始监听客户端的请求。通过对启动结果的处理,确保服务器能够安全、可靠地启动,并及时通知其他组件服务器的启动状态并调用其start
方法启动服务器。如果启动成功,则记录日志并发布ServerStartedEvent
事件;如果启动失败,则抛出异常。
5. 注册服务
在服务器启动之后,需要将服务注册到服务器上。以下是BoltServer
的服务注册示例:
@Override
public void registerProcessor(ProviderConfig providerConfig, Invoker instance) {
// 缓存Invoker对象
String key = ConfigUniqueNameGenerator.getUniqueName(providerConfig);
invokerMap.put(key, instance);
ReflectCache.registerServiceClassLoader(key, providerConfig.getProxyClass().getClassLoader());
// 缓存接口的方法
for (Method m : providerConfig.getProxyClass().getMethods()) {
ReflectCache.putOverloadMethodCache(key, m);
}
}
该方法的核心目的是在服务器端注册服务提供者的处理逻辑,让服务器知道如何处理客户端对特定服务的调用。具体来说,它完成以下几个主要任务:
- 缓存调用器对象:将服务提供者的调用器(
Invoker
)缓存起来,以便在接收到客户端请求时能够快速找到对应的处理逻辑。服务器在处理请求时确实会调用服务器的Invoker
,这个过程后面文章在进行解析 - 解析服务方法:对服务接口的方法进行解析,缓存方法信息,确保服务器能够正确处理方法调用。
- 注册服务到服务器:将服务配置和调用器注册到服务器的处理逻辑中,使得服务器能够处理该服务的请求。
6. 发布服务
最后,调用ProviderConfig
的export
方法发布服务,该方法会调用上述的初始化、启动和注册服务的步骤。