์ฌํด ์ ์ฌ ํ์ Golang์ผ๋ก ๋ง์ ์ฝ๋๋ฅผ ์์ฑํ๋ค. Golang์ผ๋ก gRPC ํต์ ํ๋ ์ฌ๋ฌ microservice ๋ค์ ๊ฐ๋ฐํ๋ค. ๊ทธ๋ฌ๋ค๊ฐ ๋ฌธ๋, Golang์ผ๋ก ๋ง์ด ๊ฐ๋ฐ ํ๊ธด ํ์ง๋ง ๋ด๊ฐ gRPC์ gRPC server, gRPC gateway, middleware๋ฑ์ ๋ํด ์ ํํ๊ฒ ์ดํดํ๊ณ ์๋? ๋ผ๋ ์๊ฐ์ด ๋ค์๋ค. ๊ทธ๋์ ๋ด๊ฐ ์ ์ดํดํ๊ณ ์๋๊ฐ ํ์ธํ๊ธฐ ์ํด ๋ด๊ฐ ์ฌ์ฉํ๊ณ ์๋ ๊ธฐ์ ๋ค์ ๊ณต๋ถํ๋ฉฐ ์ ๋ฆฌ ์ฐจ์์์ ๊ธ์ ์์ฑํ๋ ค๊ณ ํ๋ค.
gRPC ๊ฐ๋ ์ ๋ฆฌ๋ถํฐ gRPC๋ก ํต์ ํ๋ Golang server๋ฅผ ๊ตฌ์ถํ๋ ๊ฒ๊น์ง ์ด๋ก ๋ถํฐ ์ค์ ๊น์ง step by step์ผ๋ก ์ฐจ๊ทผ์ฐจ๊ทผํ ์๋ฆฌ์ฆ๋ก ์ ๋ฆฌํด๋๊ฐ๋ ค๊ณ ํ๋ค.
Contents
- Golang gRPC server ๊ตฌ์ถํ๊ธฐ (1) - gRPC๋ ๋ฌด์์ธ๊ฐ?
- Golang gRPC server ๊ตฌ์ถํ๊ธฐ (2)- gRPC server ํํค์ณ๋ณด๊ธฐ
- Golang gRPC server ๊ตฌ์ถํ๊ธฐ (3) - RESTfulํ๊ฒ gRPC server์ ํต์ ํ๊ธฐ
- Golang gRPC server ๊ตฌ์ถํ๊ธฐ (4) - gRPC middleware๋?
gRPC๋?
gRPC์ ๊ฐ๋ ๊ณผ ์ฌ์ฉ๋ฒ์ ๋ฐ๋ก ์ค๋ช ํ๋ ๊ฒ๋ณด๋ค, gRPC ๋์ ์ ์ด๋ค๊ฑธ ์ฌ์ฉํ๊ณ ์๋์ง ์ดํด๋ณด๋ฉด ๋ gRPC๋ฅผ ์ดํดํ๋๋ฐ ๋์์ด ๋ ๊ฒ ๊ฐ๋ค (๋ค๋ฅธ๋ง๋ก ํ๋ฉด, gRPC์ ๋์ฒด์ ๊ฐ ๋ฌด์์ธ์ง).
gRPC ๋์ ์ ํํ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ๊ฒ์ด JSON์ ์ฌ์ฉํ HTTP API์ด๋ค. HTTP API๋
- Client โโ Server
- Server โโ Server
๊ฐ ์๋ก ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ๋ ์ฌ์ฉ๋๋ ํต์ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ์๊ฐํ ์ ์๋ค.
๋ฐ์ ์์ ๋ฅผ ๋ณด๋ฉด Client๊ฐ HTTP๋ก RESTfulํ๊ฒ ์๋ฒ์๊ฒ GET์ผ๋ก post๋ค์ ์์ฒญ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ์๋ฒ๋ ์์ฒญ์ ๋ํด JSON์ผ๋ก post๋ค์ ๋ฆฌ์คํธ๋ก ์ ๋ฌํ๊ฒ ๋๋ค. (HTTP์ REST๋ผ๋ ๊ฐ๋ ์ ๊ฑฐ์ ํญ์ ๋ถ์ด๋ค๋ ์ ์ฌ์ฉ๋๋ค, ์ด ๋ธ๋ก๊ทธ๋ฅผ ์ฐธ๊ณ ํ๋ฉด ์ดํด์ ๋์๋๋ค)
์์ ๋ฅผ ๋ณด๋ฉด ์ ์ ์๋ฏ์ด JSON์ ์ฌ์ฉํ HTTP API๋ ๋ฐ์ดํฐ๋ฅผ ์๋ก ์ฃผ๊ณ ๋ฐ์ ๋ ์ฌ์ฉ๋๋ค.
๋ฐ์๋ gRPC ํํ์ด์ง์ ๊ฐ๋ฉด ๋ณผ ์ ์๋ ๊ทธ๋ฆผ์ด๋ค. ์์ธํ ๋ค์ฌ๋ค๋ณด๋ฉด ์ HTTP API๋ก ํต์ ํ๋ ๊ทธ๋ฆผ๊ณผ gRPC ํํ์ด์ง ๊ทธ๋ฆผ์ ์ฐจ์ด์ ์ GET๋์ ์ Proto Request
๋ฅผ ์ ๋ฌํ๋ ๊ฒ ๊ฐ๊ณ , JSON
๋์ ์ Proto Response
๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ธ๋ค. ๊ฒฐ๊ตญ, ํฐํ์์ ๋ดค์ ๋๋ JSON์ ์ฌ์ฉํ HTTP API์ ๊ฐ์ด gRPC๋ ๊ฒฐ๊ตญ์ client โโ server, server โโserver๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋ฐฉ์ ์ค ํ๋๋ผ๊ณ ์๊ฐํด ๋ณผ ์ ์๋ ๊ฒ์ด๋ค.
์ด์ gRPC๊ฐ ๋๋ต ๋ญ์ง ์ดํด๋ฅผ ํ๋ค๋ฉด, ์กฐ๊ธ ๋ ๊น์ด gRPC์ ๋ํ ์ดํด๋๋ฅผ ๋ํ๋ณด๋ ค๊ณ ํ๋ค.
์ผ๋จ gRPC๊ฐ ๋ญ์ง ์๋ ค๋ฉด RPC
๊ฐ ๋ญ์ง ์ง๊ณ ๋์ด๊ฐ์ผ ํ๋ค. RPC(Remote Procedure Call)
๋ ์๋ ๋ถ์ฐํ๊ฒฝ ์์คํ
์์ ์๋ก ๋ค๋ฅธ ์ปดํจํฐ ํ๋ก๊ทธ๋จ๋ค์ด ์๋ก ๋ค๋ฅธ ์ฃผ์์์ ์๋ก๋ฅผ ํธ์ถํ์ง๋ง ๋ง์น ๊ฐ์ ์ฃผ์์์ ํธ์ถํ๋ ๊ฒ์ฒ๋ผ ์๋ํ๊ฒ ํ๋ ์๊ฒฉ ํ๋ก์์ ธ ํ๋กํ ์ฝ์ด๋ค. ์ฆ, ํ๋ก๊ทธ๋จ๋ค์ ์๋ก๊ฐ ๋๊ตฌ์ธ์ง ์ ํ์ ์์ด ์ ํด์ง ๋ฐฉ์๋๋ก ๋จ์ํ ํจ์ ํธ์ถ
๋ง ํ๋ฉด ๋๋ ๊ฒ์ด๋ค. ์ ์ฌ์ง์ ์์ ๋ก ๋ค๋ฉด Ruby Client๋ ํต์ ํ๊ณ ์๋ C++ Service๊ฐ ์ด๋ ์์น์ ์๋, ์ด๋ค ์ฝ๋๋ก ์์ฑ๋์๋ ๊ฐ์ ์๊ด ์์ด ์ ํด์ง Proto๋ผ๋ ๊ท๊ฒฉ๋ง ์์ผ๋ฉด ์์ ๋กญ๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๋ ๊ฒ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด g
RPC๋ ๋ฌด์์ผ๊น?
Google์ด ๋ง๋ protobuf(a.k.a protocol buffer)๋ผ๋ ๋ฐฉ์์ ์ฌ์ฉํด RPC
๋ผ๋ ํ๋กํ ์ฝ์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ํ๋ซํผ์ ์๋ฏธํ๋ค.
(๐คซ Google์ด ๋ง๋ค์ด์ gRPC์ g
๋ google์ g
๋ผ๋ ์๋ฌธ์ด ์๋ค - ๊ทธ๋ฆฌ๊ณ ์ค์ ๋ก๋ ๊ทธ๋ด ๊ฒ์ด๋ผ๋ ์๊ฒฌ์ด ์ง๋ฐฐ์ ์ด๋ค)
์ฌ๊ธฐ๊น์ง ์๋ค๋ฉด, gRPC๋ protobuf๋ผ๋ ๋ฐฉ์์ ์ฌ์ฉํด์ ํต์ ํ๋ ํ๋ก๊ทธ๋จ๋ค๊ฐ ์๋ก์ ๋ํ ๋คํธ์ํฌ๋ ์ฃผ์ ๋ฑ์ ๋ํ ์ ๋ณด๋ฅผ ๋ชจ๋ฅด๊ณ ๋ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๋ ํต์ ๋ฐฉ์ ์ค ํ๋๋ผ๊ณ ์ดํดํ ์ ์์ ๊ฒ์ด๋ค.
์ gRPC๋ฅผ ์ฌ์ฉํ๋๊ฐ?
gRPC๊ฐ ๋ญ์ง ์ดํดํ๋ค๋ฉด, ๋๋์ฒด ์ด๋ค ์ฅ์ ๋ค์ด ์๊ธธ๋ gRPC ์ฌ์ฉ์ด ์์ฆ์ ๐ฅ ํซํด์ง๊ณ ๐ฅ ์๋์ง ์์๋ณด๋ ค๊ณ ํ๋ค.
feat. gRPC๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ๐ ์คํํธ์ ๋ค
- ๋ฑ ํฌ์๋ฌ๋ ๊ธฐ์ ๋ธ๋ก๊ทธ - ํ๋ก๋์ ํ๊ฒฝ์์ ์ฌ์ฉํ๋ golang๊ณผ gRPC
- ๋น๊ทผ๋ง์ผ ๊ธฐ์ ๋ธ๋ก๊ทธ - ์์ฌ๋ฒํธ ๋ง์ดํฌ๋ก์๋น์ค ๊ฐ๋ฐํ๊ธฐ
- ๋ฒ์ฆ๋น ๊ธฐ์ ๋ธ๋ก๊ทธ - gRPC๋ฅผ ์ฐ๋ฉด REST๊ฐ ๊ณต์ง!?
gRPC์ ์ฅ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
-
๋น ๋ฅด๋ค
protocol buffer๋ผ๋ ๋ฐฉ์์ XML๊ณผ ๊ฐ์ด ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ ์ง๋ ฌํ(serialize)ํ๋ ๋ฐฉ์์ธ๋ฐ ์์ถ์ ํด์ ํจ์ฌ ๋น ๋ฅด๊ณ , ์ฌ์ฉ๋ฒ๋ ๊ฐ๋จํ๊ณ , ๋ฐ์ดํฐ์ ํฌ๊ธฐ๋ ์๋ค. JSON ์ง๋ ฌํ๋ณด๋ค ์ต๋ 8๋ฐฐ ๋ ๋น ๋ฅผ ์ ์๋ค๊ณ ํ๋ค ref.
-
์๊ฒฉํ ์ฌ์ ๋๋ถ์ ์๊ธฐ๋ ๊ฐ์ด๋์ ์กด์ฌ ์ ๋ฌด (IDL)
gRPC๋ก ํต์ ํ๊ธฐ ์ํด์๋ protocol buffer๋ก ์ด๋ป๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ๊ฒ์ธ์ง ์ ์ํด๋์ผ ํ๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์, ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ๋๋ ์ด๋ค ํ์์ผ๋ก ์ ๋ฌํด์ผํ๋์ง์ ๋ํ ๊ฐ์ด๋๊ฐ ์ฝ๋๋ก ์์ฑ๋๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก API ๋ฌธ์๊ฐ์ ๊ฒ์ ๋ง๋ค ํ์๊ฐ ์๊ฒ ๋๋ ๊ฒ์ด๋ค. ์ฌ๋ฌ ํ์ฌ์์๋ ์ด๋ฐ ์๊ฒฉํ ์ฌ์ ๋๋ถ์ ๊ฐ๋ฐ ์์ฐ์ฑ์ด ๋์์ง๋ ์ด์ ๋ ์์ด์ gRPC๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ค์ ์๋ค. ref. ๋ฑ ํฌ์๋ฌ๋ ๊ธฐ์ ๋ธ๋ก๊ทธ
-
HTTP/2
gRPC๋ HTTP/2๋ฅผ ์ง์ํ๋ค. gRPC๋ HTTP/1.1๊ณผ ํธํ๋์ง๋ง HTTP/2๋ง์ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค๋ ์ฌ์ฉํ ์ ์๋ค. ๋์ผํ ์ฐ๊ฒฐ๋ก ๋ณ๋ ฌ์ ์ธ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๊ณ , ์ฐ๊ฒฐ์ ์ ์งํด์ connection์ ๋งค๋ฒ ํ๋๋ฐ ์ฌ์ฉ๋๋ cost๋ ์ค์ผ ์ ์๋ค.
gRPC์ ์ด๋ฌํ ์ฅ์ ๋ค์ ์์ ์ ํํ๊ณ ์๋ Microservice์ ์์ฑ๋ง์ถค์ด๋ค. Microservie๋ ๊ฐ๋จํ ๋งํ๋ฉด ์๋น์ค์ ๋ชจ๋ ๊ธฐ๋ฅ๋ค์ ํ๋์ ํฐ ์๋ฒ์ ๋ชฐ์๋๋ ๊ฒ์ด ์๋, ๊ฐ๊ฐ์ ๋ ๋ฆฝ์ ์ธ ๊ธฐ๋ฅ์ผ๋ก ์ฌ๋ฌ ์๋ฒ๋ก ๋ง๋ค๊ณ ์๋ฒ๋ค๊ฐ ํต์ ํ๊ฒ ๋ง๋๋ ์ํคํ ์ฒ๋ฅผ ์๋ฏธํ๋ค. ref. ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์ ์ฅ๋จ์
Microservice์๋ ๋ ๋ฆฝ์ ์ธ ๊ธฐ๋ฅ์ ์ํํ๋ ์๋ฒ๋ค์ด ์๋ง์ด ์๊ธฐ ๋๋ฌธ์ ์ด ์๋ฒ๋ค๊ฐ ๋ด๋ถ ํต์ ์ด ๋งค์ฐ ๋ง๊ฒ ๋๋ค. ์ฒ๋ฆฌํ๋ ๋ฐ์ดํฐ์์ด ๋ง๊ธฐ ๋๋ฌธ์ ํจ์จ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ํต์ ํ ์ ์์ด์ผ ํ๋ค. ๊ทธ๋ฆฌ๊ณ gRPC๋ JSON, XML๋ณด๋ค ํจ์ฌ ๋น ๋ฅด๊ณ ํจ์จ์ ์ผ๋ก ๋ง์ ๋ฐ์ดํฐ๋ฅผ ์์ถํด์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ์ํคํ ์ฒ์ ์ ํฉํ ๊ฒ์ด๋ค. ์ถ๊ฐ๋ก, ์๋ฒ๊ฐ ํต์ ํ ๋ ์ด๋ค request๋ฅผ ๋ณด๋ด์ผ ํ๋์ง ์๊ฒฉํ ์ฌ์์ผ๋ก ์ฝ๋๋ก ์ ์๋์ด ์์ผ๋ microservice๋ค์ ๊ด๋ฆฌํ๋๋ฐ ๋ ์์ํ ๊ฒ์ด๋ค.
๋ฌผ๋ก , gRPC์ ๋จ์ ๋ ์กด์ฌํ๊ธด ํ๋ค.
- ์ผ๋จ ๊ฐ์ฅ ํฌ๊ฒ ๋ธ๋ผ์ฐ์ ์ ์๋ฒ๊ฐ์ gRPCํต์ ์ด ์์ง ์ง์๋์ง ์๋๋ค. ๊ทธ๋์ ๋ธ๋ผ์ฐ์ ์์๋ ํํ JSON์ผ๋ก ์๋ฒ๋ก ์์ฒญํ๋ฉด ์๋ฒ๋ grpc-gateway๋ฅผ ํตํด protobuf ํ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณํํ ๋ค์ ์ฌ์ฉํ๊ฒ ๋๋ค.
- ๊ทธ๋ฆฌ๊ณ , ์ฌ๋์ด ์ฝ์ ์ ์๋ ๋ฐ์ดํฐ ํ์์ด๊ธฐ ๋๋ฌธ์ ๋คํธ์ํฌ ๋จ์์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๊ณ ์ถ์ผ๋ฉด ์ถ๊ฐ์ ์ธ ์์ ๋ค์ด ํ์ํ๋ค.
์ด์ ๊ฐ์ด gRPC์๋ ์ฅ๋จ์ ๋ค์ด ์์ง๋ง, microservice ์ํคํ ์ฒ์์๋ ์ฌ์ฉํ๊ธฐ ์ ํฉํ ๊ธฐ์ ์ด๊ธฐ ๋๋ฌธ์ ๋จ์ ์ด ์์์๋ ๋ถ๊ตฌํ๊ณ ๋ง์ ํ์ฌ๋ค์ด ํด๋น ๊ธฐ์ ์ ์ ์ฉํ๊ณ ์๋ ์ถ์ธ์ธ ๊ฒ์ด๋ค.
(์ฌ์ฉ๋น๋๊ฐ ๋์์ง๊ณ ๋ ์์ผ๋, JSON์ ์ฌ์ฉํ HTTP API ํต์ ์ ๋นํด์๋ ์์ง๋ ์ ์ ์จ์ ๋งค์ฐ ๋ฎ๊ธด ํ๋ค)
์์ gRPC์ protobuf์ ๋ํด ๊ฐ๋ตํ ์๊ฐ๋ฅผ ๋ค์์ผ๋ ์ด์ gRPC๋ฅผ ์ฌ์ฉํ server๋ฅผ ๊ตฌ์ถํด๋ณด๋ ค๊ณ ํ๋ค.
๊ฐ๋จํ gRPC server ๊ตฌํํ๊ธฐ
Protobuf๋ ํ์ฌ C++, Java, Python, Go, Ruby, Objective-C๋ฑ์ ์ง์ํ์ง๋ง(ref), ์ด ํฌ์คํธ์์๋ ๋ด๊ฐ ์ฃผ๋ก ์ฌ์ฉํ๋ Go ์์ฃผ๋ก ๋ค๋ฃจ๋ ค๊ณ ํ๋ค.
Go ์ธ์ด๋ฅผ ์ค์นํ๋ ๋ฐฉ๋ฒ๋ค์ ์๋ ๋ค๋ฅธ ๋ธ๋ก๊ทธ๋ค์์ ์์ธํ ๋์์๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ๋ค๋ฃจ์ง ์์ผ๋ ค๊ณ ํ๋ค. ๋ค๋ง, ๋ด๊ฐ Go๋ฅผ ์ฒ์ ์ค์นํด์ ์ฌ์ฉํ๋ ค๊ณ ํ ๋ ์ฐธ๊ณ ํ๋ ๋ธ๋ก๊ทธ๋ค ๋ช๊ฐ๋ง ๊ณต์ ํ๋ ค๊ณ ํ๋ค:
- ์์ ๋ก ๋ฐฐ์ฐ๋ Go ํ๋ก๊ทธ๋๋ฐ
- Go์ธ์ด ์์ํ๊ธฐ - Golang Korean Community
- go mod๋ฅผ ์ด์ฉํ ํจํค์ง ๊ด๋ฆฌ ๋ฐฉ๋ฒ
- golang - ์ค์น์ GoPath
- (๋ฒ์ญ) Go Modules ์ฌ์ฉํ๊ธฐ
์ ์์ ๋ค์ ์ฐธ๊ณ ํ๋ฉด Go ์ธ์ด๋ฅผ ์ปดํจํฐ์ ์ ์ค์นํ ์ ์์ ๊ฒ์ด๋ค. ๊ทธ๋ ๋ค๋ฉด ์ด์ ๊ฐ๋จํ gRPC server๋ฅผ ๊ตฌํํด๋ณด๋ ค๊ณ ํ๋ค.
(๋ธ๋ก๊ทธ์ ๋์ค๋ ์ฝ๋๋ค์ ์ ๋ถ Github Repository์์๋ ๋ณผ ์ ์๋ค)
๋จผ์ go mod init ๋ชจ๋ ์ด๋ฆ
์ผ๋ก Go ๋ชจ๋์ ๋ง๋ ๋ค.
// ์์
go mod init github.com/dojinkimm/go-grpc-example
go.mod
์ ๋ค๋ฅธ ๋ชจ๋๋ค์ importํ ์ ์๊ณ , importํ๊ฒ ๋๋ฉด ํด๋น ๋ชจ๋๋ค์ ํ๋ก์ ํธ ๋ด์์ ์์ ๋กญ๊ฒ ์ฌ์ฉ ํ ์ ์๊ฒ ๋๋ค. go.mod
ํ์ผ์ด ์์ฑ๋๊ณ ๋์๋ grpc
๋ชจ๋์ importํด์ผ ํ๋ค. Go๋ก ๊ตฌํ๋ gRPC์ ์ฝ๋๋ ์คํ์์ค๋ก grpc-go repository์ ์๋ค. go get -u
๋ชจ๋ ์ด๋ฆ ์ผ๋ก grpc-go
ํจํค์ง๋ฅผ import ํด์จ๋ค.
go get -u google.golang.org/grpc
grpc-go
๋ชจ๋์ importํ๋ฉด main.go
์์ ๋ถ๋ฌ์์ ์ฌ์ฉํ ์ ์๋ค. gRPC server๋ฅผ ๊ตฌํํ๋ ๊ฒ์ ๋งค์ฐ ๊ฐ๋จํ๋ค.
- ๋จผ์
net
ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํจํค์ง(net package)๋ฅผ ์ฌ์ฉํด์ ์ด๋ค ๋คํธ์ํฌ์ ์ด๋ค port number๋ก ์๋ฒ๋ฅผ ์คํ์ํฌ์ง ์ ์ํ๋ค. - importํ google.golang.org/grpc ๋ชจ๋์์
NewServer()
ํจ์๋ฅผ ํธ์ถํด์์ gRPC server๋ฅผ ๋ง๋ ๋ค (ref ์ฝ๋). net
ํจํค์ง๋ฅผ ์ฌ์ฉํด์ ๋ง๋ listener connection์Serve()
๋ผ๋ ํจ์์ ์ธ์๋ก ๋ฃ์ด์ค๋ค (ref ์ฝ๋).
// simple/main.go
package main
import (
"log"
"net"
"google.golang.org/grpc"
)
const portNumber = "9000"
func main(){
lis, err := net.Listen("tcp", ":" + portNumber)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
log.Printf("start gRPC server on %s port", portNumber)
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %s", err)
}
}
์ด์ ์ด main.go
๋ฅผ go run simple/main.go
๋ก ์คํ์ํค๋ฉด 9000 port number์ gRPC server๋ฅผ ์คํ์ํฌ ์ ์๊ฒ ๋๋ค.
๋ฌผ๋ก , ์๋ฌด request๋ ๋ชป ๋ฐ๋ ๋ฌด์ฉ์ง๋ฌผ์ gRPC server์ด์ง๋ง, ์ด๋ ๊ฒ ๋ช์ค์ ์ฝ๋๋ก ๊ฐ๋จํ gRPC server๋ฅผ ๊ตฌํํ๋ค.
Protobuf ์๋น์ค ์ ์ํ๊ธฐ
์์์ gRPC server๋ฅผ ๊ตฌํํ๋ ๋ฒ์ ๋ฐฐ์ ์ผ๋, ์ด์ ๋ microservice์ ๋ง๊ฒ ํน์ ์๋น์ค๋ฅผ ๋ด๋นํ๋ gRPC server๋ฅผ ๊ตฌํํด๋ณด๋ ค๊ณ ํ๋ค. gRPC sever๋ protobuf ๋ผ๋ ๋ฐฉ์์ ์ฌ์ฉํด์ ์ ํด์ง ์์๋๋ก ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋๋ค. ๊ทธ๋์, protobuf๋ก ๋ง๋ค gRPC server์ ๋ฉ์ธ์ง๋ค์ ๋จผ์ ์ ์ ํด์ผ ํ๋ค.
์ด๋ฒ์ ๊ตฌํํ๊ฒ ๋ gRPC server๋ ์ ์ ์ ์ ๋ณด๋ฅผ ๋ด๋นํ๋ microservice ํํ์ด๋ค. Protobuf ์ฌ์ฉ๋ฒ์ ๋ํด ๋ ์์ธํ ์๊ณ ์ถ์ผ๋ฉด ๋ฐ์ ๋ ์ฌ์ดํธ๋ฅผ ์ฐธ๊ณ ํ๋ฉด ๋์์ด ๋ง์ด ๋๋ค.
User ์๋น์ค๋ ๋ ๊ฐ์ rpc๋ฅผ ๊ฐ๋๋ก ๊ตฌํํ ์์ ์ด๋ค.
GetUser
๋ user id๋ฅผ ์ ๋ฌํ๋ฉด, user id์ ๋ง๋ ์ ์ ์ ์ ๋ณด๋ฅผ ๋ฆฌํดํ๋ rpc์ด๋คListUsers
๋ ์๋น์ค์ ์กด์ฌํ๋ ๋ชจ๋ ์ ์ ๋ค์ ์ ์ ์ ๋ณด๋ฅผ ๋ฆฌํดํ๋ rpc์ด๋ค
// protos/v1/user/user.proto
syntax = "proto3";
package v1.user;
option go_package = "github.com/dojinkimm/go-grpc-example/protos/v1/user";
service User {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
}
message UserMessage {
string user_id = 1;
string name = 2;
string phone_number = 3;
int32 age = 4;
}
message GetUserRequest {
string user_id = 1;
}
message GetUserResponse {
UserMessage user_message = 1;
}
message ListUsersRequest{}
message ListUsersResponse {
repeated UserMessage user_messages = 1;
}
proto
๋ฅผ ์ ์ํ๋ค๋ฉด, ๊ทธ ๋ค์์ ์ค์ ๋ก gRPC server์์ ์ฌ์ฉํ ์ ์๋๋ก protoc
์ปดํ์ผ๋ฌ๋ฅผ ์ฌ์ฉํด์ ์ปดํ์ผ ํด์ผ ํ๋ค. ref
protoc
์ปดํ์ผ๋ฌ๋ฅผ ๋ค์ด ๋ฐ๊ณ ๋์, ๋ฐ์ ๋ช
๋ น์ด๋ฅผ ํ์ฉํด์ Golang protobuf plugin์ ์ค์นํ๋ค.
go install google.golang.org/protobuf/cmd/protoc-gen-go
์ค์น๊ฐ ์๋ฃ๋์๋ค๋ฉด ์ด์ ์๋น์ค๋ฅผ ์ ์ํ proto
ํ์ผ์ gRPC server์์ ์ฌ์ฉํ ์ ์๋๋ก ์ปดํ์ผ ํด์ค๋ค. user/user.proto
์ ํ์ผ์ ์์ฑํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํด์ ์ปดํ์ผ ํด์ค๋ค.
protoc -I=. \
--go_out . --go_opt paths=source_relative \
--go-grpc_out . --go-grpc_opt paths=source_relative \
protos/v1/user/user.proto
์ปดํ์ผ์ด ๋๋ฉด user.proto
ํ์ผ์ด ์๋ ํด๋์ user.pb.go
์ user_grpc.pb.go
ํ์ผ๋ค์ด ํจ๊ป ์์ฑ๋๋ค.
์ ์ํ Protobuf๋ก gRPC server ๊ตฌํํ๊ธฐ
User ์๋น์ค๋ฅผ ์ ์ํ์ผ๋ ์ด์ , ์๋น์ค๋ฅผ ๋ด๋นํ๋ microserviceํํ์ gRPC server๋ฅผ ๊ตฌํํด๋ณด๋ ค๊ณ ํ๋ค. User๋ค์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ gRPC server์ด๊ธฐ ๋๋ฌธ์ ์ค์ microservice์์๋ผ๋ฉด user ๋ค์ ์ ๋ณด๋ฅผ DB์์ ๊ฐ์ ธ์ค๊ฒ ์ง๋ง, ์์ ์ด๊ธฐ ๋๋ฌธ์ staticํ๊ฒ ์ ์ธ์ ํด๋จ๋ค.
// data/user.go
package data
import (
userpb "github.com/dojinkimm/go-grpc-example/protos/v1/user"
)
var UserData = []*userpb.UserMessage{
{
UserId: "1",
Name: "Henry",
PhoneNumber: "01012341234",
Age: 22,
},
{
UserId: "2",
Name: "Michael",
PhoneNumber: "01098128734",
Age: 55,
},
{
UserId: "3",
Name: "Jessie",
PhoneNumber: "01056785678",
Age: 15,
},
{
UserId: "4",
Name: "Max",
PhoneNumber: "01099999999",
Age: 37,
},
{
UserId: "5",
Name: "Tony",
PhoneNumber: "01012344321",
Age: 25,
},
}
user ์๋น์ค์๋ ์ด 5๋ช
์ ์ ์ ๊ฐ ์กด์ฌํ๊ณ ์ ์ ๋ค์ด ๊ฐ๊ณ ์๋ ๊ฐ๋ค์ user.proto
์์ ์ ์ํ ๊ฐ๋ค๊ณผ ๊ฐ๋ค.
// simple-user/main.go
import (
"context"
"log"
"net"
"google.golang.org/grpc"
"github.com/dojinkimm/go-grpc-example/data"
userpb "github.com/dojinkimm/go-grpc-example/v1/user"
)
const portNumber = "9000"
type userServer struct {
userpb.UserServer
}
// GetUser returns user message by user_id
func (s *userServer) GetUser(ctx context.Context, req *userpb.GetUserRequest) (*userpb.GetUserResponse, error) {
userID := req.UserId
var userMessage *userpb.UserMessage
for _, u := range data.Users {
if u.UserId != userID {
continue
}
userMessage = u
break
}
return &userpb.GetUserResponse{
UserMessage: userMessage,
}, nil
}
// ListUsers returns all user messages
func (s *userServer) ListUsers(ctx context.Context, req *userpb.ListUsersRequest) (*userpb.ListUsersResponse, error) {
userMessages := make([]*userpb.UserMessage, len(data.Users))
for i, u := range data.Users {
userMessages[i] = u
}
return &userpb.ListUsersResponse{
UserMessages: userMessages,
}, nil
}
func main() {
lis, err := net.Listen("tcp", ":"+portNumber)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
userpb.RegisterUserServer(grpcServer, &userServer{})
log.Printf("start gRPC server on %s port", portNumber)
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %s", err)
}
}
์ ์ ๋ค์ ์ ๋ณด๋ฅผ ์ ์ํ์ผ๋, ์ด์ user.proto
๋ฅผ ์ฌ์ฉํด user service๋ฅผ ๋ด๋นํ๋ gRPC server๋ฅผ ๊ตฌํํ ์ฐจ๋ก์ด๋ค. ๊ธฐ์กด ํ์ ์์์ ๊ตฌํํ service ์๋ gRPC server์ ์ ์ฌํ๋ค. ์ถ๊ฐ๋ ๋ถ๋ถ์ 2๊ฐ์ง์ด๋ค.
- User ์๋น์ค๋ฅผ gRPC server์ ๋ฑ๋กํ๋ ๋ถ๋ถ
- User ์๋น์ค์ rpc๋ค์ ๊ตฌํํ ๋ถ๋ถ
gRPC server์์ ์ ์ํ user ์๋น์ค๋ฅผ ์ฌ์ฉํ๋๋ก ๋ง๋๋ ๊ฒ์ ๋งค์ฐ ๊ฐ๋จํ๋ค. gRPC server์ ์ ์ํ user ์๋น์ค๋ฅผ ์ฌ์ฉํ๋๋ก ๋ง๋๋ ํจ์๊ฐ ์ด๋ฏธ ์ปดํ์ผ๋ user_grpc.pb.go
ํ์ผ์ ์กด์ฌํ๋ค. RegisterUserServer
ํจ์๋ฅผ ๊ฐ์ ธ์์ user ์๋น์ค๋ฅผ ๋ฑ๋กํ๋ฉด ๋๋ค. ์ด ์์
์ด ๋๋๋ฉด ์์ฃผ ๊ฐ๋จํ๊ฒ user ์๋น์ค๋ฅผ ๋ด๋นํ๋ gRPC server๊ฐ ์์ฑ๋๋ ๊ฒ์ด๋ค.
type userServer struct {
userpb.UserServer
}
...
grpcServer := grpc.NewServer()
userpb.RegisterUserServer(grpcServer, &userServer{})
...
๊ทธ ๋ค์์๋ user.proto
์ ์ ์ํ rpc๋ค์ ๊ตฌํํ๋ ์ฐจ๋ก๊ฐ ๋จ์๋ค. user_id๋ก ์ ์ ์ ์ ๋ณด๋ฅผ ๊ฐ๊ณ ์ค๋ GetUser
rpc์ ์ ์ ๋ค์ ์ ๋ณด ๋ชจ๋๋ฅผ ๊ฐ๊ณ ์ค๋ ListUsers
rpc๋ ๋ฐ๊ณผ ๊ฐ์ด ๊ตฌํํ ์ ์์ ๊ฒ์ด๋ค. ๋ก์ง์ ๊ฐ๋จํ๋ ๋ฐ๋ก ์ค๋ช
ํ์ง ์๊ฒ ๋ค.
// GetUser returns user message by user_id
func (s *userServer) GetUser(ctx context.Context, req *userpb.GetUserRequest) (*userpb.GetUserResponse, error) {
userID := req.UserId
var userMessage *userpb.UserMessage
for _, u := range data.UserData {
if u.UserId != userID {
continue
}
userMessage = u
break
}
return &userpb.GetUserResponse{
UserMessage: userMessage,
}, nil
}
// ListUsers returns all user messages
func (s *userServer) ListUsers(ctx context.Context, req *userpb.ListUsersRequest) (*userpb.ListUsersResponse, error) {
userMessages := make([]*pb.UserMessage, len(data.UserData))
for i, u := range data.UserData {
userMessages[i] = u
}
return &userpb.ListUsersResponse{
UserMessages: userMessages,
}, nil
}
user ์๋น์ค๋ฅผ gRPC server์ ๋ฑ๋กํ๊ณ + ํ์ํ rpc๋ค์ ๊ตฌํํ๋ค๋ฉด ์ด์ ์ค์ ๋ก ์ด gRPC server๊ฐ ์๋ํ๋๋ก ์๋ํ๋์ง ํ์ธ์ด ํ์ํ๋ค. ํํ ์ฌ์ฉ๋๋ JSON์ผ๋ก ํต์ ํ๋ HTTP API๋ผ๋ฉด localhost:9000/users/:user_id
์ด๋ฐ ์์ผ๋ก curl์ด๋ Postman ๊ฐ์ ํด๋ก ์ ๋๋์ง ํ์ธํ ์ ์์์ ๊ฒ์ด๋ค. ํ์ง๋ง, gRPC server๋ ๋ฐ๋ก curl๋ก ์ ๊ทผํ ์ ์๋ HTTP endpoint๊ฐ ์๊ธฐ ๋๋ฌธ์ ๊ธฐ์กด ๋ฐฉ์์ผ๋ก๋ ํ์ธํ ์ ์๋ค.
(grpc-gateway๋ฅผ ํ์ฉํ๋ฉด ๊ฐ๋ฅํ๊ณ ์ด ๋ถ๋ถ์ ๋ค๋ฅธ ํฌ์คํธ์์ ๋ค๋ฃฐ ์์ )
gRPC server๋ก curlํ๋ ค๋ฉด ๊ทธ๊ฑธ ๊ฐ๋ฅํด์ฃผ๋ ํด๋ค์ ์ค์นํด์ผ ํ๋ค:
์ด ํฌ์คํธ์์๋ bloomrpc
๋ฅผ ํ์ฉํด์ user ์๋น์ค๋ฅผ ๋ด๋นํ๋ gRPC server๊ฐ ์ ์๋ํ๋์ง ํ์ธํด๋ณด๋ ค๊ณ ํ๋ค. ์ค์น ๋ฐฉ๋ฒ์ ์๋ตํ๊ณ ์ฌ์ฉ๋ฒ์ผ๋ก ๋ฐ๋ก ๋ค์ด๊ฐ๋ณด๋ ค๊ณ ํ๋ค.
1. Proto ํ์ผ importํ๊ธฐ
ํ๋ก๊ทธ๋จ์ ์ผ์ชฝ์์ + ๋ฅผ ๋๋ฌ์ proto
ํ์ผ์ ์ฐพ์ต๋๋ค.
user.proto
๋ฅผ ์ฐพ์์ ์ด๊ธฐ๋ฅผ ๋๋ฆ
๋๋ค.
user.proto
๋ฅผ importํ๊ฒ ๋๋ฉด ๋ฐ๊ณผ ๊ฐ์ด user.proto
์ ์ ์๋ rpc๋ค์ ํต์ ํ ์ ์๋ ํํ๊ฐ ๋ง๋ค์ด์ง๋๋ค.
2. gRPC ํต์ ํ๊ธฐ
๋จผ์ main.go
๋ฅผ ์คํ์์ผ์ http://localhost:9000
์ gRPC server๋ฅผ ์ด์ด์ค๋๋ค.
Server๋ฅผ ์คํ์ํค๊ณ ๋์ bloomrpc
์์ ์์ฑํด์ค rpc๋ค์ ํด๋ฆญํ๋ฉด ์๋์ผ๋ก request๋ฅผ ๋ณด๋ผ ์ ์๋ ํ์์ผ๋ก ํฌ๋งท์ ๋ณ๊ฒฝ์์ผ์ค๋๋ค. ๊ทธ๋ฐ ๋ค์์ request ํ์์ ๋ง๊ฒ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ฉด ์๋ํ ๋๋ก ๋ฐ์ดํฐ๊ฐ ์ response๋ก ์ค๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๋ง๋ฌด๋ฆฌ
Golang gRPC server ๊ตฌ์ถํ๊ธฐ ์๋ฆฌ์ฆ์ 1ํธ์ด ๋๋ฌ๋ค. ์ด๋ฒ ํฌ์คํธ์์๋ gRPC๊ฐ ๋ฌด์์ธ์ง์ ๋ํ ๊ฐ๋ ์ ์ ๊น ์ง์ด๋ดค๊ณ , gRPC๋ก ํต์ ํ๋ Golang server๋ฅผ ๊ฐ๋จํ๊ฒ ๊ตฌ์ถํด๋ดค๋ค. ๋ค์ ์๋ฆฌ์ฆ 2ํธ์์๋ grpc-go์์ ์ง์ํ๋ ๋ง์ ์ ์ฉํ ๊ธฐ๋ฅ๋ค์ ํํค์ณ๋ณด๋ ค๊ณ ํ๋ค(ex. health checking, load balanding ๋ฑ๋ฑ), ๊ทธ๋ฆฌ๊ณ ์ค์ ๋ก microservice์์ gRPC server๋ค๊ฐ ์ด๋ป๊ฒ ํต์ ์ ํ๋์ง ์์๋ณด๋ ค๊ณ ํ๋ค.
๋ธ๋ก๊ทธ ๊ธ์ ์์ฑํ๋ฉด์ ๋๋ ์๋ก ๋ฐฐ์ฐ๋ ๊ฒ๋ค์ด ์๋ค๋ณด๋ ์ดํด๋ฅผ ์ ํ์ง ๋ชปํด ์ถฉ๋ถํ ์ค๋ช ํ์ง ๋ชปํ ๋ถ๋ถ๋ ์์ ๊ฒ ๊ฐ๋ค. ๋ณด์์ด ํ์ํ ๋ถ๋ถ์ ๋ณด์๋ ๋ถ๋ค์ ์ธ์ ๋ ํผ๋๋ฐฑ ์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค.
์ด ์์ ์ ๋์ค๋ ๋ชจ๋ ์ฝ๋๋ค์ Github Repository์ ๊ณต๊ฐ๋์ด ์๋ค.