ray/doc/source/ray-core/cross-language.rst
Qing Wang af418fb729
[Java][API CHANGE] Move exception to api module. (#24540)
This PR moves all exception classes from runtime module to api module. It's aiming to eliminate the confusion about ray exceptions. It means that Ray users don't need to touch runtime module when API programming after this PR.

Note that this should be merged onto 2.0.
2022-05-19 10:18:20 +08:00

340 lines
8.4 KiB
ReStructuredText

.. _cross_language:
Cross-Language Programming
==========================
This page will show you how to use Ray's cross-language programming feature.
Setup the driver
-----------------
We need to set :ref:`code_search_path` in your driver.
.. tabbed:: Python
.. code-block:: python
ray.init(job_config=ray.job_config.JobConfig(code_search_path="/path/to/code"))
.. tabbed:: Java
.. code-block:: bash
java -classpath <classpath> \
-Dray.address=<address> \
-Dray.job.code-search-path=/path/to/code/ \
<classname> <args>
You may want to include multiple directories to load both Python and Java code for workers, if they are placed in different directories.
.. tabbed:: Python
.. code-block:: python
ray.init(job_config=ray.job_config.JobConfig(code_search_path="/path/to/jars:/path/to/pys"))
.. tabbed:: Java
.. code-block:: bash
java -classpath <classpath> \
-Dray.address=<address> \
-Dray.job.code-search-path=/path/to/jars:/path/to/pys \
<classname> <args>
Python calling Java
-------------------
Suppose we have a Java static method and a Java class as follows:
.. code-block:: java
package io.ray.demo;
public class Math {
public static int add(int a, int b) {
return a + b;
}
}
.. code-block:: java
package io.ray.demo;
// A regular Java class.
public class Counter {
private int value = 0;
public int increment() {
this.value += 1;
return this.value;
}
}
Then, in Python, we can call the above Java remote function, or create an actor
from the above Java class.
.. code-block:: python
import ray
ray.init(address="auto")
# Define a Java class.
counter_class = ray.cross_language.java_actor_class(
"io.ray.demo.Counter")
# Create a Java actor and call actor method.
counter = counter_class.remote()
obj_ref1 = counter.increment.remote()
assert ray.get(obj_ref1) == 1
obj_ref2 = counter.increment.remote()
assert ray.get(obj_ref2) == 2
# Define a Java function.
add_function = ray.cross_language.java_function(
"io.ray.demo.Math", "add")
# Call the Java remote function.
obj_ref3 = add_function.remote(1, 2)
assert ray.get(obj_ref3) == 3
ray.shutdown()
Java calling Python
-------------------
Suppose we have a Python module as follows:
.. code-block:: python
# ray_demo.py
import ray
@ray.remote
class Counter(object):
def __init__(self):
self.value = 0
def increment(self):
self.value += 1
return self.value
@ray.remote
def add(a, b):
return a + b
.. note::
* The function or class should be decorated by `@ray.remote`.
Then, in Java, we can call the above Python remote function, or create an actor
from the above Python class.
.. code-block:: java
package io.ray.demo;
import io.ray.api.ObjectRef;
import io.ray.api.PyActorHandle;
import io.ray.api.Ray;
import io.ray.api.function.PyActorClass;
import io.ray.api.function.PyActorMethod;
import io.ray.api.function.PyFunction;
import org.testng.Assert;
public class JavaCallPythonDemo {
public static void main(String[] args) {
Ray.init();
// Define a Python class.
PyActorClass actorClass = PyActorClass.of(
"ray_demo", "Counter");
// Create a Python actor and call actor method.
PyActorHandle actor = Ray.actor(actorClass).remote();
ObjectRef objRef1 = actor.task(
PyActorMethod.of("increment", int.class)).remote();
Assert.assertEquals(objRef1.get(), 1);
ObjectRef objRef2 = actor.task(
PyActorMethod.of("increment", int.class)).remote();
Assert.assertEquals(objRef2.get(), 2);
// Call the Python remote function.
ObjectRef objRef3 = Ray.task(PyFunction.of(
"ray_demo", "add", int.class), 1, 2).remote();
Assert.assertEquals(objRef3.get(), 3);
Ray.shutdown();
}
}
Cross-language data serialization
---------------------------------
The arguments and return values of ray call can be serialized & deserialized
automatically if their types are the following:
- Primitive data types
=========== ======= =======
MessagePack Python Java
=========== ======= =======
nil None null
bool bool Boolean
int int Short / Integer / Long / BigInteger
float float Float / Double
str str String
bin bytes byte[]
=========== ======= =======
- Basic container types
=========== ======= =======
MessagePack Python Java
=========== ======= =======
array list Array
=========== ======= =======
- Ray builtin types
- ActorHandle
.. note::
* Be aware of float / double precision between Python and Java. If Java use a
float type to receive the input argument, the double precision Python data
will be reduced to float precision in Java.
* BigInteger can support max value of 2^64-1, please refer to:
https://github.com/msgpack/msgpack/blob/master/spec.md#int-format-family.
If the value larger than 2^64-1, then transfer the BigInteger:
- From Java to Python: *raise an exception*
- From Java to Java: **OK**
The following example shows how to pass these types as parameters and how to
return return these types.
You can write a Python function which returns the input data:
.. code-block:: python
# ray_serialization.py
import ray
@ray.remote
def py_return_input(v):
return v
Then you can transfer the object from Java to Python, then returns from Python
to Java:
.. code-block:: java
package io.ray.demo;
import io.ray.api.ObjectRef;
import io.ray.api.Ray;
import io.ray.api.function.PyFunction;
import java.math.BigInteger;
import org.testng.Assert;
public class SerializationDemo {
public static void main(String[] args) {
Ray.init();
Object[] inputs = new Object[]{
true, // Boolean
Byte.MAX_VALUE, // Byte
Short.MAX_VALUE, // Short
Integer.MAX_VALUE, // Integer
Long.MAX_VALUE, // Long
BigInteger.valueOf(Long.MAX_VALUE), // BigInteger
"Hello World!", // String
1.234f, // Float
1.234, // Double
"example binary".getBytes()}; // byte[]
for (Object o : inputs) {
ObjectRef res = Ray.task(
PyFunction.of("ray_serialization", "py_return_input", o.getClass()),
o).remote();
Assert.assertEquals(res.get(), o);
}
Ray.shutdown();
}
}
Cross-language exception stacks
-------------------------------
Suppose we have a Java package as follows:
.. code-block:: java
package io.ray.demo;
import io.ray.api.ObjectRef;
import io.ray.api.Ray;
import io.ray.api.function.PyFunction;
public class MyRayClass {
public static int raiseExceptionFromPython() {
PyFunction<Integer> raiseException = PyFunction.of(
"ray_exception", "raise_exception", Integer.class);
ObjectRef<Integer> refObj = Ray.task(raiseException).remote();
return refObj.get();
}
}
and a Python module as follows:
.. code-block:: python
# ray_exception.py
import ray
@ray.remote
def raise_exception():
1 / 0
Then, run the following code:
.. code-block:: python
# ray_exception_demo.py
import ray
ray.init(address="auto")
obj_ref = ray.cross_language.java_function(
"io.ray.demo.MyRayClass",
"raiseExceptionFromPython").remote()
ray.get(obj_ref) # <-- raise exception from here.
ray.shutdown()
The exception stack will be:
.. code-block:: text
Traceback (most recent call last):
File "ray_exception_demo.py", line 10, in <module>
ray.get(obj_ref) # <-- raise exception from here.
File "ray/worker.py", line 1425, in get
raise value
ray.exceptions.CrossLanguageError: An exception raised from JAVA:
io.ray.api.exception.RayTaskException: (pid=92253, ip=10.15.239.68) Error executing task df5a1a828c9685d3ffffffff01000000
at io.ray.runtime.task.TaskExecutor.execute(TaskExecutor.java:167)
Caused by: io.ray.api.exception.CrossLanguageException: An exception raised from PYTHON:
ray.exceptions.RayTaskError: ray::raise_exception() (pid=92252, ip=10.15.239.68)
File "python/ray/_raylet.pyx", line 482, in ray._raylet.execute_task
File "ray_exception.py", line 7, in raise_exception
1 / 0
ZeroDivisionError: division by zero