BMSTU/01-dis-lab-03-report.tex

413 lines
19 KiB
TeX
Raw Normal View History

\documentclass{article}
2023-10-24 13:28:45 +03:00
\input{settings/common-preamble}
\input{settings/bmstu-preamble}
\input{settings/fancy-listings-preamble}
\def\makeyear{2021}
\def\grpNum{11М}
\begin{document}
\pagestyle{empty}
\makeBMSTUHeader
\makeReportTitle{лабораторной}{3}{Протокол удаленного вызова процедур JSON-RPC}{Распределённые информационные системы}{}{Локтев Д.А.}
\newpage
\tableofcontents
\newpage
\lstlistoflistings
\newpage
\section{Введение}
Основной целью лабораторной работы является изучение принципов построения распределённых систем и закрепление полученных теоретических знаний на практике путём разработки проекта с использованием протокола удаленного вызова процедур JSON-RPC.
\section{JSONRPC2 Java}
Для разработки программ по заданию 1 использовалась IntelliJ IDEA 2021.3 Community Edition (далее IDEA). Для работы с JSONRPC2.0 в секции зависимостей сборщика проектов Gradle были указаны следующие пакеты:
\begin{verbatim}
dependencies {
// ...
implementation 'com.thetransactioncompany:jsonrpc2-base:2.0'
implementation 'com.thetransactioncompany:jsonrpc2-server:1.11.1'
implementation 'com.thetransactioncompany:jsonrpc2-client:1.16.5'
}
\end{verbatim}
Скомпилированный проект сервера в результате компиляции принял вид, представленный на рис \hyperref[pic:srvStruct]{\ref{pic:srvStruct}}.
\begin{figure}[H]
\centering
\includegraphics[width=5cm]{01-dis-rpt-rpc-srv-struct.png}
\caption{Структура сервера RPC}
\label{pic:srvStruct}
\end{figure}
\subsection{Сервер}
Для программы реализующий сервер был использован код из примера первого задания. Код сервера представлен в листинге \hyperref[lst:server]{\ref{lst:server}}.
\begin{lstlisting}[language=Java, caption=Код сервера Java, style=JCodeStyle, label={lst:server}]
package ru.iovchinnikov.rpcsample.server;
import java.io.*;
import java.net.*;
import java.text.DateFormat;
import java.util.Date;
import com.thetransactioncompany.jsonrpc2.*;
import com.thetransactioncompany.jsonrpc2.server.*;
public class Main {
public static class DateTimeHandler implements RequestHandler {
public String[] handledRequests() {
return new String[]{"getDate", "getTime"};
}
public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) {
if (req.getMethod().equals("getDate")) {
DateFormat df = DateFormat.getDateInstance();
String date = df.format(new Date());
return new JSONRPC2Response(date, req.getID());
} else if (req.getMethod().equals("getTime")) {
DateFormat df = DateFormat.getTimeInstance();
String time = df.format(new Date());
return new JSONRPC2Response(time, req.getID());
} else {
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID());
}
}
}
public static void main(String[] arg) {
JSONRPC2Parser parse = new JSONRPC2Parser();
Dispatcher dispatcher = new Dispatcher();
String line;
JSONRPC2Request req;
JSONRPC2Response res;
dispatcher.register(new DateTimeHandler());
try {
ServerSocket ss = new ServerSocket(3333);
System.out.println("Waiting for a client...");
while (true) {
Socket socket = ss.accept();
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
line = in.readUTF();
System.out.println("Request: " + line);
req = parse.parseJSONRPC2Request(line);
res = dispatcher.process(req, null);
line = res.toJSONObject().toJSONString();
System.out.println("Response: " + line);
out.writeUTF(line);
out.flush();
}
} catch (Exception x) {
x.printStackTrace();
}
}
}
\end{lstlisting}
\subsection{Клиент}
Для программы реализующей клиента был использован код из примера первого задания. Код клиента был переработан в виде тестового стенда для сервера и представлен в листинге \hyperref[lst:client]{\ref{lst:client}}
\begin{lstlisting}[language=Java, caption=Код клиента Java, style=JCodeStyle, label={lst:client}]
package ru.iovchinnikov.rpcsample.tests;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.thetransactioncompany.jsonrpc2.*;
import java.net.*;
import java.io.*;
public class TestServer {
private static JSONRPC2Parser parse;
private static int serverPort;
private static InetAddress ipAddress;
@BeforeAll
static void setup() throws UnknownHostException {
ipAddress = InetAddress.getByName("127.0.0.1");
serverPort = 3333;
parse = new JSONRPC2Parser();
}
//функция отправки json запроса
static private JSONRPC2Response Send(JSONRPC2Request request, InetAddress ip, int port) throws Exception {
Socket socket = new Socket(ip, port);
String line;
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.writeUTF(request.toJSONObject().toJSONString());
out.flush();
line = in.readUTF();
socket.close();
return parse.parseJSONRPC2Response(line);
}
@Test
void serverReturnsTime() throws Exception {
String method = "getTime";
int requestID = 0;
JSONRPC2Request request = new JSONRPC2Request(method, requestID);
JSONRPC2Response answerJSON = Send(request, ipAddress, serverPort);
assertNotNull(answerJSON.getResult());
System.out.println("Time on Server - " + answerJSON.getResult());
}
@Test
void serverReturnsDate() throws Exception {
String method = "getDate";
int requestID = 1;
JSONRPC2Request request = new JSONRPC2Request(method, requestID);
JSONRPC2Response answerJSON = Send(request, ipAddress, serverPort);
assertNotNull(answerJSON.getResult());
System.out.println("Date on Server - " + answerJSON.getResult());
}
}
\end{lstlisting}
\subsection{Результат работы}
После запуска приложения сервера оба теста возвращают положительный результат, представленный на рисунках \hyperref[pic:serverTests]{\ref{pic:serverTests}} и \hyperref[pic:serverTestsCl]{\ref{pic:serverTestsCl}}
\begin{figure}[H]
\centering
\includegraphics[height=5cm]{01-dis-rpt-rpc-srv-test1.png}
\caption{Проверка работоспособности в терминале сервера}
\label{pic:serverTests}
\includegraphics[height=5cm]{01-dis-rpt-rpc-srv-test2.png}
\caption{Результат проверки работоспособности сервера клиентом}
\label{pic:serverTestsCl}
\end{figure}
\section{JSONRPC2 Python}
Для разработки программ по заданию 2 использовалась JetBrains PyCharm 2021.3 Community Edition (далее PyCharm). Для работы с JSONRPC была скачана реализация протокола JSONRPC. Так же была установлена необходимая для работы библиотека SimpleJSON. Библиотека SimpleJSON была подключена к проекту при помощи пакетного менеджера \code{pip}. Библиотека JSONRPC была скачана в корень проектов сервера и клиента. На рисунке \hyperref[pic:contentPy]{\ref{pic:contentPy}} представлен снимок экрана с файлами проекта и загруженными библиотеками
\begin{figure}[H]
\centering
\includegraphics[height=6cm]{01-dis-rpt-rpc-pysrv-struct.png}
\caption{Содержимое и настройки проекта}
\label{pic:contentPy}
\end{figure}
\subsection{Сервер}
Для программы, реализующей сервер был использован незначительно модифицированный код из примера второго задания. Код сервера представлен в листинге \hyperref[lst:pythonServer]{\ref{lst:pythonServer}}
\begin{lstlisting}[language=Python, caption=Сервер на языке Python, style=PyCodeStyle, label={lst:pythonServer}]
import jsonrpc
def echo(s):
return s
def multiply(a=None, b=None):
if (a or b) is not None:
return a * b
else:
return "error"
if __name__ == '__main__':
server = jsonrpc.Server(jsonrpc.JsonRpc20(),
jsonrpc.TransportTcpIp(addr=("127.0.0.1", 31415), logfunc=jsonrpc.log_file("myrpc.log")))
server.register_function(echo)
server.register_function(multiply)
print "waiting for client"
# start server
server.serve()
\end{lstlisting}
\subsection{Клиент}
Для программы, реализующей клиент также был использован незначительно модифицированный код из примера второго задания. Код клиента представлен в листинге \hyperref[lst:pythonClient]{\ref{lst:pythonClient}}
\begin{lstlisting}[language=Python, caption=Клиент на языке Python, style=PyCodeStyle, label={lst:pythonClient}]
import jsonrpc
if __name__ == '__main__':
server = jsonrpc.ServerProxy(jsonrpc.JsonRpc20(), jsonrpc.TransportTcpIp(addr=("127.0.0.1", 31415)))
result = server.echo("hello world")
print(result)
result = server.multiply(a=3, b=5)
print(result)
result = server.multiply()
print(result)
\end{lstlisting}
\subsection{Результат работы}
Приложения сервера и клиента были запушены и протестированы. Результат работы сервера, журнал работы, представлен на рисунке \hyperref[pic:pysrvWork]{\ref{pic:pysrvWork}}, а результат работы клиента на рисунке \hyperref[pic:pycliWork]{\ref{pic:pycliWork}}.
\begin{figure}[H]
\centering
\includegraphics[height=5cm]{01-dis-rpt-rpc-pysrv-log.png}
\caption{Результат работы (журнал) сервера}
\label{pic:pysrvWork}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[height=2cm]{01-dis-rpt-rpc-pyclient.png}
\caption{Результат работы клиента}
\label{pic:pycliWork}
\end{figure}
\section{JSONRPC2 Mullti Language}
Заданием для практической части является создание клиент-серверного приложения где клиент и сервер разработаны на разных языках. Клиент должен позволять формировать две матрицы заданного размера и посылать эти матрицы на сервер. Сервер в свою очередь должен суммировать полученные матрицы и отсылать результат клиенту. В качестве сервера была использована программы на Python реализованная во второй части задания. В качестве клиента была использована программа на Java реализованная в первой части задания. Код сервера был модифицирован для решения задачи сложения матриц. Код сервера представлен в листинге \hyperref[lst:pythonServerMatrix]{\ref{lst:pythonServerMatrix}}
\begin{lstlisting}[language=Python, caption=Сервер складывающий матрицы, style=PyCodeStyle, label={lst:pythonServerMatrix}]
import jsonrpc
server=jsonrpc.Server(jsonrpc.JsonRpc20(),jsonrpc.TransportTcpIp( addr=("127.0.0.1", 3340), logfunc=jsonrpc.log_file("myrpc.log")))
def matrixSum(a, b):
if (a or b) is not None:
if(len(a) == len(b)):
n = len(a);
s = [[0] * n for i in range(n)]
for i in range(len(a)):
for j in range(len(a)):
s[i][j] = a[i][j] + b[i][j]
return s
else:
return "Invalit matrix size"
server.register_function( matrixSum )
# start server
server.serve()
\end{lstlisting}
Код клиента был модифицирован для возможности ввода размерности матриц, самих матриц и их вывода в консоль. Код клиента представлен в листинге \hyperref[lst:finalClient]{\ref{lst:finalClient}}
\begin{lstlisting}[language=Java, caption=Код со вводом матриц, style=JCodeStyle, label={lst:finalClient}]
package ru.iovchinnikov.rpcsample.server;
import com.thetransactioncompany.jsonrpc2.*;
import java.net.*;
import java.io.*;
import java.util.*;
public class Client {
static JSONRPC2Parser parse = new JSONRPC2Parser();
public static void main(String[] ar) {
int serverPort = 3340;
String address = "localhost";
String method = "matrixSum";
int requestID = 0;
try {
Scanner in = new Scanner(System.in);
System.out.print("Введите размер матриц: ");
int size = in.nextInt();
System.out.println("Введите матрицe A: ");
int[][] mA = CreateMatrix(size);
System.out.println("Введите матрицe B: ");
int[][] mB = CreateMatrix(size);
InetAddress ipAddress = InetAddress.getByName(address);
List param = new LinkedList();
param.add(mA);
param.add(mB);
JSONRPC2Request request = new JSONRPC2Request(method, param, requestID);
JSONRPC2Response answerJSON = Send(request, ipAddress, serverPort);
System.out.println("Матрица A - " + prntMatrix(mA));
System.out.println("Матрица B - " + prntMatrix(mB));
System.out.println("Сумма матриц - " + answerJSON.getResult());
} catch (Exception x) {
x.printStackTrace();
}
}
private static int[][] CreateMatrix(int size) {
int [][] mtx = new int [size][size];
for(int i = 0; i < mtx.length; i++) {
for (int j = 0; j < mtx[i].length; j++) {
Scanner in = new Scanner(System.in);
System.out.printf("[%d,%d] = ",i,j);
int z = in.nextInt();
mtx[i][j] = z;
}
}
return mtx;
}
//функция отправки json запроса
static private JSONRPC2Response Send(JSONRPC2Request request, InetAddress ip, int port) throws Exception {
Socket socket = new Socket(ip, port);
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.write(request.toJSONObject().toJSONString().getBytes());
out.flush();
String line = in.readLine();
socket.close();
return parse.parseJSONRPC2Response(line);
}
private static String prntMatrix(int[][] a) {
StringBuilder str = new StringBuilder("[");
for (int i = 0; i < a.length; i++) {
if (i != 0)
str.append(",");
str.append("[");
for (int j = 0; j < a.length; j++) {
if (j != 0)
str.append(",");
str.append(a[i][j]);
}
str.append("]");
}
str.append("]");
return str.toString();
}
}
\end{lstlisting}
\subsection{Результат работы}
Приложения сервера и клиента были запушены и протестированы. Результат работы сервера представлен на рисунке \hyperref[pic:pysrvFin]{\ref{pic:pysrvFin}}, а результат работы клиента на рисунке \hyperref[pic:jcliFin]{\ref{pic:jcliFin}}.
\begin{figure}[H]
\centering
\includegraphics[height=5cm]{01-dis-rpt-rpc-srv-test-final.png}
\caption{Результат работы (журнал) сервера}
\label{pic:pysrvFin}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[height=2cm]{01-dis-rpt-rpc-srv-test-final-log.png}
\caption{Результат работы клиента}
\label{pic:jcliFin}
\end{figure}
\section{Выводы}
В ходе выполнения работы была реализованная задача создания распределенной системы, где вся логика обработки данных предоставляется серверу, тогда как работа с данными происходит на клиенте.
2023-10-24 13:28:45 +03:00
Для реализации задачи был использован протокол удаленных вызовов процедур JSON-RPC. Протокол использует JSON для кодирования сообщений. Протокол был использован исходя из рекомендаций к заданию и имеет следующий особенности:
\begin{itemize}
\item небольшой размер;
\item простота использования;
\item документированность;
\item компактность и ёмкость одного выражения;
\item независимость от способа передачи данных;
\item поддержка именованных параметров;
\item поддержка нотификаций;
\item поддержка значений null(None);
\item встроенный контроль сессий;
\end{itemize}
Использование протокола позволяет создавать системы где сервер разрабатывается на одном языке программирование, а клиенты на других.
\end{document}