Dubbo源码分析-服务导出源码解析(三)

2023-11-18

在这个版本中dubbo会通过注解@PostConstructServiceBean实例放到ConfigManager

public abstract class AbstractConfig implements Serializable {
  @PostConstruct
    public void addIntoConfigManager() {
        ApplicationModel.getConfigManager().addConfig(this);
    }
......
}

DubboBootstrapApplicationListener 会监听spring容器启动发布的ContextRefreshedEvent,遍历ConfigManager中的ServiceBean并调用export方法。

public class DubboBootstrapApplicationListener extends OnceApplicationContextEventListener implements Ordered {

    @Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
        if (DubboBootstrapStartStopListenerSpringAdapter.applicationContext == null) {
            DubboBootstrapStartStopListenerSpringAdapter.applicationContext = event.getApplicationContext();
        }
        //spring容器启动完事件
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }
..........省略代码
}

调用ServiceBeanexport方法前,会初始化配置的参数,默认配置的优先级为:
JVM环境变量->操作系统环境变量->配置中心APP配置->配置中心Global配置->本地dubbo.properties文件配置中心配置->dubbo.properties文件配置。(这里还没有涉及动态配置,在服务注册后会监听动态配置,这时候动态配置优先级最高)
最后根据配置文件的优先级对ServiceBean对中的属性进行赋值。

执行到暴露服务的代码前有较多与主流程不相关的逻辑,直接跳过。调用流程如下:

org.apache.dubbo.config.bootstrap.DubboBootstrap#exportServices
  --> org.apache.dubbo.config.bootstrap.DubboBootstrap#exportService
    --> org.apache.dubbo.config.ServiceConfig#export
	  --> org.apache.dubbo.config.ServiceConfig#doExport
	    --> org.apache.dubbo.config.ServiceConfig#doExportUrls
 private void doExportUrls() {
   .............省略部分代码
        //获得注册中心地址
        List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);

        int protocolConfigNum = protocols.size();
      // 根据协议循环注册(dubbo,http)
        for (ProtocolConfig protocolConfig : protocols) {
            String pathKey = URL.buildKey(getContextPath(protocolConfig)
                    .map(p -> p + "/" + path)
                    .orElse(path), group, version);
            // In case user specified path, register service one more time to map it to path.
            repository.registerService(pathKey, interfaceClass);     
            doExportUrlsFor1Protocol(protocolConfig, registryURLs, protocolConfigNum);
        }
    }

doExportUrls主要做两点

  1. 获取注册中心地址。
  2. 根据协议注册。

doExportUrlsFor1Protocol代码比较多,我这里删减了大部分代码,只留下关键逻辑

 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs, int protocolConfigNum) {
       .....省略
        URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
        .....省略
        exportLocal(url);
        .....省略
        if (CollectionUtils.isNotEmpty(registryURLs)) {
          Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass,
                                registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
         DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

        Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
        exporters.add(exporter);
      }
  .....省略
}
  1. 根据拼接的map生成提供者的URL
  2. 进行本地暴露
  3. 进行远程暴露

本地暴露

   private void exportLocal(URL url) {
        URL local = URLBuilder.from(url)
                 //替换protocol替换为injvm
                .setProtocol(LOCAL_PROTOCOL)
                //设置host为127.0.0.1
                .setHost(LOCALHOST_VALUE)
                .setPort(0)
                .build();
        Exporter<?> exporter = PROTOCOL.export(
                PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local));
        exporters.add(exporter);
    }

exportLocal处理步骤为

  1. 替换protocol为injvm和host为127.0.0.1
  2. PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local)生成invoker
    3.PROTOCOL.export()进行本地暴露

PROXY_FACTORYdubboAdaptive类,默认SPI扩展为javassist

 private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
public class JavassistProxyFactory extends AbstractProxyFactory {
。。。。。。。。。。。。省略
    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // 范围Wrapper 的实现类
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable { 
               //这里可以理解invokeMethod调用的是我们提供者具体的业务处理类。
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

}

入参proxyServiceBean中的ref,指向的是我们服务提供者的业务处理类(如demoServiceImpl)。getInvoker首先是动态生成Wrapper的实现类。然后返回一个AbstractProxyInvoker匿名内部类,
wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments)可以理解调用的具体的服务提供者业务处理类。

