Azure DevOps速率限制

发布于 2025-02-04 19:40:51 字数 4243 浏览 5 评论 0 原文

目标是在GO中使用其许可证和项目权利来检索Azure Devops用户。

我正在使用 Microsoft SDK

我们的Azure DevOps组织拥有1500多名用户。因此,当我请求每个用户授权时,我的错误消息是 azure devops速率限制 => 443:阅读:连接重置,但是

,用100/200限制顶部,当然可以。

,但是 限制。或者也许使用 heimdall

您对一个好设计师的建议是什么?

谢谢。

这是代码:

package main

import (
    "context"
    "fmt"
    "github.com/microsoft/azure-devops-go-api/azuredevops"
    "github.com/microsoft/azure-devops-go-api/azuredevops/memberentitlementmanagement"
    "log"
    "runtime"
    "sync"
    "time"
)

var organizationUrl = "https://dev.azure.com/xxx"
var personalAccessToken = "xxx"

type User struct {
    DisplayName         string
    MailAddress         string
    PrincipalName       string
    LicenseDisplayName  string
    Status              string
    GroupAssignments    string
    ProjectEntitlements []string
    LastAccessedDate    azuredevops.Time
    DateCreated         azuredevops.Time
}

func init() {
    runtime.GOMAXPROCS(runtime.NumCPU()) // Try to use all available CPUs.
}

func main() {
    // Time measure
    defer timeTrack(time.Now(), "Fetching Azure DevOps Users License and Projects")

    // Compute context
    fmt.Println("Version", runtime.Version())
    fmt.Println("NumCPU", runtime.NumCPU())
    fmt.Println("GOMAXPROCS", runtime.GOMAXPROCS(0))
    fmt.Println("Starting concurrent calls...")

    // Create a connection to your organization
    connection := azuredevops.NewPatConnection(organizationUrl, personalAccessToken)

    // New context
    ctx := context.Background()

    // Create a member client
    memberClient, err := memberentitlementmanagement.NewClient(ctx, connection)
    if err != nil {
        log.Fatal(err)
    }

    // Request all users
    top := 10000
    skip := 0
    filter := "Id"
    response, err := memberClient.GetUserEntitlements(ctx, memberentitlementmanagement.GetUserEntitlementsArgs{
        Top:        &top,
        Skip:       &skip,
        Filter:     &filter,
        SortOption: nil,
    })

    usersLen := len(*response.Members)

    allUsers := make(chan User, usersLen)

    var wg sync.WaitGroup
    wg.Add(usersLen)

    for _, user := range *response.Members {
        go func(user memberentitlementmanagement.UserEntitlement) {
            defer wg.Done()

            var userEntitlement = memberentitlementmanagement.GetUserEntitlementArgs{UserId: user.Id}
            account, err := memberClient.GetUserEntitlement(ctx, userEntitlement)
            if err != nil {
                log.Fatal(err)
            }

            var GroupAssignments string
            var ProjectEntitlements []string

            for _, assignment := range *account.GroupAssignments {
                GroupAssignments = *assignment.Group.DisplayName
            }

            for _, userProject := range *account.ProjectEntitlements {
                ProjectEntitlements = append(ProjectEntitlements, *userProject.ProjectRef.Name)
            }

            allUsers <- User{
                DisplayName:         *account.User.DisplayName,
                MailAddress:         *account.User.MailAddress,
                PrincipalName:       *account.User.PrincipalName,
                LicenseDisplayName:  *account.AccessLevel.LicenseDisplayName,
                DateCreated:         *account.DateCreated,
                LastAccessedDate:    *account.LastAccessedDate,
                GroupAssignments:    GroupAssignments,
                ProjectEntitlements: ProjectEntitlements,
            }
        }(user)
    }

    wg.Wait()
    close(allUsers)
    for eachUser := range allUsers {
        fmt.Println(eachUser)
    }
}

func timeTrack(start time.Time, name string) {
    elapsed := time.Since(start)
    log.Printf("%s took %s", name, elapsed)
}

Goal is to retrieve Azure DevOps users with their license and project entitlements in go.

I'm using Microsoft SDK.

Our Azure DevOps organization has more than 1500 users. So when I request each user entitlements, I have an error message due to Azure DevOps rate limit => 443: read: connection reset by peer

However, limiting top with 100/200 does the job, of course..

For a real solution, I though not using SDK anymore and using direct REST API calls with a custom http handler which would support rate limit. Or maybe using heimdall.

What is your advise for a good design guys ?

Thanks.

Here is code :

package main

import (
    "context"
    "fmt"
    "github.com/microsoft/azure-devops-go-api/azuredevops"
    "github.com/microsoft/azure-devops-go-api/azuredevops/memberentitlementmanagement"
    "log"
    "runtime"
    "sync"
    "time"
)

var organizationUrl = "https://dev.azure.com/xxx"
var personalAccessToken = "xxx"

type User struct {
    DisplayName         string
    MailAddress         string
    PrincipalName       string
    LicenseDisplayName  string
    Status              string
    GroupAssignments    string
    ProjectEntitlements []string
    LastAccessedDate    azuredevops.Time
    DateCreated         azuredevops.Time
}

func init() {
    runtime.GOMAXPROCS(runtime.NumCPU()) // Try to use all available CPUs.
}

