通过 Python+Nacos实现微服务,细解微服务架构

news2024/12/25 0:17:14

shigen坚持更新文章的博客写手,擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长,分享认知,留住感动。
个人IP:shigen

背景

一直以来的想法比较多,然后就用Python编写各种代码脚本。很多的脚本都是通过Python的Flask框架实现,如[file-server],然后部署到云服务器。但是这样只提供一个端口就可以通过http访问,无异于在互联网上裸奔。而且这样的服务有很多个,一直在想如何实现一个统一认证然后就可以访问这么多的服务。在Java领域最常见的设计就是使用微服务架构,把每个服务拆分出来,然后通过网关统一拦截、验证、分发流量。蹭了一张架构图(发现飞书的模板已经很好了):

微服务架构设计

那我的Python服务为什么不能设计成微服务架构呢,当然,还没听说过谁家的Python服务是微服务架构的,姑且一试。

代码实现

考虑到大家的技术栈就是Java,以下的python代码将省略部分细节。

有了之前python flask如何注册到nacos踩坑的经验,这次明显顺利的多了。现在本地搭建nacos环境,并支持http访问,推荐docker-compose的方式搭建:shigen/spring-cloud-platform

因为我的Nacos版本是2.0+的,官方的nacos-sdk-python是这样描述的:

Supported Python version:

Python 2.7 Python 3.6 Python 3.7

Supported Nacos version

Nacos 0.8.0 ~ 1.3.2

于是就使用的是官方的API:Open API 指南

我的服务模块是这样细分的:

microservices-demo/
├── nacos/
├── api-gateway/
│   └── app.py
├── user-service/
│   └── app.py
├── auth-service/
│   └── app.py
└── document-service/
    └── app.py

也就是分成了四个模块:网关、用户中心、鉴权中心、文档中心。接下来就是服务的注册和调用。我们以最简单的auth-service为例:

NACOS_URL = os.getenv(
    "NACOS_URL", "http://localhost:8848/nacos/v1/ns/instance")
SERVICE_NAME = "auth-service"
SERVICE_IP = socket.gethostbyname(socket.gethostname())
SERVICE_PORT = 5002
NAMESPACE = "python"

# 发送到Nacos服务注册接口
def register_service():
    payload = {
        "serviceName": SERVICE_NAME,
        "ip": SERVICE_IP,
        "port": SERVICE_PORT,
        "namespaceId": NAMESPACE,
    }
    response = requests.post(f"{NACOS_URL}", params=payload)

# 每5秒发送一次心跳
def send_heartbeat():
    while True:
        payload = {
            "serviceName": SERVICE_NAME,
            "ip": SERVICE_IP,
            "port": SERVICE_PORT,
            "namespaceId": NAMESPACE,
        }
        response = requests.put(f"{NACOS_URL}/beat", params=payload)
        time.sleep(5)

# 密码验证,获得token
@app.route('/auth', methods=['POST'])
def authenticate():
    pass

# 验证token
@app.route('/verify', methods=['POST'])
def verify_token():
    pass

# 服务启动类
if __name__ == '__main__':
    register_service()
    heartbeat_thread = threading.Thread(target=send_heartbeat)
    heartbeat_thread.daemon = True
    heartbeat_thread.start()
    app.run(port=SERVICE_PORT)

不用尝试读懂代码,很简单:在服务启动的时候注册到nacos,完了就是定时的向nacos发送心跳。@app.route(‘/auth’, methods=[‘POST’])表示提供一个POST请求方式的/auth接口,然后启动服务:

服务启动

服务启动成功之后,可以看到控制台打印的日志信息。同时提供http访问接口。测试的方式如下:

curl --location 'http://127.0.0.1:5002/auth' \
--header 'Content-Type: application/json' \
--data '{
    "username": "user",
    "password": "pass"
}'

其他的几个服务也如法炮制。最终Nacos服务注册表如下:

在这里插入图片描述

在网关这一块可能稍微有一点区别,复习前面提到的网关的作用:流量的拦截和转发、认证拦截、负载均衡…这里我的网关服务设计如下:

NACOS_URL = os.getenv(
    "NACOS_URL", "http://localhost:8848/nacos/v1/ns/instance")
NAMESPACE = "python"


