本文来说下关于dubbo的几个常见问题

文章目录

  • RPC 定义
  • Dubbo 框架设计
    • Dubbo 简介
    • Dubbo框架分层
  • Dubbo SPI 机制
    • 微内核
    • SPI 含义
    • SPI demo
    • SPI源码追踪
    • Java SPI缺点
    • Dubbo SPI
    • Dubbo SPI源码追踪
    • Adaptive 自适应扩展
  • Dubbo 服务暴露流程
    • 服务暴露总览
    • 服务暴露源码追踪
  • Dubbo 服务引用流程
    • 服务的引入时机有两种
    • 服务引用的三种方式
    • 服务引用流程
    • 调用方式
  • Dubbo 调用整体流程
    • 调用之前你可能需要考虑这些事
    • 调用大致流程
    • 调用方式
  • Dubbo集群容错负载均衡
    • 集群容错
    • 智能容错之负载均衡
    • 智能容错之服务目录
    • 智能容错之服务路由
  • 如何设计一个RPC

RPC 定义

互联网公司的系统有成千上万个大大小小的服务组成,服务各自部署在不同的机器上,服务间的调用需要用到网络通信,服务消费方每调用一个服务都要写一坨网络通信相关的代码,不仅复杂而且极易出错。还要考虑新服务依赖老服务时如何调用老服务,别的服务依赖新服务的时候新服务如何发布方便他人调用。如何解决这个问题呢?业界一般采用RPC远程调用的方式来实现。

Remote Procedure Call Protocol(RPC) 既 远程过程调用,一种能让我们像调用本地服务一样调用远程服务,可以让调用者对网络通信这些细节无感知,比如服务消费方在执行 helloWorldService.sayHello(“sowhat”) 时,实质上调用的是远端的服务。这种方式其实就是RPC,RPC思想在各大互联网公司中被广泛使用,如阿里巴巴的dubbo、当当的Dubbox 、Facebook 的 thrift、Google 的grpc、Twitter的finagle等。


Dubbo 框架设计

Dubbo 简介

Dubbo 是阿里巴巴研发开源工具,主要分为2.6.x 跟 2.7.x 版本。是一款分布式、高性能、透明化的 RPC 服务框架,提供服务自动注册、自动发现等高效服务治理方案,可以和Spring 框架无缝集成,它提供了6大核心能力:

  1. 面向接口代理的高性能RPC调用

  2. 智能容错和负载均衡

  3. 服务自动注册和发现

  4. 高度可扩展能力

  5. 运行期流量调度

  6. 可视化的服务治理与运维

调用过程:

  1. 服务提供者 Provider 启动然后向 Registry 注册自己所能提供的服务。
  2. 服务消费者 Consumer 向Registry订阅所需服务,Consumer 解析Registry提供的元信息,从服务中通过负载均衡选择 Provider调用。
  3. 服务提供方 Provider 元数据变更的话Registry会把变更推送给Consumer,以此保证Consumer获得最新可用信息。

注意点:

  1. Provider 跟 Consumer 在内存中记录调用次数跟时间,定时发送统计数据到Monitor,发送的时候是短连接。
  2. Monitor 跟 Registry 是可选的,可直接在配置文件中写好,Provider 跟 Consumer进行直连。
  3. Monitor 跟 Registry 挂了也没事, Consumer 本地缓存了 Provider 信息。
  4. Consumer 直接调用 Provider 不会经过 Registry。Provider、Consumer这俩到 Registry之间是长连接。

Dubbo框架分层

如上图,总的而言 Dubbo 分为三层。

  1. Business层:由用户自己来提供接口和实现还有一些配置信息。
  2. RPC层:真正的RPC调用的核心层,封装整个RPC的调用过程、负载均衡、集群容错、代理。
  3. Remoting层:对网络传输协议和数据转换的封装。

