Я застрял в этой проблеме в течение двух дней.
Я добавил 4 источника хранения BLOB-объектов в одну группу источников у входной двери Azure. Я провожу пробную версию токенов Blob Sas.
Я не обновлял какие-либо настройки, такие как веса и приоритет, проверки работоспособности включены. Пожалуйста, скажите мне, что я делаю неправильно.
Код по запросу:
Извините за немного неаккуратный код, потому что я очень спешил.
Расчет задержки при входе пользователя:
import { azurePingContainers } from "@/constants/azure-containers";
import axios from "axios";
export async function checkAzureLatency() {
const response = await Promise.all(azurePingContainers.map(container => {
return new Promise<{ latency: number, account: string }>((resolve) => {
const startTime = performance.now();
axios.get(container.url)
.then(() => {
const endTime = performance.now();
let latency = endTime - startTime;
console.info(`${container.account} latency: ${latency}ms`);
latency = parseFloat(latency.toFixed(2));
resolve({ latency, account: container.account });
})
.catch(error => {
console.error(`Error fetching ${container.account} latency: ${error}`);
resolve({ latency: Infinity, account: container.account });
});
})
}))
const validResponses = response.filter(result => result.latency !== Infinity)
if (validResponses.length === 0) {
return null;
}
return validResponses.sort((a, b) => a.latency - b.latency)
}
export async function averageLatency() {
const latencyChecks = (await Promise.all([
checkAzureLatency(),
checkAzureLatency(),
checkAzureLatency()
])).filter(result => result !== null)
if (latencyChecks.length === 0) {
return null
}
console.info(latencyChecks)
const totalLatencyArray = latencyChecks.reduce((acc, val) => {
val.forEach((current) => {
const sameValue = acc.findIndex((x) => x.account === current.account)
if (sameValue>-1) {
acc[sameValue] = {latency: parseFloat((current.latency + acc[sameValue].latency).toFixed(2)) ,account: current.account}
} else {
acc.push(current)
}
})
return acc
}, [])
console.info('totalLatencyArray', totalLatencyArray)
const averageLatencyArray = totalLatencyArray.map(item => ({
latency: parseFloat((item.latency / latencyChecks.length).toFixed(2)),
account: item.account
}));
console.info(averageLatencyArray)
localStorage.setItem('latency', JSON.stringify(averageLatencyArray))
return averageLatencyArray
}
Обработчик загрузки:
const getSasToken = async (container: string, account: string, ipAddress: string, fileType: string) => {
const tokenResponse = await ax.post(
`storage/sas`,
{
container,
account,
ipAddress,
fileType,
},
{
headers: {
...authHeader(),
},
},
);
if (tokenResponse.status !== 201 || !tokenResponse.data?.data?.sasToken) {
console.error("Failed to get SAS token from backend!");
return null;
}
const tokensResponseData = tokenResponse.data.data;
const { sasToken } = tokensResponseData
return sasToken
}
const handleUpload = async (container: string, account: string, ipAddress: string, file: File) => {
const token = await getSasToken(container, account, ipAddress, file.type)
if (!token) return null
const blobService = new BlobService();
return await blobService.uploadFile(file, token, container, account);
}
export const handleVideoUpload = async (file: File, container: string) => {
try {
let retryCounts = 0
const ipAddress = "192.something";
// await ax
// .get("http://api.ipify.org/?format=json")
// .then((response) => response.data)
// .then((data) => {
// if (data) {
// ipAddress = data.ip;
// }
// });
if (file) {
let leastLatencyAccounts = JSON.parse(localStorage.getItem('latency') || '[]')
if (leastLatencyAccounts && leastLatencyAccounts?.length > 0) {
const len = leastLatencyAccounts.length
for (const accountInfo of leastLatencyAccounts) {
if (len >= 5 && retryCounts === 3 || len <= 4 && retryCounts === 2 || len === retryCounts) {
console.error("Failed to upload file with retries")
return null
}
const response = await handleUpload(container, accountInfo.account, ipAddress, file)
if (!response) {
console.error(`Failed to upload the file with account storage ${accountInfo.account}!`);
leastLatencyAccounts.shift()
localStorage.setItem('latency', JSON.stringify(leastLatencyAccounts))
retryCounts++;
continue;
}
return response;
}
// todo buy to set ip address, also encryption------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
} else {
const response = await handleUpload(container, azurePingContainers[ 0 ].account, ipAddress, file)
if (!response) {
console.error(`Failed to upload the file with account storage ${azurePingContainers[ 0 ].account}!`);
}
return response;
}
}
} catch (error) {
console.error("There was an error uploading the file!", error);
}
};
Служба загрузки:
export class BlobService {
constructor() { }
async uploadFile(file: File, token: string, container: string, account: string) {
const fileName = this.generateFileName(file);
try {
const response = await this.uploadToBlobStorage(file, token, account, container, fileName);
return { response, fileName };
} catch (e) {
console.error('Error processing token:', e);
return null
}
}
generateFileName(file: File): string {
return `${file.name}-${Date.now()}.${file.name.split('.').pop()}`;
}
async uploadToBlobStorage(file: File, token: string, account: string, container: string, fileName: string): Promise<{ responseBlob: BlobUploadCommonResponse, account: string } | null> {
const blobServiceClient = new BlobServiceClient(`https://${account}.blob.core.windows.net?${token}`);
const containerClient = blobServiceClient.getContainerClient(container);
const blockBlobClient = containerClient.getBlockBlobClient(fileName);
const responseBlob = await this.upload(file, blockBlobClient);
if (responseBlob.errorCode) {
console.warn('Error in responseBlob:', responseBlob.errorCode);
return null;
}
return { responseBlob, account };
}
async upload(file: File, blockBlobClient: BlockBlobClient) {
console.info('Starting upload...');
const response = await blockBlobClient.uploadData(file, {
blobHTTPHeaders: { blobContentType: file.type },
concurrency: 20
});
store.dispatch(setProgressValue(100))
return response
}
}
Бэкэнд:
async generateSAS(createSasTokenDto: GenerateSasTokenDto) {
try {
const { container, fileType, ipAddress ,account} = createSasTokenDto
const permissions = new ContainerSASPermissions()
permissions.write = true
const blobServiceClient= await this.getGenericServiceClient(account)
const blobContainerClient = await this.getGenericContainerClient(container, blobServiceClient)
const zeroedSeconds = new Date().setSeconds(0)
const options = {
expiresOn: new Date(Date.now() + (1000 * 60 * 60 * 24 * 120 //?When to expire
)),
contentType: fileType,
permissions,
startsOn: new Date(zeroedSeconds),
protocol: SASProtocol.Https,
// ipRange: {
// start: ipAddress,
// end: ipAddress,
// }
}
const sasToken = await blobContainerClient.generateSasUrl(options)
return {
sasToken:sasToken.split(`${container}?`)[1],
}
} catch (error) {
console.error(error)
throw new Error(error.message || "Failed to create to SAS")
}
}
async getGenericServiceClient(account:string) {
let accountKey=null;
switch (account) {
case AZURE_STORAGE_ACCOUNTS.US_EAST_2:
accountKey = this.configService.getOrThrow('azure-connection-storage-eastus2-key')
break;
case AZURE_STORAGE_ACCOUNTS.UAE_NORTH:
accountKey = this.configService.getOrThrow('azure-connection-storage-uaenorth-key')
break;
default:
throw new Error('Invalid Azure Storage Account')
}
const url = `https://${account}.blob.core.windows.net`
console.info(account,accountKey)
return new BlobServiceClient(url,new StorageSharedKeyCredential(account,accountKey))
}
async getGenericContainerClient(container: string, blobServiceClient: BlobServiceClient) {
return blobServiceClient.getContainerClient(container)
}
Ожидание: Загрузите файл в лучший и ближайший к конечному пользователю источник. Результат: Front Door ВСЕГДА загружает файл в ПОСЛЕДНЮЮ добавленную учетную запись хранения или в последнюю. Настройки задержки
@DasariKamali, во-первых, моя проблема не была решена, вместо этого я вручную рассчитал задержки при входе пользователя в систему, полностью отказавшись от входной двери. Мы могли бы подумать об использовании его для доставки, но на данный момент мы не доставляем файлы. Однако я обновлю вопрос с полным кодом.
Мы можем использовать для этой цели входную дверь, мои зонды не сработали. Но такой подход устраняет необходимость в этом, если необходима только загрузка. Позже я смешал это с базой данных, сохранив учетные записи в базе данных в этом формате.
{
account:string,
url:string,
envVar:string
}
Таким образом, делая все динамичным.
Можете ли вы поделиться своим полным кодом в вопросе?