def get_service_url(service_name):
    try:
        response = requests.get(
            f"{NACOS_URL}/list?serviceName={service_name}&namespaceId={NAMESPACE}")
        data = response.json()
        if data and data['hosts']:
            service = data['hosts'][0]
            # return f"http://{service['ip']}:{service['port']}"
            # 这里是本机调用测试
            return f"http://localhost:{service['port']}"
    except Exception as e:
        print(f"Error getting service URL: {e}")
    return None


@app.route('/<service_name>/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
def proxy(service_name, path):
    service_url = get_service_url(service_name)
    if not service_url:
        return jsonify({"error": "Service not found"}), 404

    # 认证逻辑
    if service_name != "auth-service":
        token = request.headers.get("Authorization")
        if not token:
            return jsonify({"error": "Missing token"}), 401

        auth_url = get_service_url("auth-service")
        if not auth_url:
            return jsonify({"error": "Auth service not found"}), 500

        verify_response = requests.post(
            f"{auth_url}/verify", json={"token": token})
        if verify_response.status_code != 200:
            return jsonify({"error": "Invalid token"}), 401

    url = f"{service_url}/{path}"
    response = requests.request(
        method=request.method,
        url=url,
        headers={key: value for key,
                 value in request.headers if key != 'Host'},
        data=request.get_data(),
        cookies=request.cookies,
        allow_redirects=False
    )

    return (response.content, response.status_code, response.headers.items())


if __name__ == '__main__':
    app.run(port=8080)

这里其实就是请求来了之后,从nacos上拉取服务列表。这个服务列表就是服务名称和对应的服务所在机器的IP(service-name和对应的IP集合)。然后选取对应服务所在的机器之一作为目标机器(这里选用的是第一台机器),从请求头中获得token,进行验证和调用。token校验失败则打给认证服务,重新进行登录验证。为此,我还对比了一下Spring Cloud + Nacos的设计:

在这里插入图片描述

Nacos的API实现的是springframework.cloud.client.discovery的接口,意味着统一的标准:

package com.alibaba.cloud.nacos.discovery;

public class NacosDiscoveryClient implements DiscoveryClient {

        private static final Logger log = LoggerFactory.getLogger(NacosDiscoveryClient.class);

        /**
         * Nacos Discovery Client Description.
         */
        public static final String DESCRIPTION = "Spring Cloud Nacos Discovery Client";

        private NacosServiceDiscovery serviceDiscovery;

        public NacosDiscoveryClient(NacosServiceDiscovery nacosServiceDiscovery) {
                this.serviceDiscovery = nacosServiceDiscovery;
        }

        @Override
        public String description() {
                return DESCRIPTION;
        }

        @Override
        public List<ServiceInstance> getInstances(String serviceId) {
                try {
                        return serviceDiscovery.getInstances(serviceId);
                }
                catch (Exception e) {
                        throw new RuntimeException(
                                        "Can not get hosts from nacos server. serviceId: " + serviceId, e);
                }
        }

        @Override
        public List<String> getServices() {
                try {
                        return serviceDiscovery.getServices();
                }
                catch (Exception e) {
                        log.error("get service name from nacos server fail,", e);
                        return Collections.emptyList();
                }
        }

}

其中的serviceName和serviceId其实是同一概念,意味着我们可以通过服务名获得全部的部署服务的实例信息,实现自定义的负载均衡调用。这里的原理和我直接从Nacos的API中获得服务列表,默认选取第一台机器进行调用的设计如出一辙。

对于以上的Python代码段,可能文字描述有不详细或者不当之处,借助魔法进行进一步的完善:

这段代码实现了一个反向代理服务器,其主要功能是根据服务名称将请求转发到不同的服务,并在转发前进行认证。具体功能如下:

  1. 服务发现:代码通过访问 NACOS(一个服务发现和配置管理平台)来获取目标服务的 URL。NACOS 提供了服务注册和发现的功能,代码中通过 get_service_url(service_name) 函数实现这一功能。
  2. 请求转发:当接收到一个请求时,根据 URL 中的 service_name 和 path,代码会将请求转发到相应的目标服务。转发时,保留了原始请求的 HTTP 方法、头信息、数据和 cookies。
  3. 认证检查:对于非 auth-service 的请求,代码会检查请求头中是否包含 Authorization token。如果没有 token 或 token 无效,则会返回错误响应。具体步骤如下:
    1. 检查请求头中是否包含 Authorization token。
    2. 如果没有 token,返回 401 错误(未授权)。
    3. 如果有 token,向认证服务(auth-service)发送请求,验证 token 的有效性。
    4. 如果 token 无效,返回 401 错误。
  4. 错误处理:代码包含了基本的错误处理逻辑,例如当服务 URL 无法获取或认证服务不可用时,返回相应的错误响应。

通过这些功能,该反向代理服务器能够在微服务架构中充当中间层,路由请求并提供统一的认证机制。

这样下来,我们调用服务只需要直接走网关了,其它的服务端口也不用放行,极大程度上保证了数据的安全。此时,我们需要这样调用服务:

登录

curl --location 'http://127.0.0.1:8080/auth-service/auth' \
--header 'Content-Type: application/json' \
--data '{
"username": "user",
"password": "pass"
}'