调用PROTOCOL.export()进行本地服务暴露。
PROTOCOL也是dubbo的Adaptiv类,因为protocolinjvm所以最终调用的是InjvmProtocol的export方法

private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
public class InjvmProtocol extends AbstractProtocol implements Protocol{

    public static final String NAME = LOCAL_PROTOCOL;

    public static final int DEFAULT_PORT = 0;
    private static InjvmProtocol INSTANCE;

    public InjvmProtocol() {
        INSTANCE = this;
    }

    public static InjvmProtocol getInjvmProtocol() {
  
    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
       //本地暴露serviceKey就是接口的全限定类名。如org.apache.dubbo.demo.DemoService
        String serviceKey = invoker.getUrl().getServiceKey();
       // 将invoker ,serviceKey 封装在  InjvmExporter 
        InjvmExporter<T> tInjvmExporter = new InjvmExporter<>(invoker, serviceKey, exporterMap);
       // 放入exporterMap
        exporterMap.addExportMap(serviceKey, tInjvmExporter);
        return tInjvmExporter;
    }
}

本地暴露的逻辑比较简单serviceKey就是接口的全限定类名org.apache.dubbo.demo.DemoService
最后invoker被封装在InjvmExporter,并serviceKey为key存储在exporterMap

远程暴露

远程暴露与本地暴露一样,调用的也是JavassistProxyFactorygetInvoker方法。但传入的URL是注册中心+服务提供者URL

 Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass,
                                registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
 // DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
   Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
  • .注册中心URL: registry://127.0.0.1:2181/xxxxxxxxxx
  • 服务提供者URL : dubbo://192.168.2.19:20880/xxxxxxxxxx
  • 调用 registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString())合并后的URL。export后面的是拼装了服务提供者URL部分。registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider&dubbo=2.0.2&
    export=dubbo%3A%2F%2F192.168.2.19%3A20880%xxxxxxxxxx

其中对应的各自SPI处理类如下

  • registry:// ---> RegistryProtocol
  • zookeeper:// ---> ZookeeperRegistry
  • dubbo:// ---> DubboProtocol

合并URL后先执行RegistryProtocolexport方法

public class RegistryProtocol implements Protocol {

..........省略
@Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
      // registry://xxx?xx=xx&registry=zookeeper 转换为 zookeeper://xxx?xx=xx     表示注册中心
        URL registryUrl = getRegistryUrl(originInvoker);
        // 得到服务提供者url,表示服务提供者
        URL providerUrl = getProviderUrl(originInvoker);

       // overrideSubscribeUrl是老版本的动态配置监听url,表示了需要监听的服务以及监听的类型(configurators, 这是老版本上的动态配置)
        // 在服务提供者url的基础上,生成一个overrideSubscribeUrl,协议为provider://,增加参数category=configurators&check=false
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
        // 一个overrideSubscribeUrl对应一个OverrideListener,用来监听变化事件,监听到overrideSubscribeUrl的变化后,
        // OverrideListener就会根据变化进行相应处理,具体处理逻辑看OverrideListener的实现
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

  // 在这个方法里会利用providerConfigurationListener和serviceConfigurationListener去重写providerUrl
        // providerConfigurationListener表示应用级别的动态配置监听器,providerConfigurationListener是RegistyProtocol的一个属性
        // serviceConfigurationListener表示服务级别的动态配置监听器,serviceConfigurationListener是在每暴露一个服务时就会生成一个
        // 这两个监听器都是新版本中的监听器
        // 新版本监听的zk路径是:
        // 服务: /dubbo/config/dubbo/org.apache.dubbo.demo.DemoService.configurators节点的内容
        // 应用: /dubbo/config/dubbo/dubbo-demo-provider-application.configurators节点的内容
        // 注意,要喝配置中心的路径区分开来,配置中心的路径是:
        // 应用:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService/dubbo.properties节点的内容
        // 全局:/dubbo/config/dubbo/dubbo.properties节点的内容
        providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
         // 根据动态配置重写了providerUrl之后,就会调用DubboProtocol或HttpProtocol去进行导出服务了
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

