Skip to content

Commit

Permalink
Merge branch 'master' into feature/87-refactor-use-services
Browse files Browse the repository at this point in the history
  • Loading branch information
pnoltes committed Mar 24, 2024
2 parents 9776daf + d46d983 commit 1c4b4de
Show file tree
Hide file tree
Showing 27 changed files with 900 additions and 332 deletions.
83 changes: 53 additions & 30 deletions bundles/remote_services/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,31 @@ The Remote Service Admin Service subproject contains an adapted implementation o

The topology manager decides which services should be imported and exported according to a defined policy. Currently, only one policy is implemented in Celix, the *promiscuous* policy, which simply imports and exports all services.

| **Bundle** | `topology_manager.zip` |
|--|--|
| **Configuration** | *None* |
| **Bundle** | `Celix::rsa_topology_manager` |
|--|-----------------------------------------|
| **Configuration** | *None* |

### Remote Service Admin

The Remote Service Admin (RSA) provides the mechanisms to import and export services when instructed to do so by the Topology Manager.

#### Endpoints and proxies
The Remote Service Admin (RSA) provides the mechanisms to import and export services when instructed to do so by the Topology Manager.

To delegate a *received* method call to the actual service implementation, the RSA uses an "endpoint" bundle, which has all the knowledge about the marshalling and unmarshalling of data for the service. This endpoint bundle is specific to the used RSA implementation, and as such cannot be reused between various RSA implementations.
#### Remote Service Admin DFI

