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

409 lines
19 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\documentclass{article}
\input{../common-preamble}
\input{../bmstu-preamble}
\input{../fancy-listings-preamble}
\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{Выводы}
В ходе выполнения работы была реализованная задача создания распределенной системы, где вся логика обработки данных предоставляется серверу, тогда как работа с данными происходит на клиенте.
Для реализации задачи был использован протокол удаленных вызовов процедур JSON-RPC. Протокол использует JSON для кодирования сообщений. Протокол был использован исходя из рекомендаций к заданию и имеет следующий особенности:
\begin{itemize}
\item небольшой размер;
\item простота использования;
\item документированность;
\item компактность и ёмкость одного выражения;
\item независимость от способа передачи данных;
\item поддержка именованных параметров;
\item поддержка нотификаций;
\item поддержка значений null(None);
\item встроенный контроль сессий;
\end{itemize}
Использование протокола позволяет создавать системы где сервер разрабатывается на одном языке программирование, а клиенты на других.
\end{document}