func main() {
    // Time measure
    defer timeTrack(time.Now(), "Fetching Azure DevOps Users License and Projects")

    // Compute context
    fmt.Println("Version", runtime.Version())
    fmt.Println("NumCPU", runtime.NumCPU())
    fmt.Println("GOMAXPROCS", runtime.GOMAXPROCS(0))
    fmt.Println("Starting concurrent calls...")

    // Create a connection to your organization
    connection := azuredevops.NewPatConnection(organizationUrl, personalAccessToken)

    // New context
    ctx := context.Background()

    // Create a member client
    memberClient, err := memberentitlementmanagement.NewClient(ctx, connection)
    if err != nil {
        log.Fatal(err)
    }

    // Request all users
    top := 10000
    skip := 0
    filter := "Id"
    response, err := memberClient.GetUserEntitlements(ctx, memberentitlementmanagement.GetUserEntitlementsArgs{
        Top:        &top,
        Skip:       &skip,
        Filter:     &filter,
        SortOption: nil,
    })

    usersLen := len(*response.Members)

    allUsers := make(chan User, usersLen)

    var wg sync.WaitGroup
    wg.Add(usersLen)

    for _, user := range *response.Members {
        go func(user memberentitlementmanagement.UserEntitlement) {
            defer wg.Done()

            var userEntitlement = memberentitlementmanagement.GetUserEntitlementArgs{UserId: user.Id}
            account, err := memberClient.GetUserEntitlement(ctx, userEntitlement)
            if err != nil {
                log.Fatal(err)
            }

            var GroupAssignments string
            var ProjectEntitlements []string

            for _, assignment := range *account.GroupAssignments {
                GroupAssignments = *assignment.Group.DisplayName
            }

            for _, userProject := range *account.ProjectEntitlements {
                ProjectEntitlements = append(ProjectEntitlements, *userProject.ProjectRef.Name)
            }

            allUsers <- User{
                DisplayName:         *account.User.DisplayName,
                MailAddress:         *account.User.MailAddress,
                PrincipalName:       *account.User.PrincipalName,
                LicenseDisplayName:  *account.AccessLevel.LicenseDisplayName,
                DateCreated:         *account.DateCreated,
                LastAccessedDate:    *account.LastAccessedDate,
                GroupAssignments:    GroupAssignments,
                ProjectEntitlements: ProjectEntitlements,
            }
        }(user)
    }

    wg.Wait()
    close(allUsers)
    for eachUser := range allUsers {
        fmt.Println(eachUser)
    }
}

func timeTrack(start time.Time, name string) {
    elapsed := time.Since(start)
    log.Printf("%s took %s", name, elapsed)
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

素手挽清风 2025-02-11 19:40:51

您可以编写 getUserentitrempt 函数的自定义版本。

它不使用任何私人成员。

获得 http.Response 后,您可以检查 retry-terry-fefter 标题,并在存在的情况下延迟下一个环的迭代。

https:https:// github。 com/microsoft/azure-devops-go-api/blob/dev/azuredevops/memberentitementmanagement.cliant.go#l306

您的代码中的PS同时发生是多余的,可以删除。

更新 - 解释并发问题:

您无法轻松在并发代码中实现限制速率。如果您顺序执行所有请求,并在移至下一个响应之前,在每个响应中检查标题,将会更简单。

通过并行执行:1)您不能依靠重试标题值,因为您可能会同时返回另一个值的另一个请求执行。 2)您不能将延迟应用于其他请求,因为其中一些已经在进行中。

You can write custom version of GetUserEntitlement function.

https://github.com/microsoft/azure-devops-go-api/blob/dev/azuredevops/memberentitlementmanagement/client.go#L297-L314

It does not use any private members.

After getting http.Response you can check Retry-After header and delay next loop's iteration if it is present.

https://github.com/microsoft/azure-devops-go-api/blob/dev/azuredevops/memberentitlementmanagement/client.go#L306

P.S. Concurrency in your code is redundant and can be removed.

Update - explaining concurrency issue:

You cannot easily implement rate-limiting in concurrent code. It will be much simpler if you execute all requests sequentially and check Retry-After header in every response before moving to the next one.

With parallel execution: 1) you cannot rely on Retry-After header value because you may have another request executing at the same time returning a different value. 2) You cannot apply delay to other requests because some of them are already in progress.

(り薆情海 2025-02-11 19:40:51

对于真正的解决方案,我不再使用SDK并使用直接
使用自定义HTTP处理程序的REST API调用,该处理程序将支持率
限制。或者也许使用Heimdall。

您是说要通过直接使用REST API避免速率限制吗?

如果是这样,那么您的想法将不起作用。

大多数REST API都可以通过客户端库访问,如果您基于REST API或其他基于REST API的内容使用SDK,它当然会达到速率限制。

由于速率限制是基于用户的,因此我建议您可以根据多个用户完成操作(前提是您的请求不是阻止IP的服务器太多)。

For a real solution, I though not using SDK anymore and using direct
REST API calls with a custom http handler which would support rate
limit. Or maybe using heimdall.

Do you mean you want to avoid the Rate Limit by using the REST API directly?

If so, then your idea will not work.

Most REST APIs are accessible through client libraries, and if you're using SDK based on a REST API or other thing based on a REST API, it will of course hit a rate limit.

Since the rate limit is based on users, I suggest that you can complete your operations based on multiple users (provided that your request is not too much that the server blocking your IP).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文