Golang平台工程师职位 - 支持100%远程工作 - Buzzer招聘

Golang平台工程师职位 - 支持100%远程工作 - Buzzer招聘 职位概述 Buzzer Media, Inc. 正在寻找一位经验丰富的平台工程师加入Buzzer团队,该职位要求具备广泛的云基础设施和后台工程经验。这是工程团队中的一个关键角色,旨在构建一个可扩展且精准的解决方案,为客户提供关于即将到来的、有趣的体育赛事直播片段的个性化通知。随着我们与各个体育联盟和数据提供商合作,每个合作伙伴都有其独特的规范和要求,该职位对于构建我们的后端系统至关重要。

平台工程师将直接与由产品经理、用户体验设计师和工程师组成的工程和产品团队合作。

在此职位中,您将:

  • 平台工程: 直接参与后端微服务的实施、开发以及相关数据存储的管理。您将与其他工程团队紧密合作,在协作、敏捷的环境中识别并满足他们的需求。
  • 持续集成与部署: 创建和维护持续集成与部署流程,确保整个CI/CD管道按预期工作。

必备资格

  • 具备Go语言(或其他强类型语言,如Java、C、C++)经验
  • 具备关系型SQL和NoSQL数据库(如MySQL、PostgreSQL、DynamoDB、Redis)经验
  • 熟悉测试驱动开发和行为驱动开发(TDD/BDD)
  • 具备Unix/Linux系统的工作知识
  • 具备源代码控制系统(包括Git)的工作知识

优先资格

  • 有在采用两周冲刺周期的敏捷环境中工作的经验
  • 有在云中(理想情况下在AWS内)开发和管理可扩展基础设施及应用程序的经验
  • 具备Terraform(或其他IaaC工具,如CloudFormation)经验
  • 具备微服务开发经验
  • 具备体育媒体应用和/或设计经验
  • 拥有计算机科学学士学位或同等经验

关于Buzzer Buzzer是一个移动优先、基于通知的体育直播平台,可根据粉丝的兴趣和偏好进行个性化定制。我们让粉丝能够在移动设备上即时地从一场联赛切换到另一场联赛,从一场比赛切换到另一场比赛。我们的技术由微支付、推送通知以及对现有付费电视/OTT订阅的认证驱动,使我们能够提供反映每位粉丝真实兴趣的实时内容,并消除了传统直播观看的障碍。

我们的使命是简化这些直播时刻的发现和消费过程。我们为我们充满体育热情的粉丝社区感到自豪。公司成立于一个体育中心,我们不断壮大的远程团队尊重传统,同时重视创新。我们相信,作为体育生态系统的领先整合者,我们可以通过有意义的接触点并跨越代际来促进团结。

关于Strive:Buzzer对社会影响的承诺 Strive是一项有意且具有影响力的倡议,它将利用Buzzer的统一平台,投资于为黑人、原住民和有色人种创造公平的结果。Buzzer致力于通过公司的使命、价值观、产品和金融工具来创造公平的竞争环境,从而为有色人种社区创造更多机会。Strive将作为Buzzer推动社会积极变革和成果的载体。

我们的1x1x1影响模型旨在通过三种策略投资于这些社区:

  • 股权: 公司1%的股权将受到保护并分配给"创造公平竞争环境"。这笔基金将确保Strive得到优先考虑,并以与我们公司相同的增长速度扩展。再投资将支持Strive的使命、合作伙伴和事业。随着我们在此领域的投资增长,公司也会成长。
  • 净利润: 1%的净利润将再投资于专注于增强和赋能黑人社区及有色人种社区的合作非营利组织。这项近期解决方案旨在与那些为我们未来的领导者和客户提供资源和支持的有影响力的组织合作并做出贡献。
  • 消费者捐赠: 用户购买金额的1%可以通过允许用户将其0.99美元的微支付凑整到1.00美元的方式,捐赠给参与的非营利组织。此产品功能的雄心是突出消费者参与和了解那些投资于有色人种社区的组织的机会。

