サーバー側の例
私たちは、最初の例のサービス側のラベル:
public static void main(String[] args) {
ServerConfig serverConfig = new ServerConfig()
.setProtocol("bolt") // 设置一个协议,默认bolt
.setPort(12200) // 设置一个端口,默认12200
.setDaemon(false); // 非守护线程
ProviderConfig<HelloService> providerConfig = new ProviderConfig<HelloService>()
.setInterfaceId(HelloService.class.getName()) // 指定接口
.setRef(new HelloServiceImpl()) // 指定实现
.setServer(serverConfig); // 指定服务端
providerConfig.export(); // 发布服务
}
ProviderConfigの#エクスポート
我々はProviderConfig ServerConfigから良い例を設定し、開始およびエクスポート方法暴露後ProviderConfigを呼び出し
ProviderConfigの#エクスポート
public synchronized void export() {
if (providerBootstrap == null) {
providerBootstrap = Bootstraps.from(this);
}
//DefaultProviderBootstrap
providerBootstrap.export();
}
で道、私たちからのブートストラップの#「ソースコード解析--- SOFARPCクライアントサービス参照は、」分析されているので、SOFARPC自身のSPIメカニズムはDefaultProviderBootstrapインスタンスを返す呼び出すことである、そこに分析されていません。
DefaultProviderBootstrapの#エクスポート
そして、DefaultProviderBootstrap#エクスポートメソッドを呼び出します
@Override
public void export() {
if (providerConfig.getDelay() > 0) { // 延迟加载,单位毫秒
Thread thread = factory.newThread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(providerConfig.getDelay());
} catch (Throwable ignore) { // NOPMD
}
doExport();
}
});
thread.start();
} else {
doExport();
}
}
二つの分岐があり、あなたがプロパティを設定providerConfiの遅延時間を設定した場合、その後、スレッドからNamedThreadFactory#newThreadメソッドを呼び出しますし、その後doExportメソッドを呼び出して遅延させること。
DefaultProviderBootstrap#doExport
/**
* 是否已发布
*/
protected transient volatile boolean exported;
/**
* 发布的服务配置
*/
protected final static ConcurrentMap<String, AtomicInteger> EXPORTED_KEYS = new ConcurrentHashMap<String, AtomicInteger>();
private void doExport() {
//校验一下,如果服务已经暴露过了那么就不再进行暴露
if (exported) {
return;
}
// 检查参数
checkParameters();
String appName = providerConfig.getAppName();
//key is the protocol of server,for concurrent safe
Map<String, Boolean> hasExportedInCurrent = new ConcurrentHashMap<String, Boolean>();
// 将处理器注册到server
List<ServerConfig> serverConfigs = providerConfig.getServer();
for (ServerConfig serverConfig : serverConfigs) {
String protocol = serverConfig.getProtocol();
String key = providerConfig.buildKey() + ":" + protocol;
if (LOGGER.isInfoEnabled(appName)) {
LOGGER.infoWithApp(appName, "Export provider config : {} with bean id {}", key, providerConfig.getId());
}
// 注意同一interface,同一uniqueId,不同server情况
AtomicInteger cnt = EXPORTED_KEYS.get(key); // 计数器
if (cnt == null) { // 没有发布过
cnt = CommonUtils.putToConcurrentMap(EXPORTED_KEYS, key, new AtomicInteger(0));
}
//计数器加一,如果计数器的值超过了设置的exportLimit,那么就会抛出异常
int c = cnt.incrementAndGet();
hasExportedInCurrent.put(serverConfig.getProtocol(), true);
int maxProxyCount = providerConfig.getRepeatedExportLimit();
if (maxProxyCount > 0) {
if (c > maxProxyCount) {
//计数器减一
decrementCounter(hasExportedInCurrent);
// 超过最大数量,直接抛出异常
throw new SofaRpcRuntimeException("Duplicate provider config with key " + key
+ " has been exported more than " + maxProxyCount + " times!"
+ " Maybe it's wrong config, please check it."
+ " Ignore this if you did that on purpose!");
} else if (c > 1) {
if (LOGGER.isInfoEnabled(appName)) {
LOGGER.infoWithApp(appName, "Duplicate provider config with key {} has been exported!"
+ " Maybe it's wrong config, please check it."
+ " Ignore this if you did that on purpose!", key);
}
}
}
}
try {
// 构造请求调用器
providerProxyInvoker = new ProviderProxyInvoker(providerConfig);
// 初始化注册中心
if (providerConfig.isRegister()) {
//如果有设置注册中心的话就遍历注册中心
List<RegistryConfig> registryConfigs = providerConfig.getRegistry();
if (CommonUtils.isNotEmpty(registryConfigs)) {
for (RegistryConfig registryConfig : registryConfigs) {
// 提前初始化Registry到ALL_REGISTRIES对象中
RegistryFactory.getRegistry(registryConfig);
}
}
}
//如果有多个配置则逐个遍历
// 将处理器注册到server
for (ServerConfig serverConfig : serverConfigs) {
try {
Server server = serverConfig.buildIfAbsent();
// 注册请求调用器
server.registerProcessor(providerConfig, providerProxyInvoker);
if (serverConfig.isAutoStart()) {
server.start();
}
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Exception e) {
LOGGER.errorWithApp(appName, "Catch exception when register processor to server: "
+ serverConfig.getId(), e);
}
}
providerConfig.setConfigListener(new ProviderAttributeListener());
// 注册到注册中心
register();
} catch (Exception e) {
decrementCounter(hasExportedInCurrent);
if (e instanceof SofaRpcRuntimeException) {
throw (SofaRpcRuntimeException) e;
} else {
throw new SofaRpcRuntimeException("Build provider proxy error!", e);
}
}
// 记录一些缓存数据
RpcRuntimeContext.cacheProviderConfig(this);
exported = true;
}
主に以下のことを行うdoExport方法:
- パラメータが正しいチェック
- オブジェクトがインタフェースの実装クラスに注入されているかどうかを確認してください
- providerConfigは、サーバのパラメータを設定しているされています
- 同じ名前のメソッドが存在するか否かを確認する方法、白と黒のリストをフィルタリングする方法(構成フィルタリング方法を含み、除外するために)
- serverConfigsトラバーサルセット
- よりrepeatedExportLimitのセットよりも、例外をスローする場合のインターフェイス上で公開するには、カウントされます
- 建設要求の発信者
- 初期レジストリ
- 登録要求の発信者
- サービスを開始
- リスナーを設定します
- レジストリへの登録
次に、上記一歩ずつ分析します
パラメータはDefaultProviderBootstrap#checkParametersをチェック
protected void checkParameters() {
// 检查注入的ref是否接口实现类
Class proxyClass = providerConfig.getProxyClass();
String key = providerConfig.buildKey();
T ref = providerConfig.getRef();
if (!proxyClass.isInstance(ref)) {
throw ExceptionUtils.buildRuntime("provider.ref",
ref == null ? "null" : ref.getClass().getName(),
"This is not an instance of " + providerConfig.getInterfaceId()
+ " in provider config with key " + key + " !");
}
// server 不能为空
if (CommonUtils.isEmpty(providerConfig.getServer())) {
throw ExceptionUtils.buildRuntime("server", "NULL", "Value of \"server\" is not specified in provider" +
" config with key " + key + " !");
}
checkMethods(proxyClass);
}
このビットにいくつかのことを行うために主にある方法:
- するために呼び出す
providerConfig.getProxyClass();
インターフェイスクラスを取得し、我々の例では、インタフェースcom.alipay.sofa.rpc.quickstart.HelloServiceです - するために呼び出す
providerConfig.getRef();
インターフェイスの実装クラスの参照を取得し、我々はここでHelloServiceImplに対応します - するために呼び出す
proxyClass.isInstance
例外をスローしない場合は、refはインタフェースの実装クラスであるかどうかを判断します - チェックサーバは、空にすることはできません
- checkMethods方法の検証メソッドを呼び出します
checkMethodsに進みます
protected void checkMethods(Class<?> itfClass) {
ConcurrentHashMap<String, Boolean> methodsLimit = new ConcurrentHashMap<String, Boolean>();
for (Method method : itfClass.getMethods()) {
String methodName = method.getName();
if (methodsLimit.containsKey(methodName)) {
// 重名的方法
if (LOGGER.isWarnEnabled(providerConfig.getAppName())) {
LOGGER.warnWithApp(providerConfig.getAppName(), "Method with same name \"" + itfClass.getName()
+ "." + methodName + "\" exists ! The usage of overloading method in rpc is deprecated.");
}
}
// 判断服务下方法的黑白名单
Boolean include = methodsLimit.get(methodName);
if (include == null) {
//对配置的include和exclude方法进行过滤
// 检查是否在黑白名单中
include = inList(providerConfig.getInclude(), providerConfig.getExclude(), methodName);
methodsLimit.putIfAbsent(methodName, include);
}
}
providerConfig.setMethodsLimit(methodsLimit);
}
この方法では、我々は最初のクラスを達成するために、すべての道を通過します。
- メソッドと同じ名前を持つ実装クラスは、もしあれば、印刷するように求めるメッセージが表示される場合裁判官は、私たちは、公式にはそう主張していないときに我々はより良いオーバーロードされたインタフェース定義の定義を使用しないことを知ることができる場所を確認し、確認してください
- INCLUDEおよびパラメータを除外した場合、その後、これらの2つのパラメータに応じて解放されるオブジェクトのメソッド・レベルのフィルタリングになり、デフォルトで備えられて
*
、空の文字列にパラメータのデフォルト値を除外する
初期レジストリ
私たちは、通話をダウン見ることができますRegistryFactory.getRegistry(registryConfig);
レジストリを初期化します
public static synchronized Registry getRegistry(RegistryConfig registryConfig) {
// 超过3次 是不是配错了?
if (ALL_REGISTRIES.size() > 3) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Size of registry is greater than 3, Please check it!");
}
}
try {
// 注意:RegistryConfig重写了equals方法,如果多个RegistryConfig属性一样,则认为是一个对象
Registry registry = ALL_REGISTRIES.get(registryConfig);
if (registry == null) {
//通过spi根据protocol来获取注册中心实例
ExtensionClass<Registry> ext = ExtensionLoaderFactory.getExtensionLoader(Registry.class)
.getExtensionClass(registryConfig.getProtocol());
if (ext == null) {
throw ExceptionUtils.buildRuntime("registry.protocol", registryConfig.getProtocol(),
"Unsupported protocol of registry config !");
}
registry = ext.getExtInstance(new Class[]{RegistryConfig.class}, new Object[]{registryConfig});
ALL_REGISTRIES.put(registryConfig, registry);
}
return registry;
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Throwable e) {
throw new SofaRpcRuntimeException(e.getMessage(), e);
}
}
ALL_REGISTRIESオブジェクトにそれらを置くために、適切なSPIレジストリをインスタンス化し、によって主にこの方法。
初期サーバー
次に、我々は呼ぶserverConfig.buildIfAbsent();
初期化サーバを
public synchronized Server buildIfAbsent() {
if (server != null) {
return server;
}
// 提前检查协议+序列化方式
// ConfigValueHelper.check(ProtocolType.valueOf(getProtocol()),
// SerializationType.valueOf(getSerialization()));
//通过工厂获取server实例
server = ServerFactory.getServer(this);
return server;
}
public synchronized static Server getServer(ServerConfig serverConfig) {
try {
//根据端口获取实例
Server server = SERVER_MAP.get(Integer.toString(serverConfig.getPort()));
if (server == null) {
// 算下网卡和端口
resolveServerConfig(serverConfig);
ExtensionClass<Server> ext = ExtensionLoaderFactory.getExtensionLoader(Server.class)
.getExtensionClass(serverConfig.getProtocol());
if (ext == null) {
throw ExceptionUtils.buildRuntime("server.protocol", serverConfig.getProtocol(),
"Unsupported protocol of server!");
}
//通过SPI获取server实例
server = ext.getExtInstance();
//初始化server里面具体的参数
server.init(serverConfig);
SERVER_MAP.put(serverConfig.getPort() + "", server);
}
return server;
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Throwable e) {
throw new SofaRpcRuntimeException(e.getMessage(), e);
}
}
そして、私たちはここで使用initメソッドを呼び出すために別のサーバー実装クラスに応じて、初期化パラメータに来るBoltServerです
public void init(ServerConfig serverConfig) {
this.serverConfig = serverConfig;
// 启动线程池
bizThreadPool = initThreadPool(serverConfig);
boltServerProcessor = new BoltServerProcessor(this);
}
終了方法はdoExport方法がダウンしていきコールバックbuildIfAbsent後、server.registerProcessorを呼び出します。この方法は、主に対応するインスタンスインボーカである方法、及び対応するキャッシュされています。
boltServer#registerProcessor
public void registerProcessor(ProviderConfig providerConfig, Invoker instance) {
// 缓存Invoker对象 包路径名+类名+版本号 com.alipay.sofa.rpc.quickstart.HelloService:1.0
String key = ConfigUniqueNameGenerator.getUniqueName(providerConfig);
invokerMap.put(key, instance);
// 把这个实例所对应的类加载器缓存到SERVICE_CLASSLOADER_MAP中
ReflectCache.registerServiceClassLoader(key, providerConfig.getProxyClass().getClassLoader());
// 缓存接口的方法
for (Method m : providerConfig.getProxyClass().getMethods()) {
ReflectCache.putOverloadMethodCache(key, m);
}
}
次に、サービスを開始するboltServer#開始メソッドを呼び出します
public void start() {
//如果已经启动了,那么直接返回
if (started) {
return;
}
synchronized (this) {
//双重检查锁
if (started) {
return;
}
// 生成Server对象,返回的是RpcServer实例
remotingServer = initRemotingServer();
try {
//调用bolt包里面的内容,通过netty启动服务
if (remotingServer.start()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Bolt server has been bind to {}:{}", serverConfig.getBoundHost(),
serverConfig.getPort());
}
} else {
throw new SofaRpcRuntimeException("Failed to start bolt server, see more detail from bolt log.");
}
//设置started参数为true
started = true;
if (EventBus.isEnable(ServerStartedEvent.class)) {
EventBus.post(new ServerStartedEvent(serverConfig, bizThreadPool));
}
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Exception e) {
throw new SofaRpcRuntimeException("Failed to start bolt server!", e);
}
}
}
登録サービス
次に、サービスを登録する方法を登録する呼び出しがあります
protected void register() {
if (providerConfig.isRegister()) {
List<RegistryConfig> registryConfigs = providerConfig.getRegistry();
if (registryConfigs != null) {
for (RegistryConfig registryConfig : registryConfigs) {
//得到注册中心对象
Registry registry = RegistryFactory.getRegistry(registryConfig);
//初始化注册中心
registry.init();
registry.start();
try {
registry.register(providerConfig);
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Throwable e) {
String appName = providerConfig.getAppName();
if (LOGGER.isWarnEnabled(appName)) {
LOGGER.warnWithApp(appName, "Catch exception when register to registry: "
+ registryConfig.getId(), e);
}
}
}
}
}
}
ここでRegistryFactory実装クラスによって異なるレジストリへの主要なアクセスである
いくつかの登録センターで今sofarpc主な達成のためには:
SOFARegistry
飼育係
ローカルファイル
領事
ナコス
コンテンツのレジストリは、私は次の記事で説明する予定なので、ここでは省略します。
それは公開されているすべてのSOFARPCサーバです