Closed zking2000 closed 3 months ago
/* frontend/static/styles.css */
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f4f4f4;
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 30px;
}
nav {
background-color: #3498db;
padding: 10px;
margin-bottom: 20px;
}
nav a {
color: white;
text-decoration: none;
padding: 5px 10px;
margin-right: 10px;
}
nav a:hover {
background-color: #2980b9;
}
ul {
list-style-type: none;
padding: 0;
}
li {
background-color: white;
margin-bottom: 10px;
padding: 15px;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
form {
display: inline-block;
margin-left: 10px;
}
input[type="number"] {
width: 50px;
padding: 5px;
}
button {
background-color: #2ecc71;
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
border-radius: 3px;
}
button:hover {
background-color: #27ae60;
}
a {
color: #3498db;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
backend/
│
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── models.py
│ └── database.py
│
├── requirements.txt
└── Dockerfile
# 这个文件可以为空,它使 app 成为一个包
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from app import models, database
from appdynamics.agent import api as appd
from pydantic import BaseModel
app = FastAPI()
# AppDynamics 配置
appd.init({
'app_name': 'E-commerce Application',
'tier_name': 'Backend',
'node_name': 'backend-node-1',
'controller_host': 'your-appdynamics-controller.com',
'controller_port': 8090,
'controller_ssl': True,
'account_name': 'your-account-name',
'account_access_key': 'your-access-key',
})
# 初始化数据库
models.Base.metadata.create_all(bind=database.engine)
# 依赖项
def get_db():
db = database.SessionLocal()
try:
yield db
finally:
db.close()
class ProductCreate(BaseModel):
name: str
price: float
class OrderCreate(BaseModel):
product_id: int
quantity: int
@app.post("/products")
def create_product(product: ProductCreate, db: Session = Depends(get_db)):
bt = appd.start_bt("Create Product", "Backend")
try:
db_product = models.Product(name=product.name, price=product.price)
db.add(db_product)
db.commit()
db.refresh(db_product)
return db_product
finally:
appd.end_bt(bt)
@app.get("/products")
def read_products(db: Session = Depends(get_db)):
bt = appd.start_bt("Get Products", "Backend")
try:
return db.query(models.Product).all()
finally:
appd.end_bt(bt)
@app.post("/orders")
def create_order(order: OrderCreate, db: Session = Depends(get_db)):
bt = appd.start_bt("Create Order", "Backend")
try:
db_order = models.Order(product_id=order.product_id, quantity=order.quantity)
db.add(db_order)
db.commit()
db.refresh(db_order)
return db_order
finally:
appd.end_bt(bt)
@app.get("/orders")
def read_orders(db: Session = Depends(get_db)):
bt = appd.start_bt("Get Orders", "Backend")
try:
return db.query(models.Order).all()
finally:
appd.end_bt(bt)
if __name__ == "__main__":
import uvicorn
uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True)
from sqlalchemy import Column, Integer, String, Float
from app.database import Base
class Product(Base):
__tablename__ = "products"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
price = Column(Float)
class Order(Base):
__tablename__ = "orders"
id = Column(Integer, primary_key=True, index=True)
product_id = Column(Integer)
quantity = Column(Integer)
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "postgresql://user:password@db/dbname"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
我理解您的需求了。您希望保持现有的 Python Flask 应用程序,但需要一个与之兼容的 AppDynamics 客户端。这是一个很好的选择,因为我们可以使用 AppDynamics 的 Python SDK 来监控您的 Flask 应用程序。
让我们更新代码以集成 AppDynamics 的 Python SDK:
import os
from flask import Flask, jsonify, request, render_template
from flask_sqlalchemy import SQLAlchemy
from appdynamics.agent import api as appd
# AppDynamics Configuration
appd_config = {
"APP_NAME": "YOUR_APP_NAME",
"TIER_NAME": "YOUR_TIER_NAME",
"NODE_NAME": "YOUR_NODE_NAME",
"CONTROLLER_HOST": "your-controller-host.saas.appdynamics.com",
"CONTROLLER_PORT": 443,
"CONTROLLER_SSL": True,
"ACCOUNT_NAME": "YOUR_ACCOUNT_NAME",
"ACCESS_KEY": "YOUR_ACCESS_KEY",
"HTTP_PROXY_HOST": "your-proxy-host",
"HTTP_PROXY_PORT": "YOUR_PROXY_PORT"
}
# Initialize AppDynamics
appd.init(
appname=appd_config["APP_NAME"],
tiername=appd_config["TIER_NAME"],
nodename=appd_config["NODE_NAME"]
)
# Set AppDynamics controller information
appd.set_controller_host(appd_config["CONTROLLER_HOST"])
appd.set_controller_port(appd_config["CONTROLLER_PORT"])
appd.set_controller_use_ssl(appd_config["CONTROLLER_SSL"])
appd.set_controller_account(appd_config["ACCOUNT_NAME"])
appd.set_controller_access_key(appd_config["ACCESS_KEY"])
# Set proxy if needed
if appd_config["HTTP_PROXY_HOST"] and appd_config["HTTP_PROXY_PORT"]:
appd.set_proxy_properties(appd_config["HTTP_PROXY_HOST"], appd_config["HTTP_PROXY_PORT"])
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.db'
db = SQLAlchemy(app)
class Item(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
description = db.Column(db.String(200))
@app.route('/')
@appd.bt
def index():
return render_template('index.html')
@app.route('/api/items', methods=['GET', 'POST'])
@appd.bt
def handle_items():
if request.method == 'POST':
data = request.json
new_item = Item(name=data['name'], description=data.get('description', ''))
db.session.add(new_item)
db.session.commit()
return jsonify({"message": "Item created successfully"}), 201
else:
items = Item.query.all()
return jsonify([{"id": item.id, "name": item.name, "description": item.description} for item in items])
if __name__ == '__main__':
with app.app_context():
db.create_all()
appd.start() # Start AppDynamics monitoring
app.run(debug=True)
这个更新后的代码包含以下主要变化:
导入 AppDynamics Python SDK:from appdynamics.agent import api as appd
创建一个 appd_config
字典来存储 AppDynamics 配置。这些值应该替换为您的实际配置。
使用 appd.init()
初始化 AppDynamics。
设置 AppDynamics 控制器信息和代理设置(如果需要)。
在每个路由函数上使用 @appd.bt
装饰器来创建业务事务。
在应用程序启动时调用 appd.start()
来开始 AppDynamics 监控。
要使用这段代码,您需要:
安装 AppDynamics Python SDK:pip install appdynamics
用您的实际 AppDynamics 配置替换 appd_config
中的占位符值。
确保您的 AppDynamics 账户已经配置好,可以接收来自 Python 应用程序的数据。
这个设置将允许 AppDynamics 监控您的 Flask 应用程序,跟踪请求,测量性能,并提供可见性到您的应用程序运行状况。
如果您需要进一步的自定义,比如添加自定义指标或更详细的事务跟踪,我们可以进一步修改代码。您有任何其他需求或问题吗?
非常感谢您提供这段代码。我理解您希望我基于这个 FastAPI 应用程序进行修改。这是一个很好的起点,因为它已经集成了 AppDynamics 并使用了异步编程。让我们对代码进行一些优化和扩展。
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
import httpx
from appdynamics.agent import api as appd
import logging
from typing import Dict
app = FastAPI()
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# AppDynamics 配置
appd_config = {
'app_name': 'E-commerce Application',
'tier_name': 'Frontend Service',
'node_name': 'frontend-node-1',
'controller_host': 'your-appdynamics-controller.com',
'controller_port': 8090,
'controller_ssl': True,
'account_name': 'your-account-name',
'account_access_key': 'your-access-key',
}
appd.init(appd_config)
PRODUCT_SERVICE_URL = "http://product-service:8000"
ORDER_SERVICE_URL = "http://order-service:8000"
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
bt = appd.start_bt(request.url.path, "Frontend")
try:
response = await call_next(request)
return response
finally:
appd.end_bt(bt)
async def make_service_call(url: str, method: str, data: Dict = None) -> Dict:
async with httpx.AsyncClient() as client:
if method.lower() == 'get':
response = await client.get(url)
elif method.lower() == 'post':
response = await client.post(url, json=data)
else:
raise ValueError(f"Unsupported HTTP method: {method}")
response.raise_for_status()
return response.json()
@app.get("/products")
async def get_products():
logger.info("Fetching products")
try:
return await make_service_call(f"{PRODUCT_SERVICE_URL}/products", 'get')
except httpx.HTTPError as e:
logger.error(f"Error fetching products: {str(e)}")
raise HTTPException(status_code=500, detail="Error fetching products")
@app.post("/orders")
async def create_order(order: Dict):
logger.info("Creating new order")
try:
return await make_service_call(f"{ORDER_SERVICE_URL}/orders", 'post', order)
except httpx.HTTPError as e:
logger.error(f"Error creating order: {str(e)}")
raise HTTPException(status_code=500, detail="Error creating order")
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
appd.add_bt_error(str(exc))
return JSONResponse(
status_code=exc.status_code,
content={"message": exc.detail},
)
if __name__ == "__main__":
import uvicorn
logger.info("Starting the application")
uvicorn.run(app, host="0.0.0.0", port=8000)
这个更新后的代码包含以下主要改进:
添加了日志记录,使用 Python 的 logging
模块来记录重要的事件和错误。
将 AppDynamics 配置移到了一个单独的字典中,使其更易于管理和更新。
添加了一个中间件来自动为所有请求启动和结束 AppDynamics 业务事务(BT)。这样,我们不需要在每个路由函数中手动启动和结束 BT。
创建了一个通用的 make_service_call
函数来处理 HTTP 请求,减少代码重复。
改进了错误处理,添加了一个全局异常处理器来记录 HTTP 异常并将它们添加到 AppDynamics BT 错误中。
使用类型提示来提高代码的可读性和可维护性。
在主函数中添加了启动日志。
使用这段代码时,您需要:
确保已安装所有必要的依赖:fastapi
, httpx
, uvicorn
, 和 appdynamics
。
用您的实际 AppDynamics 配置替换 appd_config
中的占位符值。
根据您的实际服务 URL 更新 PRODUCT_SERVICE_URL
和 ORDER_SERVICE_URL
。
这个设置将允许 AppDynamics 监控您的 FastAPI 应用程序,跟踪所有 HTTP 请求,测量性能,并提供应用程序运行状况的可见性。
如果您需要进一步的自定义,比如添加更多路由、自定义指标,或者对特定端点进行更详细的监控,我们可以进一步修改代码。您对这个实现有什么想法或其他需求吗?
from fastapi import FastAPI, HTTPException
import httpx
from appdynamics.agent import api as appd
import os
app = FastAPI()
# AppDynamics 配置
appd.init({
'app_name': os.environ.get('APPDYNAMICS_AGENT_APPLICATION_NAME', 'default_app_name'),
'tier_name': os.environ.get('APPDYNAMICS_AGENT_TIER_NAME', 'default_tier_name'),
'node_name': os.environ.get('APPDYNAMICS_AGENT_NODE_NAME', 'default_node_name'),
'controller_host': os.environ.get('APPDYNAMICS_CONTROLLER_HOST_NAME', 'your_controller_host'),
'controller_port': int(os.environ.get('APPDYNAMICS_CONTROLLER_PORT', '443')),
'controller_ssl': os.environ.get('APPDYNAMICS_CONTROLLER_SSL_ENABLED', 'true').lower() == 'true',
'account_name': os.environ.get('APPDYNAMICS_AGENT_ACCOUNT_NAME', 'your_account_name'),
'account_access_key': os.environ.get('APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY', 'your_access_key'),
'http_proxy_host': os.environ.get('APPDYNAMICS_HTTP_PROXY_HOST', 'your_proxy_host'),
'http_proxy_port': int(os.environ.get('APPDYNAMICS_HTTP_PROXY_PORT', '0')),
})
PRODUCT_SERVICE_URL = "http://product-service:8000"
ORDER_SERVICE_URL = "http://order-service:8000"
@app.get("/products")
async def get_products():
bt = appd.start_bt("Get Products", "Frontend")
try:
async with httpx.AsyncClient() as client:
response = await client.get(f"{PRODUCT_SERVICE_URL}/products")
response.raise_for_status()
return response.json()
except httpx.HTTPError as e:
raise HTTPException(status_code=500, detail=str(e))
finally:
appd.end_bt(bt)
@app.post("/orders")
async def create_order(order: dict):
bt = appd.start_bt("Create Order", "Frontend")
try:
async with httpx.AsyncClient() as client:
response = await client.post(f"{ORDER_SERVICE_URL}/orders", json=order)
response.raise_for_status()
return response.json()
except httpx.HTTPError as e:
raise HTTPException(status_code=500, detail=str(e))
finally:
appd.end_bt(bt)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
from flask import Flask, render_template, request, redirect, url_for
import requests
from appdynamics.agent import api as appd
import os
app = Flask(__name__)
# AppDynamics 配置
appd.init({
'app_name': os.environ.get('APPDYNAMICS_AGENT_APPLICATION_NAME', 'default_app_name'),
'tier_name': os.environ.get('APPDYNAMICS_AGENT_TIER_NAME', 'default_tier_name'),
'node_name': os.environ.get('APPDYNAMICS_AGENT_NODE_NAME', 'default_node_name'),
'controller_host': os.environ.get('APPDYNAMICS_CONTROLLER_HOST_NAME', 'your_controller_host'),
'controller_port': int(os.environ.get('APPDYNAMICS_CONTROLLER_PORT', '443')),
'controller_ssl': os.environ.get('APPDYNAMICS_CONTROLLER_SSL_ENABLED', 'true').lower() == 'true',
'account_name': os.environ.get('APPDYNAMICS_AGENT_ACCOUNT_NAME', 'your_account_name'),
'account_access_key': os.environ.get('APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY', 'your_access_key'),
'http_proxy_host': os.environ.get('APPDYNAMICS_HTTP_PROXY_HOST', 'your_proxy_host'),
'http_proxy_port': int(os.environ.get('APPDYNAMICS_HTTP_PROXY_PORT', '0')),
})
BACKEND_URL = os.environ.get('BACKEND_URL', "http://backend:8000")
@app.route('/')
def index():
return render_template('index.html')
@app.route('/products')
def products():
bt = appd.start_bt("Get Products", "Frontend")
try:
response = requests.get(f"{BACKEND_URL}/products")
products = response.json()
return render_template('products.html', products=products)
finally:
appd.end_bt(bt)
@app.route('/orders', methods=['GET', 'POST'])
def orders():
if request.method == 'POST':
bt = appd.start_bt("Create Order", "Frontend")
try:
order_data = {
"product_id": request.form['product_id'],
"quantity": int(request.form['quantity'])
}
response = requests.post(f"{BACKEND_URL}/orders", json=order_data)
return redirect(url_for('orders'))
finally:
appd.end_bt(bt)
else:
bt = appd.start_bt("Get Orders", "Frontend")
try:
response = requests.get(f"{BACKEND_URL}/orders")
orders = response.json()
return render_template('orders.html', orders=orders)
finally:
appd.end_bt(bt)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000)))
e-commerce-app/ ├── frontend/ │ ├── app.py │ ├── templates/ │ │ ├── index.html │ │ ├── products.html │ │ └── orders.html │ ├── static/ │ │ └── styles.css │ ├── requirements.txt │ └── Dockerfile ├── backend/ │ ├── main.py │ ├── models.py │ ├── database.py │ ├── requirements.txt │ └── Dockerfile └── docker-compose.yml
frontend/templates/index.html
frontend/templates/products.html
frontend/templates/orders.html
backend/main.py
backend/models.py
backend/database.py
frontend/requirements.txt
backend/requirements.txt
frontend/Dockerfile
backend/Dockerfile
docker-compose.yml