Java RMI远程方法调用详解

【尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/51992182
一、Java RMI机制: 
       远程方法调用RMI(Remote Method Invocation),是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。 这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。
Java RMI:Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。
       而RPC是远程过程调用(Remote Procedure Call)可以用于一个进程调用另一个进程(很可能在另一个远程主机上)中的过程,从而提供了过程的分布能力。Java 的 RMI 则在 RPC 的基础上向前又迈进了一步,即提供分布式对象间的通讯
 (1)RMI框架
       【参考资料】
《Java网络编程精解》孙卫琴,这本书适合入门学习RMI框架基础   
http://wenku.baidu.com/view/90171bd03186bceb19e8bbdc.html?from=search
RMI框架封装了所有底层通信细节,并且解决了编组、分布式垃圾收集、安全检查和并发性等通用问题。有了现成的框架,开发人员就只需专注于开发与特定问题领域相关的各种本地对象和远程对象。
要了解RMI原理,先了解一下Stub和Skeleton两个概念。
(2)Stub和Skeleton
      RMI框架采用代理来负责客户与远程对象之间通过Socket进行通信的细节。RMI框架为远程对象分别生成了客户端代理和服务器端代理。位于客户端的代理类称为存根(Stub),位于服务器端的代理类称为骨架(Skeleton)。


【相关资料】
《RMI(Remote Method Invocation)初窥门径》 http://blog.csdn.net/smcwwh/article/details/7080997 
stub(存根)和skeleton(骨架)在RMI中充当代理角色,在现实开发中主要是用来隐藏系统和网络的的差异, 这一部分的功能在RMI开发中对程序员是透明的。Stub为客户端编码远程命令并把他们发送到服务器。而Skeleton则是把远程命令解码,调用服务端的远程对象的方法,把结果在编码发给stub,然后stub再解码返回调用结果给客户端。
RMI远程过程调用的实现过程如下图所示:
RMI 框架的基本原理大概如下图,应用了代理模式来封装了本地存根与真实的远程对象进行通信的细节
【参考资料】
《Java RMI 框架(远程方法调用)》http://haolloyin.blog.51cto.com/1177454/332426  
《 java RMI原理详解》 http://blog.csdn.net/xinghun_4/article/details/45787549

二、Java RMI 简单示例

    别急,慢慢分析~具体代码在下面,附例子代码下载:http://download.csdn.net/detail/guyuealian/9583633
大致说来,创建一个RMI应用包括以下步骤:
(1)创建远程接口:继承java.rmi.Remote接口。
      (2)创建远程类:实现远程接口。
      (3)创建服务器程序:创建远程对象,通过createRegistry()方法注册远程对象。并通过bind或者rebind方法,把远程对象绑定到指定名称空间(URL)中。
      (4)创建客户程序:通过 lookup()方法查找远程对象,进行远程方法调用 
下面具体分析每个步骤:
(1)创建远程接口:继承java.rmi.Remote接口。
远程接口中声明了可以被客户程序访问的远程方法。RMI规范要求远程对象所属的类实现一个远程接口,并且远程接口符合以下条件:
       (a)直接或间接继承java.rmi.Remote接口。
(b)接口中的所有方法声明抛出java.rmi.RemoteException。
(2)创建远程类:实现远程接口。
远程类就是远程对象所属的类。RMI规范要求远程类必须实现一个远程接口。此外,为了使远程类的实例变成能为远程客户提供服务的远程对象,可通过以下两种途径之一把它导出(export)为远程对象:
       (a)导出为远程对象的第一种方式:使远程类实现远程接口时,同时继承java.rmi.server.UnicastRemoteObject类,并且远程类的构造方法必须声明抛出RemoteException。这是最常用的方式,下面的本例子就采取这种方式。
[java] view plaincopyprint?
  1. public class RemoteImpl extends UnicastRemoteObject implements RemoteInterface
public class RemoteImpl extends UnicastRemoteObject implements RemoteInterface

