精华内容
下载资源
问答
  • 3、在index.js中导入组件,并把他们暴露出去 1、写法一 import studentCourse1 from './studentCourse.vue' import studentInfo1 from './studentInfo.vue' export var studentCourse=studentCourse1 export ...
  • 其实从这里我们就可以想到,实现的原理就是用到了动态代理.也就是实际调用的是spring帮我们生成的代理对象. 好像就是这么回事?但是这样显得浅尝辄止,如何生成代理对象,以及生成了怎样的代理对象的才是我们应该知道的...

    首先我们来看看mybatis简单的使用方法.
    我们需要配置一个UserDao.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.xiaoyu.dao.UserDao">
        <select id="getUser" resultType="com.xiaoyu.entity.User">
            select
            a.id as id,
            a.name as name,
            from biz_user as a
            where a.id =#{userId}
        </select>
    

    然后我们需要有个dao层接口,并使用spring的注解@Repository(高版本好像注解都不需要了)

    @Repository
    public class UserDao {
        User getUser(String userId);
    }
    

    这样我们就可以使用mybatis了,但是一方面我们会乐见于其使用的方便性,另一方面也可能会疑惑为什么我们的dao层(或mapper层)只需要写好一个接口就可以了,而完全没有任何实现.mybatis是如何去实现以及执行xml文件中的sql的了.我们接下来就研究下.

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath:spring.xml" })
    public class TestService {
    
        @Autowired
        private UserDao userDao;
    
        @Test
        public void testUser() {
            User a = userDao.getUser("1");
            a.toString();
        }
    
    }
    

    我们写个简单的测试类并进行debug,可以看到代码会进入org.apache.ibatis.binding.MapperProxy类中调用方法.
    顾名思义,这个MapperProxy是个代理类,并且实现了InvocationHandler.

    public class MapperProxy<T> implements InvocationHandler, Serializable {}
    

    其实从这里我们就可以想到,实现的原理就是用到了动态代理.也就是实际调用的是spring帮我们生成的代理对象.
    好像就是这么回事?但是这样显得浅尝辄止,如何生成代理对象,以及生成了怎样的代理对象的才是我们应该知道的.
    之前我一篇文章讲过spring扩展的问题.也同样适用于mybatis.我们在不深究之前可以大致想下处理流程.肯定是mybatis通过spring的扩展后,在启动的时候,spring按规则扫描mybatis的xml,并解析最后生成了一个代理对象存入spring中以供使用.所以我们来看下mybatis在spring启动的时候都做了什么吧.
    在进行spring扩展的时候,我们需要创建spring.handlers文件,因此我们只需要在mybatis-spring-1.2.2.jar(当然我用的是这个版本的jar)中找到即可.

    http\://mybatis.org/schema/mybatis-spring=org.mybatis.spring.config.NamespaceHandler
    

    核心类已经列出来了,我们看看NamespaceHandler是怎么处理的吧.

    public class NamespaceHandler extends NamespaceHandlerSupport {
    
      /**
       * {@inheritDoc}
       */
      public void init() {
        registerBeanDefinitionParser("scan", new MapperScannerBeanDefinitionParser());
      }
    
    }
    

    再找到核心的解析类MapperScannerBeanDefinitionParser.

    public class MapperScannerBeanDefinitionParser implements BeanDefinitionParser {
    
      private static String ATTRIBUTE_BASE_PACKAGE = "base-package";
      private static String ATTRIBUTE_ANNOTATION = "annotation";
      private static String ATTRIBUTE_MARKER_INTERFACE = "marker-interface";
      private static String ATTRIBUTE_NAME_GENERATOR = "name-generator";
      private static String ATTRIBUTE_TEMPLATE_REF = "template-ref";
      private static String ATTRIBUTE_FACTORY_REF = "factory-ref";
    
      /**
       * {@inheritDoc}
       */
      public synchronized BeanDefinition parse(Element element, ParserContext parserContext) {
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(parserContext.getRegistry());
        ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
        XmlReaderContext readerContext = parserContext.getReaderContext();
        scanner.setResourceLoader(readerContext.getResourceLoader());
        try {
          String annotationClassName = element.getAttribute(ATTRIBUTE_ANNOTATION);
          if (StringUtils.hasText(annotationClassName)) {
            @SuppressWarnings("unchecked")
            Class<? extends Annotation> markerInterface = (Class<? extends Annotation>) classLoader.loadClass(annotationClassName);
            scanner.setAnnotationClass(markerInterface);
          }
          String markerInterfaceClassName = element.getAttribute(ATTRIBUTE_MARKER_INTERFACE);
          if (StringUtils.hasText(markerInterfaceClassName)) {
            Class<?> markerInterface = classLoader.loadClass(markerInterfaceClassName);
            scanner.setMarkerInterface(markerInterface);
          }
          String nameGeneratorClassName = element.getAttribute(ATTRIBUTE_NAME_GENERATOR);
          if (StringUtils.hasText(nameGeneratorClassName)) {
            Class<?> nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName);
            BeanNameGenerator nameGenerator = BeanUtils.instantiateClass((Class<?>) nameGeneratorClass, BeanNameGenerator.class);
            scanner.setBeanNameGenerator(nameGenerator);
          }
        } catch (Exception ex) {
          readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
        }
        String sqlSessionTemplateBeanName = element.getAttribute(ATTRIBUTE_TEMPLATE_REF);
        scanner.setSqlSessionTemplateBeanName(sqlSessionTemplateBeanName);
        String sqlSessionFactoryBeanName = element.getAttribute(ATTRIBUTE_FACTORY_REF);
        scanner.setSqlSessionFactoryBeanName(sqlSessionFactoryBeanName);
        scanner.registerFilters();
        String basePackage = element.getAttribute(ATTRIBUTE_BASE_PACKAGE);
        scanner.scan(StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
        return null;
      }
    
    }
    

    代码有点多,我们抓住重点,一个是开头的定义"base-package";这个也是我们每次做新项目需要配置的(ps其实新项目每次都是copy),通过这个配置我们即告诉spring去哪里找到mapper.
    然后一眼扫去,这段代码里面大致都是scanner这个变量,好像设置了一些属性之类的,到最后我们看到.

    String basePackage = element.getAttribute(ATTRIBUTE_BASE_PACKAGE);
        scanner.scan(StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
    

    这里用到了basePackage,并好像去做了写扫描(scan)的工作,应该就是去扫描我们的mapper了.
    我们点进去看下,还好这是个类,并没有其他的实现ClassPathMapperScanner.

      public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    
        if (beanDefinitions.isEmpty()) {
          logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
          for (BeanDefinitionHolder holder : beanDefinitions) {
            GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
    
            if (logger.isDebugEnabled()) {
              logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
                  + "' and '" + definition.getBeanClassName() + "' mapperInterface");
            }
    
            // the mapper interface is the original class of the bean
            // but, the actual class of the bean is MapperFactoryBean
            definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
            definition.setBeanClass(MapperFactoryBean.class);
    
            definition.getPropertyValues().add("addToConfig", this.addToConfig);
    
            boolean explicitFactoryUsed = false;
            if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
              definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
              explicitFactoryUsed = true;
            } else if (this.sqlSessionFactory != null) {
              definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
              explicitFactoryUsed = true;
            }
    
            if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
              if (explicitFactoryUsed) {
                logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
              }
              definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
              explicitFactoryUsed = true;
            } else if (this.sqlSessionTemplate != null) {
              if (explicitFactoryUsed) {
                logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
              }
              definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
              explicitFactoryUsed = true;
            }
    
            if (!explicitFactoryUsed) {
              if (logger.isDebugEnabled()) {
                logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
              }
              definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            }
          }
        }
    
        return beanDefinitions;
      }
    

    这里果然就是扫描bean的地方.第一行代码就是调用父类方法,按路径从basePackages扫描到指定的beanDefinitions.接下来是对这些粗糙的bean进行处理,我们可以看到设置了mapperInterface,addToConfig,sqlSessionFactory,sqlSessionTemplate,这些都是mybatis调用所需的必要条件.不过这里有个定义是我们需要关注的.

    definition.setBeanClass(MapperFactoryBean.class);
    

    setBeanClass是指定spring实例化的bean是哪一个类的class,而这里似乎不是我们的dao层的接口,而是一个指定的通用的类MapperFactoryBean,那我们实例化后的bean都是这玩意吗.
    的确,我们知道spring是个bean容器,每个bean都是需要实例化后存放在spring的bean工厂的.那么,dao层从我们最上面的分析来看,其实她最后是一个cglib动态代理类.也就是说她是无法被spring通过构造函数进行实例化的.这是啥意思了,也许你会问,动态代理类,不是已经是个活生生的实例化的对象了吗.哈哈,好像是的,不过我们设置的是class,spring会在她必须要实例化的时候,才会进行实例化对象,就是通过class对象反射无参构造函数进行实例化的哦,而动态代理类是无法通过构造函数再次实例化的.
    所以这就是矛盾的所在,我们需要为dao层生成动态代理,但是却无法设置bean的class.怎么办呢,spring作为一个了不起的框架已经为我们提供了方案了,就是工厂bean.我们看看上面的MapperFactoryBean.

    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    
      private Class<T> mapperInterface;
    
      private boolean addToConfig = true;
    
      /**
       * Sets the mapper interface of the MyBatis mapper
       *
       * @param mapperInterface class of the interface
       */
      public void setMapperInterface(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      /**
       * If addToConfig is false the mapper will not be added to MyBatis. This means
       * it must have been included in mybatis-config.xml.
       * <p>
       * If it is true, the mapper will be added to MyBatis in the case it is not already
       * registered.
       * <p>
       * By default addToCofig is true.
       *
       * @param addToConfig
       */
      public void setAddToConfig(boolean addToConfig) {
        this.addToConfig = addToConfig;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      protected void checkDaoConfig() {
        super.checkDaoConfig();
    
        notNull(this.mapperInterface, "Property 'mapperInterface' is required");
    
        Configuration configuration = getSqlSession().getConfiguration();
        if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
          try {
            configuration.addMapper(this.mapperInterface);
          } catch (Throwable t) {
            logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
            throw new IllegalArgumentException(t);
          } finally {
            ErrorContext.instance().reset();
          }
        }
      }
    
      /**
       * {@inheritDoc}
       */
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
    
      /**
       * {@inheritDoc}
       */
      public Class<T> getObjectType() {
        return this.mapperInterface;
      }
    
      /**
       * {@inheritDoc}
       */
      public boolean isSingleton() {
        return true;
      }
    
    }
    

    这个类实现了spring的FactoryBean,里面的核心方法是getObject(),也就是说我们在spring里面的bean最后实际使用的对象是getObject返回的也就是我们开头所说的MapperProxy动态代理类,如果你愿意的话,可以试试debug的.
    概括下整个实现过程在于动态代理+工厂bean.联想下的话,其实我们常用的rpc也是一样的,rpc中consumer暴露的也是一个api,如果愿意去深入研究下的,会看到套路是一样的哦.好了,分析结束,非常nice.

    展开全文
  • 关于服务暴露,首先需要了解:ServiceBean.java 此类实现了几个Spring中比较重要的接口:如InitializingBean、ApplicationContextAware、ApplicationListener等,查看该类结构图: 接下来分析一下该类的一些主要...

    一、服务暴露

    先看一张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源码导读

    展开全文
  • 一、RPC原理 一次完整的RPC调用流程(同步调用,异步另说)如下: 1)服务消费方(client)调用以本地调用方式调用服务; 2)client stub【客户端代理】接收到调用后负责将方法、参数等组装成...

    一、RPC原理

    • 一次完整的RPC调用流程(同步调用,异步另说)如下:

      1)服务消费方(client)调用以本地调用方式调用服务;

      2)client stub【客户端代理】接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;

      3)client stub找到服务地址,并将消息发送到服务端;

      4)server stub【服务端代理】收到消息后进行解码

      5)server stub根据解码结果调用本地的服务;

      6)本地服务执行并将结果返回给server stub;

      7)server stub将返回结果打包成消息并发送至消费方;

      8)client stub接收到消息,并进行解码

      9)服务消费方得到最终结果。

      RPC框架的目标就是要2~8这些步骤都封装起来,这些细节对用户来说是透明的,不可见的。

     二、Netty通信原理

    • Netty是一个异步事件驱动的网络应用程序框架, 用于快速开发可维护的高性能协议服务器和客户端。它极大地简化并简化了TCP和UDP套接字服务器等网络编程。

    • BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 

    • NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求【某个通道的状态都准备好】时才启动一个线程进行处理。 

    • Selector 一般称 为选择器 ,也可以翻译为 多路复用器

    • Connect(连接就绪)、Accept(接受就绪)、Read(读就绪)、Write(写就绪)

    • Netty的基本原理:

    • 原理图解析:

      1)启动Netty,并绑定一个 端口,比如:dubbo的20880【这样所有给这个端口发的数据Netty都能收到】

      2)初始化通道(Channel)

      3)注册到选择器(Selector)中,负责监听一个事件

      4)监听accept事件,通道已经准备就绪了,使用轮询的方式处理通道内的一些信息,这里也有一个任务队列,监听好了去做什么,监听就绪了去做什么,整个队列完成Netty就结束了

      5)处理方式:Netty与客户端建立连接,生成NioSocketChannel,这就是与客户端连接的通道

      6)把NioSocketChannel通道注册到Selector中,用它来监听Read、Write事件,相当于通道内数据读【发过去的数据都读好了】、写【可以给客户端通道里边写响应了】都准备就绪

      7)读写都准备就绪以后,就来处理这个事件,比如读准备就绪我们处理一个任务,写准备就绪我们处理一个任务,这些任务都会抛给任务队列,Netty就把这些队列执行完

       

      注意:两个监听的上边有一个boss和worker

      boss:是主线程,用来监听来自20880的所有连接准备就绪事件

      worker:当我们准备就绪以后需要做什么工作,把这个工作抛给worker,让worker来做

     三、Dubbo原理

    • dubbo原理 -框架设计 

    • 图例说明:

      • 图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。
      • 图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。
      • 图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。
      • 图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法。

      各层说明:

      • config 配置层:【主要是来封装我们配置文件里边解析出来的一些信息】对外配置接口,以 ServiceConfigReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
      • proxy 服务代理层:【帮我们利用代理的方式生成我们客户端的代理对象以及服务端的代理对象,代理对象那个来互相调用方法】服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy为中心,扩展接口为 ProxyFactory
      • registry 注册中心层:【服务的发现与注册:服务注册到注册中心,消费者订阅服务】封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactoryRegistryRegistryService
      • cluster 路由层:【这层帮我们进行负载均衡,调用者调用服务,而服务可能在很多台机器上,这层帮助我们进行负载均衡】封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 ClusterDirectoryRouterLoadBalance
      • monitor 监控层:【我们每一次的调用信息都会发送给监控层,可以在界面上展示监控数据】RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactoryMonitorMonitorService
      • protocol 远程调用层:【封装整个RPC调用的,RPC的核心是ProtocolInvokerExporter,完成一次远程调用】封装 RPC 调用,以 InvocationResult 为中心,扩展接口为 ProtocolInvokerExporter
      • exchange 信息交换层:【创建一个客户端,一个服务端,两个架起管道,进行数据的互联互通】封装请求响应模式,同步转异步,以 RequestResponse 为中心,扩展接口为 ExchangerExchangeChannelExchangeClientExchangeServer
      • transport 网络传输层:【真正传输数据是用Transporter,而Transporter的底层是Netty框架】抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 ChannelTransporterClientServerCodec
      • serialize 数据序列化层:【在整个传输的过程中数据要序列化才能网络传输,收到数据还要反序列化,拿到对象】可复用的一些工具,扩展接口为 SerializationObjectInputObjectOutputThreadPool

      关系说明

      • 在 RPC 中,Protocol 是核心层,也就是只要有 Protocol + Invoker + Exporter 就可以完成非透明的 RPC 调用,然后在 Invoker 的主过程上 Filter 拦截点。
      • 图中的 Consumer 和 Provider 是抽象概念,只是想让看图者更直观的了解哪些类分属于客户端与服务器端,不用 Client 和 Server 的原因是 Dubbo 在很多场景下都使用 Provider, Consumer, Registry, Monitor 划分逻辑拓普节点,保持统一概念。
      • 而 Cluster 是外围概念,所以 Cluster 的目的是将多个 Invoker 伪装成一个 Invoker,这样其它人只要关注 Protocol 层 Invoker 即可,加上 Cluster 或者去掉 Cluster 对其它层都不会造成影响,因为只有一个提供者时,是不需要 Cluster 的。
      • Proxy 层封装了所有接口的透明化代理,而在其它层都以 Invoker 为中心,只有到了暴露给用户使用时,才用 Proxy 将 Invoker 转成接口,或将接口实现转成 Invoker,也就是去掉 Proxy 层 RPC 是可以 Run 的,只是不那么透明,不那么看起来像调本地服务一样调远程服务。
      • 而 Remoting 实现是 Dubbo 协议的实现,如果你选择 RMI 协议,整个 Remoting 都不会用上,Remoting 内部再划为 Transport 传输层和 Exchange 信息交换层,Transport 层只负责单向消息传输,是对 Mina, Netty, Grizzly 的抽象,它也可以扩展 UDP 传输,而 Exchange 层是在传输层之上封装了 Request-Response 语义。
      • Registry 和 Monitor 实际上不算一层,而是一个独立的节点,只是为了全局概览,用层的方式画在一起。

       

    四、启动解析、加载配置文件

    •  Spring解析配置文件中每一个标签都会有一个总接口 BeanDefinitionparser 定义的解析器,它的实现方法有一个DubboBeanDefinitionParser,里边有一个 parse 方法来解析标签,每个标签依次进入进行解析

    五、服务暴露流程

    • 当我们暴露服务的时候,要获取到invoker,获取到执行器,也就是getInvoker()方法,我们使用Protocol来暴露执行器的export(invoker),而Protocol我们使用两个DubboProtocol RegistryProtocol,都对应两个暴露者,DubboProtocol 会帮我们开启服务器,RegistryProtocol 会帮我们将服务注册到注册中心【每一个服务以及它的URL地址信息都保存在注册表ProviderConsumerRegTable 中,注册表中相当于缓存了一个url地址对应哪个服务的执行器,执行器里边有真正的服务

    六、服务引用流程

    <dubbo:reference interface="cn.e3mall.service.ItemService" id="itemService" />
    • reference标签里边的interface会自动注入的ServiceImpl,自动注入Spring就会在容器中找ReferenceBean会调用FactooyBean中的方法getObject方法,它返回的对象就会作为标签配置返回的对象,并且 init() 初始化,主要来创建对象,所谓的创建对象就是 Protocol 来引用远程服务,怎么引用呢,我们有两个 Protocol 分别是 DubboProtocol RegistryProtocol DubboProtocol负责跟远程的20880服务器进行连接创建一个客户端,RegistryProtocol 从注册中心订阅服务,并且将我们创建的客户端信息 invoke 信息,保存到注册表 ProviderConsumerRegTable 中,返回封装好的 invoke ,创建 invoke 的代理对象 invoker,之后返回 invoker 代理

    七、服务调用流程

    • Proxy 是代理对象,如果有要做其他功能使用Filter结构,没有的话使用Cluster【封装多个invoker】,如果有多个invoker的话我们可以选择一种LoadBalance负载均衡机制,调用错了还会有重试,在负载均衡调用期间还有其他的Filter来介入统计一些信息数据,由于我们使用dubbo协议来远程调用,所以最终我们真正执行功能的是DubboInvoker,而调用的底层是Client发送请求,Client底层使用Netty的客户端,连接我们目标端口的服务器,来发送请求,服务器收到请求数据之后,我们要进行解码,来整个返回,把我们整个返回数据交给代理对象,由代理对象交给我们

     

    展开全文
  • dubbo的调用原理及泛化调用

    千次阅读 2020-06-20 17:59:08
    dubbo是阿里开源出来的一个rpc框架,主要是用于微服务分布式项目的远程调用,它提供了三大核心能力:面向接口的远程方法调用,智能容错负载均衡,以及服务自动注册发现,下面是调用原理图: dubbo框架的整体...

    简单介绍

    dubbo是阿里开源出来的一个rpc框架,主要是用于微服务分布式项目的远程调用,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现,下面是调用的原理图:
    在这里插入图片描述
    dubbo框架的整体设计:
    在这里插入图片描述
    图例说明:

    1,图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。
    2,图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。
    3,图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。
    4,图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法

    远程调用细节

    服务提供者暴露一个服务的详细过程

    在这里插入图片描述
    上图是服务提供者暴露服务的主过程:

    首先 ServiceConfig 类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过 ProxyFactory 类的 getInvoker 方法使用 ref 生成一个 AbstractProxyInvoker 实例,到这一步就完成具体服务到 Invoker 的转化。接下来就是 Invoker 转换到 Exporter 的过程。

    Dubbo 处理服务暴露的关键就在 Invoker 转换到 Exporter 的过程,上图中的红色部分。下面我们以 Dubbo 和 RMI 这两种典型协议的实现来进行说明:

    Dubbo 的实现
    Dubbo 协议的 Invoker 转为 Exporter 发生在 DubboProtocol 类的 export 方法,它主要是打开 socket 侦听服务,并接收客户端发来的各种请求,通讯细节由 Dubbo 自己实现。

    RMI 的实现
    RMI 协议的 Invoker 转为 Exporter 发生在 RmiProtocol类的 export 方法,它通过 Spring 或 Dubbo 或 JDK 来实现 RMI 服务,通讯细节这一块由 JDK 底层来实现,这就省了不少工作量。

    服务消费者消费一个服务的详细过程

    在这里插入图片描述
    上图是服务消费的主过程:

    首先 ReferenceConfig 类的 init 方法调用 Protocol 的 refer 方法生成 Invoker 实例(如上图中的红色部分),这是服务消费的关键。接下来把 Invoker 转换为客户端需要的接口(如:HelloWorld)。

    dubbo直接引用

    1,xml形式

    服务提供者:

    public interface DemoService {
        String sayHello(String name);
    }
    

    具体实现:

    public class DemoServiceImpl implements DemoService {
        public String sayHello(String name) {
            return "Hello " + name;
        }
    }
    

    spring声明暴露服务:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
        xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
     
        <!-- 提供方应用信息,用于计算依赖关系 -->
        <dubbo:application name="hello-world-app"  />
     
        <!-- 使用multicast广播注册中心暴露服务地址 -->
        <dubbo:registry address="multicast://224.5.6.7:1234" />
     
        <!-- 用dubbo协议在20880端口暴露服务 -->
        <dubbo:protocol name="dubbo" port="20880" />
     
        <!-- 声明需要暴露的服务接口 -->
        <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService" />
     
        <!-- 和本地bean一样实现服务 -->
        <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl" />
    </beans>
    

    服务消费者:
    通过 Spring 配置引用远程服务:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
        xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
     
        <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
        <dubbo:application name="consumer-of-helloworld-app"  />
     
        <!-- 使用multicast广播注册中心暴露发现服务地址 -->
        <dubbo:registry address="multicast://224.5.6.7:1234" />
     
        <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
        <dubbo:reference id="demoService" interface="org.apache.dubbo.demo.DemoService" />
    </beans>
    

    远程调用:

    public class Consumer {
        public static void main(String[] args) throws Exception {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"http://10.20.160.198/wiki/display/dubbo/consumer.xml"});
            context.start();
            DemoService demoService = (DemoService)context.getBean("demoService"); // 获取远程服务代理
            String hello = demoService.sayHello("world"); // 执行远程方法
            System.out.println( hello ); // 显示调用结果
        }
    }
    
    2,api形式

    提供者暴露服务:

    /**
     * @author shihaowei
     * @date 2020-06-11 11:43
     */
    @Configuration
    //@EnableDubbo
    @DubboComponentScan("person.shw.dubbo.provider")
    public class DubboConfig {
    
        @Value("${nacos.data.id:f21d3b6b-20ed-4091-ab17-ac073abc3eba}")
        private String namespace;
    
        @Bean
        public ApplicationConfig applicationConfig(){
            ApplicationConfig applicationConfig = new ApplicationConfig();
            applicationConfig.setName("provider-nacos");
            return applicationConfig;
        }
    
        @Bean
        public RegistryConfig registryConfig(){
            RegistryConfig registryConfig = new RegistryConfig();
            registryConfig.setAddress("nacos://120.79.76.230:8848");
            registryConfig.setGroup("ONE_GROUP");
            Map<String,String> paramMap = new HashMap<String, String>();
            paramMap.put("namespace",namespace);
            registryConfig.setParameters(paramMap);
            return registryConfig;
        }
    
    
        @Bean
        public ProtocolConfig protocolConfig(){
            ProtocolConfig protocolConfig = new ProtocolConfig();
            protocolConfig.setName("dubbo");
            protocolConfig.setPort(12347);
            return protocolConfig;
        }
    
    }
    

    暴露服务的接口实现:

    import org.apache.dubbo.config.annotation.Service;
    import person.shw.dubbo.api.DubboApi;
    
    /**
     * @author shihaowei
     * @date 2020-06-09 17:21
     */
    @Service
    public class ProviderServiceImpl implements DubboApi {
        public String getConsumerData() {
            return "333333333333333333";
        }
    }
    

    org.apache.dubbo.config.annotation.Service是引用的dubbo的包,而不是Spring的@Service

    消费者消费服务:

    @Configuration
    public class DubboNacosConfig {
    
        @Bean
        public ApplicationConfig applicationConfig(RegistryConfig registryConfig){
            ApplicationConfig applicationConfig = new ApplicationConfig();
            applicationConfig.setName("consumer-nacos");
            applicationConfig.setRegistry(registryConfig);
            return applicationConfig;
        }
    
        @Bean
        public RegistryConfig registryConfig(){
            RegistryConfig registryConfig = new RegistryConfig();
            registryConfig.setCheck(false);
            registryConfig.setAddress("nacos://120.79.76.230:8848");
            registryConfig.setUsername("nacos");
            registryConfig.setPassword("nacos");
            Map<String,String> paramMap = new HashMap<String, String>();
            paramMap.put("namespace","f21d3b6b-20ed-4091-ab17-ac073abc3eba");
            registryConfig.setParameters(paramMap);
            return registryConfig;
        }
    }
    
    

    调用服务

    import org.apache.dubbo.config.annotation.Reference;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import person.shw.dubbo.api.DubboApi;
    
    /**
     * @author shihaowei
     * @date 2020-06-09 17:22
     */
    @RestController
    public class ConsumerController {
    
        @Reference
        private DubboApi dubboApi;
    
        @GetMapping(value = "/consumer/getMessage")
        public String getMessage(){
            return "【收到发来的消息】---->"+dubboApi.getConsumerData();
        }
    
    }
    

    dubbo的泛化调用

    为什么要使用泛化调用?

    一般使用dubbo,provider端需要暴露出接口和方法,consumer端要十分明确服务使用的接口定义和方法定义(还有入参返参类型等等信息,常常还需要基于provider端提供的API),两端才能正常通信调用。

    然而存在一种使用场景,调用方并不关心要调用的接口的详细定义,它只关注我要调用哪个方法,需要传什么参数,我能接收到什么返回结果即可,这样可以大大降低consumer端和provider端的耦合性。

    所以为了应对以上的需求,dubbo提供了泛化调用,也就是在consumer只知道一个接口全限定名以及入参和返参的情况下,就可以调用provider端的调用,而不需要传统的接口定义这些繁杂的结构。

    比如微服务的网关就可以采用泛化调用,又不需要强引用众多需要调用的服务

    例子

    继续使用上面使用api的配置,当我调用的时候,不时用@reference注解,而是用genericservice泛化调用,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成

    @RestController
    public class ConsumerController {
    
        @Autowired
        private ApplicationConfig applicationConfig;
    
        @GetMapping(value = "/consumer/getMessage")
        public String getMessage(){
            /** dubbo的泛化调用 */
            ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
            reference.setApplication(applicationConfig);
            reference.setInterface("person.shw.dubbo.api.DubboApi");
            reference.setTimeout(200000);
            reference.setGeneric(true);
            /** 添加缓存策略 */
            /*ReferenceConfigCache cache = ReferenceConfigCache.getCache();
            GenericService genericService = cache.get(reference);*/
            GenericService genericService = reference.get();
    
            /** 设置接口所需要的参数类型 */
            String[] parametertypes = new String[]{};
            String[] args = new String[]{};
            Object data = genericService.$invoke("getConsumerData", parametertypes, args);
            System.out.println(JSON.toJSONString(data));
            return "【收到consumer发来的消息】---->"+JSON.toJSONString(data);
        }
    
    }
    
    

    调用服务,打印日志

    2020-06-20 17:44:25.482  INFO 3033 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 924 ms
    2020-06-20 17:44:25.664  WARN 3033 --- [           main] org.apache.dubbo.config.AbstractConfig   :  [DUBBO] There's no valid metadata config found, if you are using the simplified mode of registry url, please make sure you have a metadata address configured properly., dubbo version: 2.7.3, current host: 192.168.31.132
    2020-06-20 17:44:25.669  INFO 3033 --- [           main] org.apache.dubbo.config.AbstractConfig   :  [DUBBO] There's no valid monitor config found, if you want to open monitor statistics for Dubbo, please make sure your monitor is configured properly., dubbo version: 2.7.3, current host: 192.168.31.132
    2020-06-20 17:44:25.689  INFO 3033 --- [           main] o.a.d.qos.protocol.QosProtocolWrapper    :  [DUBBO] qos won't be started because it is disabled. Please check dubbo.application.qos.enable is configured either in system property, dubbo.properties or XML/spring-boot configuration., dubbo version: 2.7.3, current host: 192.168.31.132
    2020-06-20 17:44:25.786  INFO 3033 --- [           main] o.a.dubbo.registry.nacos.NacosRegistry   :  [DUBBO] Load registry cache file /Users/edz/.dubbo/dubbo-registry-consumer-nacos-120.79.76.230:8848.cache, data: {person.shw.dubbo.api.DubboApi=dubbo://192.168.31.132:12347/person.shw.dubbo.api.DubboApi?anyhost=true&application=provider-nacos&bean.name=ServiceBean:person.shw.dubbo.api.DubboApi&category=providers&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=person.shw.dubbo.api.DubboApi&methods=getConsumerData&path=person.shw.dubbo.api.DubboApi&pid=2846&protocol=dubbo&register=true&release=2.7.3&side=provider&timestamp=1592646130152}, dubbo version: 2.7.3, current host: 192.168.31.132
    2020-06-20 17:44:25.804  INFO 3033 --- [           main] o.a.dubbo.registry.nacos.NacosRegistry   :  [DUBBO] Register: consumer://192.168.31.132/org.apache.dubbo.rpc.service.GenericService?application=consumer-nacos&category=consumers&check=false&dubbo=2.0.2&generic=true&interface=person.shw.dubbo.api.DubboApi&lazy=false&pid=3033&qos.enable=false&release=2.7.3&side=consumer&sticky=false&timeout=200000&timestamp=1592646265664, dubbo version: 2.7.3, current host: 192.168.31.132
    2020-06-20 17:44:25.967  INFO 3033 --- [           main] o.a.dubbo.registry.nacos.NacosRegistry   :  [DUBBO] Subscribe: consumer://192.168.31.132/org.apache.dubbo.rpc.service.GenericService?application=consumer-nacos&category=providers,configurators,routers&dubbo=2.0.2&generic=true&interface=person.shw.dubbo.api.DubboApi&lazy=false&pid=3033&qos.enable=false&release=2.7.3&side=consumer&sticky=false&timeout=200000&timestamp=1592646265664, dubbo version: 2.7.3, current host: 192.168.31.132
    2020-06-20 17:44:26.009  INFO 3033 --- [           main] o.a.dubbo.registry.nacos.NacosRegistry   :  [DUBBO] Notify urls for subscribe url consumer://192.168.31.132/org.apache.dubbo.rpc.service.GenericService?application=consumer-nacos&category=providers,configurators,routers&dubbo=2.0.2&generic=true&interface=person.shw.dubbo.api.DubboApi&lazy=false&pid=3033&qos.enable=false&release=2.7.3&side=consumer&sticky=false&timeout=200000&timestamp=1592646265664, urls: [dubbo://192.168.31.132:12347/person.shw.dubbo.api.DubboApi?anyhost=true&application=provider-nacos&bean.name=ServiceBean:person.shw.dubbo.api.DubboApi&category=providers&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=person.shw.dubbo.api.DubboApi&methods=getConsumerData&path=person.shw.dubbo.api.DubboApi&pid=2846&protocol=dubbo&register=true&release=2.7.3&side=provider&timestamp=1592646130152], dubbo version: 2.7.3, current host: 192.168.31.132
    2020-06-20 17:44:26.181  INFO 3033 --- [           main] o.a.d.remoting.transport.AbstractClient  :  [DUBBO] Succeed connect to server /192.168.31.132:12347 from NettyClient 192.168.31.132 using dubbo version 2.7.3, channel is NettyChannel [channel=[id: 0x5cf0891f, L:/192.168.31.132:55666 - R:/192.168.31.132:12347]], dubbo version: 2.7.3, current host: 192.168.31.132
    2020-06-20 17:44:26.181  INFO 3033 --- [           main] o.a.d.remoting.transport.AbstractClient  :  [DUBBO] Start NettyClient /192.168.31.132 connect to the server /192.168.31.132:12347, dubbo version: 2.7.3, current host: 192.168.31.132
    2020-06-20 17:44:26.216  INFO 3033 --- [client.listener] o.a.dubbo.registry.nacos.NacosRegistry   :  [DUBBO] Notify urls for subscribe url consumer://192.168.31.132/org.apache.dubbo.rpc.service.GenericService?application=consumer-nacos&category=providers,configurators,routers&dubbo=2.0.2&generic=true&interface=person.shw.dubbo.api.DubboApi&lazy=false&pid=3033&qos.enable=false&release=2.7.3&side=consumer&sticky=false&timeout=200000&timestamp=1592646265664, urls: [dubbo://192.168.31.132:12347/person.shw.dubbo.api.DubboApi?anyhost=true&application=provider-nacos&bean.name=ServiceBean:person.shw.dubbo.api.DubboApi&category=providers&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=person.shw.dubbo.api.DubboApi&methods=getConsumerData&path=person.shw.dubbo.api.DubboApi&pid=2846&protocol=dubbo&register=true&release=2.7.3&side=provider&timestamp=1592646130152], dubbo version: 2.7.3, current host: 192.168.31.132
    2020-06-20 17:44:26.218  INFO 3033 --- [           main] org.apache.dubbo.config.AbstractConfig   :  [DUBBO] Refer dubbo service org.apache.dubbo.rpc.service.GenericService from url nacos://120.79.76.230:8848/org.apache.dubbo.registry.RegistryService?anyhost=true&application=consumer-nacos&bean.name=ServiceBean:person.shw.dubbo.api.DubboApi&category=providers&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=true&interface=person.shw.dubbo.api.DubboApi&lazy=false&methods=getConsumerData&path=person.shw.dubbo.api.DubboApi&pid=3033&protocol=dubbo&qos.enable=false&register=true&register.ip=192.168.31.132&release=2.7.3&remote.application=provider-nacos&side=consumer&sticky=false&timeout=200000&timestamp=1592646265664, dubbo version: 2.7.3, current host: 192.168.31.132
    [泛化调用成功]----->"333333333333333333"
    2020-06-20 17:44:26.409  INFO 3033 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-06-20 17:44:26.627  INFO 3033 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 10009 (http) with context path ''
    2020-06-20 17:44:26.630  INFO 3033 --- [           main] p.s.dubbo.consumer.DubboConsumerLanuch   : Started DubboConsumerLanuch in 2.383 seconds (JVM running for 2.875)
    
    

    倒数第四行打印日志,泛化调用成功。

    // TODO 后面补上一个网关gateway使用dubbo泛化调用的例子

    展开全文
  • 1. H5中的JS发起请求,在android的WebViewClient.shouldOverrideUrlLoading方法中拦截URL请求,判断是否为接口调用 2. android通过webView.addJavascriptInterface方法向windows对象中注入原生代码,然后H5在JS中调用...
  • 做后端开发,避免不了要写接口,最开始我们写接口是为了满足实现具体的功能,能在客户端正常调用就行了,这种接口称为裸接口,就跟一个人没有穿衣服一样,我们在家的时候当然可以这么做,肆意放荡,这没关系,但是人...
  • Spring-Cloud-Ribbon作为微服务框架的负载均衡组件,默认使用RestTemplate接口调用外部服务接口。 原有项目中调用外部服务接口大多数采用的是使用了第三方的HttpClient库,如:Apache HttpClient或Asynchronous ...
  • 接口实例:系统与系统间的接口调用,作用:实现了两个或多个独立系统或模块间的通信数据交换能力。 常见的Web接口类型 REST接口——通过HTTP的getpost方式得到数据,返回报文json格式 SOAP...
  • jersey实现web service接口+客户端调用

    千次阅读 2015-03-13 13:42:18
    jersey实现web service接口+客户端调用 jersey百度百科:  Jersey是一个RESTFUL请求服务JAVA框架,与常规的JAVA编程使用的struts框架类似,它主要用于处理业务逻辑层。与Struts类似,它同样可以hibernate,spring...
  • Feign是声明式Web Service客户端,它让微服务之间的调用变得更简单,类似controller调用service。SpringCloud集成了RibbonEureka,可以使用Feigin提供负载均衡的http客户端 只需要创建一个接口,然后添加注解即可...
  • 我们将系统分为两个,一个称为服务端,另一个称为客户端,原理是,服务端进行配置,用于暴露接口,定义链接接口的规则.客户端进行配置,寻找有相同规则且被暴露的接口,网上的hessian教程很多,博主也是参考众家后终于写成了...
  • 【COM原理和应用】2、COM对象和接口

    千次阅读 2015-02-04 17:56:45
    ②局部接口指针:在一个局部函数块中,接口指针一直有效,因此局部接口指针被赋值并调用接口成员函数不需要调用AddRefRelease。 ③全局接口指针:任何一个函数都可以访问全局接口指针,所以将全局接口指针传入...
  • 一行代码搞定Dubbo接口调用

    千次阅读 2018-09-11 11:53:35
    本文来自网易云社区作者:吕彦峰在工作中我们经常遇到关于接口测试的问题,无论是对于QA同学还是开发同学都会有远程接口调用的需求。针对这种问题我研发了一个工具包,专门用于远程Dubbo调用,下面就让我们一起来...
  • Dubbo 服务调用原理浅析

    千次阅读 2017-09-12 19:16:22
    dubbo概念dubbo原理dubbo应用场景 Dubbo概念: Dubbo是一个分布式服务框架,致力于提供高性能透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,dubbo就是个服务框架,如果没有分布式的需求,其实是...
  • 也就是说jar包中如何暴露接口给第三方应用? 答案是:HandlerMapping swagger2实现了自己的HandlerMapping,在实现类PropertySourcedRequestMappingHandlerMapping中,把/v2/api-docs接口保存到了handlerMethods ...
  • 如何把项目打jar包,然后暴露接口给第三方应用提供服务【实战讲解】 下面这个例子,是我在开源项目CR949中使用到的部分代码,作为讲解,发布到这里。 jar包中的controller,如何对外暴露接口。 这样一个场景:比如...
  • 具体是调用方使用HttpInvokerProxyFactoryBean生成一个代理对象,通过代理对象远程通过http服务调用服务提供方法的服务并,服务提供方通过HttpInvokerServiceExporter在服务端暴漏远程调用接口。二、远程服务暴露2.1...
  • java接口调用——webservice就是一个RPC而已 很多新手一听到接口就蒙逼,不知道接口是什么!其实接口就是RPC,通过远程访问别的程序提供的方法,然后获得该方法执行的接口,而不需要在本地执行该方法。就是本地...
  • Dubbo服务暴露原理

    千次阅读 2018-09-11 21:32:12
    服务暴露原理 配置文件 IOC容器启动,加载配置文件的时候 Dubbo标签处理器,解析每一个标签 封装成对应的组件 service 解析service标签 将service标签信息,封装成ServiceBean ServiceBean 实现...
  • Davids原理探究:Dubbo服务暴露原理

    千次阅读 2020-06-12 09:50:12
    文章目录Dubbo服务暴露和服务消费原理(基于Dubbo 2.6.5)配置解析原理基于XML配置解析原理schema模块说明基于注解配置解析原理@EnableDubboServiceAnnotationBeanPostProcessor的作用...Dubbo服务暴露和服务消费原理...
  • Java RMI远程接口调用介绍与使用

    千次阅读 2016-06-01 18:59:08
    相关文章: RMI(Remote Method Invocation)原理浅析 Java RMI 实现代码动态下载 stub skeleton 的讲解,自己实现一个stubskeleton程序虽然现在在分布式... RMI的基础是接口,RMI构架基于一个重要的原理
  • 前言 ...服务提供方的接口暴露方式是通过Controller暴露Restful,而在这个Controller的代码现实中大部分都是处理请求然后再调用Service中的方法,是一个比较模板化的功能,但是工作量确不少。本文...
  • 在日常开发中,后端同事会给前端一个地址,这个地址我们不想暴露出来,具体操作该怎么做? 这里已vue3 的项目为例子。react 原理是一样的 第一步前端配置 .env.production文件 VUE_APP_NODE_ENV = "'production'" ...
  • Com原理及應用——Com對象和接口

    千次阅读 2013-09-30 08:20:56
     COM对象类似于C++语言中类的概念,类的每个实例代表一个COM对象,它也包括属性(即状态)方法(即操作),状态反映对象的存在,方法就是接口。   2、COM对象的标识-CLSID GUID是一个128位的随机数,重复...
  • 如果请求绕过了网关,那就等于绕过了重重关卡,直捣黄龙,所以,在分布式架构中,我们需要有一定的防范,来确保各个服务相互之间安全调用。 正文 思路 1、在网关中给所有请求加上一个请求密钥 2、在服务添加过滤器,...
  • springboot实现远程服务暴露调用

    千次阅读 2019-02-26 10:58:02
    服务端暴露接口: 1、定义接口 public interface DemoInterface { BaseResponse get(QueryDTO dto); } 2、接口实现类 @Slf4j @Service public class DemoInterfaceImpl implements DemoInterface { @...
  • Hessian是caucho公司开发的一种基于二进制... 相比WebService,Hessian更简单、快捷,它主要包括Hessian远程调用协议、Hessian序列化协议客户端服务端代理。特别提示,Hessian远程调用框架是构建在Http协议之上的。
  • 转自:https://zhuanlan.zhihu.com/p/20879468一、什么是短信轰炸(短信接口被刷)短信轰炸一般基于 WEB 方式(基于客户端方式的原理与之类似),由两个模块组成,包括:一个前端 Web 网页,提供输入被攻击者手机号码的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 51,265
精华内容 20,506
关键字:

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