服务调用

curl --location 'http://127.0.0.1:8080/document-service/documents' \
--header 'Authorization: xxx'

总结

之前微服务的开发中,可能我们借助Spring Cloud部分组件、Nacos,在项目中加上依赖配置,稍微改一下配置文件,服务就可以正常的调用了。其中依赖的SDK如何的工作,可能只是停留在理论上,缺少实操。这次的这个案例很好的展示Python+Nacos如何实现微服务,并从中细解微服务结构和服务之间的调用原理。是不是觉得Nacos其实也不过如此哈,没什么牛掰、独特之处,其实都是草台班子。

与shigen一起,每天不一样!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1807806.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【线性代数】向量空间,子空间,向量空间的基和维数

向量空间 设V为n维向量的集合&#xff0c;如果V非空&#xff0c;且集合V对于向量的加法以及数乘两种运算封闭&#xff0c;那么就称集合V为向量空间 x&#xff0c;y是n维列向量。 x 向量组等价说明可以互相线性表示 向量组等价则生成的向量空间是一样的 子空间 例题18是三位向…

4.大模型微调技术LoRA

大模型低秩适配(LoRA)技术 现有PEFT 方法的局限与挑战 Adapter方法,通过增加模型深度而额外增加了模型推理延时。Prompt Tuning、Prefix Tuning、P-Tuning等方法中的提示较难训练,同时缩短了模型可用的序列长度。往往难以同时实现高效率和高质量,效果通常不及完全微调(f…

【每日算法】

算法第15天| (二叉树part02)层序遍历、226.翻转二叉树(优先掌握递归)、101. 对称二叉树(优先掌握递归) 文章目录 算法第15天| (二叉树part02)层序遍历、226.翻转二叉树(优先掌握递归)、101. 对称二叉树(优先掌握递归)一、层序遍历二、226. 翻转二叉树(优先掌握递归)三、101. 对…

小程序中实现自定义头部导航组件

在页面中实现自定义头部导航的组件&#xff0c;如果仅是单个页面中需要自定义可在页面的json文件中配置"navigationStyle": “custom”&#xff0c;如果是项目中所有页面都想使用自定义的组件&#xff0c;可在app.json的window中全局配置"navigationStyle"…

2024-6-9

今日安排&#xff1a; 学校的课程作业windows SEH 机制简单入门windows 用户态 pwn / 内核态入门 计网实验报告 && 网安实验报告继续审计 nf_tables 源码&#xff0c;主要看 active 相关逻辑。复现 CVE-2022-32250 这个漏洞【 && iptables 相关学习】♥♥♥♥…

【车载开发系列】MCU选型

【车载开发系列】MCU选型 【车载开发系列】MCU选型 【车载开发系列】MCU选型一. 重要概念二. MCU选型的风险风险1风险2 三. MCU选型要点四. MCU选型维度五. MCU 选型需要考虑的因素1&#xff09;ROM/RAM2&#xff09;速度/主频3&#xff09;分析外设需求4&#xff09;工作电压(…

idea编码问题:需要 <标识符> 非法的类型 、需要为 class、interface 或 enum 问题解决

目录 问题现象 问题解决 问题现象 今天在idea 使用中遇到的一个编码的问题就是&#xff0c;出现了这个&#xff1a; Error:(357, 28) java: /home/luya...........anageService.java:357: 需要 <标识符> Error:(357, 41) java: /home/luya............anageService.ja…

表达式求值的相关语法知识(C语言)

目录 整型提升 整型提升的意义 整型提升规则 整型提升实例 算术转换 赋值转换 操作符的属性 C语言的语法并不能保证表达式的执行路径唯一&#xff01;&#xff01;&#xff01; 问题表达式 整型提升 C的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这…

基于SSM+Jsp的家用电器销售网站

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

锂电池寿命预测 | Matlab基于SSA-SVR麻雀优化支持向量回归的锂离子电池剩余寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 【锂电池剩余寿命RUL预测案例】 锂电池寿命预测 | Matlab基于SSA-SVR麻雀优化支持向量回归的锂离子电池剩余寿命预测&#xff08;完整源码和数据&#xff09; 1、提取NASA数据集的电池容量&#xff0c;以历史容量作…

LLVM Cpu0 新后端10

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?…

GitLab代码导出 gitlab4j-api 实现

目录 GitLab简介 GitLab 的主要特点包括&#xff1a; GitLab代码导出 gitlab4j-api 添加 gitlab4j-api 依赖 使用 gitlab4j-api 获取特定命名空间下的所有项目 说明 注意事项 GitLab简介 GitLab 是一个开源的代码仓库和协作平台&#xff0c;主要用于版本控制和源代码管理…

无人用过!QRTCN-BiLSTM实现区间预测!区间预测全家桶再更新!

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 今天对我们之前推出的区间预测全家桶再次进行更新&…

SPSS 27 安装教程(附安装包下载)

SPSS 27 是一款用于统计学分析运算、数据挖掘、预测分析和决策支持任务的软件产品。它最初是为社会科学领域的研究者设计的&#xff0c;但随着其功能和应用的不断扩展&#xff0c;现在已广泛应用于各个领域&#xff0c;如医学、市场调研、教育等。 [安装注意]&#xff1a;安装前…

智能制造 v3.13.11 发布,ERP、在线课堂、表白墙更新

智能制造一体化管理系统 [SpringBoot2 - 快速开发平台]&#xff0c;适用于制造业、建筑业、汽车行业、互联网、教育、政府机关等机构的管理。包含文件在线操作、工作日志、多班次考勤、CRM、ERP 进销存、项目管理、EHR、拖拽式生成问卷、日程、笔记、工作计划、行政办公、薪资模…

UE5基础1-下载安装

目录 一.下载 二.安装 三.安装引擎 四.其他 简介: UE5&#xff08;Unreal Engine 5&#xff09;是一款功能极其强大的游戏引擎。 它具有以下显著特点&#xff1a; 先进的图形技术&#xff1a;能够呈现出令人惊叹的逼真视觉效果&#xff0c;包括高逼真的光影、材…

Robust Tiny Object Detection in Aerial Images amidst Label Noise

文章目录 AbstractIntroductionRelated WorkMethodsClass-aware Label CorrectionUpdateFilteringTrend-guided Learning StrategyTrend-guided Label ReweightingRecurrent Box RegenerationExperimentpaper Abstract 精确检测遥感图像中的小目标非常困难,因为这类目标视觉信…

【C++题解】1389 - 数据分析

问题&#xff1a;1389 - 数据分析 类型&#xff1a;简单循环 题目描述&#xff1a; 该方法的操作方式为&#xff0c;如果要传递 2 个数字信息给友军&#xff0c;会直接传递给友军一个整数 n&#xff08;n 是一个 10 位以内的整数&#xff09;&#xff0c;该整数的长度代表要传…

Ps:自动批量处理照片

有很多种方法可以将调色风格一次性应用到多张照片上。 但对于要进行局部修饰的照片&#xff0c;比如人像照片中要去除皮肤上的瑕疵、柔化皮肤上的光影以及均匀肤色等&#xff0c;想要实现成批处理似乎很困难。 随着人工智能技术的不断发展&#xff0c;越来越多的插件具备自动修…

Linux下文件权限管理

任务要求 1. 在跳板机上为开发部门专门创建一个目录&#xff0c;只允许开发部门所有员工使用该目录 2. 其他人员不能进入和查看该目录里的内容 任务分解 1. 在跳板机给开发部门创建目录 2. 对该目录做好权限的管控工作 只允许开发部门的所有人使用&#xff0c;创建、删除…