Skip to content

Commit 32c755f

Browse files
committed
sockets
1 parent d1b4ce2 commit 32c755f

25 files changed

Lines changed: 21970 additions & 25 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea/
22
*.pyc
3+
conf/
34
conf/*.cfg
45
venv

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2016 Rafa Hernandez <https://github.com/fikipollo>
3+
Copyright (c) 2019 Rafa Hernandez <https://github.com/fikipollo>
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

VERSION

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
v0.2 December 2018 PySiCa, a simple Python Cache system
1+
v0.3 January 2019 PySiCa, a simple Python Cache system
22

33
CHANGELOG
44

5+
- v0.3 January 2019
6+
· sd
7+
58
- v0.2 December 2018
69
· Migrated to Python 3
710
· Moved to from Flask to Falcon framework (improves performance)
File renamed without changes.

api/pysica_api_sockets.py

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
import socket
2+
import json
3+
from struct import unpack, pack
4+
5+
6+
class SimpleCache:
7+
8+
def __init__(self, socket_file=None, server="localhost", port=4444, buffer_size=4096):
9+
self.socket_file = socket_file
10+
self.server = server
11+
self.port = port
12+
self.buffer_size = buffer_size
13+
14+
def add(self, element_id, data, data_type="object", timeout=None, compress=None, user_id=None):
15+
return cache_add(element_id, data, data_type, timeout, compress, user_id, socket_file=self.socket_file, server=self.server, port=self.port, buffer_size=self.buffer_size)
16+
17+
def get(self, element_id=None, data_type=None, user_id=None, reset_timeout=False, timeout=None):
18+
return cache_get(element_id, data_type, user_id, reset_timeout, timeout, socket_file=self.socket_file, server=self.server, port=self.port, buffer_size=self.buffer_size)
19+
20+
def remove(self, element_id, user_id=None, return_elem=False):
21+
return cache_remove(element_id, user_id=user_id, return_elem=return_elem, socket_file=self.socket_file, server=self.server, port=self.port, buffer_size=self.buffer_size)
22+
23+
def reset(self, element_id=None, timeout=None, user_id=None):
24+
return cache_reset(element_id, timeout=timeout, user_id=user_id, socket_file=self.socket_file, server=self.server, port=self.port, buffer_size=self.buffer_size)
25+
26+
27+
def cache_add(element_id, data, data_type, timeout=None, compress=None, user_id=None, socket_file=None, server="localhost", port=4444, buffer_size=4096):
28+
try:
29+
cp = SocketHandler()
30+
cp.connect(socket_file, server, port)
31+
# Prepare package
32+
data = {
33+
'target': "add",
34+
'element_id': element_id,
35+
'data': data,
36+
'data_type': data_type,
37+
'timeout': timeout,
38+
'compress': compress,
39+
'user_id': user_id
40+
}
41+
# Send data
42+
response = cp.send_data(data, buffer_size=buffer_size)
43+
cp.close()
44+
return Response(json.loads(response))
45+
except:
46+
return Response({"success": False, "message": "Unable to connect to cache server."})
47+
48+
49+
def cache_get(element_id=None, data_type=None, user_id=None, reset_timeout=False, timeout=None, socket_file=None, server="localhost", port=4444, buffer_size=4096):
50+
try:
51+
cp = SocketHandler()
52+
cp.connect(socket_file, server, port)
53+
# Prepare package
54+
data = {
55+
'target': "get",
56+
'element_id': element_id,
57+
'data_type': data_type,
58+
'user_id': user_id,
59+
'reset_timeout': reset_timeout,
60+
'timeout': timeout
61+
}
62+
# Get data
63+
response = cp.get_data(data, buffer_size=buffer_size)
64+
cp.close()
65+
return Response(json.loads(response))
66+
except:
67+
return Response({"success": False, "message": "Unable to connect to cache server."})
68+
69+
70+
def cache_remove(element_id, user_id=None, return_elem=False, socket_file=None, server="localhost", port=4444, buffer_size=4096):
71+
try:
72+
# Get data
73+
if return_elem:
74+
element = cache_get(element_id=element_id, user_id=user_id, socket_file=socket_file, server=server, port=port, buffer_size=buffer_size)
75+
# Remove element
76+
cp = SocketHandler()
77+
cp.connect(socket_file, server, port)
78+
print("sadasds")
79+
# Prepare package
80+
data={
81+
'target': "remove",
82+
'element_id': element_id,
83+
'user_id': user_id
84+
}
85+
response = cp.send_data(data, buffer_size=buffer_size)
86+
response = json.loads(response)
87+
88+
if return_elem:
89+
response["result"] = element.result
90+
91+
cp.close()
92+
return Response(response)
93+
except:
94+
return Response({"success": False, "message": "Unable to connect to cache server."})
95+
96+
97+
def cache_reset(element_id, timeout=None, user_id=None, socket_file=None, server="localhost", port=4444, buffer_size=4096):
98+
try:
99+
cp = SocketHandler()
100+
cp.connect(socket_file, server, port)
101+
# Prepare package
102+
data={
103+
'target': "reset",
104+
'element_id': element_id,
105+
'timeout': timeout,
106+
'user_id': user_id
107+
}
108+
# Get data
109+
response = cp.send_data(data, buffer_size=buffer_size)
110+
cp.close()
111+
return Response(json.loads(response))
112+
except:
113+
return Response({"success": False, "message": "Unable to connect to cache server."})
114+
115+
116+
class SocketHandler:
117+
def __init__(self):
118+
self.socket = None
119+
120+
def connect(self, socket_file=None, server_ip=None, server_port=None):
121+
if socket_file is not None:
122+
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
123+
self.socket.connect(socket_file)
124+
else:
125+
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
126+
self.socket.connect((server_ip, server_port))
127+
128+
def close(self):
129+
self.socket.shutdown(socket.SHUT_WR)
130+
self.socket.close()
131+
self.socket = None
132+
133+
def send_data(self, data, buffer_size=4096):
134+
data = json.dumps(data)
135+
# use struct to make sure we have a consistent endianness on the length
136+
length = pack('>Q', len(data))
137+
# sendall to make sure it blocks if there's back-pressure on the socket
138+
self.socket.sendall(length)
139+
self.socket.sendall(data)
140+
# Receive respond
141+
bs = self.socket.recv(8)
142+
(data_length,) = unpack('>Q', bs)
143+
data = b''
144+
while len(data) < data_length:
145+
# doing it in batches is generally better than trying
146+
# to do it all in one go, so I believe.
147+
to_read = data_length - len(data)
148+
data += self.socket.recv(buffer_size if to_read > buffer_size else to_read)
149+
return data
150+
151+
def get_data(self, data, buffer_size=4096):
152+
data = json.dumps(data)
153+
# use struct to make sure we have a consistent endianness on the length
154+
length = pack('>Q', len(data))
155+
# sendall to make sure it blocks if there's back-pressure on the socket
156+
self.socket.sendall(length)
157+
self.socket.sendall(data)
158+
# Receive respond
159+
bs = self.socket.recv(8)
160+
(data_length,) = unpack('>Q', bs)
161+
data = b''
162+
while len(data) < data_length:
163+
# doing it in batches is generally better than trying
164+
# to do it all in one go, so I believe.
165+
to_read = data_length - len(data)
166+
data += self.socket.recv(buffer_size if to_read > buffer_size else to_read)
167+
return data
168+
169+
170+
class Response(object):
171+
def __init__(self, json_object):
172+
if "success" in json_object:
173+
self.success = json_object.get("success")
174+
if "element_id" in json_object:
175+
self.element_id = json_object.get("element_id")
176+
if "result" in json_object:
177+
self.result = json_object.get("result")
178+
if "message" in json_object:
179+
self.message = json_object.get("message")
180+
181+
def __str__(self):
182+
_str = dict()
183+
try:
184+
_str["success"] = self.success
185+
except:
186+
pass
187+
try:
188+
_str["element_id"] = self.element_id
189+
except:
190+
pass
191+
try:
192+
_str["result"] = self.result
193+
except:
194+
pass
195+
try:
196+
_str["message"] = self.message
197+
except:
198+
pass
199+
return str(_str)
200+
201+
def __repr__(self):
202+
return self.__str__()
203+
204+

default/server.default.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"SERVER_SETTINGS" : {
3+
"SERVER_MODE" : "web_server",
4+
"SERVER_SOCKET_FILE" : "",
5+
"SERVER_BUFFER_SIZE" : 4096,
36
"SERVER_HOST_NAME" : "0.0.0.0",
47
"SERVER_SUBDOMAIN" : "",
58
"SERVER_PORT_NUMBER" : 4444,

docker/configs/entrypoint.sh

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,52 @@
11
#!/bin/ash
22

3-
if [[ -f /tmp/pysica.pid ]]; then
4-
kill -9 `cat /tmp/pysica.pid`
5-
rm /tmp/pysica.*
6-
fi
7-
3+
# First create the required directories
84
mkdir -p /var/log/pysica
95
mkdir -p /var/www/pysica/conf
106
touch /var/log/pysica/cache.log
117

12-
uwsgi --ini /var/pysica_uwsgi.ini
8+
if [[ ! -f /var/www/pysica/conf/server.cfg ]]; then
9+
cp /var/www/pysica/default/logging.default.cfg /var/www/pysica/conf/logging.cfg
10+
cp /var/www/pysica/default/server.default.cfg /var/www/pysica/conf/server.cfg
11+
fi
12+
13+
# Read environment variables
14+
SERVER_MODE="${SERVER_MODE:-web_server}"
15+
SERVER_SOCKET_FILE="${SERVER_SOCKET_FILE:-}"
16+
SERVER_BUFFER_SIZE=${SERVER_BUFFER_SIZE:-4096}
17+
SERVER_HOST_NAME="${SERVER_HOST_NAME:-0.0.0.0}"
18+
19+
# Change the default configuration based on environment
20+
sed -i 's/"SERVER_MODE".*/"SERVER_MODE" : "'${SERVER_MODE}'",/' /var/www/pysica/conf/server.cfg
21+
sed -i 's/"SERVER_SOCKET_FILE".*/"SERVER_SOCKET_FILE" : "'${SERVER_SOCKET_FILE}'",/' /var/www/pysica/conf/server.cfg
22+
sed -i 's/"SERVER_BUFFER_SIZE".*/"SERVER_BUFFER_SIZE" : '${SERVER_BUFFER_SIZE}',/' /var/www/pysica/conf/server.cfg
23+
sed -i 's/"SERVER_HOST_NAME".*/"SERVER_HOST_NAME" : "'${SERVER_HOST_NAME}'",/' /var/www/pysica/conf/server.cfg
24+
25+
26+
echo "#---------------------------------------------------------------------------------------------------"
27+
echo "# Welcome to PySiCa (Python Simple Cache)"
28+
29+
# Launch server based on the current configuration
30+
if [[ "$SERVER_MODE" == "socket_based" ]]; then
31+
echo "# Cache server is running in SOCKET mode"
32+
echo "# To change the running mode, please set the value for the variable SERVER_MODE in the"
33+
echo "# the server.cfg file. Available options are 'web_based' for RESTFUL mode based on HTTP "
34+
echo "# and 'socket_based' to use pure socket communication."
35+
echo "#---------------------------------------------------------------------------------------------------"
36+
python /var/www/pysica/server_sockets.py
37+
else
38+
echo "# Cache server is running as web server"
39+
echo "# To change the running mode, please set the value for the variable SERVER_MODE in the"
40+
echo "# the server.cfg file. Available options are 'web_based' for RESTFUL mode based on HTTP "
41+
echo "# and 'socket_based' to use pure socket communication."
42+
echo "#---------------------------------------------------------------------------------------------------"
43+
44+
if [[ -f /tmp/pysica.pid ]]; then
45+
kill -9 `cat /tmp/pysica.pid`
46+
rm /tmp/pysica.*
47+
fi
48+
49+
uwsgi --ini /var/pysica_uwsgi.ini
50+
tail -f /var/log/pysica/cache.log
51+
fi
1352

