From 3ef7c65962da0b06e251e34ba4c7428a6428761f Mon Sep 17 00:00:00 2001 From: Pavel <––ƒ––iputilin201@gmail.com> Date: Mon, 2 Dec 2024 23:57:46 +0400 Subject: [PATCH] putilin_pavel_lab_3 --- putilin_pavel_lab_3/README.md | 66 ++++++++++++++++ putilin_pavel_lab_3/client_service/Dockerfile | 10 +++ putilin_pavel_lab_3/client_service/app.py | 39 ++++++++++ .../client_service/requirements.txt | 2 + putilin_pavel_lab_3/docker-compose.yml | 36 +++++++++ putilin_pavel_lab_3/image_1.png | Bin 0 -> 14289 bytes putilin_pavel_lab_3/image_2.png | Bin 0 -> 13127 bytes putilin_pavel_lab_3/nginx/Dockerfile | 5 ++ putilin_pavel_lab_3/nginx/nginx.conf | 17 +++++ putilin_pavel_lab_3/order_service/Dockerfile | 10 +++ putilin_pavel_lab_3/order_service/app.py | 72 ++++++++++++++++++ .../order_service/requirements.txt | 3 + 12 files changed, 260 insertions(+) create mode 100644 putilin_pavel_lab_3/README.md create mode 100644 putilin_pavel_lab_3/client_service/Dockerfile create mode 100644 putilin_pavel_lab_3/client_service/app.py create mode 100644 putilin_pavel_lab_3/client_service/requirements.txt create mode 100644 putilin_pavel_lab_3/docker-compose.yml create mode 100644 putilin_pavel_lab_3/image_1.png create mode 100644 putilin_pavel_lab_3/image_2.png create mode 100644 putilin_pavel_lab_3/nginx/Dockerfile create mode 100644 putilin_pavel_lab_3/nginx/nginx.conf create mode 100644 putilin_pavel_lab_3/order_service/Dockerfile create mode 100644 putilin_pavel_lab_3/order_service/app.py create mode 100644 putilin_pavel_lab_3/order_service/requirements.txt diff --git a/putilin_pavel_lab_3/README.md b/putilin_pavel_lab_3/README.md new file mode 100644 index 0000000..77bc0f0 --- /dev/null +++ b/putilin_pavel_lab_3/README.md @@ -0,0 +1,66 @@ +# Лабораторная работа №3 - REST API, шлюз и синхронный обмен данными между микросервисами + +## Задание + +### Цель: +Изучение принципов проектирования с использованием паттерна шлюза, организации синхронной передачи данных между микросервисами и применения архитектурного стиля RESTful API. + +### Задачи: +1. Создание двух микросервисов, которые реализуют операции CRUD для связанных сущностей (клиенты и заказы). +2. Реализация механизма синхронного обмена данными между микросервисами. +3. Настройка шлюза на базе Nginx в качестве прозрачного прокси-сервера. + +### Микросервисы: +1. **client_service** — сервис, который управляет информацией о клиентах. +2. **order_service** — сервис, который обрабатывает данные о заказах, сделанных клиентами. + +### Связь между микросервисами: +- Один клиент может иметь множество заказов (соотношение 1 ко многим). + +### Как запустить проект: +Для запуска приложения необходимо выполнить команду: +``` +docker-compose up +``` + +## Описание работы: + +Для разработки микросервисов был выбран язык программирования Python с использованием фреймворка Flask. Каждый микросервис реализует стандартные операции CRUD (создание, чтение, обновление и удаление) для своей сущности. + +### Синхронный обмен данными +Микросервис `order_service` отправляет HTTP-запросы к сервису `client_service` для получения актуальной информации о клиентах, с которыми связаны заказы. Это позволяет создавать заказ с привязкой к конкретному клиенту, а также обновлять или удалять данные о заказах, учитывая информацию о клиенте. + +### Docker Compose +Конфигурационный файл `docker-compose.yml` представляет собой многоконтейнерное приложение, которое включает в себя три сервиса: `client_service`, `order_service` и `nginx`. С помощью Docker Compose можно запустить все три контейнера одновременно. Функция маршрутизации запросов возложена на сервер Nginx, который управляет входящими запросами и перенаправляет их на соответствующие микросервисы. + +### Nginx +Конфигурационный файл Nginx (`nginx.conf`) управляет маршрутизацией входящих запросов. В зависимости от URL, запросы перенаправляются на сервисы: + +- Запросы по пути `/client/` перенаправляются на сервис `client_service`. +- Запросы по пути `/order/` перенаправляются на сервис `order_service`. + +Пример конфигурации Nginx: +```nginx +server { + listen 80; + + location /client/ { + proxy_pass http://client_service:8000; + } + + location /order/ { + proxy_pass http://order_service:8001; + } +} +``` + +### Взаимодействие между микросервисами +- **client_service**: Этот сервис управляет информацией о клиентах (например, имя клиента, номер и дата выдачи). +- **order_service**: Этот сервис управляет заказами и хранит информацию о каждом заказе, связанном с конкретным клиентом. Заказы могут быть созданы, обновлены или удалены через RESTful API. + +Когда заказ создается или обновляется в `order_service`, он отправляет запрос к `client_service`, чтобы получить подробную информацию о клиенте (например, имя, номер и дата выдачи абонемента). + +### ВИДЕО + +https://cloud.mail.ru/public/oG1Y/GXywRodDN + diff --git a/putilin_pavel_lab_3/client_service/Dockerfile b/putilin_pavel_lab_3/client_service/Dockerfile new file mode 100644 index 0000000..4fd96e9 --- /dev/null +++ b/putilin_pavel_lab_3/client_service/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.10-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/putilin_pavel_lab_3/client_service/app.py b/putilin_pavel_lab_3/client_service/app.py new file mode 100644 index 0000000..5c81452 --- /dev/null +++ b/putilin_pavel_lab_3/client_service/app.py @@ -0,0 +1,39 @@ +from fastapi import FastAPI, HTTPException +from uuid import uuid4 + +app = FastAPI() + +# Временное хранилище для клиентов +clients = {} + +@app.get("/") +def get_clients(): + return list(clients.values()) + +@app.get("/{client_id}") +def get_client(client_id: str): + if client_id not in clients: + raise HTTPException(status_code=404, detail="Client not found") + return clients[client_id] + +@app.post("/") +def create_client(client: dict): + client_id = str(uuid4()) + new_client = {"id": client_id, **client} + clients[client_id] = new_client + return new_client + +@app.put("/{client_id}") +def update_client(client_id: str, client: dict): + if client_id not in clients: + raise HTTPException(status_code=404, detail="Client not found") + updated_client = {"id": client_id, **client} + clients[client_id] = updated_client + return updated_client + +@app.delete("/{client_id}") +def delete_client(client_id: str): + if client_id not in clients: + raise HTTPException(status_code=404, detail="Client not found") + del clients[client_id] + return {"detail": "Client deleted successfully"} diff --git a/putilin_pavel_lab_3/client_service/requirements.txt b/putilin_pavel_lab_3/client_service/requirements.txt new file mode 100644 index 0000000..97dc7cd --- /dev/null +++ b/putilin_pavel_lab_3/client_service/requirements.txt @@ -0,0 +1,2 @@ +fastapi +uvicorn diff --git a/putilin_pavel_lab_3/docker-compose.yml b/putilin_pavel_lab_3/docker-compose.yml new file mode 100644 index 0000000..388fedd --- /dev/null +++ b/putilin_pavel_lab_3/docker-compose.yml @@ -0,0 +1,36 @@ +version: "3.8" + +services: + client_service: + build: + context: ./client_service + container_name: client_service + ports: + - "8000:8000" + networks: + - app_network + + order_service: + build: + context: ./order_service + container_name: order_service + ports: + - "8001:8001" + networks: + - app_network + environment: + - CLIENT_SERVICE_URL=http://client_service:8000 + + nginx: + image: nginx:alpine + container_name: nginx + ports: + - "80:80" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + networks: + - app_network + +networks: + app_network: + driver: bridge diff --git a/putilin_pavel_lab_3/image_1.png b/putilin_pavel_lab_3/image_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f8712e29413af464d693580ec53c94398bdbb74d GIT binary patch literal 14289 zcmdUWbySq^+vixIAfSMtq>8k3N=u4#OAXS}H4HGMA|Ne|z|aiMNaui(0@5)w(lLa@ z(6JAG-E;Q*_C4>}-QRom?Eb+&56^Sm_kCTT>vPpT!K%t~1b7s9AP|T^{*|;k2y|@; z_Y{NH)^U^h?Wdd06 zm{_zW)bd~By=J){jmM&Q_Fm!vp0z~w{qT3gciYr+tM)r$d&dU9^m0y?R%qZ(mX=b7 z3U3-hF$T~MRIQu$^Fx8lI-Ljzfk3~$&*m|L zKoV~xk=H;+e&8FR-`7w$pkFs&1fXxY`xGEQqE$W+i1uvD)y&Kc0@>g2>kp}@gM|X0 z-nfKOKX}lVM9HZK=g*vB2Z6%cBf;ep1vfUt!^*CKuK$ul`V-p(^5@I|D~0`t1ox$i z>Q7Hg3?PQ}aY26f!yS+-O^#yLYMC$=V7*^WJDb>z&79Dg%Q4Y$U|GMW>)U+caPL}G zoI4t5mn8 zr;3LV!lvfexf7^wfsE;YgBiUK2%Z8e=hob9oC3RgtQ#dlmODI%Ox2iGqU$dG;0jGFHB zxMyeyM7yk7fle*>pu-mjN;S)m`s;(?6Xlr-|Jv29UTb`ZI4LcFQ z=JrJ)`(^HFbOE@Krs;8MMXiGS$7)!8(o*nRT!QDxyZP|c2y3U$m=i^r<(bo6Q^@S6 zDa>F523r4BymeFCZGXzKmZK`cnzC&K?isB8DE;Me|M|6~rRNph5!d~G6Q_XXkdqP% z@7B4KCBwCq)p|{2w<`U;gdaVO)^8{JOc5(|i)Fv+vCH z9~3~F&_C^;;rn-8RPPI!BVB|yD#}d1`6x`)WFfT#FI6Y$rjVw;stm^}T4tg+*j|jO zpvV|zNOasbD^GTY0>vJZ(p(zW*Yx&F=)7pF#vuIgLss<0aCS@A90Y~6^axCeht1Kr-`!x1lUK6WPjlFwen*A6@d7Ih1-wC38dj6hs zx@Wbc)>frs;75k72Mi2&_@7p8_8#8RuYVeAm@sSAwEeXSoUL!iBx=DQ?4~orCiczR z6**1Q&3cia2V+kM+bksU6}f@Nw_oc zP{=`;s#-WyWWP?+UDd7r?)-YQ=JC%yf3ajoqu{FK<{@I04|%;oe~`~(x?bsZVEPd} z6fmt>BJ7OJ*=v$i@&Xn_uO7?Ri2SG_G%1&G3#EA}W)=kZ6U_BB3Pn6WW_(9shyIGr;nb5s@l z*T&GJ)yhO=IU~x`!HT2!OP@5m>L+l%1d8+bwUFw3OoStYlwfhjQNzGlspCbnL8r%2PRvd9k?myq_- z91Yq)=Ua`pi`dyjM@Li+A-NjJEIFo>Ats|@1xq)G;Za5Z(iaxx=~t9%+d>TX(t_Uy z*R8zRQptS#XSH%w&^tkj!3Kl{BBE(u?}R(WK&Joh9!(-I-uF*c)tE(R zCz~orXJu==Pgb1hXo~o2E~F9%{s3C){b3S(>($8y<)N?t)(+sFBCySIu*=Gb_KxsF zX`{)}@qu@ax{tl$l9A9V?W51j>5f-!9dUizH9K+R7RTlDsxoapZ_Sq*t{w`Omiom< zQBq2;8O=KbvV3zpVD3o#T!Vg{@rw6U8qbmoByweAZ{E4X)Sc9ddxaee&AG66uJX zqP%CjijOCE>$k2ow;@CCgTB#R`nJ1m>0>b)#=!p`Y{r88b{6A*nXSMo3mlAIL(QVvH3F%KzLCyG;rxvPaDA#xy+cLp$O-CfNS2Z}uh{uN(mL6p^Z zQhq)T2NESU1Ach&wwPLt*JptiSAQZBUpIs@+L*S?Est?s$ew5%`AQ&S|1ROdFpFhm z^OR>(6RS?$PHEO98>(7(SEqRYjR8%@Y-td<@Eu&Pv880!+=57&Da=1|{n>!;JRE+vO(7WC{qYpg|-x?)0R<&I(BbuUh z_Ohuq+Gd)b9`LkuT>bv`lFd^mOIc2!UJ*yZjh4+9Rjy9)f`UR=h=QP(vc4V{r%nMb z@H&99IZ<-}GZIHF>V2SpFvISWFY+UY0*W1MZA>NfC=Y&6JdNHw(DxQifM-C~up?c{ zPWFe#>K;dF;OPMue})4nUpL7RE63p{qMGhl6#)g|l0zb~hZGH_)ox!>Li>~bl8a}m z9`m7xT^01cpEKX~Y&U>`$9TL&t6n>0cBh)LvTWUcTy(P@%`>F!>oe+(a$Hti`8cLS z5pG1I5B{}*jjy`J-mPF!MeV{)R-z0Sii?iUOpz3roSZyYs-hJh3g#B#bua9I{v3Al z{x-Lo6(3s-IUu}C;59Wqu3gwyTWnltiu4O_D|F$RW1eFK%C6vt+G@G|H; zW+7G74 zeKr&styO(?ELD= zibg%!YH*^Ah{X=1u3nF(Dtj!kiCeKG2IBmM~a510|#8{L+S`zsV!7IZ3eHJmhp-x!|1>( zk9R+~#yH@=)i`SPk8Z1aDcY}8$`pWyB&CR`nzM*GczemUonCZ{H|Q81EdHjx_7VQ} zu&%^Qu(b%iO+z9()sceYZ%7dmwIQsR?xh7shJrEKX1G)~a3kTN&PycEG#3?Q2{)3)6aH#J_tYG|RR+hIE*QF*V|b&2ndHiKitv~tB+)fQd_forQ!Svw%Tk}5AHnL}1r~_3)T3B%no!?i$z8vERKYuBZZs`=_Dn6!$2D$S~ z?Re1HnfyxH*Il?jH{Ar0s>^Bz;}U!e=sRzXX(MX&TZSHpe-@Y=;ZW8zm_cJtIr+n>SAiLN=zf#w*-P*9(5?xVk<;Hiw^8kZ&g4a;!!UVsX1 z8+fHDIide^8@rQ?+O8fI78Z6k@#SeBO4)Xe+KZDHQms&A=dl7H{)XJPhjxz7sh>N` zulHG(dc|WR$9uBgXu|}jLK7;MSv8Z*N}nKiF=K%BW@T<9+trN89j;Do=*Kl%1fXv? zg_!pHcz#O&W1?;9w+DsH-o5+U$I!@f*I|6_;VM^osqZmn|6w2%ZFPvl^+1(hHMYj- z)ml>@>d+y%oDC#E;#SINxPANa&zk@~4I#GrT8+~l(L$I0&0|{gX4QM>u2Oy&HLrTy z;GWjyjCAajG3|wODQC)t9rTO%b6ul~$zKPC%a9iP5epKmY||y*+swv1UZYC8LpBA2 zz8^fPRlg#t)-FgJ_JB||XA0y(y!3Ri5Y7@D^7osH;x(yF+TF#K^0S|Z?ieNWbPF`2 z!R5v5&mItrQ(wDjM&(IB6Mov8{Zzq-9>*`}&VNOi{uAfZVCO)VplWncbyfnyIU+*g z_G)aTrOCf<&0LVjdZ7AQEFil0aKFhcC7YFj8+hHoGi-85* zS&s8qt>q?%3+C`LwN1aj*@co%&HVWG=Bc(l0lR6_!)u_kIU8grk6_GMJXtU8%3CNvU*+O;T{sbPp<5{K7>%x=mX94zs!~jHoM{2+5{d-zgz0_u0JFD zMnNhbXs!!*@<~zjj(gw@yl?mS^@bcJt_gks+fWcPf#mvalv=Lc`gI#tY$ccuqI!ws zBfur~qx-%AG;tQEYF)_L4H7}deuOuv35A%}7qU5buif(dN?ZZfAXi=Il*TpxXch^` zdpvcs7|k22k$b)Q1~mnCAUe!ZqLzz?g`4iRs2VrDtMZ~3RvM&ys~G*zAc#$fcRh># z(VSmbSz&M5*q6-2&+5*Z`O+`Gi<=r4+F4AN67&pgoWw@wmX!#s%-RmSS@a|_D^HmI z^k8%eF*#1}E-8Ec6H!q19LW)3U13rQ7ImVM&ao}DFZ&dUY-#B7j(_B7IOpoNd?t8VUUFTTsux&gF~U8fRd&6J+h9$qQ0S7 zkp_qg#BBYnWA@#1cVVTU`)4_{{3R?-y393qv77P!x_5XDbQ7a%hs?@jPmfHbSw~m$ z8F37o30+RVglxo)jg5We&FX}fGE63z$hGDcQ*uRk?5`562frkbGISZR;6}7{w72J) z>7-Z*Z2vy#F4AW|vyc}yI<_IZpBD9EK4vZuiAsGYiF}a*YfZ!R6Q@zZ$d^mw@!qya~$(X*PPFMygd8Z=Cw7{{8lMj2@S>vOeCv<36&w}oH688p9qcD##6PArB^I+xEvpQ+Qb1y5n_M{lCgrGc=*$`U~y zdbUG}7;IQ#zHTj++)fGCd58@0U|8QSsO@`-;A>(AgjxJ2U(F(PsRwq{1>1&EALQ5J z)nzlEmzEOkaVX#UL&F+^78$-tC`Bv2dC^y*vA-#(*l2M*M*Wgo%xhV7I}UC$--Hwi>c1(%6*ae ziLr}q6cN&6#f%BT^yy+;Od#dzE^c3nv^B=7KKzv7%70fs@m%^2WwxBOOl_nyjqi%>C>PZA`>u)}o|v~7%6?$ql6y$p zw$mzx-%zz?DMk|7WhT;p!@hd_BehE@>fA&_zV40e#EX`t=#U0Pvyh0MB9rhE)v@g2 zh;ZoR#ZJw~;g$Z+_-%Ahy6(2|h=O_E6_D=n?-YOg^-6Q2f{OS|^kz#?@VbNO7du$)5XI`)}L=YdcoqG^o3 zRHJC#Zi8I(DRXW^-S_8a=b87Q{EiwaT%RvLxAu2pG*nxgd=n10@8{Dq7HLYkNV$#& z&c(2Dqrqv@{m;*h*Y=znpGTUaJDSjg<)NQ+b!|-?NDWlBg%15Nt#g(v4S{?Q`Ucxb zlWZH|U_uGaBF@2T!=gTTj*oj7##nuSLT^rFDO;l`Z%ne4gkA{}%N{~oeRAZ1XnJZA z{V@YlZyxy@U&}tDKE~H2rOBcZtLHNjVq~g33&dRe(ggN$! zRr}APA`T+4-V*W$6q3U>FPC2y_LHLFF?D_O{9MhB20jRruhoo9LE$fnAJnT}Bz^4l z$bB(SNnUNbQi%>pz2gwK?i^ra-?qZ?oKQ zFt&yu)_9-NSX`N!MPRi$&cB!j8jdkj(VLa%=Hx?!34dzHE4D%; z$162ik*8J(<1c<^xXqGd2j*b zj>c9f*2g>D5`x>5HbZ!CZ+EegExqCw{3viVs5~ko0)AT-%qXB4DJ^nm0fput8k!G6 z-OpdL%+^uja+x1Knqtv7rdUxcsCrFXjU)B3u^n2XcXGtXvgsFqy1b{{ttrIVu{^*L z>@J$~)15cSXPET z94hXnF+RfG_Y11jRq`w!BiAJr?wF{MI;&Cpq!9kKyr_AP?ZrE_yGw&Cy_Lc$1h`t# zCR_xt$$)ZPewaIU5^e-l6%&aO9PmUYuVaeX<#fmFEdp}jufYwfrOZOBr`#hBAKIVs zWBhalJ=lC~O?Y$!n`YQknMhQEZ+ljMMEzbR#*5IOv>UbFaf$sX8B^I~Q4H~6ng?LP z&OMw$+*_1rbJx6g$5nbgg28yQIi_rs9CnV6S9%XCdKgLObJAReVJxFa(~XUN zMWVAgrukcqL@AbolC6H4agcs{rLwayK3@P&Oaq8q94MObU0Mb~nKp(z#?t)Mi-23G z73|lqA6ANAu)VqH+*63VTsO1OG>hJ~m{{*CB#vi7e2iU(eEhML=K3QK-LgIFA8}yZ zEP069UYpB*K`9qG;q86Pi2ngo-F85#4{pPm?6JbmlYF26pY!o|!u`Hbi(VHul7EW= zJ8+Xtvc*obIZ$_Qb3JmVN=euLD&n}}yGYB__n^J^iL`SS-6-F2^fr8~-AlwtF!Jk9!LO?MN^~4ED%`k}pB1 zvK2LobdlI9?{!ycUN{>f_1XoXu+El#n+y6PR;IC{qiUHs`X?^>2((dpkd)@@1LXr1 zQ@oT}x4Swtu;Pmrt=sA7FMUfxoF3;pCfKl#H}{3i02bz!L^3`Z2N_#Td7`RHz8*#9 z4jnEJx*i+g8V*9&bdf+uERLh%Yr|6*wKS0LzC>OVrkp9xx-Xz) zjOtwztTAXxGK^)+AHX~YvXDD4f^YX-kt4okc#&c!??5Q!>AI5DK#y*HIWi6<6OFuI zlMe7+FLv4$DM?xHif0gswXX${6(3-?K#79DfZ^a;)^GUdH0sWx-dTu!-$9>V)y4W7 z=LzRgOoN*>Iyq!~>CS?zhs?gvC*T+#z(CP}28*-crK3N}jwZ8juw7)i7o1f6etB_w`NcmHgg~4xf#O5;?{W9_NdE4{>`#6W znTejdO32F4kudox7&R#IaSKZO^2_U3-@aA8T;Sb#g+J<_d-4tiu5ru|BECnn+MgF0 ze-nlC>s3EY)NRYamGQAie}>vWOGKq(g}Awir8a=7C4ev-*a%S9MF3@s5Z5?wtW;&o zP3qf1RCKL+L4ZQ&6Hz!@R~x@7hyZ~Xu2QWc*sa1uF zy0VHc2=3+U-0%zL+(LwflbWlt8pe_Yx(;t$0P}i5?k$vkN#~ib z8%raG_3|u1_{6vRv}ubbGYvDiz|_;^V@ElC)@|AR{6rpYg{Oo@8vX9+@T4-L&Q|x` z-80dH7Y~l4$|=lTD6RRu|wz#LdfDajg`= zhUKx0yDZ$d+}Z|T@P_*M>|h2jYrNB;z7+aP;R?y)WZ)`bfSBau+ldEtaZ|%1C~P`P zm#)LTO$F#K>hnR;**yJ?>FnT*kmKXZ-RNRO5v~z!rlLj}^VFeze40pfr*+9kFz)@U zx9?$3vZ|{!EQ=Xa&#LuLnOkM_qw{_pZsMkTTYRyW*FM+_Z;qC_)I2tFBRU_FGv_HeY}08D z+)=pw&so{hh@#+Pbz9=wRIu$XZD`E;)E?}bQpQ@VSq~f*IEs4J~QK%VK?jcq7-P0~c(t_|*=kFrl5;{3!si z#uca-iqOwIr$11l!(!Jmd<(MEAaHNK(-zre zMtxh@&xYJ&vFz@%CCaL_b2@!+V}GnJzD|uT`3K;N$}d0yY)5FJB*L?8l%tx;(|0m| zHUYJZwp&&~$o1*gRzmwmF4NO&OFJw&$aTfCx*HZ1&*ucJ<~|Jhrr%m+G|Xila3Qa^ zfA|6PkNHt^zZ4+eyNk>xbPk(dYxOcVcqnXA$yerrTW>|KSmTZrvU6g3WtBlm8rgE~ zABgH*PGMZgieGzJ&qk!3jl0jO@GQiqqv!t0R9Z4muw=(_^gbV~U3!R0y?14NAdx-x z`eb{&EM+c!ei&w%G~SbVM4E*yJX?-@iV$r_q@$kzH*p-znqi_gb9f5@!onPv`1GDu z2Q$Foy1>Uab*1uP5Q^~H74?ve+Uy>6rDTrknUsm0xy zpNM_w(JN!Z0y)C@@-d}M?GnUgq|6nWa!jqS-q;V^gmJ-lN%QVJvq{=Lsh!o?5_#c0 zVai)tlpcuaS2^D59c>yI9Y(#qF!8_(yD}R9tU6B1?aHc2B>ep7!x}Z-mS@7Hba+H^ z{b!3!PAXXSdEuea$q8h$Q0U1ltBUv~1>1phw}aKw^K1-n0754=#T|Lh`g$)X&`9-r zJ;!V*jjyN0{)Tt^LdCQ5*zGO#1wOsUPke$!ZpmJm8Q>~0wGm}>uHqNxx`Rriit%cT z?abtrh(?(^h)+#U7emAJHj7%C55SeS=gFg#I(ipT?F+Vaxz^weyTSMY4A&Jw5D$W5 z>bzHUt(p5{*Fp)J#_CZKvg3XB$QT_pts3C4IA~l2iltl2Jsu*aZ8#^v;K;u$n0Wd(j)%w-Rv?hSO{NKr3J^B=N{HNPxvyp@4n0a{CH5 zAM?=8ip)L7P<<2DfG9wtvYP6eCV-}Tk_gJ*t zK}rVs4XBmF1wCw2_zJ#HfIA{$A2WD0(tQUkERC*vbR2XXvR?+bPuP|epeeN}XBziw zok8Hr!eb>2ZI}nHlbBi9AL>;^GWT>zlxsW{w}LrXRDAW;3K+)?<85sm{vA4fOHT0t z|HQ9uwx%4WEcDe}+0pDeOi2yF%c|n{b%>7l6C$Icw-{}vy`K^&Ww+6Q7jV%Qvz^P~ zJGc=zB!o5=21u%r?tu?N5MGyH4cRLmN4FLzK zIy-I_S&|iDm+#JF7z|)w*;Y%!4&v5ojcBIMb*D3$^e7tb1sm*l zB$Kp~;aJ~Z{sU2?m*rzQtHDdQ#}U!juVi_+55ekolem&uNGIwdl0pZm+ z+<(nJH5(lkH~S^BvECOQ+Zm%9u(oo$n};UBY~D0e9Er@vH#F7|vA-tr2i@yn4TRO> zsabck@v~YQ){W9C;KhDS=~eI}UI&xZU2JwvXyMPl30T5slf|#+K*qp|^`d)rM{SmN zX{3mRfJIqJ`Y|@AZY-YDWqd&;L+0jZXOE(ywz@Id;KF#b@&@clI5(YsB6~S)D8&pJ z(;v!rMQSW8drvzawX9pxhKB9C#{*dMq+;;I@bv~AUH>O?oJVT)B{>UBgj5gyI1%0h z+8uF1`OOZS(|}g!y&YxDr;A5NlZegts&f1ZE-8$kK5`?MW}#&=e)_8I#; z^M*+!0~1IT(f&N#w7kmOS-p{oTwm>rf(Adz+0k0_S#qX$h z0AxA5go;Lje}|*It|epl9h6ATq4FM368%65_@wLD(b9M`CCAThMQ!^K zwzNYZ!_sNV;|L|A=MIm~0-m^G`K6J98D6cPNuY3jX^yeBu@5^fIW0^_rcU$cV zhbpPu9_~uNaj$|T=O!Otk_Ek3IkeleZ^(S2r~_i3oT-QrX05&`ak3cnN{IVz`8Rwo zBv{6gnSTrrIAu}A9tmQVdlqqHZ)sD2+aRN(R=ewl8@&CyZgfQxlwBig` z>&nnwy0%nGt_azc25)y6t*wbsc$-m*aX54v+a7Vgihc73b!tda!jNgpPJGfywp(fZ z<;V}VO|D7dm0H~$&%-BAj9WAuw7ZQSU-P{poi9gV%xqq!6K{R1ot~^ALa1D#e2GWd ze)ghcC^U4GDZyykK#?-T_$brv(Qf>Aqrb7gYY)vETBqo$&EBURS=97ZzgC3c+n$dr zub5|T!^4|@BjNuysFNJV@Ri3y%&E>z7TRWtc=}o`mEa-)3}XD|6C+wdR!fGu$(BlkyYQ8~OqS;(%X|ADRQ1$G@$|9J?u`fTL9FD*`CP0?lV|F@91fG( zkB2NulOap~KN+e<&8E#-8t&G~Q)Z`8t`wy++j80c;inL=L!|y8#|v>(M)98}{wuV*Y?emd_WDd6jWHY86e?nY5NKRM=1@T-hB4#$l5nSz}065U`qGPh1qo z7!|0nWZJR&*n(#RT`tdtyWv)R5a3#G48!2_iQ!+%Xxn_;r>!~xM{6#tCF6RN%a#`| zy>W*;D#Oq0Pqi#aL<^?Iv`UDy_5YIh0-~pG#|-0XLt*liW+<3$Dob95v}@x9t1fbl zn2j(TTgu9ekR#q34=JdX-r}=cY!2}@vTRCxRQ|C?PuPaJCv&b!d7W?i7D{>5z*DJQ z%)m00`h|Zg_E8PLie(~q5r7l<(o8raqA2^82MVFCj#5PUr$Jp3Jl;5u|Lz6<*1yOHXWo64B z@Dct)d_uk#Ne-znW|vW74?KJ(t+)rZmUes59rGF%A{XlgbhcAHWRw|bTl|ig&?UR| z)>0m&Y~78n6J6$EOax;A)3`IzQW0)%2=?JP1V&r=Yg(aqqhfMd1Ty)qTmhgfmA>vH zg{?wOJCCjpYo!ND33D zRNT}`7b+rAB*5Bpp>tGlP2q89*0S1ntEWBDo=S)A_Q&85Zq(a;J)T@yJ`OfEqr?{N zjAU9f{(zp|N=T;&61m%!dm<}$rgT0!IvIm=~{w9}zQi()#*=bwKj(gU_U zio}4KLiFVlB1hBW#eHHui%G;av(4ZAX4gq2%Obx^u^?XU&Wf^4c~fS3#9oQ}kJ6K* zl~eU%YBrN|!I}0m-KZ12iKwHsX6V!(;o~-CR>~{{rQ@}vKxiL_m?K>Wb>znMAWaSk zag`RkgI9o!sZK{jEUpOZj?7gvsV_K;@MOhP(&mLi z$^n%+WsPBc(dQOF6HA>IWPxT4x=44Zzn{})^f^{?hW^>JVKn-*vg_fMQh<+nG&7f% zEl%=NcO2Eklz`u8e6YC=L%sd}0f9i!E5!F>kB;2y8g0kRR#H8;`ZJ`8i;IDQzo6IA z?+UA-cUxU?tSWFJS8!}{a^cilTicC+EIF%h%H{72t3;$Sve&7_eNvWFU$`(ZGY2!U z8`S)64JN7JR06IIti&>Fyen>K5J{2|x8j05i;9Y>m=7eNj7=7TZagf06&bQsQdSl{ zLu=6B&c(>s;hoII#RbXTuxr&&5tYFt^(@CQstop&TlOJxa%z#>y3#sh!jT09EWjBW zzW9M-LPA0^GoRK;8fHnBs;W!mD#Kqdd0!l_;N#;17bRX68&umL7Z z!%8ga4KJ-}bH1jh|N0R{M;0pcLc!zd2Ywm-RAD#vww*7QspBk}N&^r*xTO4{Bz}L9 zsOLd{D6S~YL|KPYXXIxFb*QNQ(B$#GW8;2Nc5p+Yo!9mZ%G}(1e|YslfKmpo6jBtx zs{5iGQvNGUNnXh3>^{`r}D6LaFtJ7T%3z5Dtc1~d50j! zN>$}hU0q#rCuhgg&W??sdi)V_WHLo9+BR9_K+pBlNYk~aSZfgHec+fvJBl(I8bj}I z;EpMEJz9!xucGM-YHe>5(CBQQihIGfeU$*E!~%ClI+?d2o~JG;ez*{orPN!h88 z>pH2h_nebP0oZfO!ploUCdJNcG3ZGe(G!UI^}e7y!qO7~iEJZ z^*zN2vGd{JEJKR3!F2tZ2SLuZpZh4a zg5I1{K2}HKfJmN5qoCEc1<{KBQU-y6#j+B zxL+d~YZ)j}K0oqc#bO7D% z$V(lMZ*)Sj5DB>m?6ZA}$F@gk46_EJ|I^)0pW9dWisW%gfn5doqI@(J`3br#Zmg_6 zW-&cyf)U(1@B(JhXtT?)5xQt!)uX87%eA&kP+I;WTpES+-KSeB{Y>EfUb{LrlMqJb96Hhy_8+uC0kEA}8C+PGW86q#`H2DV+fj=lk6_zc{BS&X+`n!SuX zSE*8*e)3xxwQZ0Ot>68WM;O1ZdNm^X^P=i6poM>W@AQ7P*=5VSk-$>bZ5#9(apKiq zFk;|H%EPG9;}~1>TRt07Wsh8KAFHm5vZ_(v9x+y@md0{vt6o}wzI{BoOzuf{94OJs S1AeLpl9y4IE|Glu;lBYd?V0lc literal 0 HcmV?d00001 diff --git a/putilin_pavel_lab_3/image_2.png b/putilin_pavel_lab_3/image_2.png new file mode 100644 index 0000000000000000000000000000000000000000..0e39decc374f711565a6d2085dd1a4952f9ba0dd GIT binary patch literal 13127 zcmd6ObySpXyYCQ6Nl1r?;)`@h3J3$D(hQQ)3eqtk0+It#Qlc;2DkUH#or54C4bnMu z4;{nQbB6cb-`?vxvDUZuKI^RW$E;cRJl7r16~8O)Yoc^@RH-T1DM26*wYu6PJrIb< zknl+$Cno&Df?ys7fh1_uA1NEWu|#J18yFa8?{6SxtNaGi!FH$pNcFq`x~O;2l!xJb z?6IHdc@zccir&35Ab#>F;1)fTvM}8ieJHtrw&c(EoJX4<+!%hl$|yL7rcI?*^C#4L zG{`zbk(qPk_N=eiCqD&ZIrNzv|6%*#9ZF41G`+3PX-{1`hjps(DNzM z0bt9FY901^-jn>`^fxH@g@gcpbWSlb4tsusIH2X^m%7*n^RoepvRs3{GvYptx@(^` zzsUOdm&tyF+`Q6WY|{3DjR|mc<3cmH_?4QOKvP6^HXHVL-IfxV{K;;piJ4Jd6Q>Oi zo0!Gn>dY)2(|Dhqo_pPtltQ@6?RzIS+H=wf&ktf0IBhNWVVm_ZeW|rSbK!`Q?J6Lj z(}F!C&V>GXH+ovHo$pS&r8J?PvmIRT#`2|nt1 zTC{&TV@2p&!4@2CBZt;qTit`wo1%Qt#dRBN_{jGd8AFHqX#`?+snLr|*ST@Nm)Bnv z$}HB!$^@_k;dyISl!IIxFES@#5BaNAIAShrq}TK)^G6lKL0#W1MF+4A=EK7y$old5 zk!PR!r^b;A=jfX>pFe zks?&)RgncKMYX*|M@g1IAd5 z@2jiv-hq*^e`$8Asb6JP20iq3ZF0T<#U8?-bHU3BuByzlyEDr!-#9+;$=|uD;H86L z6&nsB?2K$)3Oe2PmUAg}+pPalCN(VdJoer;UjGf`_<`@B-Y;L9=tIVioqR)B=1-ZH z=vCa{URT%;obip;{oN+SPW}8&p4PcsAHTuE!h&WvwEnJytw1xv)}Ab?^bhBuCcA_Y zpx=m$$@@&sttd&pi6ik%Q?q_US=D*1O3}=g!@$I=v(pcN#2I zjf4VO6u?A;_MfRr3hZlAc)eKv>-*MMiCJyo3PbCVh4;wbvOPFGTF?69pQ)c->atM9 z&1<-(81Q_by>XuPzV4N_pzsw{r1a^lPR?!WWz>z@w(F z%7D!w+SMz+VVjCt1TQlx^Qv>TT_Y7xaNWkGiU??&H+Q<#TR2j60>o?5`?Y3cLK?Vl z566M*KB4JSlQveHYV2e#P8nMm$I0I3*HS%;Hc9&!u?oN0QPO$A)FM|UE7Cx0mS3T2 zGR_^iRkFGdA1}7-(x=c=iOj7V+A=8cu~jtM(`d#|?&VNZ&dicc&Zndar6f9!bmtux1JC+8VE3aY zHK}md6{nv>o=&y4YtyszN-m6M_s}Hv4ExR}S){1?c26KiyA~>WYe)LkeaAk1DzAFG zW*Eeg%ipME@$A0HEakWLu|5TrBba;)gZZl>pYr)34NXT2=Wc2#A(jAr+d-esRZD?; z=>4FM-WZZ`SU&y6z{FeRm6-y3>*6}xtSb24lPOHO;&io^EnKIBNLbjX&P>nZ5C+>f z^#9>)hT-ISW!(Oz0|Ul*@l<$GQ>1Aa%+3Fzxw@+Euxmt}r=gKie%@fT^0?P4(`UwY zmdeFRQu~T#(&IN!|Dd;ywng+|w4Jb8U3a;eUP{XMwdcc0d(~+@L;gA&$lB$k0t4^Q z&b=7JGxrw`Md zv?0oUK(S`bSjc$OA!#^#(5s~Ua4g2cxgz!I-Xo90S~-N`986$*ZhoV<5U})-@Hcz$ zbOA4jL4}gJkcZuZF)8C-EN5`Zl2`C~ChV2Fbs$h45i&I?&01746 zWiCJ)1;9w?B2WV`20We&v=jlxk%7Wk{ugKZFZkPkBI$(U!Q#h7aR0{briplX)lNL% zx?eZ-953!I3Fs^L+Z^+QU{ozx=`t4Su8~!ri;>z&=vz?R2`cj${Gj>ytBr*Dtt6 zVFm3dwe2DeIRlFW(_>bK_K0r-_!A|kflLY^DM4q2eAe1idprNT0<+`a`1hFJ^8~nR zB@~U1>^rP6XqVPuI8&-aL$d+{v(~ysX@4wu!3!1if{7z=NecDlnu={Zw77dl9WqFI*uLIa{{bUtZWQWa#Fav0*bZ9Vg{b%KsZS~?lx$Eqk*5dFp0%*1R`ICABQ zs!FT4)btEz%Q+?sai8GzscP%I=eKrX!;_wUBbKh>BQKf;^bjN37v(icPWRsCH`dh5 zEXz$TjLgnHU6kX~rK!!B!&;fl%uFNe*EXH%ST+;ftwUN`V(6RX%}$*dG}z_JWN*|p z%8Mo!6!W{`twdb)hrA`3H}##IOq@5UHFllK#~JiBMZJq<=G^)UI^Iq=D+JP_F?;uK zAf=6&DfQf>-;^p}Oq~7hd+qNO^7C!Pd$+YRQ;pfHSjc5~kx^My6FVg3}Z1p>oj>6C!Nf^XR6>eHFzB7F;rN~nPuT&_{FdkyGHmUX+3DyY5TWlLqKll>qq zG4Z9==ME|s5tJM$YB*Iytq?OxOz8#WFqthDEUx)p$oGoT)k%#pqp5n-GPO2`thR5rY;aO>()M4$qP-e zoUX_TPKGTa_(dP|sOX*rYs#9+G2qtZ*6fxW65&>uYZ$XlUEXBQvK(D6^1A28iFfzI z!>W}G#f`wiV`X?DfaY|H${a5fyle3KY3T%foz@NK7P!fesXbwc(1N_K7#%U z|9Ove;f~upf~LNvKlOGn-k6d0-2fI?{7QfQhSvquSHbrc`daPd!JB+6%=nDz+s~(t zG6I~mSXwdA;gt6E){@>?6p@494c1$+>$2f%gFCzPIVtS-0Wvqk7FY`4$0`+UCec{a zKSel?sA-*1;L?WQ2BcX_@NHufrd<&~`T5_EVTm=iwu=#Lh@HUBQ|~U_WkC1Pon8#N zdEFo1oIk+F7jL$FX<3r{8a6%?;(eR4*pb~}p`$1UNml$J%~gT+#A()l>`{l>$C-dp zSyDi|)>#J`{*aibm?~~=okJn3#%Ummdj1?E*XK}PeVkp@lx?!hoE+oiexB1b9^}oj z-L7nAn`q^AUqrBj+b4lUSR#V_6HvwyR4BY}+lkbCFR$M`;xXeVZ05`H6`g}qo@g$9 zUE8_#WGlb@7jS)9-=_E-4)S$pJNE;|tqI-6TsQA=26p$B5@+zc(c+YdX`A-UHxFDs zJ%_EIHMt!ZzbKv*P1Fs_kL=Z>#?hi1hfJm%`x@LDzm&}QXeK!&9X{6fIFofdr|E?0 zTl;of2;@Fl44fa>=9AE^8l9eXw3l!{G8hjft=UHz{NpKJp@-ngjjNw`RB zUk<311W*oUD*|8%IDJ1$M@AIp(HZOv)Io+11xe@uz}}X?*{|P92WtHf@sbBuP_)GT zYB&JTu!Rst#VXXmFmv&Ilx=)sE{kAtu{)i88lY5CItT@Ro+l$G za)YnC0Po{m12Dv5a;R?D^2am?Zhq7dQ4@sCvVfh?`M={R52@xr)PrJaf{wXBbl(3| zvp6gS7KPxkP<3$!9p_}T3Y<>a#2@l1)hlPISw!7dx{6%~YqNiV z^($GZbM_X#y%&kVay_|D*ZWDu7KA6cx2ek(3@BZbGq}14rVFJ>B0|yr{EtWE9{_`m z{xpe=!7O>&FwofbG0rjm05BVCv&xUDTXcB+a4|6HXPu}MMbc}(KR@rMBL^Idm$($@ z*mWllTJrCyMMhBT=W-iAI*ejx_|yK;;5vit9Xh0Ri9R^grx z9};2BcM++J9Cw5UC2H0-XJ#B~!MB0w6X%*~nUVq5E&YOGb@^{>`-R$KtLO3@XEKgc zt{AG(7Mj`z1{MANyzhyh;t#>x+}0nsu_|N_DO&3!zp50OT^RTXF_pLKt**m70s{~A zRNg{kE9&L-^^JmzSi22m?9-q(pEci+6Zbu1fEsT!r+je&`yY0X_Z`nuUa)Acg`-=x zjvde$8Hu$66XsSAe*TD~G_pI43bJ2_@dl%~K8vee$48NTCPkRB_X9s7gMVOi7;~(J zWnXXVnmo1p^JeIfRu8{G?oWDW4eTGn_XPOu0zTE=&GH7-B?`5nJ5pG^URS}JfK zfCHBNHC4`GA8xlb0hP(U1qIQ~)p?2i3R2qTU*mDr{k@(8QvTxl>jkR@(o$SgpJG7? z(l@N5)EzInw=*&<@ZENX(pg3QW?6|x=IYw|fuGYIit>)7cqTIYZSNIaT$~l8FMFvU z>m9pnq-z+l?mZ089J)U=abA^TDc@NACa8VKf6{x`!cxNi)_OT#EnXld?n-XR2=I06 zPqe4)Rs@IQs=3Y()dSwuY8pd!OdzB-=Pwt~pc&=e*-=1-c^d5aT0-1E7xX zc-40ipz>p|LXGgY=g^aI<}*o%Y3fsRBU|H$D9>IcWuAoSZR+daZz_IW-dt19)}Yel ze8vRP7z8go@}4f~rJSS@y8C(#2X3h~qRJdFVY6cDuEdR;TVcEttdrNKpWRZ4%MId; zD7iAnK-O9$es^M3TcXpYQi@CO&RsY$U>mtF9QP3o)5;k4nn;wIOmS6KstC-w&#?LnLIWNI(=r`34rTwzvfdNzK1fp;osk6AMq zYpgaqYXDav)|(1<~IO#KlZGJT{R7w!D5 zY*W!!)ppH}r+frJ#fs0CNhaTJF*xnb;@^lj6=__?_Sn;G1bk35VG4BW8!1Jn!=1`c zNxak*QG7t;Z-{nlO5Tw8(Ms?p(y*n=c@Onw}6#E!ayDMJCu7ah|S0whhG&#h_f5gL8wNB2d3Vy%k zbkx;o?9;3v;ps!+*I{E`K+dJU3q>bD8mFbE58SuIVx~;s# z%!c7=RezgB@LitOD+pkWlPyD0?)zF5mrJYngBEo71r-CI$3@JJr()6PM}cO>@Og=k zwcm1*c}1++jVU9p5nxQ^5O|>#9ThXVfNFRboz=}-Y2anjc8~wA<%j9udTEyGU2yXh zs@$RA$zygv8abAjWVM@9_v-__ZsI`(+y~w65s^FK$coO2$=;!j(RGf3rN(`LqCF%${k^lw?I}0Kq@ble=!=RiBfy3X+Zj zg@U!KxIF`>r+1M%l?tx>qG@TsM7homhwTJaI}F@@o_rOX67mTRvTpML5HxmMSsmuxx~h#eMgc2BDx6DQP^2K#CHQo2z9ib-Wl4RLi^4aSl!Up zGU>$*`N0F?gyUTyCX-uNmf&0j^e4gh1#@A!3c3HEn%Pm_q%=o_sBN>KF_rR%k- z4}lR?u5iHS+P3btk&WmvB$(2Qu-1`vJnp>#l zce;v(;l91o8L_uHl}K#YG0A~occV{X^E2ABr{{HU=7m@;A-?c&o4Iu7$*oD^Rsv?< zO2tp!)WE=fG+L1&_~i}JGprp3XljaAG%oD=xD5~Rl;Ff)c&;%9{#MM_ZS)<;1Y01M zuzsRxz!v2A>wUrBLAPUGE#M7*z~-mVx$3+}6q~!gcnQ}xz8$={qxp4Ei&x#qlfmB#)(tq}`ZV24%^ri(r&0+kl={VUuxQ}tkY&~IZJzI(n5DwgOzPSbGT4;r%qX1_2~jD#&J8V_+|cW& zeN_T6DFT2j%xWx1bHXAfj{or9>k60Tp7YtAzG$Zx;pO0GFGS7RsVwsbN1d&pQ$2)t1a%Tavj<)WwWJ8M;g8Rle|8v%t{P|q;91GJpVyJkmmoZ z0YN8+%m^7b27^t8I?T=-tfglSJRM!;3*7R=@P#Xy)b`w~$}Yc1t)14KbW^~|CtD%- zY-h|T5x|wdL?VW>s+d9R?X)x(luO^lIUM)EL;_YsGmtqt@3Sji^e^7`0qQwIvn!)3 zo;%<|S0n56i#GX0*^MzMKgp${KN>P$w7hpfLg3BYePE`gd<2g=WauYD%%9EF9UHqI zu7i;kj3Okg)c>ZIqq9Q-TVd(oGWJ%74P3pfzoq{t&?5yl7l%Onzd zT>O^;hTc}2CqynBfcY*pq80GNmRFrL1crmr@l2v)-#b!Ke>jg$PS@mU`R#fn69Xh5 z0>bE?z-H!nOYQevnov+lQlSOi>S?^=5(iAZW@3|V5eB*xZZw_&BGmWGwcZfb6?lZr zp9Gp4H|N5lKB7%fT%b$a7zHAarV_UpvGH8s5HQgbE&?{)MuN$?|5k>7lJ)-|n(=lE zZfk)zvsd)rpWCR$)#DRv{D+2OATJA<2OvR6GI&7Y;S)Ezdz>Ox-_dc#K^?EQIr4So zvgZG6IF`bQ@~@Rj0OpOzpD#~`NSW4ed|^b7iTX4iA(PYJ%T^tBw?~o^xceLp=gHmF z=tIgttiaCxIi&c5<^7(E`ZUk2v6$pQE^`z*cM|VjjP1T_d;gjD_CZD>^0d)PpZhOx z^i#(l(uxLX7w43t+2Y7KS@tD4{h>}LCl(Vni3y6QeOcwL582i`MSttJ-TGVR-RD-H za1;!8GX-GB_QLPAIV`_)NT@FGjmI{YYw}0D* z$u7YmmtrUDAZJXHD)LW_{#B?2ryk44d9X|O4mbqxZ=Wv%0%23`)YU!NGnv675F z(jt37xCpW?u)(K*{twjuH1+?&fS{&1SaRLN3He%cuN4MYUPM*(TaKp{w@fp5n_kU*KPb^5eXpMOR$Cb?Gq4FCa6c zkLL9Q-FtS^9+rfkQF@9m0&17Y^$^cc4Gcsbt$8k|j-#JU^HtdgC9H!}60(lg;rHO~ zi=ewGQL@i#K+^ndEPm3p_fz0Za_3MsJ=<0qBJW~o?PX!}1>pTT$+o~1z4DaAi|7)< zAN~SX>rG%^8c^di=}# zmEz7u($gwyqs%Fd1T4eQ-5o=UNP2}Us7v5q#N-6Eeh{+dKyu(@vaOFFOzRBVH~TF1 z;L;#6E`69IruF7RV8_&_1Niv252xaF9n$xUc%T|MXkW8y2uzq2J`-wNi#NGZx3yAi zkKiAL-2(j;VCUgv;4L}pzO|jDRu8iikXmn&bCN-3Fdrx7JpmhhQjM@Q@e&~M+@FO7 zBK6sLN@Rtc{SzI9me<`I&K!;qB;iI4Vkz6^2dHm*HTL+^>B(6= znvo~+uhpVnM;CAV?+vW4DkO|Oy&H6Ntx;02#@BlWZfB0>L!v&14Zzh(J~#EoTI>Z? zy(0R{K)MyAkzEqhTxG@4X?a*zNIEoLsGsLhy8l@hwouqKt8t*RPi-)iV*1-HpX6a& zp~j$M!wxNnrFM+TPaOpX*LzuC+}rXlASs_bx>xtWS?D)4fqtuuGdcJ8)4xP;dJcTM zD*HFAOW2D5(#J;=^-_Ml?cFPG<hV)sOus8U7Tq zjqp*lXxcnP<6m7{qJ z=>N`Vc^?hjopo#|i@{lAB_xMf9#jsFy5GsR_X7B5c>SRdM(GM$R;}oyVxEu^PTL5) z?v&7sQG3N6{B>+B>S;|3RrH2k1^4SWQl$}ZY0hlIGXJ)>KVEdU3nT0-gS<@CyZYD2 zm|eIdr+1Vf9dU26rCI?6ZUy!enyijRX@ zi7~Xf`8fl%N;gZ(eRuVXOCO>IP?*~2r)n=L_^LVY%jnZ|L5#K!CFkC4nB)RK(QG}8 z#1%Cp$l=?XGHB@UAol0^n~nQKJd~~VT*^uYwu99-(k@3o( z{8QOv4c9BI$OybvL(x8@BS`eWZrcC33dZ025Z}1NpMpcF%I=92yCBSJzG1S7F4Ia*u6I zg04oXGIPp%#}2k0^$HStE%JUgcy8PT2W5)uFW0)x-qlJ~%K!fT`zB+co^&IF6N5m# zH`3I`P&NK${@d5auN!{<){C|x+?}O-{J3OducdX(A~9TcND(8HR5Obt+~9rc7%hYp z=HzVLA=#iUJVV6SDv1ZkqKyu3fsAE;9tWh(Zlh-;L8Fz2p$_39PP%?Hn{te_8UuQeA56#K6Cr~NTYCE8q} z9$x(AwSdm@h#?8a(drKKTGA9@d@ISaa`s_phEfRIvnW&hU+;p;Y1Z#;t&ezvh>*3ifDci3y zucK9U<}#|1Wh~2tx-%I&v=h$|cyZ%~DR<-Zv? zIyg8uIdKEi5QpmG!l~BE%EP$^Pf0`eJIB@4k^+_`4_AII;zwy@)o;rg4z^h3p|?i! z2D>C>^{X~d-3>kPR6BX{b1nWd^9M6E&SY@v-j3Gm%9^z3w~6$L=P+}Yf3G~ABN!+f z9DB!eyzE#U_b*OH=NtlB$|XFOAEjdn1*Rp=;>S8r~?q8z+N~E zq!Iy@r*18U1A#VHo18zrGAkh$c+7bO{R=RpK$<@n_5zDh6+U{11s;JQIGx!ZMfNiJqbqqUIwV2F%daHidQWhHvC8h6bf_s@oRC`ICP)(BX;HLX%6hJ(iGwU z5_cjVkUrQBWE@`NvcFvWBzT{MskP1k733B97fBUx7oaAJr+L8$3JQVxP`Bn7pl%YL zu`AtQfdO}#uMw>@#}i+}r(L-d3bpb)eev=R?qD%emSeZD&9m{#e(>8M8(O*2` zYz1FCXxStJZUl?jr{Xh+rAR?Q`*|2qEAU`32SkS_bgy0yr6H2TGEicv5zE}DY=$3T zqFY$DR-oWBHPLkyjw`I#1gZ!s#P@|I|2`^$65RXo4l6_LOaAV_mhlZAwnKy=z$_Nj z8rj@&F2^cJ=mAX-RZ$AFQY8{|L0Pgfw!(j~w$tJ>0&WnqV((Bq;cBIg03k-7yBq>U aZ5vB@F4a;Ogg?lD)F10SDtq|+?f(GtN83aI literal 0 HcmV?d00001 diff --git a/putilin_pavel_lab_3/nginx/Dockerfile b/putilin_pavel_lab_3/nginx/Dockerfile new file mode 100644 index 0000000..ff0efa0 --- /dev/null +++ b/putilin_pavel_lab_3/nginx/Dockerfile @@ -0,0 +1,5 @@ +FROM nginx:alpine + +COPY nginx.conf /etc/nginx/nginx.conf + +EXPOSE 80 diff --git a/putilin_pavel_lab_3/nginx/nginx.conf b/putilin_pavel_lab_3/nginx/nginx.conf new file mode 100644 index 0000000..3fb4a37 --- /dev/null +++ b/putilin_pavel_lab_3/nginx/nginx.conf @@ -0,0 +1,17 @@ +server { + listen 80; + + location /client/ { + proxy_pass http://client_service:8000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /order/ { + proxy_pass http://order_service:8001/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} diff --git a/putilin_pavel_lab_3/order_service/Dockerfile b/putilin_pavel_lab_3/order_service/Dockerfile new file mode 100644 index 0000000..7c3e688 --- /dev/null +++ b/putilin_pavel_lab_3/order_service/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.10-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8001"] diff --git a/putilin_pavel_lab_3/order_service/app.py b/putilin_pavel_lab_3/order_service/app.py new file mode 100644 index 0000000..8505d6c --- /dev/null +++ b/putilin_pavel_lab_3/order_service/app.py @@ -0,0 +1,72 @@ +from fastapi import FastAPI, HTTPException +from uuid import uuid4 +import requests + +app = FastAPI() + +# Временное хранилище для заказов +orders = {} + +# URL микросервиса клиентов (обновим при подключении Docker Compose) +CLIENT_SERVICE_URL = "http://client_service:8000" + + +@app.get("/") +def get_orders(): + return list(orders.values()) + + +@app.get("/{order_id}") +def get_order(order_id: str): + if order_id not in orders: + raise HTTPException(status_code=404, detail="Order not found") + return orders[order_id] + + +@app.post("/") +def create_order(order: dict): + # Проверка существования клиента + client_id = order.get("client_id") + if not client_id: + raise HTTPException(status_code=400, detail="client_id is required") + + try: + response = requests.get(f"{CLIENT_SERVICE_URL}/{client_id}") + response.raise_for_status() + except requests.exceptions.RequestException: + raise HTTPException(status_code=404, detail="Client not found") + + # Создание заказа + order_id = str(uuid4()) + new_order = {"id": order_id, **order} + orders[order_id] = new_order + return new_order + + +@app.put("/{order_id}") +def update_order(order_id: str, order: dict): + if order_id not in orders: + raise HTTPException(status_code=404, detail="Order not found") + + # Проверка существования клиента + client_id = order.get("client_id") + if not client_id: + raise HTTPException(status_code=400, detail="client_id is required") + + try: + response = requests.get(f"{CLIENT_SERVICE_URL}/{client_id}") + response.raise_for_status() + except requests.exceptions.RequestException: + raise HTTPException(status_code=404, detail="Client not found") + + updated_order = {"id": order_id, **order} + orders[order_id] = updated_order + return updated_order + + +@app.delete("/{order_id}") +def delete_order(order_id: str): + if order_id not in orders: + raise HTTPException(status_code=404, detail="Order not found") + del orders[order_id] + return {"detail": "Order deleted successfully"} diff --git a/putilin_pavel_lab_3/order_service/requirements.txt b/putilin_pavel_lab_3/order_service/requirements.txt new file mode 100644 index 0000000..5e7e855 --- /dev/null +++ b/putilin_pavel_lab_3/order_service/requirements.txt @@ -0,0 +1,3 @@ +fastapi +uvicorn +requests