FastApi通过nacos注册 实现SpringCloudGateway服务调用

本文介绍了如何使用Python和Nacos进行服务注册,包括安装依赖、编写注册方法、配置类设置,以及在FastAPI中实现主入口调用。重点展示了如何通过Nacos进行服务实例的管理和配置管理。

安装依赖

pipenv install apscheduler

pipenv install nacos-sdk-python

编写注册方法 app/nacos.py

import nacos
from .settings import settings

client = nacos.NacosClient(
    settings.SERVER_HOST,
    namespace=settings.SERVER_NAMESPACE_ID)



async def beat():
    client.add_naming_instance(
        settings.SERVICE_NAME,settings.SERVICE_IP,settings.SERVICE_PORT,
        group_name=settings.SERVICE_GROUP_NAME)


# 微服务注册nacos
def register_nacos():
    client.add_naming_instance(
        settings.SERVICE_NAME,settings.SERVICE_IP,settings.SERVICE_PORT,
        group_name=settings.SERVICE_GROUP_NAME)

编写配置类


import os
from pydantic import BaseSettings

#  None
fast_api_env = os.environ.get('FAST_API_ENV')


class Development(BaseSettings):
    MODEL = 'dev'

    DB_URL = r'mysql://root:123456@127.0.0.1:3306/fastapi'
    MODULES = {'models': ['app.models.users']}

    # db_url='mysql://root:123