(b)导出为远程对象的第二种方式:如果一个远程类已经继承了其他类,无法再继承UnicastRemoteObject类,那么可以在构造方法中调用UnicastRemoteObject类的静态exportObject()方法,同样,远程类的构造方法也必须声明抛出RemoteException。
[java] view plaincopyprint?
  1. public class RemoteImpl extends OtherClass implements RemoteInterface{
  2. private String name;
  3. public RemoteImpl (String name)throws RemoteException{
  4. this.name=name;
  5. UnicastRemoteObject.exportObject(this,0);
  6. }
public class RemoteImpl extends OtherClass implements RemoteInterface{private String name;public RemoteImpl (String name)throws RemoteException{this.name=name;UnicastRemoteObject.exportObject(this,0);}

在构造方法RemoteImpl 中调用了UnicastRemoteObject.exportObject(this,0)方法,将自身导出为远程对象。

exportObject()是UnicastRemoteObject的静态方法,源码是:
[java] view plaincopyprint?
  1. protected UnicastRemoteObject(int port) throws RemoteException
  2. {
  3. this.port = port;
  4. exportObject((Remote) this, port);
  5. }
    protected UnicastRemoteObject(int port) throws RemoteException{this.port = port;exportObject((Remote) this, port);}
[java] view plaincopyprint?
  1. public static Remote exportObject(Remote obj, int port)
  2. throws RemoteException
  3. {
  4. return exportObject(obj, new UnicastServerRef(port));
  5. }
    public static Remote exportObject(Remote obj, int port)throws RemoteException{return exportObject(obj, new UnicastServerRef(port));}

exportObject(Remote obj, int port),该方法负责把参数obj指定的对象导出为远程对象,使它具有相应的存根(Stub),并监听远程客户的方法调用请求;参数port指导监听的端口,如果值为0,表示监听任意一个匿名端口。
(3)创建服务器程序:创建远程对象,在rmiregistry注册表中注册远程对象,并绑定到指定的URL中。

       RMI采用一种命名服务机制来使得客户程序可以找到服务器上的一个远程对象。在JDK的安装目录的bin子目录下有一个rmiregistry.exe程序,它是提供命名服务的注册表程序。
服务器程序的一大任务就是向rmiregistry注册表注册远程对象。从JDK1.3以上版本开始,RMI的命名服务API被整合到JNDI(Java Naming and Directory Interface,Java名字与目录接口)中。在JNDI中,javax.naming.Context接口声明了注册、查找,以及注销对象的方法:
    【1】 bind(String name,Object obj):注册对象,把对象与一个名字name绑定,这里的name其实就是URL格式。如果该名字已经与其它对象绑定,就会抛出NameAlreadyBoundException。
    【2】rebind(String name,Object obj):注册对象,把对象与一个名字绑定。如果该名字已经与其它对象绑定,不会抛出NameAlreadyBoundException,而是把当前参数obj指定的对象覆盖原先的对象。
    【3】 lookup(String name):查找对象,返回与参数name指定的名字所绑定的对象。
    【4】unbind(String name):注销对象,取消对象与名字的绑定。
注册一个远程对象remoteObj2 关键代码如下:
[java] view plaincopyprint?
  1. RemoteInterface remoteObj2 = new RemoteImpl();// 创建远程对象
  2. Context namingContext = new InitialContext();// 初始化命名内容
  3. LocateRegistry.createRegistry(8892);// 在本地主机上创建和导出注册表实例,并在指定的端口上接受请求
  4. namingContext.rebind(”rmi://localhost:8892/RemoteObj2”, remoteObj2);// 注册对象,即把对象与一个名字绑定。
RemoteInterface remoteObj2 = new RemoteImpl();// 创建远程对象
Context namingContext = new InitialContext();// 初始化命名内容
LocateRegistry.createRegistry(8892);// 在本地主机上创建和导出注册表实例,并在指定的端口上接受请求
namingContext.rebind("rmi://localhost:8892/RemoteObj2", remoteObj2);// 注册对象,即把对象与一个名字绑定。

但在JDK1.3版本或更低的版本,需要使用java.rmi.Naming来注册远程对象,注册代码代替如下:
[java] view plaincopyprint?
  1. RemoteInterface remoteObj = new RemoteImpl();
  2. LocateRegistry.createRegistry(8891);
  3. Naming.rebind(”rmi://localhost:8891/RemoteObj”, remoteObj);
RemoteInterface remoteObj = new RemoteImpl();
LocateRegistry.createRegistry(8891);
Naming.rebind("rmi://localhost:8891/RemoteObj", remoteObj);

(4)创建客户程序:通过 lookup()方法查找远程对象,进行远程方法调用

关键代码如下:
[java] view plaincopyprint?
  1. Context namingContext = new InitialContext();// 初始化命名内容
  2. RemoteInterface RmObj2 = (RemoteInterface) namingContext.lookup(”rmi://localhost:8892/RemoteObj2”);//获得远程对象的存根对象
  3. System.out.println(RmObj2.doSomething());//通过远程对象,调用doSomething方法
Context namingContext = new InitialContext();// 初始化命名内容
RemoteInterface RmObj2 = (RemoteInterface) namingContext.lookup("rmi://localhost:8892/RemoteObj2");//获得远程对象的存根对象
System.out.println(RmObj2.doSomething());//通过远程对象,调用doSomething方法

在JDK1.3版本或更低的版本,如下方式调用;

[java] view plaincopyprint?
  1. RemoteInterface RmObj = (RemoteInterface) Naming.lookup(“rmi://localhost:8891/RemoteObj”);
  2. System.out.println(RmObj.doSomething());
RemoteInterface RmObj = (RemoteInterface) Naming.lookup("rmi://localhost:8891/RemoteObj");
System.out.println(RmObj.doSomething());

例子具体代码:
1. 创建远程接口:继承java.rmi.Remote接口。
[java] view plaincopyprint?
  1. package rmi;
  2. import java.rmi.Remote;
  3. import java.rmi.RemoteException;
  4. //声明一个远程接口RemoteInterface,该接口必须继承Remote接口
  5. //接口中需要被远程调用的方法,必须抛出RemoteException异常
  6. public interface RemoteInterface extends Remote {
  7. // 声明一个doSomething方法
  8. public String doSomething() throws RemoteException;
  9. // 声明一个计算方法Calculate
  10. public int Calculate(int num1, int num2) throws RemoteException;
  11. }
package rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
//声明一个远程接口RemoteInterface,该接口必须继承Remote接口
//接口中需要被远程调用的方法,必须抛出RemoteException异常
public interface RemoteInterface extends Remote {// 声明一个doSomething方法public String doSomething() throws RemoteException;// 声明一个计算方法Calculatepublic int Calculate(int num1, int num2) throws RemoteException;
}
在Java中,只要一个类extends了java.rmi.Remote接口,即可成为存在于服务器端的远程对象, 供客户端访问并提供一定的服务。JavaDoc描述:Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口。任何远程对象都必须直接或间接实现此接口。只有在“远程接口”  (扩展 java.rmi.Remote 的接口)中指定的这些方法才可被远程调用。注意:接口中需要被远程调用的方法,必须抛出RemoteException异常。

2. 创建远程类:实现远程接口

[java] view plaincopyprint?
  1. package rmi;
  2. import java.rmi.RemoteException;
  3. import java.rmi.server.UnicastRemoteObject;
  4. //实现远程接口RemoteInterface,并继承UnicastRemoteObject
  5. //注意RemoteObject这个类,实现了Serializable, Remote这两个接口
  6. public class RemoteImpl extends UnicastRemoteObject implements RemoteInterface {
  7. // 这个实现必须有一个显式的构造函数,并且要抛出一个RemoteException异常
  8. public RemoteImpl() throws RemoteException {
  9. }
  10. // 实现doSomething方法
  11. public String doSomething() throws RemoteException {
  12. return “OK ,You can do……”;
  13. }
  14. // 实现Calculate方法,返回计算结果
  15. public int Calculate(int num1, int num2) throws RemoteException {
  16. return (num1 + num2);
  17. }
  18. }
package rmi;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
//实现远程接口RemoteInterface,并继承UnicastRemoteObject
//注意RemoteObject这个类,实现了Serializable, Remote这两个接口
public class RemoteImpl extends UnicastRemoteObject implements RemoteInterface {// 这个实现必须有一个显式的构造函数,并且要抛出一个RemoteException异常public RemoteImpl() throws RemoteException {}// 实现doSomething方法public String doSomething() throws RemoteException {return "OK ,You can do......";}// 实现Calculate方法,返回计算结果public int Calculate(int num1, int num2) throws RemoteException {return (num1 + num2);}
}
远程对象必须实现java.rmi.server.UniCastRemoteObject类,该类的构造函数中将生成stub和skeleton, 这样才能保证客户端访问获得远程对象时,该远程对象将会把自身的一个拷贝以Socket的形式传输给客户端,此时客户端所获得的这个拷贝称为Stub(存根), 而服务器端本身已存在的远程对象则称之为Skeleton(骨架)。其实此时的存根是客户端的一个代理,用于与服务器端的通信,  而骨架也可认为是服务器端的一个代理,用于接收客户端的请求之后调用远程方法来响应客户端的请求。
 3.创建服务器程序:在rmiregistry注册表中注册远程对象,向客户端提供远程对象服务  
[java] view plaincopyprint?
  1. package rmi2;
  2. import javax.naming.Context;
  3. import javax.naming.InitialContext;
  4. import rmi.RemoteInterface;
  5. public class ClientTest {
  6. public static void main(String args[]) {
  7. try {
  8. Context namingContext = new InitialContext();// 初始化命名内容
  9. RemoteInterface RmObj2 = (RemoteInterface) namingContext
  10. .lookup(”rmi://localhost:8892/RemoteObj2”);//获得远程对象的存根对象
  11. System.out.println(RmObj2.doSomething());//通过远程对象,调用doSomething方法
  12. System.out.println(”远程服务器计算结果为:” + RmObj2.Calculate(90, 2));
  13. } catch (Exception e) {
  14. }
  15. }
  16. }
package rmi2;
import javax.naming.Context;
import javax.naming.InitialContext;import rmi.RemoteInterface;public class ClientTest {public static void main(String args[]) {try {Context namingContext = new InitialContext();// 初始化命名内容RemoteInterface RmObj2 = (RemoteInterface) namingContext.lookup("rmi://localhost:8892/RemoteObj2");//获得远程对象的存根对象System.out.println(RmObj2.doSomething());//通过远程对象,调用doSomething方法System.out.println("远程服务器计算结果为:" + RmObj2.Calculate(90, 2));} catch (Exception e) {}}
}

在JDK1.3版本或更低的版本,如下方式注册;

[java] view plaincopyprint?
  1. package rmi;
  2. import java.net.MalformedURLException;
  3. import java.rmi.AlreadyBoundException;
  4. import java.rmi.Naming;
  5. import java.rmi.RemoteException;
  6. import java.rmi.registry.LocateRegistry;
  7. //在JDK1.3版本或更低的版本,需要使用java.rmi.Naming来注册远程对象
  8. //创建RMI注册表,启动RMI服务,并将远程对象注册到RMI注册表中。
  9. public class RMIServer {
  10. public static void main(String args[])
  11. throws java.rmi.AlreadyBoundException {
  12. try {
  13. // 创建一个远程对象RemoteObj,实质上隐含了是生成stub和skeleton,并返回stub代理引用
  14. RemoteInterface remoteObj = new RemoteImpl();
  15. // 本地创建并启动RMI Service,被创建的Registry服务将在指定的端口,侦听请求
  16. // Java默认端口是1099,缺少注册表创建,则无法绑定对象到远程注册表上
  17. LocateRegistry.createRegistry(8891);
  18. // 把远程对象注册到RMI注册服务器上,并命名为RemoteObj(名字可自定义,客户端要对应)
  19. // 绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的)
  20. Naming.rebind(”rmi://localhost:8891/RemoteObj”, remoteObj);// 将stub代理绑定到Registry服务的URL上
  21. // Naming.bind(“//localhost:8880/RemoteObj”,remoteObj);
  22. System.out.println(”>>>>>INFO:远程IHello对象绑定成功!”);
  23. } catch (RemoteException e) {
  24. System.out.println(”创建远程对象发生异常!”);
  25. e.printStackTrace();
  26. } catch (MalformedURLException e) {
  27. System.out.println(”发生URL畸形异常!”);
  28. e.printStackTrace();
  29. }
  30. }
  31. }
package rmi;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
//在JDK1.3版本或更低的版本,需要使用java.rmi.Naming来注册远程对象
//创建RMI注册表,启动RMI服务,并将远程对象注册到RMI注册表中。
public class RMIServer {public static void main(String args[])throws java.rmi.AlreadyBoundException {try {// 创建一个远程对象RemoteObj,实质上隐含了是生成stub和skeleton,并返回stub代理引用RemoteInterface remoteObj = new RemoteImpl();// 本地创建并启动RMI Service,被创建的Registry服务将在指定的端口,侦听请求// Java默认端口是1099,缺少注册表创建,则无法绑定对象到远程注册表上LocateRegistry.createRegistry(8891);// 把远程对象注册到RMI注册服务器上,并命名为RemoteObj(名字可自定义,客户端要对应)// 绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的)Naming.rebind("rmi://localhost:8891/RemoteObj", remoteObj);// 将stub代理绑定到Registry服务的URL上// Naming.bind("//localhost:8880/RemoteObj",remoteObj);System.out.println(">>>>>INFO:远程IHello对象绑定成功!");} catch (RemoteException e) {System.out.println("创建远程对象发生异常!");e.printStackTrace();} catch (MalformedURLException e) {System.out.println("发生URL畸形异常!");e.printStackTrace();}}
}

RMIServer类主要实现注册远程对象,并向客户端提供远程对象服务。远程对象是在远程服务上创建的,你无法确切地知道远程服务器上的对象的名称 。但是,将远程对象注册到RMI Service之后,客户端就可以通过RMI Service请求到该远程服务对象的stub了,利用stub代理就可以访问远程服务对象了 。

  4. 客户端代码

Server端的代码已经全部写完,这时把服务器的接口RemoteInterface打包成jar,以便在Client端的项目中使用。
项目–>右键–>导出–>jar->选择RemoteInterface.java–>finish;关于客户端如何导入jar包,请看这里:http://jingyan.baidu.com/article/ca41422fc76c4a1eae99ed9f.html
[java] view plaincopyprint?
  1. package rmi2;
  2. import javax.naming.Context;
  3. import javax.naming.InitialContext;
  4. import rmi.RemoteInterface;
  5. public class ClientTest {
  6. public static void main(String args[]) {
  7. try {
  8. Context namingContext = new InitialContext();// 初始化命名内容
  9. RemoteInterface RmObj2 = (RemoteInterface) namingContext
  10. .lookup(”rmi://localhost:8892/RemoteObj2”);//获得远程对象的存根对象
  11. System.out.println(RmObj2.doSomething());//通过远程对象,调用doSomething方法
  12. System.out.println(”远程服务器计算结果为:” + RmObj2.Calculate(90, 2));
  13. } catch (Exception e) {
  14. }
  15. }
  16. }
package rmi2;
import javax.naming.Context;
import javax.naming.InitialContext;import rmi.RemoteInterface;
public class ClientTest {public static void main(String args[]) {try {Context namingContext = new InitialContext();// 初始化命名内容RemoteInterface RmObj2 = (RemoteInterface) namingContext.lookup("rmi://localhost:8892/RemoteObj2");//获得远程对象的存根对象System.out.println(RmObj2.doSomething());//通过远程对象,调用doSomething方法System.out.println("远程服务器计算结果为:" + RmObj2.Calculate(90, 2));} catch (Exception e) {}}
}

在JDK1.3版本或更低的版本,如下方式调用

[java] view plaincopyprint?
  1. import java.net.MalformedURLException;
  2. import java.rmi.Naming;
  3. import java.rmi.NotBoundException;
  4. import java.rmi.RemoteException;
  5. public class ClientTest {
  6. public static void main(String args[]) {
  7. try {
  8. // 在RMI服务注册表中查找名称为RemoteObj的对象,并调用其上的方法
  9. // 客户端通过命名服务Naming获得指向远程对象的远程引用
  10. RemoteInterface RmObj = (RemoteInterface) Naming
  11. .lookup(”rmi://localhost:8881/RemoteObj”);
  12. System.out.println(RmObj.doSomething());
  13. System.out.println(”远程服务器计算结果为:” + RmObj.Calculate(1, 2));
  14. } catch (NotBoundException e) {
  15. e.printStackTrace();
  16. } catch (MalformedURLException e) {
  17. e.printStackTrace();
  18. } catch (RemoteException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;public class ClientTest {public static void main(String args[]) {try {// 在RMI服务注册表中查找名称为RemoteObj的对象,并调用其上的方法// 客户端通过命名服务Naming获得指向远程对象的远程引用RemoteInterface RmObj = (RemoteInterface) Naming.lookup("rmi://localhost:8881/RemoteObj");System.out.println(RmObj.doSomething());System.out.println("远程服务器计算结果为:" + RmObj.Calculate(1, 2));} catch (NotBoundException e) {e.printStackTrace();} catch (MalformedURLException e) {e.printStackTrace();} catch (RemoteException e) {e.printStackTrace();}}
}

输出结果:
[html] view plaincopyprint?
  1. OK ,You can do……
  2. 远程服务器计算结果为:92
OK ,You can do......
远程服务器计算结果为:92

例子代码下载:http://download.csdn.net/detail/guyuealian/9583633

如果你觉得该帖子帮到你,还望贵人多多支持,鄙人会再接再厉,继续努力的~

(转)Java RMI远程方法调用详解相关推荐

  1. Java RMI远程方法调用详解

    Java RMI远程方法调用详解     [尊重原创,转载请注明出处]http://blog.csdn.net/guyuealian/article/details/51992182 一.Java R ...

  2. Java RMI、JRMP详解

    JRMP Java远程方法协议(Java Remote Method Protocol,JRMP),适用于RMI过程中的协议,只有使用这个协议,方法调用双方才能正常进行数据交流. RMI 远程方法调用 ...

  3. Java单元测试之JUnit4详解

    2019独角兽企业重金招聘Python工程师标准>>> Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @B ...

  4. java -jar 和 -cp详解

    java -jar 和 -cp详解 命令行执行程序 假如我们有一个程序,把它打包成Test.jar,如何运行才能成功输出Hello World package com.test; public cla ...

  5. java访问修饰符详解——学java,零基础不怕,不只要理论,更要实践+项目,a href=http://www.bjweixin.com太原维信科技提供 /a...

    java访问修饰符详解--学java,零基础不怕,不只要理论,更要实践+项目 <a href=http://www.bjweixin.com>太原维信科技提供 </a> pub ...

  6. Java编程配置思路详解

    Java编程配置思路详解 SpringBoot虽然提供了很多优秀的starter帮助我们快速开发,可实际生产环境的特殊性,我们依然需要对默认整合配置做自定义操作,提高程序的可控性,虽然你配的不一定比官 ...

  7. Java 8 Stream API详解--转

    原文地址:http://blog.csdn.net/chszs/article/details/47038607 Java 8 Stream API详解 一.Stream API介绍 Java 8引入 ...

  8. 你真的弄明白了吗?Java并发之AQS详解

    你真的弄明白了吗?Java并发之AQS详解 带着问题阅读 1.什么是AQS,它有什么作用,核心思想是什么 2.AQS中的独占锁和共享锁原理是什么,AQS提供的锁机制是公平锁还是非公平锁 3.AQS在J ...

  9. java定时任务框架elasticjob详解

    这篇文章主要介绍了java定时任务框架elasticjob详解,Elastic-Job是ddframe中dd-job的作业模块中分离出来的分布式弹性作业框架.该项目基于成熟的开源产品Quartz和Zo ...

最新文章

  1. 知识蒸馏在推荐系统的应用
  2. Python文件与目录操作
  3. telnet不能用,提示:-bash: telnet: command not found
  4. sunos 查cpu主频指令prtdiag
  5. 类string的构造函数、拷贝构造函数和析构函数
  6. 数据库中字段随机添加汉字
  7. 幻想乡三连A:五颜六色的幻想乡
  8. 企业管理器开启和关闭数据库时出现Error Messsage
  9. cct2级考试c语言试题,CCT2样题
  10. php dp0,DOS批处理中%cd%与%~dp0的区别详解
  11. NVIDIA cuDNN 下载
  12. 求助动态贝叶斯网络参数学习函数的使用方法
  13. 卡方分布、t分布、F分布
  14. 汇总我关注的技术博主的2021年度总结
  15. qchart 怎么点击一下 出一条线_动漫日系雨伞怎么画?教你用集中线尺画一把日本雨伞!...
  16. 使用cairo和freetype进行字体布局和渲染
  17. 背后的力量 | 升级电子病历基础架构 华云数据助力华中科技大学同济医学院附属协和医院打造就医新模式
  18. python脚本自动消除安卓版_微信跳一跳脚本重出江湖,python实现安卓iOS自动版与手动版!...
  19. 基于arduino超声波测距学习
  20. 辨别 利用AAC转成无损格式音乐 的假无损

热门文章

  1. 计算机音乐谱牵丝戏,天谕手游牵丝戏乐谱代码分享
  2. Java类的热加载原理与实现
  3. 再见!杭州!再见!阿里巴巴!
  4. 2021年1月6日订阅朋友的问题与解决方案汇总
  5. 博文推荐|深度解析如何在 Pulsar 中实现隔离
  6. 关于隐藏id传值问题(笔记)
  7. 第十一届蓝桥杯——解码
  8. java hashtable import_Java Hashtable 接口 - Java 教程 - 自强学堂
  9. 三星电子股东大会今日召开 股东或借机要求把李在镕干掉?
  10. 深圳10年,从月入5千到年薪百万,从家庭主妇到卖保险,这些年我经历了什么?