Google Cloud Platform服务
📖 概述
Google Cloud Platform (GCP) 是Google提供的云计算平台,提供计算、存储、数据分析、机器学习等200多种服务。本文档将详细介绍如何在Node.js应用中集成各种GCP服务。
🎯 学习目标
- 掌握GCP客户端库的使用
- 学习核心GCP服务的集成
- 了解GCP服务的最佳实践
- 实现安全的GCP服务访问
🔧 环境准备
1. 安装GCP客户端库
bash
# 核心库
npm install @google-cloud/storage @google-cloud/firestore
npm install @google-cloud/functions-framework @google-cloud/pubsub
npm install @google-cloud/translate @google-cloud/vision
npm install @google-cloud/speech @google-cloud/text-to-speech
# 认证库
npm install google-auth-library
2. 身份验证配置
javascript
// 方法1: 服务账户密钥文件
process.env.GOOGLE_APPLICATION_CREDENTIALS = '/path/to/service-account-key.json';
// 方法2: 环境变量设置
const { GoogleAuth } = require('google-auth-library');
const auth = new GoogleAuth({
keyFilename: process.env.GOOGLE_APPLICATION_CREDENTIALS,
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
});
// 方法3: 在GCP环境中自动认证
const auth2 = new GoogleAuth();
🗄️ Cloud Storage 对象存储
1. 基本操作
javascript
const { Storage } = require('@google-cloud/storage');
class GCPStorageService {
constructor() {
this.storage = new Storage({
projectId: process.env.GOOGLE_CLOUD_PROJECT_ID,
keyFilename: process.env.GOOGLE_APPLICATION_CREDENTIALS,
});
this.bucketName = process.env.GCS_BUCKET_NAME;
this.bucket = this.storage.bucket(this.bucketName);
}
// 上传文件
async uploadFile(fileName, fileBuffer, options = {}) {
try {
const file = this.bucket.file(fileName);
const stream = file.createWriteStream({
metadata: {
contentType: options.contentType || 'application/octet-stream',
metadata: options.metadata || {},
},
resumable: false,
});
return new Promise((resolve, reject) => {
stream.on('error', reject);
stream.on('finish', () => {
resolve({
success: true,
fileName,
publicUrl: `https://storage.googleapis.com/${this.bucketName}/${fileName}`,
});
});
stream.end(fileBuffer);
});
} catch (error) {
throw new Error(`上传失败: ${error.message}`);
}
}
// 下载文件
async downloadFile(fileName) {
try {
const file = this.bucket.file(fileName);
const [contents] = await file.download();
return contents;
} catch (error) {
throw new Error(`下载失败: ${error.message}`);
}
}
// 生成签名URL
async generateSignedUrl(fileName, action = 'read', expiresInMinutes = 60) {
try {
const file = this.bucket.file(fileName);
const expires = Date.now() + expiresInMinutes * 60 * 1000;
const [signedUrl] = await file.getSignedUrl({
version: 'v4',
action,
expires,
});
return signedUrl;
} catch (error) {
throw new Error(`生成签名URL失败: ${error.message}`);
}
}
// 列出文件
async listFiles(prefix = '') {
try {
const [files] = await this.bucket.getFiles({ prefix });
return files.map(file => ({
name: file.name,
size: parseInt(file.metadata.size),
updated: file.metadata.updated,
contentType: file.metadata.contentType,
}));
} catch (error) {
throw new Error(`列出文件失败: ${error.message}`);
}
}
// 删除文件
async deleteFile(fileName) {
try {
await this.bucket.file(fileName).delete();
return { success: true };
} catch (error) {
throw new Error(`删除失败: ${error.message}`);
}
}
// 移动文件
async moveFile(sourceFileName, destFileName) {
try {
await this.bucket.file(sourceFileName).move(destFileName);
return { success: true };
} catch (error) {
throw new Error(`移动失败: ${error.message}`);
}
}
}
// 使用示例
const storageService = new GCPStorageService();
app.post('/upload', upload.single('file'), async (req, res) => {
try {
const fileName = `uploads/${Date.now()}-${req.file.originalname}`;
const result = await storageService.uploadFile(fileName, req.file.buffer, {
contentType: req.file.mimetype,
metadata: { uploadedBy: req.user.id },
});
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
🔥 Firestore 数据库
1. 基本操作
javascript
const { Firestore } = require('@google-cloud/firestore');
class FirestoreService {
constructor() {
this.db = new Firestore({
projectId: process.env.GOOGLE_CLOUD_PROJECT_ID,
keyFilename: process.env.GOOGLE_APPLICATION_CREDENTIALS,
});
}
// 创建文档
async createDocument(collection, docId, data) {
try {
const docRef = this.db.collection(collection).doc(docId);
await docRef.set({
...data,
createdAt: new Date(),
updatedAt: new Date(),
});
return { id: docId, ...data };
} catch (error) {
throw new Error(`创建文档失败: ${error.message}`);
}
}
// 获取文档
async getDocument(collection, docId) {
try {
const docRef = this.db.collection(collection).doc(docId);
const doc = await docRef.get();
if (!doc.exists) {
return null;
}
return { id: doc.id, ...doc.data() };
} catch (error) {
throw new Error(`获取文档失败: ${error.message}`);
}
}
// 更新文档
async updateDocument(collection, docId, updates) {
try {
const docRef = this.db.collection(collection).doc(docId);
await docRef.update({
...updates,
updatedAt: new Date(),
});
const updatedDoc = await this.getDocument(collection, docId);
return updatedDoc;
} catch (error) {
throw new Error(`更新文档失败: ${error.message}`);
}
}
// 查询文档
async queryDocuments(collection, filters = [], orderBy = null, limit = null) {
try {
let query = this.db.collection(collection);
// 应用过滤条件
filters.forEach(filter => {
query = query.where(filter.field, filter.operator, filter.value);
});
// 应用排序
if (orderBy) {
query = query.orderBy(orderBy.field, orderBy.direction || 'asc');
}
// 应用限制
if (limit) {
query = query.limit(limit);
}
const snapshot = await query.get();
return snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
} catch (error) {
throw new Error(`查询文档失败: ${error.message}`);
}
}
// 删除文档
async deleteDocument(collection, docId) {
try {
await this.db.collection(collection).doc(docId).delete();
return { success: true };
} catch (error) {
throw new Error(`删除文档失败: ${error.message}`);
}
}
// 批量操作
async batchOperations(operations) {
try {
const batch = this.db.batch();
operations.forEach(op => {
const docRef = this.db.collection(op.collection).doc(op.docId);
switch (op.type) {
case 'create':
case 'set':
batch.set(docRef, op.data);
break;
case 'update':
batch.update(docRef, op.data);
break;
case 'delete':
batch.delete(docRef);
break;
}
});
await batch.commit();
return { success: true };
} catch (error) {
throw new Error(`批量操作失败: ${error.message}`);
}
}
// 事务操作
async runTransaction(transactionFunction) {
try {
return await this.db.runTransaction(transactionFunction);
} catch (error) {
throw new Error(`事务操作失败: ${error.message}`);
}
}
}
// 使用示例
const firestoreService = new FirestoreService();
app.post('/users', async (req, res) => {
try {
const userId = req.body.id || this.db.collection('users').doc().id;
const user = await firestoreService.createDocument('users', userId, {
name: req.body.name,
email: req.body.email,
status: 'active',
});
res.json(user);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.get('/users', async (req, res) => {
try {
const filters = [];
if (req.query.status) {
filters.push({ field: 'status', operator: '==', value: req.query.status });
}
const users = await firestoreService.queryDocuments('users', filters,
{ field: 'createdAt', direction: 'desc' }, 50);
res.json(users);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
📨 Pub/Sub 消息服务
1. 发布订阅
javascript
const { PubSub } = require('@google-cloud/pubsub');
class PubSubService {
constructor() {
this.pubsub = new PubSub({
projectId: process.env.GOOGLE_CLOUD_PROJECT_ID,
});
}
// 发布消息
async publishMessage(topicName, data, attributes = {}) {
try {
const topic = this.pubsub.topic(topicName);
const messageId = await topic.publishMessage({
data: Buffer.from(JSON.stringify(data)),
attributes,
});
return { messageId };
} catch (error) {
throw new Error(`发布消息失败: ${error.message}`);
}
}
// 批量发布消息
async publishMessages(topicName, messages) {
try {
const topic = this.pubsub.topic(topicName);
const messageIds = await Promise.all(
messages.map(msg => topic.publishMessage({
data: Buffer.from(JSON.stringify(msg.data)),
attributes: msg.attributes || {},
}))
);
return { messageIds };
} catch (error) {
throw new Error(`批量发布消息失败: ${error.message}`);
}
}
// 创建订阅
async createSubscription(topicName, subscriptionName, options = {}) {
try {
const topic = this.pubsub.topic(topicName);
const [subscription] = await topic.createSubscription(subscriptionName, {
ackDeadlineSeconds: options.ackDeadlineSeconds || 60,
messageRetentionDuration: options.messageRetentionDuration || 604800, // 7天
});
return subscription.name;
} catch (error) {
throw new Error(`创建订阅失败: ${error.message}`);
}
}
// 监听消息
listenForMessages(subscriptionName, messageHandler) {
const subscription = this.pubsub.subscription(subscriptionName);
const messageListener = (message) => {
try {
const data = JSON.parse(message.data.toString());
messageHandler(data, message.attributes, message);
message.ack();
} catch (error) {
console.error('处理消息失败:', error);
message.nack();
}
};
subscription.on('message', messageListener);
subscription.on('error', (error) => {
console.error('订阅错误:', error);
});
return subscription;
}
// 拉取消息
async pullMessages(subscriptionName, maxMessages = 10) {
try {
const subscription = this.pubsub.subscription(subscriptionName);
const [messages] = await subscription.receivedMessages({
maxMessages,
allowExcessMessages: false,
});
const results = messages.map(message => ({
id: message.id,
data: JSON.parse(message.data.toString()),
attributes: message.attributes,
publishTime: message.publishTime,
ackId: message.ackId,
}));
// 确认消息
messages.forEach(message => message.ack());
return results;
} catch (error) {
throw new Error(`拉取消息失败: ${error.message}`);
}
}
}
// 使用示例
const pubsubService = new PubSubService();
// 发布事件
app.post('/publish-event', async (req, res) => {
try {
const result = await pubsubService.publishMessage('user-events', {
eventType: 'user-registered',
userId: req.body.userId,
timestamp: new Date().toISOString(),
}, {
source: 'api-server',
version: '1.0',
});
res.json({ success: true, messageId: result.messageId });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 消息监听器
pubsubService.listenForMessages('user-events-subscription', (data, attributes, message) => {
console.log('收到消息:', data);
switch (data.eventType) {
case 'user-registered':
// 处理用户注册事件
handleUserRegistration(data.userId);
break;
case 'order-created':
// 处理订单创建事件
handleOrderCreation(data.orderId);
break;
default:
console.warn('未知事件类型:', data.eventType);
}
});
🤖 AI/ML 服务
1. 翻译服务
javascript
const { Translate } = require('@google-cloud/translate').v2;
class TranslateService {
constructor() {
this.translate = new Translate({
projectId: process.env.GOOGLE_CLOUD_PROJECT_ID,
});
}
// 检测语言
async detectLanguage(text) {
try {
const [detection] = await this.translate.detect(text);
return {
language: detection.language,
confidence: detection.confidence,
};
} catch (error) {
throw new Error(`语言检测失败: ${error.message}`);
}
}
// 翻译文本
async translateText(text, targetLanguage, sourceLanguage = null) {
try {
const options = { to: targetLanguage };
if (sourceLanguage) {
options.from = sourceLanguage;
}
const [translation] = await this.translate.translate(text, options);
return {
originalText: text,
translatedText: translation,
sourceLanguage: sourceLanguage || (await this.detectLanguage(text)).language,
targetLanguage,
};
} catch (error) {
throw new Error(`翻译失败: ${error.message}`);
}
}
// 批量翻译
async translateTexts(texts, targetLanguage, sourceLanguage = null) {
try {
const options = { to: targetLanguage };
if (sourceLanguage) {
options.from = sourceLanguage;
}
const [translations] = await this.translate.translate(texts, options);
return texts.map((text, index) => ({
originalText: text,
translatedText: translations[index],
targetLanguage,
}));
} catch (error) {
throw new Error(`批量翻译失败: ${error.message}`);
}
}
// 获取支持的语言
async getSupportedLanguages() {
try {
const [languages] = await this.translate.getLanguages();
return languages.map(lang => ({
code: lang.code,
name: lang.name,
}));
} catch (error) {
throw new Error(`获取支持语言失败: ${error.message}`);
}
}
}
// 使用示例
const translateService = new TranslateService();
app.post('/translate', async (req, res) => {
try {
const result = await translateService.translateText(
req.body.text,
req.body.targetLanguage,
req.body.sourceLanguage
);
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
2. 视觉识别服务
javascript
const vision = require('@google-cloud/vision');
class VisionService {
constructor() {
this.client = new vision.ImageAnnotatorClient({
projectId: process.env.GOOGLE_CLOUD_PROJECT_ID,
});
}
// 文本识别 (OCR)
async detectText(imageBuffer) {
try {
const [result] = await this.client.textDetection({
image: { content: imageBuffer },
});
const detections = result.textAnnotations;
if (!detections || detections.length === 0) {
return { text: '', details: [] };
}
return {
text: detections[0].description,
details: detections.slice(1).map(detection => ({
text: detection.description,
boundingBox: detection.boundingPoly,
})),
};
} catch (error) {
throw new Error(`文本识别失败: ${error.message}`);
}
}
// 标签检测
async detectLabels(imageBuffer, maxResults = 10) {
try {
const [result] = await this.client.labelDetection({
image: { content: imageBuffer },
maxResults,
});
return result.labelAnnotations.map(label => ({
description: label.description,
score: label.score,
confidence: label.confidence,
}));
} catch (error) {
throw new Error(`标签检测失败: ${error.message}`);
}
}
// 人脸检测
async detectFaces(imageBuffer) {
try {
const [result] = await this.client.faceDetection({
image: { content: imageBuffer },
});
return result.faceAnnotations.map(face => ({
boundingBox: face.boundingPoly,
confidence: face.detectionConfidence,
emotions: {
joy: face.joyLikelihood,
sorrow: face.sorrowLikelihood,
anger: face.angerLikelihood,
surprise: face.surpriseLikelihood,
},
landmarks: face.landmarks,
}));
} catch (error) {
throw new Error(`人脸检测失败: ${error.message}`);
}
}
// 对象检测
async detectObjects(imageBuffer) {
try {
const [result] = await this.client.objectLocalization({
image: { content: imageBuffer },
});
return result.localizedObjectAnnotations.map(object => ({
name: object.name,
score: object.score,
boundingBox: object.boundingPoly,
}));
} catch (error) {
throw new Error(`对象检测失败: ${error.message}`);
}
}
// 安全搜索检测
async detectSafeSearch(imageBuffer) {
try {
const [result] = await this.client.safeSearchDetection({
image: { content: imageBuffer },
});
const safeSearch = result.safeSearchAnnotation;
return {
adult: safeSearch.adult,
violence: safeSearch.violence,
racy: safeSearch.racy,
medical: safeSearch.medical,
spoof: safeSearch.spoof,
};
} catch (error) {
throw new Error(`安全搜索检测失败: ${error.message}`);
}
}
}
// 使用示例
const visionService = new VisionService();
app.post('/analyze-image', upload.single('image'), async (req, res) => {
try {
const [labels, text, faces, objects, safeSearch] = await Promise.all([
visionService.detectLabels(req.file.buffer),
visionService.detectText(req.file.buffer),
visionService.detectFaces(req.file.buffer),
visionService.detectObjects(req.file.buffer),
visionService.detectSafeSearch(req.file.buffer),
]);
res.json({
labels,
text,
faces,
objects,
safeSearch,
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
🔐 安全和身份验证
1. Identity and Access Management
javascript
const { GoogleAuth } = require('google-auth-library');
class GCPAuthService {
constructor() {
this.auth = new GoogleAuth({
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
});
}
// 获取访问令牌
async getAccessToken() {
try {
const client = await this.auth.getClient();
const accessToken = await client.getAccessToken();
return accessToken.token;
} catch (error) {
throw new Error(`获取访问令牌失败: ${error.message}`);
}
}
// 验证JWT令牌
async verifyIdToken(idToken, audience) {
try {
const client = await this.auth.getClient();
const ticket = await client.verifyIdToken({
idToken,
audience,
});
const payload = ticket.getPayload();
return {
userId: payload.sub,
email: payload.email,
name: payload.name,
picture: payload.picture,
};
} catch (error) {
throw new Error(`验证ID令牌失败: ${error.message}`);
}
}
// 获取项目信息
async getProjectId() {
try {
return await this.auth.getProjectId();
} catch (error) {
throw new Error(`获取项目ID失败: ${error.message}`);
}
}
}
📊 监控和日志
1. Cloud Logging
javascript
const { Logging } = require('@google-cloud/logging');
class CloudLoggingService {
constructor() {
this.logging = new Logging({
projectId: process.env.GOOGLE_CLOUD_PROJECT_ID,
});
this.log = this.logging.log('nodejs-app');
}
// 记录日志
async writeLog(severity, message, metadata = {}) {
const entry = this.log.entry({
resource: { type: 'global' },
severity,
}, {
message,
...metadata,
timestamp: new Date().toISOString(),
});
try {
await this.log.write(entry);
} catch (error) {
console.error('写入日志失败:', error);
}
}
// 便捷方法
info(message, metadata) {
return this.writeLog('INFO', message, metadata);
}
warn(message, metadata) {
return this.writeLog('WARNING', message, metadata);
}
error(message, metadata) {
return this.writeLog('ERROR', message, metadata);
}
debug(message, metadata) {
return this.writeLog('DEBUG', message, metadata);
}
}
// Express中间件
const logger = new CloudLoggingService();
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
logger.info('HTTP Request', {
method: req.method,
url: req.url,
statusCode: res.statusCode,
duration,
userAgent: req.get('User-Agent'),
ip: req.ip,
});
});
next();
});
🚀 实际应用示例
完整的GCP集成应用
javascript
const express = require('express');
const multer = require('multer');
class GCPIntegratedService {
constructor() {
this.storage = new GCPStorageService();
this.firestore = new FirestoreService();
this.pubsub = new PubSubService();
this.vision = new VisionService();
this.translate = new TranslateService();
this.logger = new CloudLoggingService();
}
async processImageUpload(file, userId, metadata = {}) {
const fileId = require('uuid').v4();
const fileName = `images/${userId}/${fileId}-${file.originalname}`;
try {
// 1. 上传到Cloud Storage
const uploadResult = await this.storage.uploadFile(fileName, file.buffer, {
contentType: file.mimetype,
metadata: { ...metadata, uploadedBy: userId },
});
// 2. 图像分析
const [labels, text, safeSearch] = await Promise.all([
this.vision.detectLabels(file.buffer),
this.vision.detectText(file.buffer),
this.vision.detectSafeSearch(file.buffer),
]);
// 3. 保存到Firestore
await this.firestore.createDocument('images', fileId, {
userId,
fileName: file.originalname,
storagePath: fileName,
fileSize: file.size,
mimeType: file.mimetype,
analysis: {
labels,
text: text.text,
safeSearch,
},
status: 'processed',
});
// 4. 发布事件
await this.pubsub.publishMessage('image-processed', {
fileId,
userId,
labels: labels.slice(0, 5),
hasText: text.text.length > 0,
});
// 5. 记录日志
await this.logger.info('图像处理完成', {
fileId,
userId,
labelCount: labels.length,
hasText: text.text.length > 0,
});
return {
fileId,
status: 'processed',
url: uploadResult.publicUrl,
analysis: { labels, text: text.text, safeSearch },
};
} catch (error) {
await this.logger.error('图像处理失败', {
error: error.message,
userId,
fileName: file.originalname,
});
throw error;
}
}
async searchImages(userId, query, limit = 20) {
try {
// 翻译查询到英文进行搜索
const translation = await this.translate.translateText(query, 'en');
// 在Firestore中搜索
const filters = [
{ field: 'userId', operator: '==', value: userId },
];
const images = await this.firestore.queryDocuments('images', filters,
{ field: 'createdAt', direction: 'desc' }, limit);
// 简单的标签匹配搜索
const searchTerm = translation.translatedText.toLowerCase();
const filteredImages = images.filter(image =>
image.analysis?.labels?.some(label =>
label.description.toLowerCase().includes(searchTerm)
)
);
return filteredImages;
} catch (error) {
await this.logger.error('图像搜索失败', {
error: error.message,
userId,
query,
});
throw error;
}
}
}
// Express应用
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
const gcpService = new GCPIntegratedService();
app.use(express.json());
// 图像上传和处理
app.post('/upload-image', upload.single('image'), async (req, res) => {
try {
const result = await gcpService.processImageUpload(
req.file,
req.user.id,
{ source: 'web-upload' }
);
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 图像搜索
app.get('/search-images', async (req, res) => {
try {
const results = await gcpService.searchImages(
req.user.id,
req.query.q,
parseInt(req.query.limit) || 20
);
res.json(results);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 获取用户图像
app.get('/images', async (req, res) => {
try {
const filters = [
{ field: 'userId', operator: '==', value: req.user.id },
];
const images = await gcpService.firestore.queryDocuments('images', filters,
{ field: 'createdAt', direction: 'desc' }, 50);
res.json(images);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000, () => {
console.log('GCP集成应用启动在端口 3000');
});
📚 最佳实践总结
- 身份验证:使用服务账户和IAM进行安全认证
- 错误处理:实现重试机制和优雅降级
- 监控日志:使用Cloud Logging进行全面日志记录
- 资源管理:正确管理客户端连接和资源
- 性能优化:使用批量操作和并行处理
- 成本控制:合理使用配额和限制
- 安全性:遵循最小权限原则
通过掌握这些GCP服务集成技术,您将能够构建强大、智能的云原生Node.js应用程序。