精华内容
下载资源
问答
  • 此类实现了几个Spring中比较重要的接口:如InitializingBean、ApplicationContextAware、ApplicationListener等,查看该类结构图: 接下来分析一下该类一些主要方法: 在ServiceBean.afterPropertiesSet()方法中...

    一、服务暴露

    先看一张Dubbo服务暴露的流程图:
    在这里插入图片描述
    关于服务暴露,首先需要了解:ServiceBean.java
    此类实现了几个Spring中比较重要的接口:如InitializingBean、ApplicationContextAware、ApplicationListener等,查看该类结构图:
    在这里插入图片描述

    接下来分析一下该类的一些主要方法:

    ServiceBean.afterPropertiesSet()方法中,就是将配置文件的各项属性信息都配置到该Bean中。最后通过一个ServiceConfig.export();方法暴露服务。

     @SuppressWarnings({ "unchecked", "deprecation" })
        public void afterPropertiesSet() throws Exception {
            if (getProvider() == null) {
                Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false);
                if (providerConfigMap != null && providerConfigMap.size() > 0) {
                    Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
                    if ((protocolConfigMap == null || protocolConfigMap.size() == 0)
                            && providerConfigMap.size() > 1) { // 兼容旧版本
                        List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();
                        for (ProviderConfig config : providerConfigMap.values()) {
                            if (config.isDefault() != null && config.isDefault().booleanValue()) {
                                providerConfigs.add(config);
                            }
                        }
                        if (providerConfigs.size() > 0) {
                            //
                            setProviders(providerConfigs);
                        }
                    } else {
                        ProviderConfig providerConfig = null;
                        for (ProviderConfig config : providerConfigMap.values()) {
                            if (config.isDefault() == null || config.isDefault().booleanValue()) {
                                if (providerConfig != null) {
                                    throw new IllegalStateException("Duplicate provider configs: " + providerConfig + " and " + config);
                                }
                                providerConfig = config;
                            }
                        }
                        if (providerConfig != null) {
                            setProvider(providerConfig);
                        }
                    }
                }
            }
            if (getApplication() == null
                    && (getProvider() == null || getProvider().getApplication() == null)) {
                Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
                if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
                    ApplicationConfig applicationConfig = null;
                    for (ApplicationConfig config : applicationConfigMap.values()) {
                        if (config.isDefault() == null || config.isDefault().booleanValue()) {
                            if (applicationConfig != null) {
                                throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
                            }
                            applicationConfig = config;
                        }
                    }
                    if (applicationConfig != null) {
                        setApplication(applicationConfig);
                    }
                }
            }
            if (getModule() == null
                    && (getProvider() == null || getProvider().getModule() == null)) {
                Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
                if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
                    ModuleConfig moduleConfig = null;
                    for (ModuleConfig config : moduleConfigMap.values()) {
                        if (config.isDefault() == null || config.isDefault().booleanValue()) {
                            if (moduleConfig != null) {
                                throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
                            }
                            moduleConfig = config;
                        }
                    }
                    if (moduleConfig != null) {
                        setModule(moduleConfig);
                    }
                }
            }
            if ((getRegistries() == null || getRegistries().size() == 0)
                    && (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().size() == 0)
                    && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
                Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
                if (registryConfigMap != null && registryConfigMap.size() > 0) {
                    List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
                    for (RegistryConfig config : registryConfigMap.values()) {
                        if (config.isDefault() == null || config.isDefault().booleanValue()) {
                            registryConfigs.add(config);
                        }
                    }
                    if (registryConfigs != null && registryConfigs.size() > 0) {
                        super.setRegistries(registryConfigs);
                    }
                }
            }
            if (getMonitor() == null
                    && (getProvider() == null || getProvider().getMonitor() == null)
                    && (getApplication() == null || getApplication().getMonitor() == null)) {
                Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
                if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
                    MonitorConfig monitorConfig = null;
                    for (MonitorConfig config : monitorConfigMap.values()) {
                        if (config.isDefault() == null || config.isDefault().booleanValue()) {
                            if (monitorConfig != null) {
                                throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
                            }
                            monitorConfig = config;
                        }
                    }
                    if (monitorConfig != null) {
                        setMonitor(monitorConfig);
                    }
                }
            }
            if ((getProtocols() == null || getProtocols().size() == 0)
                    && (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) {
                Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
                if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
                    List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
                    for (ProtocolConfig config : protocolConfigMap.values()) {
                        if (config.isDefault() == null || config.isDefault().booleanValue()) {
                            protocolConfigs.add(config);
                        }
                    }
                    if (protocolConfigs != null && protocolConfigs.size() > 0) {
                        super.setProtocols(protocolConfigs);
                    }
                }
            }
            if (getPath() == null || getPath().length() == 0) {
                if (beanName != null && beanName.length() > 0 
                        && getInterface() != null && getInterface().length() > 0
                        && beanName.startsWith(getInterface())) {
                    setPath(beanName);
                }
            }
            if (! isDelay()) {
                //此方法最为关键,即服务暴露的动作从此时触发
                export();
            }
        }
    
    

    再来看看一下ServiceConfig.export()这个方法:

    public class ServiceConfig<T> extends AbstractServiceConfig {
    
    //...
    
    public synchronized void export() {
            if (provider != null) {
                if (export == null) {
                    export = provider.getExport();
                }
                if (delay == null) {
                    delay = provider.getDelay();
                }
            }
            if (export != null && ! export.booleanValue()) {
                return;
            }
            if (delay != null && delay > 0) {
                Thread thread = new Thread(new Runnable() {
                    public void run() {
                        try {
                            Thread.sleep(delay);
                        } catch (Throwable e) {
                        }
                        //主要核心操作在于doExport()
                        doExport();
                    }
                });
                thread.setDaemon(true);
                thread.setName("DelayExportServiceThread");
                thread.start();
            } else {
                doExport();
            }
        }
    }
    

    接下来看看ServiceConfig.doExport()方法:

    public class ServiceConfig<T> extends AbstractServiceConfig {
    
    //...
    
    protected synchronized void doExport() {
            if (unexported) {
                throw new IllegalStateException("Already unexported!");
            }
            if (exported) {
                return;
            }
            exported = true;
            if (interfaceName == null || interfaceName.length() == 0) {
                throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
            }
            checkDefault();
            if (provider != null) {
                if (application == null) {
                    application = provider.getApplication();
                }
                if (module == null) {
                    module = provider.getModule();
                }
                if (registries == null) {
                    registries = provider.getRegistries();
                }
                if (monitor == null) {
                    monitor = provider.getMonitor();
                }
                if (protocols == null) {
                    protocols = provider.getProtocols();
                }
            }
            if (module != null) {
                if (registries == null) {
                    registries = module.getRegistries();
                }
                if (monitor == null) {
                    monitor = module.getMonitor();
                }
            }
            if (application != null) {
                if (registries == null) {
                    registries = application.getRegistries();
                }
                if (monitor == null) {
                    monitor = application.getMonitor();
                }
            }
            if (ref instanceof GenericService) {
                interfaceClass = GenericService.class;
                generic = true;
            } else {
                try {
                    interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                            .getContextClassLoader());
                } catch (ClassNotFoundException e) {
                    throw new IllegalStateException(e.getMessage(), e);
                }
                checkInterfaceAndMethods(interfaceClass, methods);
                checkRef();
                generic = false;
            }
            if(local !=null){
                if(local=="true"){
                    local=interfaceName+"Local";
                }
                Class<?> localClass;
                try {
                    localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
                } catch (ClassNotFoundException e) {
                    throw new IllegalStateException(e.getMessage(), e);
                }
                if(!interfaceClass.isAssignableFrom(localClass)){
                    throw new IllegalStateException("The local implemention class " + localClass.getName() + " not implement interface " + interfaceName);
                }
            }
            if(stub !=null){
                if(stub=="true"){
                    stub=interfaceName+"Stub";
                }
                Class<?> stubClass;
                try {
                    stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub);
                } catch (ClassNotFoundException e) {
                    throw new IllegalStateException(e.getMessage(), e);
                }
                if(!interfaceClass.isAssignableFrom(stubClass)){
                    throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + interfaceName);
                }
            }
            checkApplication();
            checkRegistry();
            checkProtocol();
            appendProperties(this);
            checkStubAndMock(interfaceClass);
            if (path == null || path.length() == 0) {
                path = interfaceName;
            }
            //暴露url操作
            doExportUrls();
        }
    
    

    再来看看上述代码中暴露url的代码doExportUrls()(比较烦躁,坚持啊)。

     @SuppressWarnings({ "unchecked", "rawtypes" })
        private void doExportUrls() {
            List<URL> registryURLs = loadRegistries(true);
            for (ProtocolConfig protocolConfig : protocols) {
                //获取注册中心的信息,根据协议暴露对应的url
                doExportUrlsFor1Protocol(protocolConfig, registryURLs);
            }
        }
    

    ServiceConfig.doExportUrlsFor1Protocol(protocolConfig, registryURLs)源码如下,进行Url暴露的细节:

    
    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
            String name = protocolConfig.getName();
            if (name == null || name.length() == 0) {
                name = "dubbo";
            }
    
            String host = protocolConfig.getHost();
            if (provider != null && (host == null || host.length() == 0)) {
                host = provider.getHost();
            }
            boolean anyhost = false;
            if (NetUtils.isInvalidLocalHost(host)) {
                anyhost = true;
                try {
                    host = InetAddress.getLocalHost().getHostAddress();
                } catch (UnknownHostException e) {
                    logger.warn(e.getMessage(), e);
                }
                if (NetUtils.isInvalidLocalHost(host)) {
                    if (registryURLs != null && registryURLs.size() > 0) {
                        for (URL registryURL : registryURLs) {
                            try {
                                Socket socket = new Socket();
                                try {
                                    SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
                                    socket.connect(addr, 1000);
                                    host = socket.getLocalAddress().getHostAddress();
                                    break;
                                } finally {
                                    try {
                                        socket.close();
                                    } catch (Throwable e) {}
                                }
                            } catch (Exception e) {
                                logger.warn(e.getMessage(), e);
                            }
                        }
                    }
                    if (NetUtils.isInvalidLocalHost(host)) {
                        host = NetUtils.getLocalHost();
                    }
                }
            }
    
            Integer port = protocolConfig.getPort();
            if (provider != null && (port == null || port == 0)) {
                port = provider.getPort();
            }
            final int defaultPort = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();
            if (port == null || port == 0) {
                port = defaultPort;
            }
            if (port == null || port <= 0) {
                port = getRandomPort(name);
                if (port == null || port < 0) {
                    port = NetUtils.getAvailablePort(defaultPort);
                    putRandomPort(name, port);
                }
                logger.warn("Use random available port(" + port + ") for protocol " + name);
            }
    
            Map<String, String> map = new HashMap<String, String>();
            if (anyhost) {
                map.put(Constants.ANYHOST_KEY, "true");
            }
            map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
            map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
            map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
            if (ConfigUtils.getPid() > 0) {
                map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
            }
            appendParameters(map, application);
            appendParameters(map, module);
            appendParameters(map, provider, Constants.DEFAULT_KEY);
            appendParameters(map, protocolConfig);
            appendParameters(map, this);
            if (methods != null && methods.size() > 0) {
                for (MethodConfig method : methods) {
                    appendParameters(map, method, method.getName());
                    String retryKey = method.getName() + ".retry";
                    if (map.containsKey(retryKey)) {
                        String retryValue = map.remove(retryKey);
                        if ("false".equals(retryValue)) {
                            map.put(method.getName() + ".retries", "0");
                        }
                    }
                    List<ArgumentConfig> arguments = method.getArguments();
                    if (arguments != null && arguments.size() > 0) {
                        for (ArgumentConfig argument : arguments) {
                            //类型自动转换.
                            if(argument.getType() != null && argument.getType().length() >0){
                                Method[] methods = interfaceClass.getMethods();
                                //遍历所有方法
                                if(methods != null && methods.length > 0){
                                    for (int i = 0; i < methods.length; i++) {
                                        String methodName = methods[i].getName();
                                        //匹配方法名称,获取方法签名.
                                        if(methodName.equals(method.getName())){
                                            Class<?>[] argtypes = methods[i].getParameterTypes();
                                            //一个方法中单个callback
                                            if (argument.getIndex() != -1 ){
                                                if (argtypes[argument.getIndex()].getName().equals(argument.getType())){
                                                    appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                                                }else {
                                                    throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
                                                }
                                            } else {
                                                //一个方法中多个callback
                                                for (int j = 0 ;j<argtypes.length ;j++) {
                                                    Class<?> argclazz = argtypes[j];
                                                    if (argclazz.getName().equals(argument.getType())){
                                                        appendParameters(map, argument, method.getName() + "." + j);
                                                        if (argument.getIndex() != -1 && argument.getIndex() != j){
                                                            throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }else if(argument.getIndex() != -1){
                                appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                            }else {
                                throw new IllegalArgumentException("argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
                            }
    
                        }
                    }
                } // end of methods for
            }
    
            if (generic) {
                map.put("generic", String.valueOf(true));
                map.put("methods", Constants.ANY_VALUE);
            } else {
                String revision = Version.getVersion(interfaceClass, version);
                if (revision != null && revision.length() > 0) {
                    map.put("revision", revision);
                }
    
                String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
                if(methods.length == 0) {
                    logger.warn("NO method found in service interface " + interfaceClass.getName());
                    map.put("methods", Constants.ANY_VALUE);
                }
                else {
                    map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
                }
            }
            if (! ConfigUtils.isEmpty(token)) {
                if (ConfigUtils.isDefault(token)) {
                    map.put("token", UUID.randomUUID().toString());
                } else {
                    map.put("token", token);
                }
            }
            if ("injvm".equals(protocolConfig.getName())) {
                protocolConfig.setRegister(false);
                map.put("notify", "false");
            }
            // 导出服务
            String contextPath = protocolConfig.getContextpath();
            if ((contextPath == null || contextPath.length() == 0) && provider != null) {
                contextPath = provider.getContextpath();
            }
            URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
    
            if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .hasExtension(url.getProtocol())) {
                url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                        .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
            }
    
            String scope = url.getParameter(Constants.SCOPE_KEY);
            //配置为none不暴露
            if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
    
                //配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
                if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                    exportLocal(url);
                }
                //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
                if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
                    if (logger.isInfoEnabled()) {
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }
                    if (registryURLs != null && registryURLs.size() > 0
                            && url.getParameter("register", true)) {
                        for (URL registryURL : registryURLs) {
                            url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
                            URL monitorUrl = loadMonitor(registryURL);
                            if (monitorUrl != null) {
                                url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                            }
                            if (logger.isInfoEnabled()) {
                                logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                            }
                            /**
                             * 暴露远程服务:<br>
                             * 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br>
                             * 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>
                             * 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br>
                             * 
                             * @param <T> 服务的类型
                             * @param invoker 服务的执行体
                             * @return exporter 暴露服务的引用,用于取消暴露
                             * @throws RpcException 当暴露服务出错时抛出,比如端口已占用
                             */
                            Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
    
                            Exporter<?> exporter = protocol.export(invoker);
                            exporters.add(exporter);
                        }
                    } else {
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                        /**
                         * 暴露远程服务:<br>
                         * 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br>
                         * 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>
                         * 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br>
                         * 
                         * @param <T> 服务的类型
                         * @param invoker 服务的执行体
                         * @return exporter 暴露服务的引用,用于取消暴露
                         * @throws RpcException 当暴露服务出错时抛出,比如端口已占用
                         */
                        Exporter<?> exporter = protocol.export(invoker);
                        exporters.add(exporter);
                    }
                }
            }
            this.urls.add(url);
        }
    
    

    然后通过protocol.export(invoker)方法把服务暴露出去(会根据当前的协议做相应的暴露操作),最后将Url和对应的执行器存起来,存放在注册表中,以供消费者调用。
    在这里插入图片描述

    这就是一个简洁的Dubbo服务暴露的过程了。

    二、服务引用

    如下为服务引用的流程图,可以参考服务暴露的过程进行源码调试。
    其过程简而言之通过一系列的信息,获取到接口对应的代理对象,存放于注册表中。
    在这里插入图片描述

    三、调用流程

    Dubbo的调用流程,可以参考官网的一张服务调用流程图来对源码进行一步步的调试,便于理解(防止文章过长此处就不贴源码了):
    在这里插入图片描述

    更多原理了解,可以到官网查看Dubbo源码导读

    展开全文
  • # API 接口安全性设计## ... signKey- 原理- 通过 appKey 标识不同调用方- 利用 signKey 传入参数 生成 sign 进行加密- signKey 只在本地加密,不参与网络传输- 比较好加密方式是 通过 rsa 加密方式。 公钥给...

    # API 接口安全性设计

    ## 实现约定规则

    ### 调用方按照规则加密,传输对应参数

    ### 服务方按照规则解密

    ## 防止被其他人调用

    ### appKey & signKey

    - 原理

    - 通过 appKey 标识不同调用方

    - 利用 signKey 和 传入参数 生成 sign 进行加密

    - signKey 只在本地加密,不参与网络传输

    - 比较好的加密方式是 通过 rsa 加密的方式。 公钥给对方,用于解密。私钥自己保存,用于加密

    ### Token

    - 原理

    - 0 提前给调用方分配好 用户名和密码

    - 1 用户登录向服务器提供认证信息(如账号和密码),服务器验证成功后返回Token给客户端;

    - 2客户端将Token保存在本地,后续发起请求时,携带此Token;(只携带 token,不带上 appKey。 需要接口提供方根据 token 找出是哪个调用方)

    - 3服务器检查Token的有效性,有效则放行,无效(Token错误或过期)则拒绝。

    - 安全隐患

    - Token 被劫持之后,可以伪造请求和篡改参数

    - 解决方案:服务方配置 ip 白名单

    ## 防止接口请求参数被更改

    ### 将请求中的所有参数和值按照字典升序排序,生成字符串,然后利用 signKey 进行加密

    ### 具体流程

    - 1 按照请求参数名的字母升序排列非空请求参数(包含AccessKey),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA;

    - 2在stringA最后拼接上Secretkey得到字符串stringSignTemp;

    - 3对stringSignTemp进行MD5运算,并将得到的字符串所有字符转换为大写,得到sign值。

    ## 如何防止中间人替换返回结果

    ### 服务接口方在返回结果的时候,同样带上 验签字段。

    ### 调用方在收到返回的时候,根据 验签字段,校验是否符合规则。如果符合规则,则说明未被替换

    ## 防止接口被重放

    ### nonce + timestamp

    ### 但是是否存在意义?服务端是否应该做接口幂等?

    ### nonce指唯一的随机字符串,用来标识每个被签名的请求。通过为每个请求提供一个唯一的标识符,服务器能够防止请求被多次使用(记录所有用过的nonce以阻止它们被二次使用)。

    ### 然而,对服务器来说永久存储所有接收到的nonce的代价是非常大的。可以使用timestamp来优化nonce的存储。

    ## 其他安全性

    ### IP 白名单

    ## 接口设计常用字段

    ### appId 调用方唯一标识

    ### method 接口调用方法 (或者以 url 区分)

    ### version 调用接口的版本

    ### 加密相关

    - sign_type 加密算法

    - sign 加密后的字符串,用于接口提供方校验

    ### timstamp

    - 标识发出请求时间

    - 接口提供方根据时间判断是否要继续处理请求

    - 业务层面区分

    - 防止中间人回放攻击

    ### notify_url

    - 回调的 url 。接口提供方主动通知接口调用方指定页面路径

    ### biz_content

    - 接口请求参数的集合 的字符串 (或者以接口字段的形式暴露出来)

    ## 其他相关

    ### oath2.0

    - 主要是 客户,平台,开发商之间的关系

    - 用于 客户授权开发商获取/操作客户在平台上的信息

    *XMind: ZEN - Trial Version*

    展开全文
  • 这样服务端的接口和模型必须暴露调用方项目。服务端如何暴露呢?客户端如何引用呢? 接口信息 、模型信息 、异常 暴露接口的通常做法是 接口与实现分离,服务端将 接口、模型、异常 等统一放置于一个模块,实现...

    课程概要:

    1. 分布式项目开发与联调
    2. 控制管理后台使用
    3. Dubbo注册中心详解

    一、分布式项目开发与联调


    接口暴露与引用

    在一个RPC场景中 ,调用方是通过接口来调用服务端,传入参数并获得返回结果。这样服务端的接口和模型必须暴露给调用方项目。服务端如何暴露呢?客户端如何引用呢?

    接口信息

    、模型信息

    、异常

    图片

    暴露接口的通常做法是 接口与实现分离,服务端将 接口、模型、异常 等统一放置于一个模块,实现置于另一个模块。调用方通过Maven进行引用。

    图片

    自动化构建与协作

    当项目越来越多,服务依懒关系越发复杂的时候,为了提高协作效率,必须采用自动化工具 完成 接口从编写到构建成JAR包,最后到引用的整个过程。

    图片

    流程描述:

    1. 服务提供者项目发人员编写Client 接口
    2. push 至远程仓库
    3. jenkins 构建指定版本
    4. jenkins Deploye 至私服仓库 nexus
    5. 服务消费者项目开发人员基于maven 从私服务仓库下载

    接口平滑升级:

    在项目迭代过程当中, 经常会有多个项目依懒同一个接口,如下图 项目B、C都依懒了项目A当中的接口1,此时项目B业务需要,需要接口1多增加一个参数,升级完成后。项目B能正确构建上线,项目C却不行。

    图片

    解决办法与原则:

    1. 接口要做到向下兼容:接口参数尽量以对象形式进行封装。Model属性只增不删,如果需要作废,可以添加@Deprecated 标识。
    2. 如果出现了不可兼容的变更,则必须通知调用方整改,并制定上线计划。

    开发联调:

    在项目开发过程当中,一个开发或测试环境的注册中心很有可能会同时承载着多个服务,如果两组服务正在联调,如何保证调用的是目标服务呢?

    1、基于临时分组联调

    group 分组

    在reference 和server 当中采用相同的临时组 ,通过group 进行设置

    2、直连提供者:

    在reference 中指定提供者的url即可做到直连

    <dubbo:reference  url="dubbo://127.0.0.1:20880" id="demoService"
                      timeout="2000"
                      interface="com.tuling.teach.service.DemoService" check="false"/>
    

    3、只注册:

    一个项目有可能同是为即是服务提供者又消费者,在测试时需要调用某一服务同时又不希望正在开发的服务影响到其它订阅者如何实现?

    通过修改 register=false 即可实现

    <dubbo:registry address="multicast://224.5.6.7:1234" register="false"/>
    

    ## 二、Dubbo控制管理后台使用


    Dubbo 控制后台版本说明:

    2.5.8 duboo-admin

    2.6 dubbo-admin

    Dubbo 控制后台的安装:

    #从github 中下载dubbo 项目
    git clone https://github.com/apache/incubator-dubbo.git
    #更新项目
    git fetch
    #临时切换至 dubbo-2.5.8 版本
    git checkout dubbo-2.5.8
    #进入 dubbo-admin 目录
    cd dubbo-admin
    #mvn 构建admin war 包
    mvn clean pakcage -DskipTests
    #得到 dubbo-admin-2.5.8.war 即可直接部署至Tomcat
    #修改 dubbo.properties 配置文件
    dubbo.registry.address=zookeeper://127.0.0.1:2181
    

    注:如果实在懒的构建 可直接下载已构建好的:

    链接:https://pan.baidu.com/s/1zJFNPgwNVgZZ-xobAfi5eQ 提取码:gjtv

    控制后台基本功能介绍 :

    • 服务查找:
    • 服务关系查看:
    • 服务权重调配:
    • 服务路由:
    • 服务禁用
      

    三、Dubbo注册中心详解


    注册中心的作用

    为了到达服务集群动态扩容的目的,注册中心存储了服务的地址信息与可用状态信息,并实时推送给订阅了相关服务的客户端。

    图片

    一个完整的注册中心需要实现以下功能:

    1. 接收服务端的注册与客户端的引用,即将引用与消费建立关联,并支持多对多。
    2. 当服务非正常关闭时能即时清除其状态
    3. 当注册中心重启时,能自动恢复注册数据,以及订阅请求
    4. 注册中心本身的集群

    Dubbo所支持的注册中心

    1. Multicast 注册中心
    2. 基于组网广播技术,只能用在局域网内,一般用于简单的测试服务
    3. Zookeeper 注册中心(推荐)
    4. Zookeeper 是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用
    5. Redis 注册中心
    6. 基于Redis的注册中心
    7. Simple 注册中心
    8. 基于本身的Dubbo服务实现(SimpleRegistryService),不支持集群可作为自定义注册中心的参考,但不适合直接用于生产环境。

    Redis 注册中心

    关于Redis注册中心我们需要了解两点,

    1. 如何存储服务的注册与订阅关系
    2. 是当服务状态改变时如何即时更新

    Dubbo使用Redis的发布订阅特性实现 提供者与消费者之前数据实时同步其原理如下:

    Redis 发布订阅

    Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

    Redis 客户端可以订阅任意数量的频道。

    下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

    图片

    当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

    演示使用Redis 做为注册中心的使用。

    • 启动Redis服务
    • 服务端配置注册中心
    • 启动两个服务端
    • 通过RedisClient 客户端观察Redis中的数据

    redis 注册中心配置:

    <dubbo:registry protocol="redis" address="192.168.0.147:6379"/>
    

    当我们启动两个服务端后发现,Reids中增加了一个Hash 类型的记录,其key为/dubbo/tuling.dubbo.server.UserService/providers。Value中分别存储了两个服务提供者的URL和有效期。
    图片

    同样消费者也是类似其整体结构如下:

    //服务提供者注册信息 
    /dubbbo/com.tuling.teach.service.DemoService/providers
      dubbo://192.168.246.1:20880/XXX.DemoService=1542619052964 
      dubbo://192.168.246.2:20880/XXX.DemoService=1542619052964 
    //服务消费订阅信息
    /dubbbo/com.tuling.teach.service.DemoService/consumers
      dubbo://192.168.246.1:20880/XXX.DemoService=1542619788641
    
    • 主 Key 为服务名和类型
    • Map 中的 Key 为 URL 地址
    • Map 中的 Value 为过期时间,用于判断脏数据,脏数据由监控中心删除

    接下来回答第二个问题 当提供者突然 宕机状态能即里变更吗

    这里Dubbo采用的是定时心跳的机制 来维护服务URL的有效期,默认每30秒更新一次有效期。即URL对应的毫秒值。具体代码参见:com.alibaba.dubbo.registry.redis.RedisRegistry#expireExecutor

    图片

    com.alibaba.dubbo.registry.redis.RedisRegistry#deferExpired

    com.alibaba.dubbo.registry.integration.RegistryDirectory

    com.alibaba.dubbo.registry.support.ProviderConsumerRegTable

    Zookeeper 注册中心

    关于Zookeeper 注册中心同样需要了解其存储结构和更新机制。

    Zookeper是一个树型的目录服务,本身支持变更推送相比redis的实现Publish/Subscribe功能更稳定。

    结构:

    图片

    失败重连

    com.alibaba.dubbo.registry.support.FailbackRegistry

    提供者突然断开:

    基于Zookeeper 临时节点机制实现,在客户端会话超时后 Zookeeper会自动删除所有临时节点,默认为40秒。

    // 创建临时节点

    com.alibaba.dubbo.remoting.zookeeper.curator.CuratorZookeeperClient#createEphemeral
    

    提问:
    在zookeeper 断开的40秒内 如果 有客户端加入 会调用 已失效的提供者连接吗?

    答:不会,提供者宕机后 ,其与客户端的链接也随即断开,客户端在调用前会检测长连接状态。

    // 检测连接是否有效
    com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker#isAvailable
    

    创建 configurators与routers 会创建持久节点

    // 创建持久节点

    com.alibaba.dubbo.remoting.zookeeper.curator.CuratorZookeeperClient#createPersistent
    

    服务订阅机制实现:

    // 注册目录
    com.alibaba.dubbo.registry.integration.RegistryDirectory
    

    源码解析:
    图片

    com.alibaba.dubbo.registry.integration.RegistryDirectory

    构成图如下:
    在这里插入图片描述

    展开全文
  • RPC 实现原理

    2020-12-16 16:15:52
    RPC 的实现原理 首先需要有处理网络连接通讯的模块, 负责连接建立、管理消息的传输。...客户调用服务接口的一个代理实现, 这个代理实现负责收集数据、 编码并传输给服务器然后等待结果返回 ...

    RPC 的实现原理

    首先需要有处理网络连接通讯的模块,
    负责连接建立、管理和消息的传输。

    其次需要有编解码的模块,
    因为网络通讯都是传输的字节码,需要将我们使用的对象序列化和反序列化。

    剩下的就是客户端和服务器端的部分,
    服务器端暴露要开放的服务接口,
    客户调用服务接口的一个代理实现,
    这个代理实现负责收集数据、
    编码并传输给服务器然后等待结果返回

    展开全文
  • 服务器端提供服务,服务中要暴露可以调用的远程方法,以接口的形式表现,这样在客户端可以通过服务接口来调用远程方法,实现复杂的业务逻辑。在服务器端,首先要对接口中提供的方法实现,,完成了客户请求的调用的...
  • app开放接口签名设计与实现

    千次阅读 2017-10-21 23:55:53
    试想有一个发送注册验证码的接口,如果仅仅知道接口地址参数(手机号)就可以调用,那短信接口早被人盗刷不知道多少了。 理想情况下,我们只希望我们的接口被我们自己客户端去调用, 那么问题来了,我们如何...
  • 服务暴露

    2019-06-11 23:55:00
    须知 强烈建议看官方文档 服务暴露文档地址 服务发布-原理 发布步骤: 1. 暴露本地服务 2. 暴露远程服务 ...例如:在同一个服务,自己调用自己的接口,就i没必要进行IP连接来通信 暴露远程服务:指暴露...
  • Dubbo是一个RPC远程调用的框架,对于一个服务提供方,暴露了一个接口给外部消费方调用。如果服务提供方自身也需要调用这个接口会怎么样呢,难道也需要走远程编解码数据网络传输这套流程吗?对于Dubbo这么优秀...
  • 模板技术是做BS开发绕不掉...postman是前端众多调试工具中一个,如果restful风格接口开发时候,往往接口需要调试,这一次采用前后端分离,调用接口全是restfu风格。用restful风格接口暴露,来让别人调用...
  • Zuul工作原理

    2020-05-21 20:23:13
    外界系统调用API接口时,都是由网关对外暴露的API接口,外界系统不需要知道微服务系统中各服务相互调用的复杂性。微服务系统也保护了其内部微服务单元API接口,防止其被外界直接调用,导致服务敏感信息对
  • Dubbo工作原理

    千次阅读 2018-11-17 12:13:08
    Dubbo是一款RPC(Remote Procedure Call)框架,其主要作用是实现远程过程调用,比如在一个公司内有好几个开发部门,A部门想调用B部门... 比如B部门给A部门暴露了一个接口可以供B部调用接口服务,那么...
  • “ 分布式应用场景有高并发,高可扩展高性能要求。还涉及到,序列化/反序列化,网络,多线程以及设计模式问题。幸好 Dubbo 框架将上述知识进行...它实现了面向接口代理 RPC 调用,服务注册发现,负载均衡...
  • dubbo作为RPC框架,其实现效果就是调用远程的方法和调用本地一样,其实就是本地有对远程方法的描述(方法名,参数和返回值),其实现方式也相同,即RPC接口和本地接口的使用是相同的。dubbo分为两部分:服务的提供方...
  • Runloop 实现原理及应用

    千次阅读 2016-11-21 14:19:03
    程序从启动开始,一直都是按照苹果封装好代码运行着,暴露的一些属性方法作为接口,是让我们在给定方法里写代码实现自定义功能,做出各种各样应用。这些方法的调用顺序最为关键,熟悉了程序运转方法调用的...
  • 一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错负载均衡,以及服务自动注册发现 原理图 特性: 面向接口代理的高性能RPC调用 智能负载均衡 服务自动...
  • 1、zuul作为整个应用流量入口,接收所有请求,如app、网页等,并且将不同...2、feign则是将当前微服务部分服务接口暴露出来,并且主要用于各个微服务之间服务调用。 两者应用层次以及原理均不相同。 ...
  • 暴露的Http接口接口定义见 ),待调用后开启后台线程,通过主键分批同步指定数据库中数据到Elasticsearch 读取数据库会加读锁主键必须为数字类型 过程 首先会根据所给数据库主键分段,拿到最大主键数字max_id...
  • 提供服务暴露的接口,在流量低情况或许并不需要考虑限流,因为在数据库或缓存允许下就能正常工作,但是当调用突然飙升时候,那么就会出现异常情况,比如数据库连接池线程池就是一种限流手段,通过限制...
  • 微服务暴露给Sentinel DashboardAPI接口列表:http://localhost:8719/api Sentinel Dashboard控制台配置修改: #懒加载、饥饿加载:true表示饥饿加载 #开启饥饿加载 就是一开始启动时候一次性创建完,而不是设置...
  • 前言 上一篇文章主要对计算机硬件部分作了简要介绍...操作系统是控制应用程序执行程序,屏蔽了底层硬件细节,只暴露给上层应用程序接口以方便调用。它有下面三个目标: 方便:操作系统使计算机更容易使用。 ...
  • 随着博客人数增加, Blog 作为一种新生活方式、新工作方式、新学习方式已经被越来越多人所接受,并且在改变传统网络社会结构:网络信息不再是虚假不可验证,交流沟通更有明确选择方向性,单一...
  • 个人理解的通信原理就是客户端服务端使用同一个接口,服务端通过service实现这个接口的方法并且暴露(注册),客户端通过与service连接得到接口关联,之后客户端就能调用服务端接口的方法了(粗略理解,个人觉得...
  • Dubbo入门

    2019-07-16 00:03:58
    Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错负载均衡,以及服务自动注册发现。 2.基本原理 服务提供者(Provider):暴露服务的服务提供方,服.....
  • 反射机制的原理 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性方法;对于任意一个对象,都能够调用它的任意一个方法属性;这种动态获取的信息以及动态调用对象的方法的功能称为java...
  • WebSocket客户端服务端实例源码

    千次下载 热门讨论 2015-08-14 13:30:01
    基于 Flash,AdobeFlash 通过自己 Socket 实现完成数据交换,再利用 Flash 暴露出相应的接口为 JavaScript 调用,从而达到实时传输目的。此方式比轮询要高效,且因为 Flash 安装率高,应用场景比较广泛,但在移动...
  • Dubbo框架知识总结(二)

    2018-05-07 17:14:33
    Dubbo高级用法之泛化与接口自适应dubbo 是阿里巴巴开源分布式开发框架,在互联网企业...为什么需要泛化一般情况下,服务提供者需要暴露接口和方法,服务调用者需要明确地知道服务使用的接口和方法定义,然后双...
  • webView相关知识点 原生H5交互 一、原生访问 h5 H5暴露全局方法提供给app调用 1.使用webView.loadUrl...原理:原生通过webViewdAPI-addJavascriptInterFace,传递一个自己事先写好的接口类并声明接口..
  • Dubbo学习日记(一)

    2019-07-19 00:44:36
    它提供了三大核心能力:面向接口的远程方法调用,智能容错负载均衡,以及服务自动注册发现。 工作原理 官网上给的结构图如下: Provider: 暴露服务的服务提供方。 Consumer: 调用远程服务的服务消费方。 ...
  • socket编程注意事项

    2017-11-17 16:58:33
    Socket的实现原理和接口的具体使用就不详细描述,本文主要是对本人在使用socket传输数据时遇到的问题需要注意的事项进行了分析总结。主要包括以下这些事项: 数据丢失 死锁 传输文本文档时,编码问题 数据传输...

空空如也

空空如也

1 2 3 4
收藏数 61
精华内容 24
关键字:

暴露接口和调用接口的原理