Plasma GitLab Archive
Projects Blog Knowledge

Module Netplex_internal

module Netplex_internal: sig .. end
Internal services


Internal services are available to multi-threaded programs only, and implemented via Netsys_polysocket.

See below for an introduction.

val register_server : string -> Netplex_types.polyserver_box -> unit
register_server name server: registers a server under the given name. It is an error if a server is already registered for the same name.

The registration is thread-safe.

val connect_client : 'a Netplex_types.kind_check ->
int -> string -> 'a Netsys_polysocket.polyclient
connect_client is_kind n name: Creates a new polyclient that is connected to the registered service name. The arg is_kind determines the type of the messages that can be exchanged. The number n is the size of the pipe buffer, in number of messages.

The function fails if the service is not registered, or assumes the wrong message kind.

The client lookup is thread-safe.

Example how to invoke a service exchanging strings (kind Tstring):

let same : type s t . s polysocket_kind * t polysocket_kind -> (s,t) eq =
  function
  | Tstring, Tstring -> Equal
  | _ -> Not_equal

let client =
  Netplex_internal.connect_client
    { Netplex_types.kind_check = fun k -> same(Tstring,k) }
    n
    "name"
      

(You should strictly stick to this pattern. Any abbreviation will probably not type-check.)


How to configure and use internal services

Internal services are a fast mechanism to invoke containers from other containers. So far, internal services are only available for multi-threaded programs. The messages can have any type.

You add an internal service by configuring an address "internal" in the protocol section of the configuration file, e.g.

protocol {
  name = "my-protocol";
  address {
    type = "internal";
    name = "my-identifier";
  }
  address {                    (* other addresses ok *)
    ...
  }
}

The internal service is only activated when multi-threading is selected. In programs using multi-processing the internal service is simply ignored.

You need to choose the types of your messages. There is the GADT Netplex_types.polysocket_kind listing possible types. It comes with the variants Txdr (for RPC messaging) and Tstring (for any custom strings):

type _ polysocket_kind = ..
type _ polysocket_kind +=
   | Txdr : Netxdr.xdr_value polysocket_kind
   | Tstring : string polysocket_kind

This is an extensible GADT, i.e. you can add more variants with the "+=" notation. (Note that extensible variants are first available since OCaml-4.02. With older OCaml versions, you cannot extend more variants.)

Let's add integers:

type _ polysocket_kind +=
  | Tint : int polysocket_kind

The connections to the internal services do not arrive via the normal process mechanism. There is a second path using the new processor hooks config_internal and process_internal. The hook config_internal defines which message type you are really using. The hook process_internal is invoked when a new connection to the internal service is established. It works very much like process, only that it doesn't use file descriptors but so-called polysockets (see Netsys_polysocket).

class hello_world_processor hooks : processor =
object(self)
  inherit Netplex_kit.processor_base hooks

  method config_internal =
     [ "my-protocol", Polysocket_kind_box Tint ]

  method process_internal ~when_done container srvbox proto_name =
    let Polyserver_box(kind, srv) = srvbox in
    match kind with
      | Tint ->
         let endpoint = Netsys_polysocket.accept ~nonblock:false srv in
         (* Now send and receive messages over endpoint *)
         ...;
         when_done()
      | _ ->
         failwith "wrong kind"

  method process ~when_done container fd proto_name =
    (* this is still invoked when non-internal connections arrive *)
    ...
  method supported_ptypes = 
    ...
end

The endpoint is actually a pair of polypipes (Netsys_polypipe):

let (rd, wr) = endpoint

Over rd you receive messages of type int from the client, and via wr you can send messages to it.

Use Netplex_internal.connect_client to get a client (usually in a different container, or in an arbitrary other thread):

let same : type s t . s polysocket_kind * t polysocket_kind -> (s,t) eq =
  function
  | Tint, Tint -> Equal
  | _ -> Not_equal

let client =
  Netplex_internal.connect_client
    { Netplex_types.kind_check = fun k -> same(Tint,k) }
    5
    "my-identifier" in
let client_endpoint =
   Netsys_polysocket.endpoint ~synchronous:true ~nonblock:false client

Again, the endpoint is a pair of polypipes in reality:

let (client_rd, client_wr) = client_endpoint

Complete example

You find a complete example in the distribution tarball at code/examples/netplex/internal_service.ml.

Using internal services via RPC

RPC clients and servers have now support for polysockets. Note that you need to select Txdr as message kind. The messages are not serialized into strings, but instead the structured XDR format is used as transport encoding.

XXX TODO

This web site is published by Informatikbüro Gerd Stolpmann
Powered by Caml