<think> 我们使用FastAPI来构建微服务基础框架,并希望它能够对标Spring Cloud的功能。在Spring Cloud中,我们通常有服务发现(Eureka)、配置中心(Config Server)、API网关(Zuul或Gateway)、负载均衡(Ribbon)、熔断器(Hystrix)等。 FastAPI本身是一个现代、快速(高性能)的Web框架,用于构建API。为了构建一个类似Spring Cloud的微服务生态系统,我们可以使用以下组件: 1. 服务发现:使用Consul、Etcd或Zookeeper。在Python中,我们可以使用`consul`库来与Consul交互。另外,也可以使用`py-eureka-client`来连接Eureka服务器(如果已有Eureka)。 2. 配置中心:可以使用Spring Cloud Config,但更常见的Python方式是使用Consul的KV存储,或者使用专门的配置中心如`config`库(但通常需要自己构建客户端)。另外,也可以使用`python-consul`来从Consul获取配置。 3. API网关:可以使用FastAPI本身构建,或者使用专门的网关如Kong、Traefik,或者使用FastAPI的中间件和子应用来构建网关功能。 4. 负载均衡:由于我们使用服务发现,客户端负载均衡可以通过`httpx`或`requests`结合服务发现来实现。也可以使用服务网格如Istio,但这里我们更关注应用层。 5. 熔断器:可以使用`circuitbreaker`库来实现类似Hystrix的功能。 下面,我们将构建一个基础框架,包括: - 服务注册与发现(使用Consul) - 一个简单的配置中心(使用Consul的KV存储) - 一个API网关(使用FastAPI构建,作为入口) - 负载均衡(在网关中实现简单的轮询) - 熔断器(在服务调用时使用) 由于这是一个基础框架,我们将创建两个微服务和一个网关: 1. `service_gateway`:网关服务,接收所有外部请求,并根据路径转发到相应的微服务。 2. `service_a`:一个示例微服务A,提供一些API。 3. `service_b`:一个示例微服务B,提供另一些API。 步骤: 1. 安装必要的库: ```bash pip install fastapi uvicorn python-consul httpx circuitbreaker ``` 2. 确保已经安装并运行了Consul(用于服务发现和配置中心)。可以从Consul官网下载并运行。 现在,我们开始编写代码。 ### 第一步:服务注册与发现(使用Consul) 我们将为每个微服务创建一个Consul客户端,用于注册服务,并在需要时从Consul发现其他服务。 #### 公共模块:consul_utils.py 我们先编写一个公共模块,用于处理与Consul的交互。 ```python import consul from pydantic import BaseModel from typing import List, Optional class ServiceInfo(BaseModel): id: str name: str address: str port: int class ConsulClient: def __init__(self, host: str = "127.0.0.1", port: int = 8500): self.consul = consul.Consul(host=host, port=port) def register_service(self, service_id: str, service_name: str, address: str, port: int, tags: Optional[List[str]] = None, check: Optional[dict] = None): """ 注册服务 """ tags = tags or [] check = check or { "HTTP": f"http://{address}:{port}/health", "Interval": "10s", "Timeout": "5s", "DeregisterCriticalServiceAfter": "30s" } self.consul.agent.service.register( name=service_name, service_id=service_id, address=address, port=port, tags=tags, check=check ) def deregister_service(self, service_id: str): """ 注销服务 """ self.consul.agent.service.deregister(service_id) def get_service(self, service_name: str) -> Optional[ServiceInfo]: """ 根据服务名获取一个健康的服务实例(简单轮询) """ _, services = self.consul.health.service(service_name, passing=True) if services: # 这里简单取第一个,实际可以根据负载均衡策略选择 service = services[0] return ServiceInfo( id=service['Service']['ID'], name=service['Service']['Service'], address=service['Service']['Address'], port=service['Service']['Port'] ) return None ``` ### 第二步:微服务A和微服务B 我们创建两个微服务,每个微服务在启动时向Consul注册自己,并在关闭时注销。同时,它们提供一个健康检查接口。 #### 微服务A:service_a.py ```python import uvicorn from fastapi import FastAPI, HTTPException from consul_utils import ConsulClient, ServiceInfo import socket import os app = FastAPI() # 获取本机IP def get_local_ip(): try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ip = s.getsockname()[0] s.close() return ip except: return "127.0.0.1" # 服务注册 consul_client = ConsulClient() service_id = "service-a-1" service_name = "service-a" port = 8001 local_ip = get_local_ip() @app.on_event("startup") def startup(): consul_client.register_service(service_id, service_name, local_ip, port) @app.on_event("shutdown") def shutdown(): consul_client.deregister_service(service_id) @app.get("/health") def health(): return {"status": "UP"} @app.get("/service_a") def service_a_endpoint(): return {"message": "Hello from Service A"} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=port) ``` #### 微服务B:service_b.py ```python import uvicorn from fastapi import FastAPI from consul_utils import ConsulClient, ServiceInfo import socket import os app = FastAPI() def get_local_ip(): try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ip = s.getsockname()[0] s.close() return ip except: return "127.0.0.1" consul_client = ConsulClient() service_id = "service-b-1" service_name = "service-b" port = 8002 local_ip = get_local_ip() @app.on_event("startup") def startup(): consul_client.register_service(service_id, service_name, local_ip, port) @app.on_event("shutdown") def shutdown(): consul_client.deregister_service(service_id) @app.get("/health") def health(): return {"status": "UP"} @app.get("/service_b") def service_b_endpoint(): return {"message": "Hello from Service B"} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=port) ``` ### 第三步:API网关 网关将接收所有请求,并根据路径前缀转发到相应的微服务。我们将使用`httpx`来转发请求,并使用Consul进行服务发现。 #### 网关:gateway.py ```python import uvicorn from fastapi import FastAPI, Request, HTTPException from fastapi.responses import JSONResponse from consul_utils import ConsulClient, ServiceInfo import httpx from circuitbreaker import circuit app = FastAPI() consul_client = ConsulClient() # 定义熔断器设置(这里只是示例,实际需调整) @circuit(failure_threshold=5, recovery_timeout=30) async def call_service(service_name: str, path: str, request: Request): service = consul_client.get_service(service_name) if not service: raise HTTPException(status_code=503, detail=f"Service {service_name} not found") url = f"http://{service.address}:{service.port}{path}" async with httpx.AsyncClient() as client: # 转发请求 response = await client.request( method=request.method, url=url, headers=request.headers.raw, content=await request.body(), timeout=5 ) return JSONResponse( content=response.json(), status_code=response.status_code, headers=dict(response.headers) ) @app.get("/service_a/{path:path}") async def route_service_a(path: str, request: Request): return await call_service("service-a", f"/service_a/{path}", request) @app.get("/service_b/{path:path}") async def route_service_b(path: str, request: Request): return await call_service("service-b", f"/service_b/{path}", request) if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000) ``` ### 第四步:测试 1. 启动Consul:`consul agent -dev` 2. 启动service_a:`python service_a.py` 3. 启动service_b:`python service_b.py` 4. 启动gateway:`python gateway.py` 测试网关: - 访问 `http://localhost:8000/service_a/` -> 应返回Service A的响应 - 访问 `http://localhost:8000/service_b/` -> 应返回Service B的响应 ### 配置中心 我们还可以使用Consul的KV存储作为配置中心。例如,我们可以在Consul中设置一个键值对,然后在服务启动时读取。 在`consul_utils.py`中添加: ```python def get_config(self, key: str) -> Optional[str]: """ 获取配置 """ index, data = self.consul.kv.get(key) if data: return data['Value'].decode('utf-8') return None ``` 在微服务启动时,可以读取配置,例如端口号、服务名等。但注意,这只是一个简单的示例,实际生产环境中可能需要更复杂的配置管理。 ### 总结 我们构建了一个基于FastAPI的微服务基础框架,包括: - 使用Consul实现服务注册与发现。 - 使用FastAPI构建网关,实现路由转发和负载均衡(轮询)。 - 使用`circuitbreaker`实现熔断机制。 这个框架可以进一步扩展,例如实现配置中心动态更新、更复杂的负载均衡策略(如随机、权重)、链路追踪等。 ### 注意事项 1. 服务发现中,我们只是简单获取第一个健康实例,实际生产环境需要更复杂的负载均衡策略。 2. 熔断器设置需要根据实际情况调整。 3. 网关目前只处理GET请求,需要支持其他方法。 4. 网关的路由规则是硬编码的,可以设计成从配置文件或配置中心读取。 ### 相关改进方向 1. 使用异步Consul客户端(如`aioconsul`)提高性能。 2. 网关支持更多的转发配置,如基于路径、域名等。 3. 集成链路追踪(如Jaeger)。 4. 集成监控(Prometheus)。 §§相关问题§§: 1. 如何实现配置的动态更新(当Consul中的配置改变时,微服务如何感知并更新)? 2. 在FastAPI中如何实现类似Ribbon的客户端负载均衡(如轮询、随机、权重)? 3. 如何将熔断器(circuitbreaker)与FastAPI集成,并在网关中实现服务降级? 4. 如何利用Consul实现服务之间的健康检查,并在服务不可用时自动剔除? </think> 下面我将构建一个基于 FastAPI 的微服务基础框架,对标 Spring Cloud 的核心功能。这个框架包含服务注册与发现、API 网关、配置中心和熔断机制等核心组件。 ### 完整解决方案 #### 项目结构 ```bash fastapi-microservices/ ├── service_registry/ # 服务注册中心 │ ├── main.py │ └── requirements.txt ├── api_gateway/ # API 网关 │ ├── main.py │ └── requirements.txt ├── config_server/ # 配置中心 │ ├── main.py │ ├── config/ │ │ └── application.yml │ └── requirements.txt ├── user_service/ # 示例微服务 │ ├── main.py │ └── requirements.txt └── common/ # 公共模块 └── consul_client.py ``` ### 1. 服务注册中心 (使用 Consul) ```python # service_registry/main.py import uvicorn from fastapi import FastAPI import consul app = FastAPI() consul_client = consul.Consul() @app.on_event("startup") async def startup_event(): # 注册服务自身(可选) consul_client.agent.service.register( name="service-registry", service_id="service-registry-1", address="localhost", port=8500 ) @app.get("/health") def health_check(): return {"status": "UP"} if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=8500, reload=True) ``` #### 解释: - 使用 `python-consul` 库实现服务注册 - 服务启动时在 Consul 注册自身 - 提供健康检查端点 `/health` - 运行在 8500 端口(Consul 默认端口) ### 2. 配置中心 ```python # config_server/main.py import uvicorn import yaml from fastapi import FastAPI from common.consul_client import ConsulClient app = FastAPI() consul = ConsulClient() # 加载初始配置 with open("config/application.yml") as f: config_data = yaml.safe_load(f) for key, value in config_data.items(): consul.put_config(key, value) @app.get("/config/{key}") def get_config(key: str): value = consul.get_config(key) return {key: value} @app.put("/config/{key}") def update_config(key: str, value: str): consul.put_config(key, value) return {"status": "updated"} if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=8888, reload=True) ``` #### 配置文件示例 ```yaml # config/application.yml database: url: jdbc:postgresql://localhost:5432/mydb username: admin password: secret logging: level: INFO ``` #### 解释: - 使用 Consul KV 存储作为配置存储 - 提供配置的读取和更新接口 - 启动时加载本地配置文件到 Consul - 运行在 8888 端口 ### 3. API 网关 ```python # api_gateway/main.py import uvicorn import httpx from fastapi import FastAPI, HTTPException, Request from common.consul_client import ConsulClient from circuitbreaker import circuit app = FastAPI() consul = ConsulClient() @circuit(failure_threshold=5, recovery_timeout=30) async def forward_request(service_name: str, path: str, request: Request): # 服务发现 service = consul.get_service(service_name) if not service: raise HTTPException(status_code=503, detail="Service unavailable") # 构建目标URL url = f"http://{service['Address']}:{service['Port']}{path}" # 转发请求 async with httpx.AsyncClient() as client: response = await client.request( method=request.method, url=url, headers=dict(request.headers), content=await request.body() ) return response @app.api_route("/{service_name}/{path:path}", methods=["GET", "POST", "PUT", "DELETE"]) async def gateway_route(service_name: str, path: str, request: Request): response = await forward_request(service_name, f"/{path}", request) return response.content if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) ``` #### 解释: - 实现动态路由:`/{service_name}/**` 转发到对应服务 - 使用 Consul 进行服务发现 - 集成了熔断器 (`circuitbreaker` 库) - 支持所有 HTTP 方法 - 运行在 8000 端口 ### 4. 示例微服务 (用户服务) ```python # user_service/main.py import uvicorn from fastapi import FastAPI from common.consul_client import ConsulClient from pydantic import BaseModel app = FastAPI() consul = ConsulClient() @app.on_event("startup") async def register_service(): # 注册到服务发现 consul.register_service( name="user-service", service_id="user-service-1", address="localhost", port=8080 ) # 从配置中心获取配置 db_config = consul.get_config("database") print(f"Database config: {db_config}") class User(BaseModel): id: int name: str email: str users_db = [ User(id=1, name="Alice", email="alice@example.com"), User(id=2, name="Bob", email="bob@example.com") ] @app.get("/users") def get_users(): return users_db @app.get("/users/{user_id}") def get_user(user_id: int): user = next((u for u in users_db if u.id == user_id), None) if not user: return {"error": "User not found"}, 404 return user if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=8080, reload=True) ``` #### 解释: - 启动时注册到服务发现中心 - 从配置中心获取数据库配置 - 提供简单的用户管理 API - 运行在 8080 端口 ### 5. 公共 Consul 客户端 ```python # common/consul_client.py import consul class ConsulClient: def __init__(self, host="localhost", port=8500): self.client = consul.Consul(host=host, port=port) def register_service(self, name, service_id, address, port): return self.client.agent.service.register( name=name, service_id=service_id, address=address, port=port, check={ "HTTP": f"http://{address}:{port}/health", "Interval": "10s" } ) def get_service(self, name): _, services = self.client.health.service(name, passing=True) return services[0]['Service'] if services else None def put_config(self, key, value): return self.client.kv.put(key, value) def get_config(self, key): _, data = self.client.kv.get(key) return data['Value'].decode() if data else None ``` #### 解释: - 封装 Consul 客户端操作 - 提供服务注册、服务发现、配置管理功能 - 支持健康检查自动配置 ### 运行步骤 1. 安装依赖: ```bash pip install fastapi uvicorn python-consul httpx python-dotenv circuitbreaker pyyaml ``` 2. 启动 Consul (需要先安装): ```bash consul agent -dev ``` 3. 启动各服务 (每个目录下运行): ```bash uvicorn main:app --reload ``` 4. 测试: - 访问网关:http://localhost:8000/user-service/users - 查看注册服务:http://localhost:8500/ui/dc1/services - 查看配置:http://localhost:8888/config/database ### 对标 Spring Cloud 的功能 | Spring Cloud 组件 | FastAPI 实现方案 | |------------------|-----------------------------| | Eureka/Nacos | Consul 服务注册与发现 | | Spring Cloud Config | Consul KV 配置中心 | | Zuul/Gateway | FastAPI 动态路由网关 | | Hystrix | circuitbreaker 熔断库 | | Feign/Ribbon | httpx + Consul 服务发现 | ### 总结 这个框架提供了微服务核心功能: 1. **服务注册与发现**:通过 Consul 实现 2. **动态路由网关**:统一入口和请求转发 3. **集中配置管理**:使用 Consul KV 存储 4. **熔断机制**:circuitbreaker 库实现 5. **健康检查**:自动服务健康监测
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恒云客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值