        // // 得到注册中心-ZookeeperRegistry
        final Registry registry = getRegistry(originInvoker);
       //得到存入到注册中心去的providerUrl,会对服务提供者url中的参数进行简化
        final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);

        //是否需要注册到注册中心
        boolean register = providerUrl.getParameter(REGISTER_KEY, true);
        if (register) {
           // 注册服务,把简化后的服务提供者url注册到registryUrl中去
            registry.register(registeredProviderUrl);
        }

       // 针对老版本的动态配置,需要把overrideSubscribeListener绑定到overrideSubscribeUrl上去进行监听
        registerStatedUrl(registryUrl, registeredProviderUrl, register);


        exporter.setRegisterUrl(registeredProviderUrl);
        exporter.setSubscribeUrl(overrideSubscribeUrl);

        // Deprecated! Subscribe to override rules in 2.6.x or before.
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

        notifyExport(exporter);
        //Ensure that a new exporter instance is returned every time export
        return new DestroyableExporter<>(exporter);
    }
}

这是服务暴露比较重要分方法,主要步骤如下
1.获取注册中心的URL,并把 registry://xxx?xx=xx&registry=zookeeper 转换为 zookeeper://xxx?xx=xx 表示注册中心
2. 得到服务提供者url,表示服务提供者。
3. 构建老板本dubbo监听的URL. overrideSubscribeUrl是老版本的动态配置监听url,表示了需要监听的服务以及监听的类型(configurators, 这是老版本上的动态配置)
4. 监听动态配置,其中动态配置分为应用动态配置和服务动态配置并重写providerUrl。(注意不是配置中心)
服务配置路径: /dubbo/config/dubbo/org.apache.dubbo.demo.DemoService.configurators
应用配置路径: /dubbo/config/dubbo/dubbo-demo-provider-application.configurators
5. 根据动态配置重写了providerUrl之后,就会调用DubboProtocolHttpProtocol去进行导出服务了
6. 得到注册中心实现-ZookeeperRegistry
7. 注册服务,把简化后的服务提供者url注册到registryUrl中去
8. 得到存入到注册中心去的providerUrl,会对服务提供者url中的参数进行简化
9. 针对老版本的动态配置,需要把overrideSubscribeListener绑定到overrideSubscribeUrl上去进行监听

监听动态配置和重写提供者URL

private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) {
        // 应用配置,providerConfigurationListener是在属性那里直接初始化好的,providerConfigurationListener会监听配置中心的应用配置信息变动
        providerUrl = providerConfigurationListener.overrideUrl(providerUrl);
        // 服务配置,new ServiceConfigurationListener的时候回初始化,ServiceConfigurationListener会监听配置中心的服务信息配置信息变动
        ServiceConfigurationListener serviceConfigurationListener = new ServiceConfigurationListener(providerUrl, listener);
        serviceConfigurationListeners.put(providerUrl.getServiceKey(), serviceConfigurationListener);
        return serviceConfigurationListener.overrideUrl(providerUrl);
    }

应用动态配置由 ProviderConfigurationListener 实现,它是RegistryProtocol的一个成员变量,在RegistryProtocol实例化是同时也实例化ProviderConfigurationListener,在ProviderConfigurationListener 的构造函数中调用initWith方法获取应用动态配置(initWith在AbstractConfiguratorListener类构造方法中调用,AbstractConfiguratorListener也是ServiceConfigurationListener的父类,所以服务监听实例化时也会调用这个方法获取服务动态配置)。

 protected final void initWith(String key) {
        // 添加Listener,进行了订阅
        ruleRepository.addListener(key, this);
        // 从配置中心ConfigCenter获取属于当前应用的动态配置数据,从zk中拿到原始数据(主动从配置中心获取数据)
        String rawConfig = ruleRepository.getRule(key, DynamicConfiguration.DEFAULT_GROUP);
        if (!StringUtils.isEmpty(rawConfig)) {
            // 如果存在应用配置信息则根据配置信息生成Configurator
            genConfiguratorsFromRawRule(rawConfig);
        }
    }

overrideUrlWithConfig方法中首先根据应用动态配置调用overrideUrl方法重写提供者URL,再实例化ServiceConfigurationListener获取服务动态配置,调用overrideUrl方法重写提供者URL。

服务导出

doLocalExport方法中,会调用protocol.export()服务导出,默认使用的是DubboProtocol

public class DubboProtocol extends AbstractProtocol {
 @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        URL url = invoker.getUrl();

        // export service.
        String key = serviceKey(url);
        // 构造一个Exporter进行服务导出
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        exporterMap.addExportMap(key, exporter);

       ......省略
        // 开启NettyServer
        openServer(url);
        // 特殊的一些序列化机制,比如kryo提供了注册机制来注册类,提高序列化和反序列化的速度
        optimizeSerialization(url);