Buzzer自豪地宣布,我们的第一个产品是Strive导师计划,这是一个全球性的导师计划,连接了超过500名行业专业人士和超过500名主要来自传统黑人学院和大学(HBCUs)及其他大学的黑人大学生。这个导师计划最终启发了Strive倡议,并代表了我们通过为潜在客户、合作伙伴和员工做正确的事情来引领行业的雄心。

请在此申请:https://angel.co/company/buzzer-inc/jobs/1099022-platform-engineer


更多关于Golang平台工程师职位 - 支持100%远程工作 - Buzzer招聘的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

你好 @KevinMartin 希望你一切顺利。

我已经给你发了一条私信,请查收。

此致, Henry

更多关于Golang平台工程师职位 - 支持100%远程工作 - Buzzer招聘的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是一个非常典型的Go语言平台工程师职位,要求候选人具备构建和维护可扩展后端系统及云基础设施的能力。从技术栈来看,Go是核心语言,结合AWS云服务和微服务架构。

以下是一个符合该职位描述的Go语言微服务示例,展示了如何构建一个处理体育赛事通知的后端服务:

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/dynamodb"
    "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
    "github.com/go-redis/redis/v8"
    "github.com/gorilla/mux"
)

// 体育赛事数据结构
type SportEvent struct {
    ID          string    `json:"id" dynamodbav:"id"`
    League      string    `json:"league" dynamodbav:"league"`
    Match       string    `json:"match" dynamodbav:"match"`
    StartTime   time.Time `json:"start_time" dynamodbav:"start_time"`
    IsLive      bool      `json:"is_live" dynamodbav:"is_live"`
    HighlightID string    `json:"highlight_id" dynamodbav:"highlight_id"`
}

// 用户偏好设置
type UserPreference struct {
    UserID      string   `json:"user_id" dynamodbav:"user_id"`
    FavoriteLeagues []string `json:"favorite_leagues" dynamodbav:"favorite_leagues"`
    NotifyBefore int     `json:"notify_before" dynamodbav:"notify_before"` // 提前通知的分钟数
}

// 数据库客户端
var (
    dynamoClient *dynamodb.DynamoDB
    redisClient  *redis.Client
    ctx          = context.Background()
)

func init() {
    // 初始化AWS会话
    sess := session.Must(session.NewSession(&aws.Config{
        Region: aws.String("us-east-1"),
    }))
    dynamoClient = dynamodb.New(sess)

    // 初始化Redis客户端
    redisClient = redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "",
        DB:       0,
    })
}

// 获取即将开始的赛事
func GetUpcomingEvents(w http.ResponseWriter, r *http.Request) {
    // 从Redis缓存获取
    cacheKey := "upcoming_events"
    cached, err := redisClient.Get(ctx, cacheKey).Result()
    if err == nil {
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(cached))
        return
    }

    // 从DynamoDB查询
    input := &dynamodb.ScanInput{
        TableName: aws.String("SportEvents"),
        FilterExpression: aws.String("is_live = :live AND start_time > :now"),
        ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
            ":live": {BOOL: aws.Bool(false)},
            ":now":  {S: aws.String(time.Now().Format(time.RFC3339))},
        },
    }

    result, err := dynamoClient.Scan(input)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    var events []SportEvent
    err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &events)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // 序列化并缓存结果
    jsonData, _ := json.Marshal(events)
    redisClient.Set(ctx, cacheKey, jsonData, 5*time.Minute)

    w.Header().Set("Content-Type", "application/json")
    w.Write(jsonData)
}

