Skip to content

Commit aff0858

Browse files
authored
[Feature] Dubbo-Python Optimization (#40)
* feat: New stream-related features * docs: update some samples * docs: update some samples * docs: update some samples
1 parent e5e9f7b commit aff0858

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1625
-629
lines changed

README.md

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,76 +29,91 @@ Visit [the official website](https://dubbo.apache.org/) for more information.
2929
- **Serialization**: Customizable(protobuf, json...)
3030

3131

32-
## Getting started
3332

34-
Before you begin, ensure that you have **`python 3.11+`**. Then, install Dubbo-Python in your project using the following steps:
33+
## Installation
34+
35+
Before you start, make sure you have **`python 3.11+`** installed.
3536

36-
```shell
37-
git clone https://github.com/apache/dubbo-python.git
38-
cd dubbo-python && pip install .
39-
```
37+
1. Install from source
38+
39+
```sh
40+
git clone https://github.com/apache/dubbo-python.git
41+
cd dubbo-python && pip install .
42+
```
43+
44+
45+
## Getting started
4046

41-
Get started with Dubbo-Python in just 5 minutes by following our [Quick Start Guide](https://github.com/apache/dubbo-python/tree/main/samples).
47+
Get up and running with Dubbo-Python in just 5 minutes by following our [Quick Start Guide](https://github.com/apache/dubbo-python/tree/main/samples).
4248

43-
It's as simple as the following code snippet. With just a few lines of code, you can launch a fully functional point-to-point RPC service :
49+
It's as simple as the code snippet below. With just a few lines of code, you can launch a fully functional point-to-point RPC service:
4450

45-
1. Build and start the Server
51+
1. Build and start the server
4652

4753
```python
4854
import dubbo
4955
from dubbo.configs import ServiceConfig
50-
from dubbo.proxy.handlers import RpcServiceHandler, RpcMethodHandler
56+
from dubbo.proxy.handlers import RpcMethodHandler, RpcServiceHandler
5157

5258

53-
def handle_unary(request):
54-
s = request.decode("utf-8")
55-
print(f"Received request: {s}")
56-
return (s + " world").encode("utf-8")
59+
class UnaryServiceServicer:
60+
def say_hello(self, message: bytes) -> bytes:
61+
print(f"Received message from client: {message}")
62+
return b"Hello from server"
5763

5864

59-
if __name__ == "__main__":
65+
def build_service_handler():
6066
# build a method handler
61-
method_handler = RpcMethodHandler.unary(handle_unary)
67+
method_handler = RpcMethodHandler.unary(
68+
method=UnaryServiceServicer().say_hello, method_name="unary"
69+
)
6270
# build a service handler
6371
service_handler = RpcServiceHandler(
6472
service_name="org.apache.dubbo.samples.HelloWorld",
65-
method_handlers={"unary": method_handler},
73+
method_handlers=[method_handler],
6674
)
75+
return service_handler
6776

68-
service_config = ServiceConfig(service_handler)
6977

78+
if __name__ == "__main__":
79+
# build service config
80+
service_handler = build_service_handler()
81+
service_config = ServiceConfig(
82+
service_handler=service_handler, host="127.0.0.1", port=50051
83+
)
7084
# start the server
7185
server = dubbo.Server(service_config).start()
7286

7387
input("Press Enter to stop the server...\n")
7488
```
7589

76-
2. Build and start the Client
90+
1. Build and start the Client
7791

7892
```python
7993
import dubbo
8094
from dubbo.configs import ReferenceConfig
8195

8296

8397
class UnaryServiceStub:
84-
8598
def __init__(self, client: dubbo.Client):
8699
self.unary = client.unary(method_name="unary")
87100

88-
def unary(self, request):
89-
return self.unary(request)
101+
def say_hello(self, message: bytes) -> bytes:
102+
return self.unary(message)
90103

91104

92105
if __name__ == "__main__":
106+
# Create a client
93107
reference_config = ReferenceConfig.from_url(
94108
"tri://127.0.0.1:50051/org.apache.dubbo.samples.HelloWorld"
95109
)
96110
dubbo_client = dubbo.Client(reference_config)
97-
98111
unary_service_stub = UnaryServiceStub(dubbo_client)
99112

100-
result = unary_service_stub.unary("hello".encode("utf-8"))
101-
print(result.decode("utf-8"))
113+
# Call the remote method
114+
result = unary_service_stub.say_hello(b"Hello from client")
115+
print(result)
116+
102117
```
103118

104119

dubbo/classes.py

Lines changed: 213 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,44 @@
1313
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
16-
16+
import abc
1717
import threading
18+
from typing import Callable, Optional, Tuple, Any, Union
19+
20+
from dubbo.types import DeserializingFunction, RpcType, RpcTypes, SerializingFunction
21+
22+
__all__ = [
23+
"EOF",
24+
"SingletonBase",
25+
"MethodDescriptor",
26+
"ReadStream",
27+
"WriteStream",
28+
"ReadWriteStream",
29+
]
30+
31+
32+
class _EOF:
33+
"""
34+
EOF is a class representing the end flag.
35+
"""
36+
37+
_repr_str = "<dubbo.classes.EOF>"
38+
39+
def __bool__(self):
40+
return False
41+
42+
def __len__(self):
43+
return 0
1844

19-
__all__ = ["SingletonBase"]
45+
def __repr__(self) -> str:
46+
return self._repr_str
47+
48+
def __str__(self) -> str:
49+
return self._repr_str
50+
51+
52+
# The EOF object -> global constant
53+
EOF = _EOF()
2054

2155

2256
class SingletonBase:
@@ -39,3 +73,180 @@ def __new__(cls, *args, **kwargs):
3973
if cls._instance is None:
4074
cls._instance = super(SingletonBase, cls).__new__(cls)
4175
return cls._instance
76+
77+
78+
class MethodDescriptor:
79+
"""
80+
MethodDescriptor is a descriptor for a method.
81+
It contains the method name, the method, and the method's serialization and deserialization methods.
82+
"""
83+
84+
__slots__ = [
85+
"_callable_method",
86+
"_method_name",
87+
"_rpc_type",
88+
"_arg_serialization",
89+
"_return_serialization",
90+
]
91+
92+
def __init__(
93+
self,
94+
method_name: str,
95+
arg_serialization: Tuple[
96+
Optional[SerializingFunction], Optional[DeserializingFunction]
97+
],
98+
return_serialization: Tuple[
99+
Optional[SerializingFunction], Optional[DeserializingFunction]
100+
],
101+
rpc_type: Union[RpcType, RpcTypes, str] = RpcTypes.UNARY.value,
102+
callable_method: Optional[Callable] = None,
103+
):
104+
"""
105+
Initialize the method model.
106+
107+
:param method_name:
108+
The name of the method.
109+
:type method_name: str
110+
111+
:param arg_serialization:
112+
A tuple containing serialization and deserialization methods for the function's arguments.
113+
:type arg_serialization: Optional[Tuple[SerializingFunction, DeserializingFunction]]
114+
115+
:param return_serialization:
116+
A tuple containing serialization and deserialization methods for the function's return values.
117+
:type return_serialization: Optional[Tuple[SerializingFunction, DeserializingFunction]]
118+
119+
:param rpc_type:
120+
The RPC type. default is RpcTypes.UNARY.
121+
:type rpc_type: RpcType
122+
123+
:param callable_method:
124+
The main callable method to be executed.
125+
:type callable_method: Optional[Callable]
126+
"""
127+
self._method_name = method_name
128+
self._arg_serialization = arg_serialization
129+
self._return_serialization = return_serialization
130+
self._callable_method = callable_method
131+
132+
if isinstance(rpc_type, str):
133+
rpc_type = RpcTypes.from_name(rpc_type)
134+
elif isinstance(rpc_type, RpcTypes):
135+
rpc_type = rpc_type.value
136+
elif not isinstance(rpc_type, RpcType):
137+
raise TypeError(
138+
f"rpc_type must be of type RpcType, RpcTypes, or str, not {type(rpc_type)}"
139+
)
140+
self._rpc_type = rpc_type
141+
142+
def get_method(self) -> Callable:
143+
"""
144+
Get the callable method.
145+
:return: The callable method.
146+
:rtype: Callable
147+
"""
148+
return self._callable_method
149+
150+
def get_method_name(self) -> str:
151+
"""
152+
Get the method name.
153+
:return: The method name.
154+
:rtype: str
155+
"""
156+
return self._method_name
157+
158+
def get_rpc_type(self) -> RpcType:
159+
"""
160+
Get the RPC type.
161+
:return: The RPC type.
162+
:rtype: RpcType
163+
"""
164+
return self._rpc_type
165+
166+
def get_arg_serializer(self) -> Optional[SerializingFunction]:
167+
"""
168+
Get the argument serializer.
169+
:return: The argument serializer. If not set, return None.
170+
:rtype: Optional[SerializingFunction]
171+
"""
172+
return self._arg_serialization[0] if self._arg_serialization else None
173+
174+
def get_arg_deserializer(self) -> Optional[DeserializingFunction]:
175+
"""
176+
Get the argument deserializer.
177+
:return: The argument deserializer. If not set, return None.
178+
:rtype: Optional[DeserializingFunction]
179+
"""
180+
return self._arg_serialization[1] if self._arg_serialization else None
181+
182+
def get_return_serializer(self) -> Optional[SerializingFunction]:
183+
"""
184+
Get the return value serializer.
185+
:return: The return value serializer. If not set, return None.
186+
:rtype: Optional[SerializingFunction]
187+
"""
188+
return self._return_serialization[0] if self._return_serialization else None
189+
190+
def get_return_deserializer(self) -> Optional[DeserializingFunction]:
191+
"""
192+
Get the return value deserializer.
193+
:return: The return value deserializer. If not set, return None.
194+
:rtype: Optional[DeserializingFunction]
195+
"""
196+
return self._return_serialization[1] if self._return_serialization else None
197+
198+
199+
class ReadStream(abc.ABC):
200+
"""
201+
ReadStream is an abstract class for reading streams.
202+
"""
203+
204+
@abc.abstractmethod
205+
def read(self, *args, **kwargs) -> Any:
206+
"""
207+
Read the stream.
208+
:param args: The arguments to pass to the read method.
209+
:param kwargs: The keyword arguments to pass to the read method.
210+
:return: The read value.
211+
"""
212+
raise NotImplementedError()
213+
214+
215+
class WriteStream(abc.ABC):
216+
"""
217+
WriteStream is an abstract class for writing streams.
218+
"""
219+
220+
@abc.abstractmethod
221+
def can_write_more(self) -> bool:
222+
"""
223+
Check if the stream can write more data.
224+
:return: True if the stream can write more data, False otherwise.
225+
:rtype: bool
226+
"""
227+
raise NotImplementedError()
228+
229+
@abc.abstractmethod
230+
def write(self, *args, **kwargs) -> None:
231+
"""
232+
Write to the stream.
233+
:param args: The arguments to pass to the write method.
234+
:param kwargs: The keyword arguments to pass to the write method.
235+
"""
236+
raise NotImplementedError()
237+
238+
@abc.abstractmethod
239+
def done_writing(self, **kwargs) -> None:
240+
"""
241+
Done writing to the stream.
242+
:param kwargs: The keyword arguments to pass to the done
243+
"""
244+
raise NotImplementedError()
245+
246+
247+
class ReadWriteStream(ReadStream, WriteStream, abc.ABC):
248+
"""
249+
ReadWriteStream is an abstract class for reading and writing streams.
250+
"""
251+
252+
pass

0 commit comments

Comments
 (0)