Remote RPC method calling through Redis
One of the things I find I often study and excited about is the expansion of the system. Now, this has different meanings for different people. As part of the migration of Monolithic applications to the Microservices architecture method, how to handle the Microservices architecture is the reason why I study RPC.
RPC or remote process call) is a concept that has existed in the computer science field for a long time. A very simple understanding of this is the ability to send a message to a remote process, whether on the same system or remote system. In general, this is very vague and open to many implementations. In my opinion, when talking about RPC, there will be a considerable amount of content for discussion, such as the message format and how you can send messages to remote processes. There are many methods to implement RPC, and this is one of the methods I have used. But for this article, I am going to use 'json-RPC 'to process the Message format, use Redis to publish messages.
RPC and Message Queue
The principle is basically the same, but when RPC is used, the client will wait for a response message containing the RPC call result. If your message queue system allows you to process callback messages for the sender, you may be able to use it for RPC. In most message queues, they are used to trigger tasks that no longer need to be returned to the client.
Why use Redis instead of others?
You should be able to find Redis is a very advanced technology in a landlord. If you say no, what's wrong with you? Redis is a great tool for many things. You should study it carefully. The learning path is smooth, and there is no need to learn too much new content. Redis perfectly fits these ideas, so let's see what we can do.
Code
Client
- require 'redis'
- require 'securerandom'
- require 'msgpack'
-
- class RedisRpcClient
-
- def initialize(redis_url, list_name)
- @client = Redis.connect(url: redis_url)
- @list_name = list_name.to_s
- end
-
- def method_missing(name, *args)
- request = {
- 'jsonrpc' => '2.0',
- 'method' => name,
- 'params' => args,
- 'id' => SecureRandom.uuid
- }
-
- @client.lpush(@list_name, request.to_msgpack)
- channel, response = @client.brpop(request['id'], timeout=30)
-
- MessagePack.unpack(response)['result']
- end
-
- end
-
- client = RedisRpcClient.new('redis://localhost:6379', :fib)
- (1..30).each { |i| puts client.fib(i) }
Server
- require 'redis'
- require 'msgpack'
-
-
- class Fibonacci
-
- def fib(n)
- case n
- when 0 then 0
- when 1 then 1
- else
- fib(n - 1) + fib(n - 2)
- end
- end
-
- end
-
-
- class RedisRpcServer
-
- def initialize(redis_url, list_name, klass)
- @client = Redis.connect(url: redis_url)
- @list_name = list_name.to_s
- @klass = klass
- end
-
- def start
- puts "Starting RPC server for #{@list_name}"
- while true
- channel, request = @client.brpop(@list_name)
- request = MessagePack.unpack(request)
-
- puts "Working on request: #{request['id']}"
-
- args = request['params'].unshift(request['method'])
- result = @klass.send *args
-
- reply = {
- 'jsonrpc' => '2.0',
- 'result' => result,
- 'id' => request['id']
- }
-
- @client.rpush(request['id'], MessagePack.pack(reply))
- @client.expire(request['id'], 30)
- end
-
- end
-
- end
-
- RedisRpcServer.new('redis://localhost:6379', :fib, Fibonacci.new).start
Indeed, it works because Redis has commands to block the wait when you wait for the data to be sent back from the server. This is an excellent practice. It makes your client code look like calling a local method.
Ruby is cool,...
What if you want to use other languages? No problem. As long as your language has a good Redis database, you can do the same thing. Let's take a look at using Python to build a server program.
- import redis
- import msgpack
-
- class Fibonacci:
-
- def fib(self,n):
- if n == 0:
- return 0
- elif n == 1:
- return 1
- else:
- return self.fib(n-1) + self.fib(n-2)
-
-
- class RedisRpcServer:
-
- def __init__(self, redis_url, list_name, klass):
- self.client = redis.from_url(redis_url)
- self.list_name = list_name
- self.klass = klass
-
- def start(self):
- print("Starting RPC server for " + self.list_name)
- while True:
- channel, request = self.client.brpop('fib')
- request = msgpack.unpackb(request, encoding='utf-8')
-
- print("Working on request: " + request['id'])
-
- result = getattr(self.klass, request['method'])(*request['params'])
-
- reply = {
- 'jsonrpc': '2.0',
- 'result': result,
- 'id': request['id']
- }
-
- self.client.rpush(request['id'], msgpack.packb(reply, use_bin_type=True))
- self.client.expire(request['id'], 30)
-
-
- RedisRpcServer('redis://localhost:6379', 'fib', Fibonacci()).start()
Conclusion
This proves some ideas in your mind. Of course, more work is needed to handle exceptions. If you encounter any problems using this method, I will be happy to help you. I do want to use RabbitMQ in the same way, but if you have already used Redis in your project, this will be a very good method.
RPC using Redis
Http://www.oschina.net/translate/rpc-using-redis.