// 根据用户偏好筛选赛事
func GetPersonalizedEvents(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    userID := vars["userID"]

    // 获取用户偏好
    prefInput := &dynamodb.GetItemInput{
        TableName: aws.String("UserPreferences"),
        Key: map[string]*dynamodb.AttributeValue{
            "user_id": {S: aws.String(userID)},
        },
    }

    prefResult, err := dynamoClient.GetItem(prefInput)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    var preference UserPreference
    err = dynamodbattribute.UnmarshalMap(prefResult.Item, &preference)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // 查询符合用户偏好的赛事
    input := &dynamodb.ScanInput{
        TableName: aws.String("SportEvents"),
        FilterExpression: aws.String("is_live = :live AND start_time BETWEEN :now AND :future AND contains(league, :league)"),
        ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
            ":live":   {BOOL: aws.Bool(false)},
            ":now":    {S: aws.String(time.Now().Format(time.RFC3339))},
            ":future": {S: aws.String(time.Now().Add(24 * time.Hour).Format(time.RFC3339))},
            ":league": {SS: aws.StringSlice(preference.FavoriteLeagues)},
        },
    }

    result, err := dynamoClient.Scan(input)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    var events []SportEvent
    err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &events)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    jsonData, _ := json.Marshal(events)
    w.Header().Set("Content-Type", "application/json")
    w.Write(jsonData)
}

// 发送通知的Worker
func NotificationWorker() {
    ticker := time.NewTicker(1 * time.Minute)
    defer ticker.Stop()

    for {
        select {
        case <-ticker.C:
            // 查询需要发送通知的赛事
            now := time.Now()
            future := now.Add(30 * time.Minute)

            input := &dynamodb.ScanInput{
                TableName: aws.String("SportEvents"),
                FilterExpression: aws.String("is_live = :live AND start_time BETWEEN :now AND :future"),
                ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
                    ":live":   {BOOL: aws.Bool(false)},
                    ":now":    {S: aws.String(now.Format(time.RFC3339))},
                    ":future": {S: aws.String(future.Format(time.RFC3339))},
                },
            }

            result, err := dynamoClient.Scan(input)
            if err != nil {
                log.Printf("查询赛事失败: %v", err)
                continue
            }

            var events []SportEvent
            dynamodbattribute.UnmarshalListOfMaps(result.Items, &events)

            // 为每个赛事发送通知
            for _, event := range events {
                SendNotification(event)
            }
        }
    }
}

func SendNotification(event SportEvent) {
    // 实现推送通知逻辑
    log.Printf("发送通知: %s - %s 即将开始", event.League, event.Match)
}

func main() {
    r := mux.NewRouter()

    // API路由
    r.HandleFunc("/api/events/upcoming", GetUpcomingEvents).Methods("GET")
    r.HandleFunc("/api/events/personalized/{userID}", GetPersonalizedEvents).Methods("GET")

    // 启动通知Worker
    go NotificationWorker()

    // 启动HTTP服务器
    log.Println("服务器启动在 :8080")
    log.Fatal(http.ListenAndServe(":8080", r))
}

这个示例展示了:

  1. 使用Go标准库和流行框架(gorilla/mux)构建RESTful API
  2. 集成AWS DynamoDB进行数据存储
  3. 使用Redis作为缓存层提高性能
  4. 实现后台Worker处理定时通知任务
  5. 符合微服务架构模式

对于CI/CD部分,以下是一个简单的GitHub Actions工作流配置:

name: Go CI/CD Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    
    - name: Set up Go
      uses: actions/setup-go@v2
      with:
        go-version: 1.19
    
    - name: Run tests
      run: go test ./... -v -cover

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    
    - name: Set up Go
      uses: actions/setup-go@v2
      with:
        go-version: 1.19
    
    - name: Build
      run: go build -v ./...
    
    - name: Docker build
      run: docker build -t buzzer-service:latest .

  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
    - name: Deploy to AWS ECS
      run: |
        # Terraform部署脚本
        terraform init
        terraform apply -auto-approve

这个职位要求的技术栈与当前Go语言生态系统的标准实践高度一致,特别是在云原生和微服务领域。候选人需要展示在AWS环境中使用Go构建可扩展系统的实际经验。

回到顶部