--- SOFARPC server source code analysis exposed

Examples of server-side

We first labeled the service side of our example:

    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#export

Call ProviderConfig after the start we set a good example from ProviderConfig ServerConfig and export methods exposed
ProviderConfig # export

    public synchronized void export() {
        if (providerBootstrap == null) {
            providerBootstrap = Bootstraps.from(this);
        }
        //DefaultProviderBootstrap
        providerBootstrap.export();
    }

Bootstraps # from the way we in the "source code analysis --- SOFARPC client service reference" has been analyzed, so there is not analyzed, which is to call SOFARPC own SPI mechanism returns DefaultProviderBootstrap instance.

DefaultProviderBootstrap#export

Then call DefaultProviderBootstrap # export method

    @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();
        }
    }

There are two branches, if you set providerConfi time delay setting properties, then will call NamedThreadFactory # newThread method from a thread, and then to delay calling doExport method.

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 method which mainly do the following things:

  1. Check the parameters are correct
    1. Check whether the object is injected interface implementation class
    2. Are providerConfig have set server parameters
    3. The method of checking whether there is a method of the same name, a method of filtering white and black lists (to include and exclude configuration filtering method)
  2. serverConfigs traversal set
  3. To publish on the interface are counted, if more than a set of repeatedExportLimit then throw an exception
  4. Construction request caller
  5. Initialization registry
  6. Registration request caller
  7. Start Service
  8. Set up listeners
  9. Registration to the registry

Next, we analyze one by one step above

Parameter check 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);
    } 

This method which is mainly to do a bit a few things:

  1. Call to providerConfig.getProxyClass();get an interface class, in our example is the interface com.alipay.sofa.rpc.quickstart.HelloService
  2. Call to providerConfig.getRef();get the interface implementation class references, we here corresponds HelloServiceImpl
  3. Call to proxyClass.isInstancedetermine whether ref is the interface implementation class, if not throw an exception
  4. Check server can not be empty
  5. Call checkMethods method validation methods

The method proceeds to 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);
    }

In this method we will first go through all the way to achieve the class.

  1. Judge to see if the implementation class which has the same name as the method, if any, will be prompted to print, so to see where we can know that we better not use the definition of overloaded interface definition when the official does not advocate doing so
  2. If Include and Exclude parameters, then, will be the method-level filtering of the object to be released according to these two parameters, the default is Include *, Exclude parameter defaults to an empty string
Initialization registry

We can see down calls RegistryFactory.getRegistry(registryConfig);to initialize registry

    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);
        }
    }

This method which is mainly by instantiating the appropriate SPI registry, and then to put them into ALL_REGISTRIES objects.

Initialization server

Next, we will call serverConfig.buildIfAbsent();initialization server

    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);
        }
    }

Then we will come to initialization parameters depending on the different server implementation class to call the init method we use here is BoltServer

    public void init(ServerConfig serverConfig) {
        this.serverConfig = serverConfig;
        // 启动线程池
        bizThreadPool = initThreadPool(serverConfig);
        boltServerProcessor = new BoltServerProcessor(this);
    }

After completion method buildIfAbsent call back doExport methods continue to go down, call server.registerProcessor. This method is a method which is mainly Invoker the corresponding instance, and the corresponding cached.
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);
        }
    }

Then call boltServer # start method to start service

    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);
            }
        }
    }
Registration Service

Then there is the call to register method of registering service

   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);
                        }
                    }
                }
            }
        }
    }

Here is the main access to different registries by RegistryFactory implementation class
for now sofarpc main achievement at several registration centers:

SOFARegistry
Zookeeper
local file
Consul
Nacos

Content registry I intend to explain in the next article, so here we skip.

That's all SOFARPC server exposed

Guess you like

Origin www.cnblogs.com/luozhiyun/p/11298534.html