14-
tail -f /var/log/pysica/cache.log

docker/configs/pysica.zip

4.33 KB
Binary file not shown.

docker/configs/pysica_uwsgi.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[uwsgi]
22
chdir = /var/www/pysica
3-
wsgi-file = server.py
3+
wsgi-file = server_http.py
44
callable = application
55
processes = 1
66
enable-threads = true

docker/docker-compose.tpl.yml

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@ services:
44
$${INSTANCE_NAME}-cache:
55
image: fikipollo/pysica
66
container_name: $${INSTANCE_NAME}-cache
7-
depends_on:
8-
- $${INSTANCE_NAME}-data
9-
volumes_from:
10-
- $${INSTANCE_NAME}-data
117
ports:
12-
- "4444:4444"
13-
14-
$${INSTANCE_NAME}-data:
15-
image: busybox
16-
container_name: $${INSTANCE_NAME}-data
8+
- "$${INSTANCE_PORT_1}:4444"
9+
environment:
10+
- SERVER_MODE=web_based # web_based or socket_based
11+
- SERVER_SOCKET_FILE=/some/file.sock
12+
- SERVER_BUFFER_SIZE=4096
13+
- SERVER_HOST_NAME=my.cache.com
1714
volumes:
1815
- /data/$${INSTANCE_NAME}-data/logs:/var/log/pysica
1916
- /data/$${INSTANCE_NAME}-data/conf:/var/www/pysica/conf

0 commit comments

Comments
 (0)