微服务架构下RPC IDL及代码如何统一管理?
Posted 亚军进化史
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微服务架构下RPC IDL及代码如何统一管理?相关的知识,希望对你有一定的参考价值。
背景
随着云原生技术的日益发展和普及,微服务架构下多语言(python,go,php....)多系统之间通过rpc来通信,一旦接口需要升级,协调 server 端有发布和升级,IDL 文件仓库的变更,client 端变更发布就变成了一个很麻烦的事情。我们需要一个方便透明的规范来协调各端,一个更加自动化的工具来协同。以主流的grpc为例来探讨下, 这个 proto 及代码到底放在哪里?应该如何做到自动化版本控制?
方案一:放在各自的代码仓库
如下图所示,直接将项目所依赖到的以及自己的所有 proto 文件和client代码都存放在 protobuf/
目录下,不借助外部工具。
很明显该方案有以下优缺点。
缺点:
-
1️⃣ 项目所有依赖的 proto 都存储在各自项目的代码仓库下,因此所有依赖 proto 都需要人工的向其它业务组 “要” 来,再放到
protobuf/
目录下,人工介入极度麻烦。依赖多了拷贝会成为一种负担。 -
2️⃣ 版本的控制和更新没有办法去维护,沟通成本太高。
-
3️⃣ 代码的复用性比较差。
优点:
-
简单易用,每个项目的proto依赖一目了然。不需要看这看那。
方案二:独立proto仓库
每个项目都有自己的proto仓库。需要依赖的都从proto仓库拉(包括服务自己)。
不过这种方式也是有比较明显的缺点:
缺点:
-
1️⃣ 各服务需要同时关注自己的服务本身还有服务的proto仓库,开发时需要先在proto仓库定义好proto和生成好client代码,所以在开发时需要在服务仓库和proto仓库切换。
-
2️⃣ 如果服务依赖的太多,也有可能跨业务组,例如下图,如果跨组之后,可能需求找业务组负责人开多个proto仓库权限。还需要一个一个地去依赖。
-
3️⃣ 每次有新的服务都需要对应去申请一个proto仓库比较麻烦。
优点:
-
1️⃣ 各自服务有各自的proto仓库,方便版本维护和升级。
-
2️⃣ 依赖时可以按需拉取。
方案三:集中仓库
按业务组维度来管理proto仓库,这样的话如果依赖某个业务组多个依赖时只需要拉取一个proto仓库即可。同时每次新起一个服务时,只需要在各自业务组的prooto仓库加上自己服务的,不需要单独去申请一个自己服务的proto仓库。
优点:
-
1️⃣ 依赖多个服务的proto时只需要依赖中央仓库即可。
-
2️⃣ 新增服务时不需要单独去申请proto仓库,只需要在各自业务组的proto仓库添加即可。
缺点:
-
1️⃣ 各服务开发时还是需要关注两个仓库,需要切来切去。
-
2️⃣ 每个中央仓库下不能独立的对各服务的proto进行版本管理。
-
3️⃣ 可能只依赖某个svc的proto,而多引入了一些其他不必要的。
方案四:镜像仓库+git branch
为解决上述方案的一些痛点,综合各方案的优点我们在中央仓库的基础之上采取了镜像仓库结合git branch的方式。说明:
-
1️⃣ 各业务组的中央仓库的master的readme维护了各分支和各服务的对应关系,如下图。
-
2️⃣ 这样主要是为了新增新的svc时可以拿到下一个分支的分支名(由于go.mod的分支版本管理限制,只能以V2,V3这种命名),同时方便人们去辨识自己依赖的svc在哪个模块。common表示是所有svc proto的集合分支。每次新增一个服务时便会在cicd的过程中往这个reame添加说明。
-
3️⃣ 中央仓库的分支的readme维护了各自自己的版本情况。
-
4️⃣ 使用时可以根据自己的需要去依赖对应的分支的版本即可。
-
5️⃣ python cleint版本和go的版本都是一一对应的。
-
6️⃣ 各svc只需要依赖一个.gitlab.yml文件即可实现。具体的gitlab cicd的job如下:
.push_tmpl: &push_proto
script:
- echo "push test"
- echo $CI_PROJECT_NAME
- |
userMail=$GITLAB_USER_EMAIL
git config --global user.email "$GITLAB_USER_EMAIL"
userName=${userMail%@*}
echo "$userMail"
git config --global user.name "$userName"
git clone -v https://xxxxx/proto-center.git
cd proto-center
if [ `grep -c $CI_PROJECT_NAME README.md` -eq '0' ];then
echo "- $CI_PROJECT_NAME-->v$(sed -n '$p' README.md | awk -F "-->v" '{print $2+1}' | head)" >>README.md
export branchName=$(sed -n '$p' README.md | awk -F "-->" '{print $2}')
echo $branchName
git add . && git commit -m "add $CI_PROJECT_NAME "
git push https://xxxxxxxx/proto-center.git master
git checkout -b $branchName
mkdir -p $CI_PROJECT_NAME && cp -r ../protobuf/* $CI_PROJECT_NAME/
go mod init xxxxxxxx/proto-center/$branchName
go mod tidy
git add .
git commit -m "add $CI_PROJECT_NAME proto"
git push --set-upstream https:/xxxxxxxx/proto-center.git $branchName
git checkout v2
mkdir -p $CI_PROJECT_NAME && cp -r ../protobuf/* $CI_PROJECT_NAME/
git add .
git commit -m "add $CI_PROJECT_NAME proto"
git push https://xxxxxxxx/proto-center.git v2
else
export branchName=$(grep $CI_PROJECT_NAME README.md | awk -F "-->" '{print $2}' | head)
git checkout $branchName
rm -rf $CI_PROJECT_NAME/
mkdir -p $CI_PROJECT_NAME
cp -r ../protobuf/* $CI_PROJECT_NAME/ && git add .
git commit -m "update $CI_PROJECT_NAME proto"
git push https://xxxxxxxx/proto-center.git $branchName;
git checkout v2
rm -rf $CI_PROJECT_NAME/
mkdir -p $CI_PROJECT_NAME
cp -r ../protobuf/* $CI_PROJECT_NAME/ && git add .
git commit -m "update $CI_PROJECT_NAME proto"
git push https://xxxxxxxx/proto-center.git v2;
fi -
7️⃣ 中央仓库的gitlab的cicd的job如下:
.buld_tmpl: &tag_proto
script:
- echo "tag proto "
- |
userMail=$GITLAB_USER_EMAIL
userName=${userMail%@*}
echo "$userMail"
git config --global user.email $userMail
git config --global user.name $userName
branch=$CI_BUILD_REF_NAME
isnottag="false"
git describe --tag || isnottag="true"
git clone -b $branch -v https://xxxxx/proto-center.git
cd proto-center
userMail=$( git log --pretty=format:%ae ${CI_COMMIT_SHA} -1)
userName=${userMail%@*}
echo "$userMail"
git config --global user.email $userMail
git config --global user.name $userName
version=""
if [ $isnottag = "true" ];then
echo $isnottag
version=$branch.0.1
echo $version
git tag $version
git push https://xxxxx/proto-center.git --tags
echo "- $version" >> README.md
git add .
git commit -m "add $version"
git push https://xxxxx/proto-center.git $branch
else
echo $isnottag
version_ref=$(git describe --tags | awk -F "-" '{print $1}' | head)
echo $version_ref
version=`echo $version_ref | awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{if(length($NF+1)>length($NF))$(NF-1)++; $NF=sprintf("%0*d", length($NF), ($NF+1)%(10^length($NF))); print}'`
echo $version
git tag $version
git push https://xxxxx/proto-center.git --tags
echo "- $version" >> README.md
git add .
git commit -m "add $version"
git push https://xxxxx/proto-center.git $branch
fi
mkdir -p python
cp -r **/*.proto python
pkgName=$( grep "\-->" README.md | tail -1 | awk -F "-->v" '{print $1}' | awk -F " " '{print $2}')
pkgName=${pkgName//-/_}
echo $pkgName
export pip_pkg_name=$pkgName
echo $version
export pip_tag_name=$version
pip install grpcio-tools==1.4.0
pip install protobuf==3.3.0
echo "#!/bin/env python
# -*- encoding=utf8 -*-
import os
from setuptools import (setup, find_packages)
version = os.getenv('pip_tag_name')
name = os.getenv('pip_pkg_name')
setup(
name=name,
version=version,
description='LLS grpc protocol',
packages=find_packages(exclude=[]),
include_package_data=True,
author='LLS DEV Team',
author_email='',
package_data={'': ['*.*']},
install_requires=[
'grpcio==1.18.0',
'protobuf==3.3.0'
],
zip_safe=False,
classifiers=[
'Programming Language :: python :: 2.7',
],
)" > python/setup.py
echo "
[distutils]
index-servers = internal
[internal]
repository: https://xxxxx.com/
username: xxxxx
password: xxxxx
" > ~/.pypirc
mkdir -p python/$pkgName
echo "" >> python/$pkgName/__init__.py
python -m grpc_tools.protoc -I python/ --python_out=python/$pkgName --grpc_python_out=python/$pkgName/ python/*.proto
cd python
ls
python setup.py bdist_wheel upload -r internal
cd ..
mkdir -p python3
cp -r -n **/*.proto python3
echo "#!/bin/env python
# -*- encoding=utf8 -*-
import os
from setuptools import (setup, find_packages)
version = os.getenv('pip_tag_name')
name = os.getenv('pip_pkg_name')
setup(
name=name,
version=version,
description='LLS grpc protocol',
packages=find_packages(exclude=[]),
include_package_data=True,
author='LLS DEV Team',
author_email='',
package_data={'': ['*.*']},
install_requires=[
'grpcio==1.18.0',
'protobuf==3.12.4'
],
zip_safe=False,
classifiers=[
'Programming Language :: python :: 3.7',
],
)" > python3/setup.py
python3 -m pip install grpcio-tools==1.4.0
python3 -m pip install protobuf
mkdir -p python3/$pkgName
echo "" >> python3/$pkgName/__init__.py
python3 -m grpc_tools.protoc -I python3/ --python_out=python3/$pkgName --grpc_python_out=python3/$pkgName/ python3/*.proto
cd python3/$pkgName
ls
pb_files=`ls | grep -v '__init__' | grep -v 'grpc.py'`
echo $pb_files
need_replace_strs=()
for pb_file in ${pb_files[@]}
do
prefix=${pb_file/.py/}
after_handle_package_name=${prefix//_/__}
need_replace_str="import $prefix as $after_handle_package_name"
echo $need_replace_str
need_replace_strs[${#need_replace_strs[@]}]="$need_replace_str"
#echo ${need_replace_strs[0]}
done
need_replace_str_num=${#need_replace_strs[@]}
all_files=`ls | grep -v '__init__'`
echo $all_files
for file in ${all_files[@]}
do
for ((i=0;i<$need_replace_str_num;i++))
do
need_replace_str=${need_replace_strs[${i}]}
echo $need_replace_str
sed -i "s/^$need_replace_str$/from . $need_replace_str/" $file
done
done
echo "finished"
cd ..
python3 setup.py bdist_wheel upload -r internal
echo ${CI_COMMIT_SHA}
noticeMail=$userMail
content=" version: $version project: $pkgName commit: https://xxxxx/proto-center/commit/${CI_COMMIT_SHA} "
curl --location --request POST 'https://xxxxx/webhookurl'
--header 'Content-Type: application/json'
--data-raw '{
"msg": {
"content":"'"${content}"'"
}' -
8️⃣ 构建完通知,在中央仓库的job里面可以添加钉钉,微信,slack这种类似的机器人
以上是关于微服务架构下RPC IDL及代码如何统一管理?的主要内容,如果未能解决你的问题,请参考以下文章
微服务架构下的服务治理:如何在 SpringCloud 框架中实现服务的注册与发现