{"id":15706886,"url":"https://github.com/threez/marilyn-rpc","last_synced_at":"2025-05-12T15:43:17.434Z","repository":{"id":56882776,"uuid":"1847216","full_name":"threez/marilyn-rpc","owner":"threez","description":"A simple, beautiful event-based (EventMachine) RPC service and client library","archived":false,"fork":false,"pushed_at":"2012-03-26T17:31:54.000Z","size":192,"stargazers_count":5,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-25T03:21:40.403Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/marilyn-rpc","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/threez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-06-04T16:13:11.000Z","updated_at":"2019-08-13T14:46:13.000Z","dependencies_parsed_at":"2022-08-20T13:10:48.577Z","dependency_job_id":null,"html_url":"https://github.com/threez/marilyn-rpc","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threez%2Fmarilyn-rpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threez%2Fmarilyn-rpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threez%2Fmarilyn-rpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threez%2Fmarilyn-rpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/threez","download_url":"https://codeload.github.com/threez/marilyn-rpc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248813803,"owners_count":21165634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-03T20:30:21.273Z","updated_at":"2025-04-20T14:30:41.016Z","avatar_url":"https://github.com/threez.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"![alt text](https://raw.github.com/threez/marilyn-rpc/master/kiss.png \"MarilynRPC\")\n\n# MarilynRPC ![build status](https://secure.travis-ci.org/threez/marilyn-rpc.png)\n\nMarilyn is a simple, elegant rpc service and client infrastructure that has\nlearned some lessons on how we organize our code in typical web projects like\nrails. It's purpose is to call multiple services over a persistent connection.\nThe services are unique per connection, so if you have 50 connections, 50 \nservice objects will be used, if (and only if) they are requested by the client.\n\nSince this is a session dedicated to one connection, marilyn has support for per \nconnection caching by simply using instance variables.\n\nLike in IMAP marilyn supports sending of multiple requests to a server over one\nconnection. This feature is called multiplexing supported by the current\n`NativeClient` implementation.\n\nThe services rely on the eventmachine reactor. Marilyn supports asynchronous\nresponses based on the so called `Gentleman`. This class is a proxy object that\nwill handle the async responses for you.\n\nDue to it's internals marilyn is very fast. On my local machine i can achieve\nabout 5000 (req + resp)/s.\n\nSerialization of all data is done using ruby's build in `Marshal#dump` and `#load`. Since it was the fastest solution i found for typical scenarios.\n\nIt is especially designed for local connections too and therefore build to run\non both tcp and unix domain socket connections. Due to it's implementation in\noperation systems is a unix domain socket typically faster than a tcp localhost\nconnection. I my tests up to 30 percent faster.\n\n## Install\n\nEasy and common using gems:\n\n    gem install marilynrpc\n\n## Server Example\n\nThis is a sample server that exposes 2 Services that can be easily exposed using\nthe eventmachine `start_server` function:\n\n    require \"marilyn-rpc\"\n    require \"eventmachine\"\n\n    class CalcService \u003c MarilynRPC::Service\n      register :calc\n\n      def add(a, b)\n        a + b\n      end\n    end\n    \n    class TimeService \u003c MarilynRPC::Service\n      register :time\n      \n      def current\n        Time.now\n      end\n    end\n\n    EM.run {\n      EM.start_server \"localhost\", 8483, MarilynRPC::Server\n    }\n\n## NativeClient Example (pure ruby)\n\nThe native client is a pure ruby implementation and dosn't require eventmachine\nat all. Therefor it is very easy to use. However the downside is, that the\nclient is blocking for the call. But, since marilyn calls last only for\nfractions of a millisecond (on a local connection) there should be no problem in\ntypical setups.\n\n    require \"marilyn-rpc\"\n    \n    client = MarilynRPC::NativeClient.connect_tcp('localhost', 8483)\n    CalcService = client.for(:calc)\n    TimeService = client.for(:time)\n\n    p CalcService.add(1, 2)\n    p TimeService.current\n\n    client.disconnect\n    \n## Service Events\n\nBecause a client has a dedicated service it is possible to add connect and\ndisconnect callbacks. These callbacks may help your application to\nrequest/cache/optimize certain aspects of your service. Here is an example:\n\n    class EventsService \u003c MarilynRPC::Service\n      register :events\n      after_connect :connected\n      after_disconnect :disconnected\n  \n      def connected\n        puts \"client connected\"\n      end\n  \n      def notify(msg)\n        puts msg\n      end\n  \n      def disconnected\n        puts \"client disconnected\"\n      end\n    end\n\n## Security\n\nIf you are using a tcp connection you can secure the connection using tls/ssl.\nTo enable it on the server side one has to pass the secure flag:\n\n    EM.run {\n      EM.start_server(\"localhost\", 8008, MarilynRPC::Server, :secure =\u003e true)\n    }\n\nThe client also simply has to enable a secure connection:\n\n    client = MarilynRPC::NativeClient.connect_tcp('localhost', 8008, :secure =\u003e true)\n\n## Authentication\n\nIf some remote service methods only should be called using a username/password\nprotection, one simply has to define which method and what mechanism:\n\n    MarilynRPC::Service.authenticate_with do |username, password|\n      username == \"testuserid\" \u0026\u0026 password == \"secret\"\n    end\n\n    class TestService \u003c MarilynRPC::Service\n      register :test\n      authentication_required :add\n  \n      def time # unauthenticated\n        puts session_username\n        puts session_authenticated?\n        Time.now\n      end\n  \n      def add(a, b) # authenticated\n        puts session_username\n        puts session_authenticated?\n        a + b\n      end\n    end\n    \nThe client simple has to do the authentication before start marking calls:\n\n    client = MarilynRPC::NativeClient.connect_tcp('localhost', 8000)\n    TestService = client.for(:test)\n\n    p TestService.time.to_f\n    client.authenticate \"testuserid\", \"secret\"\n    p TestService.add(1, 2)\n\n    client.disconnect\n\n\n## Async Server Example \u0026 NativeClient\n\nAs previously said, the server can use the `Gentleman` to issue asynchronous\nresponses:\n\n    class SimpleCommandService \u003c MarilynRPC::Service\n      register :cmd\n\n      def exec(line)\n        MarilynRPC::Gentleman.proxy do |helper|\n          EM.system(line, \u0026helper)\n\n          lambda do |output,status|\n            if (code = status.exitstatus) == 0\n              output \n            else\n              code\n            end\n          end\n        end\n      end\n    end\n\nThe asynchronous server is transparent to the client. The client, doen't even\nknow, that his request is processed asynchronously. If the client make use of\nthe multiplexing feature he can use multiple threads to do so:\n\n    client = MarilynRPC::NativeClient.connect_tcp('localhost', 8000)\n    SimpleCommandService = client.for(:cmd)\n\n    start_time = Time.now\n\n    Thread.new do\n      SimpleCommandService.exec(\"sleep 5\")\n      puts \"=== ls -al\\n: \" + SimpleCommandService.exec(\"ls -al\")\n    end\n\n    Thread.new do\n      SimpleCommandService.exec(\"sleep 2\")\n      puts \"=== uname -a\\n: \" + SimpleCommandService.exec(\"uname -a\")\n    end\n\n## License / Author\n\nCopyright (c) 2011 Vincent Landgraf\nAll Rights Reserved. Released under a [MIT License](LICENCE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthreez%2Fmarilyn-rpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthreez%2Fmarilyn-rpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthreez%2Fmarilyn-rpc/lists"}