        return exporter;
    }
}

把传入的Invoker封装成DubboExporter放入到exporterMap中。然后开启Netty Server。
开启netty流程不在赘述了,有兴趣可以执行阅读,流程如下。

org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#openServer
  -->org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#createServer
    --> org.apache.dubbo.remoting.exchange.Exchangers#bind
	  --> org.apache.dubbo.remoting.exchange.support.header.HeaderExchanger#bind
	    --> org.apache.dubbo.remoting.Transporters#bind
		  --> org.apache.dubbo.remoting.transport.netty.NettyTransporter#bind
		   --> org.apache.dubbo.remoting.transport.netty.NettyServer
		     --> org.apache.dubbo.remoting.transport.netty.NettyServer#doOpen

服务注册

服务注册根据不同的注册中心有不同实现,ZK实现为ZookeeperRegistry,所以 registry.register(registeredProviderUrl)方法最终会调用到ZookeeperRegistrydoRegister方法。

  public void doRegister(URL url) {
        try {
            zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
        } catch (Throwable e) {
            throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

至此,服务导出源码解析的源码就分析完了

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Dubbo源码分析-服务导出源码解析(三) 的相关文章

  • Java:使用indexOf方法根据另一个数组对数组进行排序

    我想根据另一个数组 索引 的排序顺序迭代两个数组 A B 在本例中为 10 34 32 21 String A a b c d String B e f g h int indexes 10 34 32 21 为这里的坏例子道歉 我已经更新
  • 一般如何处理 CXF 请求处理程序中的输入参数?

    我一直在使用 apache CXF 版本 2 2 2 JAX RS 进行一些工作 我试图在调用业务方法之前在 CXF 请求处理程序中引入数据验证层 幸运的是 我在请求处理程序 DataValidationHandler 中遇到输入参数处理问
  • 使用祖父母接口的默认方法

    我完全不明白为什么这行不通 interface Test default void doMagic System out println Abracadabra class TestImpl implements Test class Sp
  • 访问 java jigsaw 模块中的资源文件[重复]

    这个问题在这里已经有答案了 我正在尝试从项目中的类访问 Eclipse 项目中的文件 我需要将该项目声明为 jigsaw 模块才能从其他项目访问它 但是通过这样做 我无法再访问项目中的 example png 等文件 这是我的项目结构 pr
  • Android 在 ROOM 数据库中插入大量数据

    我有大约 10 个模型 每个模型都有超过 120K 行和 90 列的记录 其中包含双数组值 在 Room 中插入任何模型都需要超过 125 130 秒 任何人都可以建议我需要做什么才能使用一些批量插入技术来保存所有这些 120K 该技术大约
  • 使用 Morphia 配置 Spring Boot?

    我不想利用 Spring DATA MongoDB 支持 我想利用名为 Morphia 的 MongoDB ORM https github com mongodb morphia https github com mongodb morp
  • Android 中的 ImageView 拖动限制

    我在布局中有一个 ImageView 并在 ImageView 上设置 OnTouchListener 来拖动 ImageView 它工作得很好 我的问题是如何防止将 ImageView 移动到布局范围之外 这是我的代码 活动类别 publ
  • 二维数组中的寻路

    假设我有这个二维数组地图 0 0 0 0 7 1 1 1 1 1 1 1 1 0 7 7 7 7 1 1 1 24 1 1 1 1 0 7 24 24 24 24 24 24 24 1 1 3 1 0 7 23 23 23 23 23 23
  • 如何正确安装mysqlconnecter java?

    上网冲浪后 我意识到我应该在系统环境变量中设置类路径连接器 jar 文件的路径文件我这样做了 并在命令行中输入此命令我有这个 C Users User gt echo classpath D classpath mysql connecto
  • Java 中的逻辑回归

    我们需要用 Java 进行逻辑回归 我们在 Python 中使用了这段代码http blog smellthedata com 2009 06 python logistic regression with l2 html http blo
  • 获取运行时提供的类名的 n 维数组的类

    给定一个完全限定的类名和多个维度 我想获取该类的类名 我相信我可以这样做 public Class elementType Class forName className return Array newInstance elementTy
  • 将 JAR 文件打包为 WAR 文件

    我有一系列依赖的Java项目 我想将它们打包成一个 JAR 文件 以便在我的 WAR 文件中使用 这些项目依赖于大量的外部库和项目 如log4j apache commons等 我选择 Eclipse 中的所有项目并导出为 JAR 文件 然
  • 使用 Hibernate 作为 ORM 机制的 Web 应用程序中的 L1 和 L2 缓存有什么区别?

    我只想要一些有关使用 L1 缓存和 L2 缓存的标准用途的一般信息 我很好奇 因为我正在研究使用赤土陶器作为二级缓存的系统 并且我发现它也有一级缓存 L1 缓存是每个 Hibernate 会话都存在的缓存 并且该缓存不在线程之间共享 该缓存
  • 带有 CONTAINS 查询的PreparedStatement

    我有一个查询需要连续运行 28000 次 所以我认为使用准备好的语句可能是一个聪明的主意 这是我的查询 String requestWithFirstName SELECT SE ELEMENT ID SE LASTNAME SE FIRS
  • Cordova Android 应用程序中的网页不可用

    编辑 我一直在解决这个问题并回顾我的所有步骤 我很乐意缩小这个问题的规模 并在令人困惑的情况下获得更多确切的细节 目前 我觉得 Keycloak 似乎只想将我重定向到 https 据我所知 这应该是 Wildfly 服务器配置问题 编辑 我
  • 无法渲染非字符串的属性,toString 返回:null SPRING MVC 中的错误

    我正在尝试按照本教程为我的 spring mvc 项目创建一个通用错误处理程序 http www mkyong com spring mvc spring mvc exceptionhandler example http www mkyo
  • java POI XSSF 公式评估器

    我在保存新的 Excel 文件时遇到问题 我希望当它被保存时 公式会自行计算 但目前它只是返回 Excel 文件中的一个字符串 公式是正确的 我不知道到底要得到FormulaEvaluator上班 这是我输入返回字符串的公式的地方 data
  • 更改 Logger 实例的全局设置

    我在用着java util logging Logger http download oracle com javase 1 4 2 docs api java util logging Logger html作为我的应用程序的日志引擎 每
  • 如何提高QNX6下Eclipse IDE的性能

    我们在 VMWare 环境中通过 QNX6 运行 Eclipse 速度非常慢 Eclipse 是这样启动的 usr qnx630 host qnx6 x86 usr qde eclipse eclipse data root workspa
  • 确定方法调用顺序的接口设计模式

    我想创建一个具有多种方法的 Java 接口 但我希望界面的用户只能按照我定义的顺序或顺序调用方法 例如buyTicket 不应在此之前调用reserveTicket 问 有没有设计模式或任何关于如何做到这一点的提示 我考虑过 A 接口被包装

随机推荐

  • 树状数组理论与实现

    理论 http www cnblogs com zhangshu archive 2011 08 16 2141396 html 今天听了大神的讲课 了解了点东西 发现是之前学过的 于是试着再写一遍 include
  • flutter text 左对齐_Flutter 基础布局Widgets之Wrap详解

    概述 Wrap好似Row和Column的结合 在横轴的表现和Row一致 而竖轴的表现和Column一致 比如当 this direction Axis horizontal 时 横轴 的child放置不下时就会在竖轴自动扩展一行 构造函数
  • webpack4 devServer 参数分析

    devServer module exports 各个参数 after function app server 在服务内部的所有其他中间件之后 提供执行自定义中间件的功能 allowedHosts array 此选项允许你添加白名单服务 允
  • Nginx路由--Location 的使用

    1 Location 语法规则 语法规则 location uri 首先匹配 其次匹配 其次是按文件中顺序的正则匹配 最后是交给 通用 匹配 当有匹配成功时候 停止匹配 按当前匹配规则处理请求 符号 含义 开头表示精确匹配 开头表示 uri
  • docker 与 k8s

    kubernetes 简称k8s 因为k和s 中间有 ubernete 8个单词 所以简称k8s 是一个开源的 用于管理云平台中多个主机上的容器化的应用 k8s 的目标是让部署容器化的应用简单并且高效 k8s 提供了应用部署 规划 更新 维
  • SQL注入类型

    SQL注入类型详解 SQL 注入是指web应用程序对用户输入数据控制不严格 导致用户输入数据被拼接到SQL语句中被数据库执行导致的安全问题 按照注入方式可以分为联合注入 布尔盲注 时间盲注 堆叠注入以及报错注入等五种注入方式 在sql注入时
  • Pygame详解(十二):Surface 对象

    pygame Surface Pygame 中用于表示图像的对象 Surface width height flags 0 depth 0 masks None gt Surface Surface width height flags 0
  • 崩坏3》画面效果为何惊艳?看米哈游怎么做卡通渲染的你就明白了

    http youxiputao com articles 11839 崩坏3 上架以来 我们看到了卡通渲染技术在游戏领域的高品质画面表现力 米哈游对这种技术恰到好处的运用也让 崩坏3 受到了广大玩家的喜爱 以及业内诸多从业人士的认可 事实上
  • Java正则校验密码至少包含:字母数字特殊符号中的2种

    一 语法 字符 说明 将下一字符标记为特殊字符 文本 反向引用或八进制转义符 例如 n匹配字符 n n 匹配换行符 序列 匹配 匹配 匹配输入字符串开始的位置 如果设置了 RegExp 对象的 Multiline 属性 还会与 n 或 r
  • 软件测试工程师自我介绍(范本)

    一 先介绍自己的基本信息 要注意扬长避短 1 年纪太大与太小 就不要主动去说明 比如你的年纪只有20岁 例子 我叫 从事软件测试工作有几年了 2 不是计算机相关专业毕业的也不要过多的去提 比如你的专业是机械专业 例子 我叫 从事软件测试工作
  • python selenium 滑动验证码

    def png from PIL import Image img Image open 1 png z img size data img convert RGB load for i in range z 0 dd for e in r
  • 【CMD】执行adb devices找不到设备的问题

    首先描述一下当时遇到的情况 手机USB连接电脑提示正常 设备管理器显示手机设备正常 问题是执行命令adb devices找不到设备 在Windows系统 资源管理器下 找到Android Sdk路径打开 看看那个文件夹platform to
  • spring的jdbcTemplate批量添加数据,单条数据中文正常,多数据第二条数据开始中文乱码

    今天用spring的jdbcTemplate org springframework jdbc core JdbcTemplate 批量添加数据 方法是jdbcTemplate bathUpdate 然后遇到一个很神奇的问题 批量添加的数据
  • 【精】彻底吃透HDFS写流程(5)-- DataStreamer线程类run方法分析以及如何构建pipeline?

    有关HDFS写流程的系列文章 精 彻底吃透HDFS写流程 1 BlockConstructionStage 精 彻底吃透HDFS写流程 2 Namenode侧create文件 精 彻底吃透HDFS写流程 3 DataStreamer线程和输
  • Android Studio及JDK完整详细安装

    本博文源于安卓基础旨在讨论如何搭建Android开发环境 下面进入步骤 了解安卓开发需要的工具 安装步骤 安装文件的下载 JDK的安装 Android Studio的安装与Android SDK的下载 基本开发的环境配置 安装文件的准备 首
  • 还是 “月饼” 后续,玩转炫彩 “月饼” 之 问题说明

    画一个 月饼 陪我过中秋 开发板后续问题跟进说明 目录 前言 一 出现问题 二 寻求办法 三 若有所思 四 问题测试 结语 悬赏送开发板 前言 本文有纯理论玩家是永远不会经历的实际问题 嵌入式工程师不动手永远出不了作品 本文最后有送开发板的
  • sqoop初步使用

    一 概述 Sqoop是一款开源的数据导入导出工具 可以将传统的关系型数据库导出至HDFS 也可以将HDFS中的数据导出至关系型数据库 官网 http sqoop apache org 原理 在Hadoop生态体系中 计算基本依赖于MR 那么
  • PHP如何使用Ds\Queue Capacity()函数?代码实例

    Ds Queue capacity PHP中的函数用于检查Queue实例的当前容量 语法 int public Ds PriorityQueue capacity void 参数 此功能不接受任何参数 返回值 此函数返回Queue实例的当前
  • stata学习笔记

    离散被解释变量 二值选择型 二值选择模型 多值选择型 多项选择模型 条件选择 混合 排序数据 排序模型 非负整数计数型 泊松 负二项 二值选择型 采用logit和probit模型 probit即把logit换一下就好 logit y x1
  • Dubbo源码分析-服务导出源码解析(三)

    在这个版本中dubbo会通过注解 PostConstruct把ServiceBean实例放到ConfigManager中 public abstract class AbstractConfig implements Serializabl