露出--- SOFARPCサーバのソースコード解析

サーバー側の例

私たちは、最初の例のサービス側のラベル:

    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方法:

  1. パラメータが正しいチェック
    1. オブジェクトがインタフェースの実装クラスに注入されているかどうかを確認してください
    2. providerConfigは、サーバのパラメータを設定しているされています
    3. 同じ名前のメソッドが存在するか否かを確認する方法、白と黒のリストをフィルタリングする方法(構成フィルタリング方法を含み、除外するために)
  2. serverConfigsトラバーサルセット
  3. よりrepeatedExportLimitのセットよりも、例外をスローする場合のインターフェイス上で公開するには、カウントされます
  4. 建設要求の発信者
  5. 初期レジストリ
  6. 登録要求の発信者
  7. サービスを開始
  8. リスナーを設定します
  9. レジストリへの登録

次に、上記一歩ずつ分析します

パラメータは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);
    } 

このビットにいくつかのことを行うために主にある方法:

  1. するために呼び出すproviderConfig.getProxyClass();インターフェイスクラスを取得し、我々の例では、インタフェースcom.alipay.sofa.rpc.quickstart.HelloServiceです
  2. するために呼び出すproviderConfig.getRef();インターフェイスの実装クラスの参照を取得し、我々はここでHelloServiceImplに対応します
  3. するために呼び出すproxyClass.isInstance例外をスローしない場合は、refはインタフェースの実装クラスであるかどうかを判断します
  4. チェックサーバは、空にすることはできません
  5. 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);
    }

この方法では、我々は最初のクラスを達成するために、すべての道を通過します。

  1. メソッドと同じ名前を持つ実装クラスは、もしあれば、印刷するように求めるメッセージが表示される場合裁判官は、私たちは、公式にはそう主張していないときに我々はより良​​いオーバーロードされたインタフェース定義の定義を使用しないことを知ることができる場所を確認し、確認してください
  2. 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サーバです

おすすめ

転載: www.cnblogs.com/luozhiyun/p/11298534.html
おすすめ