如果每一层再细分下去,一共有十层。

  1. 接口服务层(Service):该层与业务逻辑相关,根据 provider 和 consumer 的业务设计对应的接口和实现。
  2. 配置层(Config):对外配置接口,以 ServiceConfig 和 ReferenceConfig 为中心初始化配置。
  3. 服务代理层(Proxy):服务接口透明代理,Provider跟Consumer都生成代理类,使得服务接口透明,代理层实现服务调用跟结果返回。
  4. 服务注册层(Registry):封装服务地址的注册和发现,以服务 URL 为中心。
  5. 路由层(Cluster):封装多个提供者的路由和负载均衡,并桥接注册中心,以Invoker 为中心,扩展接口为 Cluster、Directory、Router 和 LoadBlancce。
  6. 监控层(Monitor):RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory、Monitor 和 MonitorService。
  7. 远程调用层(Protocal):封装 RPC 调用,以 Invocation 和 Result 为中心,扩展接口为 Protocal、Invoker 和 Exporter。
  8. 信息交换层(Exchange):封装请求响应模式,同步转异步。以 Request 和Response 为中心,扩展接口为 Exchanger、ExchangeChannel、ExchangeClient 和 ExchangeServer。
  9. 网络传输层(Transport):抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel、Transporter、Client、Server 和 Codec。
  10. 数据序列化层(Serialize):可复用的一些工具,扩展接口为 Serialization、ObjectInput、ObjectOutput 和 ThreadPool。

他们之间的调用关系直接看下面官网图即可。


Dubbo SPI 机制

Dubbo 采用 微内核设计 + SPI 扩展技术来搭好核心框架,同时满足用户定制化需求。这里重点说下SPI。


微内核


SPI 含义

主流的数据库有MySQL、Oracle、DB2等,这些数据库是不同公司开发的,它们的底层协议不大一样,那怎么约束呢?一般就是定制统一接口,具体实现不管,反正面向相同的接口编程即可。等到真正使用的时候用具体的实现类就好,问题是哪里找用那个实现类呢?这时候就采用约定好的法则将实现类写到指定位置即可

SPI 全称为 Service Provider Interface,是一种服务发现机制。它约定在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类


SPI demo

接口:

package com.example.demo.spi;public interface SPIService {void execute();
}

实现类1:

public class SpiImpl1 implements SPIService{@Overridepublic void execute() {System.out.println("SpiImpl1.execute()");}
}

实现类2:

public class SpiImpl2 implements SPIService{@Overridepublic void execute() {System.out.println("SpiImpl2.execute()");}
}

配置路径

调用加载类

package com.example.demo.spi;
import sun.misc.Service;
import java.util.Iterator;
import java.util.ServiceLoader;public class Test {public static void main(String[] args) {   Iterator<SPIService> providers = Service.providers(SPIService.class);ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class);while(providers.hasNext()) {SPIService ser = providers.next();ser.execute();}System.out.println("--------------------------------");Iterator<SPIService> iterator = load.iterator();while(iterator.hasNext()) {SPIService ser = iterator.next();ser.execute();}}
}

SPI源码追踪

ServiceLoader.load(SPIService.class) 底层调用大致逻辑如下


iterator.hasNext() 跟 iterator.next()底层调用大致如下


Java SPI缺点

  1. 不能按需加载,Java SPI在加载扩展点的时候,会一次性加载所有可用的扩展点,很多是不需要的,会浪费系统资源。
  2. 获取某个实现类的方式不够灵活,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类。
  3. 不支持AOP与依赖注入,JAVA SPI可能会丢失加载扩展点异常信息,导致追踪问题很困难。

Dubbo SPI

JDK自带的不好用Dubbo 就自己实现了一个 SPI,该SPI 可以通过名字实例化指定的实现类,并且实现了 IOC 、AOP 与 自适应扩展 SPI 。

key = com.sowhat.value