Invoking a *remote* method is done by using "proxy" bundles. Similar as to endpoints, proxy bundles encapsulate all knowledge to marshall and unmarshall data for a remote method call and as such can not be shared between RSA implementations.
Provides remote service admin using HTTP and JSON. The serialization is done using [libdfi](../../libs/dfi/README.md) to convert function call information into [JSON representation](https://amdatu.atlassian.net/wiki/spaces/AMDATUDEV/pages/21954571/Amdatu+Remote#AmdatuRemote-AdminHTTP%2FJson).
`libffi` is configured using descriptor files in the bundles.

Both proxy and endpoint bundles are loaded on demand when a service is imported or exported by the RSA. As such, these bundles **must** not be added to the list of "auto started" bundles, but placed in a separate location. By default, `endpoints` is used as location for locating proxy and/or endpoint bundles.
| **Bundle** | `Celix::rsa_dfi` |
|--|--------------------------------------------------------------------|
| **Configuration** | See [Remote Service Admin DFI](remote_service_admin_dfi/README.md) |

Note that since endpoints and proxies need to be created manually, one has full control about the handling of specifics of marshalling and unmarshalling data and dealing with exceptions.
#### Remote Service Admin SHM

#### HTTP/JSON
Provides remote service admin using shared memory. The serialization implementation is pluggable, and the default serialization is done using [libdfi](../../libs/dfi/README.md) to convert function call information into [JSON representation](https://amdatu.atlassian.net/wiki/spaces/AMDATUDEV/pages/21954571/Amdatu+Remote#AmdatuRemote-AdminHTTP%2FJson).
`libffi` is configured using descriptor files in the bundles.

Provides a RSA implementation that uses JSON to marshal requests and HTTP as transport mechanism for its remote method invocation. It is compatible with the *Remote Service Admin HTTP* implementation provided by [Amdatu Remote](https://amdatu.atlassian.net/wiki/display/AMDATUDEV/Amdatu+Remote).

| **Bundle** | `remote_service_admin_http.zip` |
|--|--|
| **Configuration** | `RSA_PORT`: defines the port on which the HTTP server should listen for incoming requests. Defaults to port `8888`; |
| | `ENDPOINTS`: defines the location in which service endpoints and/or proxies can be found. Defaults to `endpoints` in the current working directory |
| **Bundle** | `Celix::rsa_shm` |
|-------------------|------------------------------------------------------------------------|
| **Configuration** | See [Remote Service Admin SHM](remote_service_admin_shm_v2/README.md) |

### Discovery

Expand All @@ -62,7 +61,7 @@ Actively discovers the presence of remote exported services and provides informa

Provides a service discovery with preconfigured discovery endpoints, allowing a static mesh of nodes for remote service invocation to be created. The configured discovery bundle in Celix is compatible with the configured discovery implementation provided by [Amdatu Remote](https://amdatu.atlassian.net/wiki/display/AMDATUDEV/Amdatu+Remote).

| **Bundle** | `discovery_configured.zip` |
| **Bundle** | `Celix::rsa_discovery` |
|--|--|
| **Configuration** | `DISCOVERY_CFG_POLL_ENDPOINTS`: defines a comma-separated list of discovery endpoints that should be used to query for remote services. Defaults to `http://localhost:9999/org.apache.celix.discovery.configured`; |
| | `DISCOVERY_CFG_POLL_INTERVAL`: defines the interval (in seconds) in which the discovery endpoints should be polled. Defaults to `10` seconds. |
Expand All @@ -72,15 +71,38 @@ Provides a service discovery with preconfigured discovery endpoints, allowing a

Note that for configured discovery, the "Endpoint Description Extender" XML format defined in the OSGi Remote Service Admin specification (section 122.8 of OSGi Enterprise 5.0.0) is used.

See [etcd discovery](discovery_etcd/README.md)

#### etcd discovery

| **Bundle** | `discovery_etcd.zip` |

Provides a service discovery using etcd distributed key/value store.

See [etcd discovery](discovery_etcd/README.md)
| **Bundle** | `Celix::rsa_discovery_etcd` |
|------------|----------------------|
| **Configuration** | See [etcd discovery](discovery_etcd/README.md)|

#### Zero configuration discovery

Provides a service discovery using Bonjour.

| **Bundle** | `Celix::rsa_discovery_zeroconf` |
|--|----------------------------|
| **Configuration** | See [Zeroconf Discovery](discovery_zeroconf/README.md) |

## Dynamic IP Mechanism For Remote Service Admin

In order to make remote services work without configuring the IP of the RSA, we have designed the following dynamic IP mechanism.

- The remote service admin service adds the property "celix.rsa.dynamic.ip.support". If RSA sets this property to true, the RSA will support dynamic IP address.
- If the RSA supports dynamic IP addresses, it should bind the network service address to any address(0.0.0.0/::), and set the property "celix.rsa.port" (which indicates the network port number of the remote service) for the exported remote service endpoint.
- The endpoint listener service of discovery adds the property "celix.rsa.discovery.interface.specific.endpoints.support". If this property is set to true, it means that the discovery support dynamic IP address filling.
- Add the configuration property "CELIX_RSA_INTERFACES_OF_PORT_<port>", which indicates which network interfaces is used to expose the specified port service.
- When the topology manager exports remote services, it should detect whether the "celix.rsa.dynamic.ip.support" property of the remote service admin service is true. If so, the topology manager should create multiple endpoints that support dynamic IP address for a single export registration base on CELIX_RSA_INTERFACES_OF_PORT_<port>. These endpoints are then forwarded to the discovery endpoint listener services that support dynamic IP address filling.
- The endpoint that supports dynamic IP address adds the property "celix.rsa.ifname", which indicates which network interface is used for exported endpoint exposure. This property is set by the topology manager based on CELIX_RSA_INTERFACES_OF_PORT_<port>.
- The endpoint that supports dynamic IP address adds the property "celix.rsa.ip.addresses", which indicates the list of IP addresses corresponding to the endpoint. When the topology manager creates the endpoint description that supports dynamic IP address, the value of this property should be set to null, and the discovery that support dynamic IP address filling will replace the value(Discovery will decide whether to fill in the dynamic IP addresses based on whether the "celix.rsa.ip.addresses" key exists or not).

The sequence diagram of the dynamic IP mechanism is as follows:
![dynamic_ip_filling](diagrams/dynamic_ip_filling_seq.png)

The example of dynamic IP mechanism see `remote-services-zeroconf-server` and `remote-services-zeroconf-client`.

## Usage

Expand Down Expand Up @@ -129,26 +151,27 @@ Note that the `RSA_PORT` property needs to be unique for at least the client in

## Building

To build the Remote Service Admin Service the CMake build option "`BUILD_REMOTE_SERVICE_ADMIN`" has to be enabled.
To build the Remote Service Admin Service the CMake build option "`BUILD_REMOTE_SERVICE_ADMIN`" has to be enabled.If you use conan to build it, you should set the conan option `celix:build_remote_service_admin` to true.

## Dependencies

The Remote Service Admin Service depends on the following subprojects:

- Framework
- Utils
- dfi
- log_helper

Also the following libraries are required for building and/or using the Remote Service Admin Service subproject:

- Jansson (build and runtime)
- cURL (build and runtime)

## RSA Bundles

* [Remote Service Admin DFI](remote_service_admin_dfi) - A Dynamic Function Interface (DFI) implementation of the RSA.
* [Topology Manager](topology_manager) - A (scoped) RSA Topology Manager implementation.
* [Remote Service Admin DFI](remote_service_admin_dfi/README.md) - A Dynamic Function Interface (DFI) implementation of the RSA.
* [Remote Service Admin SHM](remote_service_admin_shm_v2/README.md) - A Shared Memory (SHM) implementation of the RSA.
* [Remote Service Admin RPC Using JSON](rsa_rpc_json/README.md) - A Remote Procedure Call (RPC) implementation of the RSA using JSON.
* [Topology Manager](topology_manager/README.md) - A (scoped) RSA Topology Manager implementation.
* [Discovery Configured](discovery_configured) - A RSA Discovery implementation using static configuration (xml).
* [Discovery Etcd](discovery_etcd/README.md) - A RSA Discovery implementation using etcd.
* [Discovery Zeroconf](discovery_zeroconf/README.md) - A RSA Discovery implementation using Bonjour.


## Notes
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions bundles/remote_services/diagrams/dynamic_ip_filling_seq.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@startuml
'https://plantuml.com/sequence-diagram

Box "Framework A"
participant "RSA" as RSA_A
participant "TopologyManager" as TM_A
participant "Discovery" as Discovery_A
end Box

Box "Framework B"
participant "Discovery" as Discovery_B
participant "TopologyManager" as TM_B
participant "RSA" as RSA_B
end Box

TM_A -> RSA_A: exportService()
note over RSA_A: Exported service endpoint description e.g.:\n{\ncelix.rsa.port: 80"(set by RSA),\n...\n}"
TM_A -> TM_A: Create endpoint descriptions\n that support dynamic ip
note over TM_A: Dynamic ip endpoint description e.g.:\n{\ncelix.rsa.ifname: "eth0"(set by TopologyManager),\ncelix.rsa.port: 80",\ncelix.rsa.ip.addresses:""(set by TopologyManager),\n...\n}
TM_A -> Discovery_A: endpointAdded()

Discovery_A -> Discovery_B: Announce endpoint \nto other frameworks

Discovery_B -> Discovery_B: Watch remote endpoints \nand fill in dynamic ip
Discovery_B -> TM_B: endpointAdded()
note over TM_B: Dynamic ip endpoint description e.g.:\n{\ncelix.rsa.port: 80",\ncelix.rsa.ip.addresses:"192.168...1.1, 192.168...1.2"(replaced by Discovery),\n...\n}"
TM_B -> RSA_B: importService()
@enduml
78 changes: 78 additions & 0 deletions bundles/remote_services/discovery_zeroconf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
title: Discovery Zeroconf
---

<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

## Discovery Zeroconf

The `Discovery_zeroconf` is implemented based on [Bonjour](https://github.com/apple-oss-distributions/mDNSResponder), and its operation depends on the mDNS daemon.

The mapping between celix and mdns services is as follows:

| **mDNS service** | **celix service** |
|------------------|-------------------------------|
| instance name | service name+pid(process id) |
| service type | "${last word of service configuration type}._sub._celix-rpc._udp"|
| domain name | "local" |
| txt record | service properties |
| host | hostname of OS(It can be got by gethostname function).|
| port | The property value of "celix.rsa.port". It is set by RSA. If it is not network server, Discovery will set a dummy value(65535) to mDNS daemon.|


The domain name value is set to "local" , because for remote discovery the mDNS query will only use link-local multicast.

For readability and debuggability of instance names, we set the instance name of the mDNS service as `service_name + pid(process id)`. If there is a conflict in the instance name of same process, we will add a conflict number to the end of the instance name, because the mDNS daemon thinks that services with the same instance name on the same host are the same service. If there is a conflict in the instance name of different host, mDNS daemon will resolve it.

### Supported Platform
- Linux

### Conan Option
build_rsa_discovery_zeroconf=True Default is False

### CMake Option
RSA_DISCOVERY_ZEROCONF=ON Default is OFF

### Software Design

#### The Remote Service Endpoint Announce Process

In the process of publishing remote service endpoints, `discovery_zeroconf` provides an `endpoint_listener_t` service, and sets the service property `DISCOVERY=true`. `topology_manager` updates service endpoint description to `discovery_zeroconf` by calling this service. At the same time, `discovery_zeroconf` establishes a domain socket connection with `mDNS_daemon`, and `discovery_zeroconf` publishes the service information to mDNS_daemon through this connection. Then, `mDNS_daemon` publishes the service information to other `mDNS_daemons`. The sequence diagram is as follows:

![remote_service_endpoint_announce_process](diagrams/service_announce_seq.png)

#### The Remote Service Endpoint Discovery Process

In the process of discovering remote service endpoints, discovery_zeroconf also establishes a domain socket connection with mDNS_daemon. discovery_zeroconf listens to remote endpoint information through this connection, and updates the listened remote endpoint information to topology_manager. The sequence diagram is as follows:

![remote_service_endpoint_discovery_process](diagrams/service_discovery_seq.png)

#### Lager txt record(service properties) process

According to [rfc6763](https://www.rfc-editor.org/rfc/rfc6763.txt) 6.1 and 6.2 section, DNS TXT record can be up to 65535 (0xFFFF) bytes long in mDNS message. and we should keep the size of the TXT record under 1300 bytes(allowing it to fit in a single 1500-byte Ethernet packet). Therefore, `Discovery_zeroconf` announce celix service endpoint using multiple txt records and each txt record max size is 1300 bytes. When the service with large properties,the `Discovery_zeroconf` will split the properties into multiple txt records, and mDNS daemon send them to the remote mDNS daemon. The mDNS message snapshot of wireshark is as follows:

![multiple_txt_record_snapshot.png](diagrams/multiple_txt_record_snapshot.png)

As in the above figure, the mDNS message contains multiple txt records, and each txt record message contains the same service instance name, but the service properties in the txt record are different. In addition, to avoid mDNS message packet loss, we set a specific service property `DZC_SVC_PROPS_SIZE_KEY`, which indicates the size of the service properties. When the number of txt records received by remote `Discovery_zeroconf` is equal to the number indicated by `DZC_SVC_PROPS_SIZE_KEY`, the remote `Discovery_zeroconf` will combine the txt records into a service properties.


### Example

See the cmake target `remote-services-zeroconf-server` and `remote-services-zeroconf-client`.

**Notes:** Before running the example, you should start the mDNS daemon first.You can get the mDNS daemon from bonjour project.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@startuml

box server process
participant Announcer
control AnnouncerThread
participant Fw
participant mDNS_daemon
end box
Announcer->Fw:register endpoint listener service
create AnnouncerThread
Announcer ->AnnouncerThread:
==setup==
--\Announcer: add endpoint\n (use endpoint listener service)
loop
alt mDNS server disconnect
AnnouncerThread->mDNS_daemon:DNSServiceCreateConnection
return DNSServiceRef
end alt

alt Have new endpoint
AnnouncerThread->AnnouncerThread:Converting endpoint properties to txt records(max 1300 bytes)
AnnouncerThread->mDNS_daemon:DNSServiceRegister(DNSServiceRef,instanceName, txtRecord ...)
return registerRef
loop Have more endpoint properties
AnnouncerThread->mDNS_daemon:DNSServiceAddRecord(registerRef, txtRecord ...)
end loop
end alt
end loop

@enduml
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1c4b4de

Please sign in to comment.