Dubbo 对配置文件目录的约定,不同于 Java SPI ,Dubbo 分为了三类目录。

  1. META-INF/services/ :该目录下 SPI 配置文件是为了用来兼容 Java SPI 。
  2. META-INF/dubbo/ :该目录存放用户自定义的 SPI 配置文件。
  3. META-INF/dubbo/internal/ :该目录存 Dubbo 内部使用的 SPI 配置文件。

使用的话很简单 引入依赖,然后百度教程即可

 @Testvoid sowhat(){//按需获取实现类对象ExtensionLoader<SPIService> spiService = ExtensionLoader.getExtensionLoader(SPIService.class);        SPIService demo1 = spiService.getExtension("SpiImpl1");demo1.execute();}

Dubbo SPI源码追踪

ExtensionLoader.getExtension 方法的整个思路是 查找缓存是否存在,不存在则读取SPI文件,通过反射创建类,然后设置依赖注入这些东西,有包装类就包装下,执行流程如下图所示:


说下重要的四个部分:

injectExtension IOC

查找 set 方法,根据参数找到依赖对象则注入。

WrapperClass AOP

包装类,Dubbo 帮你自动包装,只需要某个扩展类的构造函数只有一个参数,并且是扩展接口类型,就会被判定为包装类。

Activate

Active 有三个属性,group 表示修饰在哪个端,是 provider 还是 consumer,value 表示在 URL参数中出现才会被激活,order 表示实现类的顺序。


Adaptive 自适应扩展

需求:根据配置来进行 SPI 扩展的加载后不想在启动的时候让扩展被加载,想根据请求时候的参数来动态选择对应的扩展。

实现:Dubbo用代理机制实现了自适应扩展,为用户想扩展的接口 通过JDK 或者 Javassist 编译生成一个代理类,然后通过反射创建实例。实例会根据本来方法的请求参数得知需要的扩展类,然后通过 ExtensionLoader.getExtensionLoader(type.class).getExtension(name)来获取真正的实例来调用,看个官网样例。

public interface WheelMaker {Wheel makeWheel(URL url);
}// WheelMaker 接口的自适应实现类
public class AdaptiveWheelMaker implements WheelMaker {public Wheel makeWheel(URL url) {if (url == null) {throw new IllegalArgumentException("url == null");}// 1. 调用 url 的 getXXX 方法获取参数值String wheelMakerName = url.getParameter("Wheel.maker");if (wheelMakerName == null) {throw new IllegalArgumentException("wheelMakerName == null");}// 2. 调用 ExtensionLoader 的 getExtensionLoader 获取加载器// 3. 调用 ExtensionLoader 的 getExtension 根据从url获取的参数作为类名称加载实现类WheelMaker wheelMaker = ExtensionLoader.getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName);// 4. 调用实现类的具体方法实现调用。return wheelMaker.makeWheel(URL url);}
}

查看Adaptive注解源码可知该注解可用在类或方法上,Adaptive 注解在类上或者方法上有不同的实现逻辑。

1 Adaptive 注解在类上

Adaptive 注解在类上时,Dubbo 不会为该类生成代理类,Adaptive 注解在类上的情况很少,在 Dubbo 中,仅有两个类被 Adaptive 注解了,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory,表示拓展的加载逻辑由人工编码完成,这不是我们关注的重点。

2 Adaptive 注解在方法上

Adaptive 注解在方法上时,Dubbo 则会为该方法生成代理逻辑,表示拓展的加载逻辑需由框架自动生成,大致的实现机制如下:

  1. 加载标注有 @Adaptive 注解的接口,如果不存在,则不支持 Adaptive 机制;
  2. 为目标接口按照一定的模板生成子类代码,并且编译生成的代码,然后通过反射生成该类的对象;
  3. 结合生成的对象实例,通过传入的URL对象,获取指定key的配置,然后加载该key对应的类对象,最终将调用委托给该类对象进行。
@SPI("apple")
public interface FruitGranter {Fruit grant();@AdaptiveString watering(URL url);
}
---
// 苹果种植者
public class AppleGranter implements FruitGranter {@Overridepublic Fruit grant() {return new Apple();}@Overridepublic String watering(URL url) {System.out.println("watering apple");return "watering finished";}
}
---
// 香蕉种植者
public class BananaGranter implements FruitGranter {@Overridepublic Fruit grant() {return new Banana();}@Overridepublic String watering(URL url) {System.out.println("watering banana");return "watering success";}
}

调用方法实现:

public class ExtensionLoaderTest {@Testpublic void testGetExtensionLoader() {// 首先创建一个模拟用的URL对象URL url = URL.valueOf("dubbo://192.168.0.1:1412?fruit.granter=apple");// 通过ExtensionLoader获取一个FruitGranter对象FruitGranter granter = ExtensionLoader.getExtensionLoader(FruitGranter.class).getAdaptiveExtension();// 使用该FruitGranter调用其"自适应标注的"方法,获取调用结果String result = granter.watering(url);System.out.println(result);}
}

通过如上方式生成一个内部类。大致调用流程如下:


Dubbo 服务暴露流程

服务暴露总览

Dubbo框架是以URL为总线的模式,运行过程中所有的状态数据信息都可以通过URL来获取,比如当前系统采用什么序列化,采用什么通信,采用什么负载均衡等信息,都是通过URL的参数来呈现的,所以在框架运行过程中,运行到某个阶段需要相应的数据,都可以通过对应的Key从URL的参数列表中获取。URL 具体的参数如下:

protocol:指的是 dubbo 中的各种协议,如:dubbo thrift http
username/password:用户名/密码 host/port:主机/端口
path:接口的名称 parameters:参数键值对

protocol://username:password@host:port/path?k=v

服务暴露从代码流程看分为三部分

  1. 检查配置,最终组装成 URL。
  2. 暴露服务到本地服务跟远程服务。
  3. 服务注册至注册中心。

服务暴露从对象构建转换看分为两步:

  1. 将服务封装成Invoker。
  2. 将Invoker通过协议转换为Exporter。

服务暴露源码追踪

  1. 容器启动,Spring IOC 刷新完毕后调用 onApplicationEvent 开启服务暴露,ServiceBean 。
  2. export 跟 doExport 来进行拼接构建URL,为屏蔽调用的细节,统一暴露出一个可执行体,通过ProxyFactory 获取到 invoker。
  3. 调用具体 Protocol 将把包装后的 invoker 转换成 exporter,此处用到了SPI。
  4. 然后启动服务器server,监听端口,使用NettyServer创建监听服务器。
  5. 通过 RegistryProtocol 将URL注册到注册中心,使得consumer可获得provider信息。

Dubbo 服务引用流程

Dubbo中一个可执行体就是一个invoker,所以 provider 跟 consumer 都要向 invoker 靠拢。通过上面demo可知为了无感调用远程接口,底层需要有个代理类包装 invoker。

服务的引入时机有两种

饿汉式:

通过实现 Spring 的 InitializingBean 接口中的 afterPropertiesSet 方法,容器通过调用 ReferenceBean的 afterPropertiesSet 方法时引入服务。

懒汉式(默认):

懒汉式是只有当服务被注入到其他类中时启动引入流程。


服务引用的三种方式

  1. 本地引入:服务暴露时本地暴露,避免网络调用开销。
  2. 直接连接引入远程服务:不启动注册中心,直接写死远程Provider地址 进行直连。
  3. 通过注册中心引入远程服务:通过注册中心抉择如何进行负载均衡调用远程服务。

服务引用流程

  1. 检查配置构建map ,map 构建 URL ,通过URL上的协议利用自适应扩展机制调用对应的 protocol.refer 得到相应的 invoker 。
  2. 想注册中心注册自己,然后订阅注册中心相关信息,得到provider的 ip 等信息,再通过共享的netty客户端进行连接。
  3. 当有多个 URL 时,先遍历构建出 invoker 然后再由 StaticDirectory 封装一下,然后通过 cluster 进行合并,只暴露出一个 invoker 。
  4. 然后再构建代理,封装 invoker 返回服务引用,之后 Comsumer 调用的就是这个代理类。

调用方式

  1. oneway:不关心请求是否发送成功。
  2. Async异步调用:Dubbo天然异步,客户端调用请求后将返回的 ResponseFuture 存到上下文中,用户可随时调用 future.get 获取结果。异步调用通过唯一ID 标识此次请求。
  3. Sync同步调用:在 Dubbo 源码中就调用了 future.get,用户感觉方法被阻塞了,必须等结果后才返回。

Dubbo 调用整体流程

调用之前你可能需要考虑这些事

  1. consumer 跟 provider 约定好通讯协议,dubbo支持多种协议,比如dubbo、rmi、hessian、http、webservice等。默认走dubbo协议,连接属于单一长连接,NIO异步通信。适用传输数据量很小(单次请求在100kb以内),但是并发量很高。
  2. 约定序列化模式,大致分为两大类,一种是字符型(XML或json 人可看懂 但传输效率低),一种是二进制流(数据紧凑,机器友好)。默认使用 hessian2作为序列化协议。
  3. consumer 调用 provider 时提供对应接口、方法名、参数类型、参数值、版本号。
  4. provider列表对外提供服务涉及到负载均衡选择一个provider提供服务。
  5. consumer 跟 provider 定时向monitor 发送信息。

调用大致流程

  1. 客户端发起请求来调用接口,接口调用生成的代理类。代理类生成RpcInvocation 然后调用invoke方法。
  2. ClusterInvoker获得注册中心中服务列表,通过负载均衡给出一个可用的invoker。
  3. 序列化跟反序列化网络传输数据。通过NettyServer调用网络服务。
  4. 服务端业务线程池接受解析数据,从exportMap找到invoker进行invoke。
  5. 调用真正的Impl得到结果然后返回。

调用方式

  1. oneway:不关心请求是否发送成功,消耗最小。
  2. sync同步调用:在 Dubbo 源码中就调用了 future.get,用户感觉方法被阻塞了,必须等结果后才返回。
  3. Async 异步调用:Dubbo天然异步,客户端调用请求后将返回的 ResponseFuture 存到上下文中,用户可以随时调用future.get获取结果。异步调用通过唯一ID标识此次请求。

Dubbo集群容错负载均衡

Dubbo 引入了Cluster、Directory、Router、LoadBalance、Invoker模块来保证Dubbo系统的稳健性,它们的关系如下图:

  1. 服务发现时会将多个多个远程调用放入Directory,然后通过Cluster封装成一个Invoker,该invoker提供容错功能。
  2. 消费者代用的时候从Directory中通过负载均衡获得一个可用invoker,最后发起调用。
  3. 你可以认为Dubbo中的Cluster对上面进行了大的封装,自带各种鲁棒性功能。

集群容错

集群容错是在消费者端通过Cluster子类实现的,Cluster接口有10个实现类,每个Cluster实现类都会创建一个对应的ClusterInvoker对象。核心思想是让用户选择性调用这个Cluster中间层,屏蔽后面具体实现细节


智能容错之负载均衡

Dubbo中一般有4种负载均衡策略。

  1. RandomLoadBalance:加权随机,它的算法思想简单。假设有一组服务器 servers = [A, B, C],对应权重为 weights = [5, 3, 2],权重总和为10。现把这些权重值平铺在一维坐标值上,[0, 5) 区间属于服务器 A,[5, 8) 区间属于服务器 B,[8, 10) 区间属于服务器 C。接下来通过随机数生成器生成一个范围在 [0, 10) 之间的随机数,然后计算这个随机数会落到哪个区间上。默认实现。
  2. LeastActiveLoadBalance:最少活跃数负载均衡,选择现在活跃调用数最少的提供者进行调用,活跃的调用数少说明它现在很轻松,而且活跃数都是从 0 加起来的,来一个请求活跃数+1,一个请求处理完成活跃数-1,所以活跃数少也能变相的体现处理的快。
  3. RoundRobinLoadBalance:加权轮询负载均衡,比如现在有两台服务器 A、B,轮询的调用顺序就是 A、B、A、B,如果加了权重,A 比B 的权重是2:1,那现在的调用顺序就是 A、A、B、A、A、B。
  4. ConsistentHashLoadBalance:一致性 Hash 负载均衡,将服务器的 IP 等信息生成一个 hash 值,将hash 值投射到圆环上作为一个节点,然后当 key 来查找的时候顺时针查找第一个大于等于这个 key 的 hash 值的节点。一般而言还会引入虚拟节点,使得数据更加的分散,避免数据倾斜压垮某个节点。如下图 Dubbo 默认搞了 160 个虚拟节点。


智能容错之服务目录

关于 服务目录Directory 你可以理解为是相同服务Invoker的集合,核心是RegistryDirectory类。具有三个功能。

  1. 从注册中心获得invoker列表。
  2. 监控着注册中心invoker的变化,invoker的上下线。
  3. 刷新invokers列表到服务目录。

智能容错之服务路由

服务路由其实就是路由规则,它规定了服务消费者可以调用哪些服务提供者。条件路由规则由两个条件组成,分别用于对服务消费者和提供者进行匹配。比如有这样一条规则:

host = 10.20.153.14 => host = 10.20.153.12

该条规则表示 IP 为 10.20.153.14 的服务消费者只可调用 IP 为 10.20.153.12 机器上的服务,不可调用其他机器上的服务。条件路由规则的格式如下:

[服务消费者匹配条件] => [服务提供者匹配条件]

如果服务消费者匹配条件为空,表示不对服务消费者进行限制。如果服务提供者匹配条件为空,表示对某些服务消费者禁用服务。


如何设计一个RPC

通读下Dubbo的大致实现方式后其实就可以依葫芦画瓢了,一个RPC框架大致需要下面这些东西

  1. 服务的注册跟发现的搞一个吧,你可以用ZooKeeper或者Redis来实现。
  2. 接下来consumer发起请求的时候你的面向接口编程啊,用到动态代理来实现调用。
  3. 多个provider提供相同服务你的用到LoadBalance啊。
  4. 最终选择一个机器后你的约定好通信协议啊,如何进行序列化跟反序列化呢?
  5. 底层就用现成的高性能Netty框架 NIO模式实现呗。
  6. 服务开启后的有monitor啊。

关于dubbo的几个问题相关推荐

  1. SpringBoot (七) :SpringBoot 整合dubbo

    Dubbo是什么 Dubbo是alibaba的一款开源软件,它是基于java的RPC调用框架. Dubbo主要提供了三种功能: 提供了基于接口的远程调用接口 容错性和负载均衡 服务自动注册及发现 安装 ...

  2. Dubbo 18 问

    dubbo是什么 dubbo是一个分布式框架,远程服务调用的分布式框架,其核心部分包含: 集群容错:提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集 ...

  3. 如何参与贡献Dubbo社区

    如何参与贡献Dubbo社区 文字版首发于dubbo.io官网:http://dubbo.apache.org/zh-cn/blog/how-to-involve-dubbo-community.htm ...

  4. Spring 集成dubbo 找不到dubbo.xsd 文件的问题的想法概述

    想法概述: 网上的很多关于找不到dubbo.xsd 都是指向的原因是: 阿里的这个域名(http://code.alibabatech.com/schema/dubbo/dubbo.xsd)已经停用了 ...

  5. dubbo入门--Hello World

    Dubbo入门--Hello World 转载自:http://blog.csdn.net/hanmov/article/details/66973957?locationNum=2&fps= ...

  6. Dubbo的使用及原理浅析.

    前面几个博文中关于SSM 框架已经搭建完成, 这里来讲下项目中使用到的Dubbo以及自己了解到的关于Dubbo的一些知识. Dubbo是什么? Dubbo是阿里巴巴SOA服务化治理方案的核心框架,每天 ...

  7. 2016.1.20 dubbo启动之后机器ip有问题

    环境 100.0.4.222  zookerper  2182 100.0.4.221  dubbo        20871 1.问题    dubbo  对应的机器ip 是202.106.199. ...

  8. http和dubbo的区别_(转载)Dubbo 接口是什么? 与http 接口有什么区别

    面试的时候会被问到 除了http  https 请求外 有接触了解到其他的接口么? 一,什么是Dubbo 接口 Dubbo 接口是阿里巴巴开源的致力于提供高性能和透明化的RPC远程服务调用方案,以及S ...

  9. kafka实现异步发送_Kafka 异步消息也会阻塞?记一次 Dubbo 频繁超时排查过程

    线上某服务 A 调用服务 B 接口完成一次交易,一次晚上的生产变更之后,系统监控发现服务 B 接口频繁超时,后续甚至返回线程池耗尽错误 Thread pool is EXHAUSTED.因为服务 B ...

  10. cstring只获取到第一个数_一文讲透 Dubbo 负载均衡之最小活跃数算法

    (给ImportNew加星标,提高Java技能) 作者:why技术(本文来自作者投稿) 本文是对于Dubbo负载均衡策略之一的最小活跃数算法的详细分析.文中所示源码,没有特别标注的地方均为2.6.0版 ...

最新文章

  1. 微信小程序:一把瑞士军刀
  2. Linux 关闭桌面方法
  3. Android JNI入门第五篇——基本数据类型使用
  4. java中不能定义为变量名称_Java,“变量名”不能解析为变量
  5. javabean和java类_java对象 POJO和JavaBean的区别
  6. 【spring-boot】前端传图像到后端
  7. 假期要如何过才有意义
  8. Scratch3.0中文版官方介绍
  9. 软件测试——bug相关知识
  10. android 常用模块,超实用的几款Xposed模块,安卓发烧必备
  11. Hive3第五章:函数
  12. 2022年湖南省基金从业资格(私募股权投资基金基础知识)练习题及答案
  13. 【沧小海笔记】之基于FPGA的以太网设计相关知识——第二章 物理层介绍(基于88E1111)
  14. Code First开发系列之领域建模和管理实体关系
  15. Vue 使用 Viewers 实现图片的 放大缩小、 旋转 、 拖拉等操作、支持多图片
  16. 鸿蒙os适配平板,华为新平板搭载高通4G芯片,鸿蒙OS已适配完成,友商随时可用...
  17. Maven中不能引入ojdbc解决方法:com.oracle:ojdbc6:jar:11.2.0.3
  18. linux网络被限制网速,linux iptables限速及限制每IP连接数
  19. mysql取rownum_MySQL中的ROWNUM的实现
  20. 工业智能网关BL110应用之11:实现三菱 PLC FX3S 接入MQTT Client One云平台

热门文章

  1. How to Plan My Life?
  2. express不是内部或外部命令,也不是可运行的程序或批处理文件
  3. 使用Powershell远程管理Windows Server(WinRM)
  4. Hadoop系列之五:MapReduce进阶(2)
  5. 一起谈.NET技术,.NET缓存机制探讨与比对
  6. jquery autocomplete的使用
  7. 使用Microsoft SQL Server 2000全文搜索功能构建Web搜索应用程序 --作者:Andrew B. Cencini...
  8. asp.net 的web.config文件编写
  9. Android 升级下载 它们的定义Updates 兼容版本
  10. 对Repository模式误用的反思和纠正