使用Bark构建针对iOS设备的消息推送服务
  • 分类:Golang
  • 发表:2021-09-11
  • 围观(134)
  • 评论(0)

缘起

在日常开发的过程中,经常有向移动设备发送消息通知的需求,无论是用来接收服务运行状况,还是获取及时的消息提醒,亦或者是用作接收告警信息,类似基础设施组件都是必不可少的。目前常用的解决方案包括无非包括以下几种:

  • 使用电信运营商相关的通讯接口,直接给设备发送短信或者电话消息。
  • 使用第三方通讯工具提供的Hook接口发送消息(钉钉,Telegram等)。
  • 使用推送服务商提供的接口来间接实现消息推送(Server 酱)。

之前我尝试过用接Servier酱的API,方便快捷钉钉接收消息。但是最近好像他们的企业账户推送失效了,所以我也没有再去研究。正好手上有项目有类似监控告警的需求,所以找一个替代项目就显得理所当然了,这同样也是驱使我发现Bark的原动力。


一. 什么是Bark?

Bark is an iOS App which allows you to push customed notifications to your iPhone (iOS Devices).

项目地址:点我跳转

Bark是一套基于iOS特有消息推送机制实现的信息推送APP。只要你的iOS设备安装了Bark,那么你只需通过简单调用下Barking(我是这么叫的[]~( ̄▽ ̄)~*)接口就可以轻松的向你的设备发送消息通知,在我看来这个效果实质上是和电信运营商的短信是没有本质区别的。

总结了下,Bark有以下几点优势:

  • 接入简单,所有的消息类型只通过一个接口就可以实现,只有简单存粹的消息通知功能。
  • 支持通知分组、留档;支持自定义提示音;支持通知操作行为(目前只支持复制)。
  • 免费、无接口频率限制,由于是走苹果推送服务所以发送大量无意义通知存在IP被Ban的风险。

总之如果你使用的是iOS生态的话体验近乎完美。Bark的通知是基于设备维度的,与接入第三方APP(钉钉,Telegram)不同的是Bark的消息不能够跨设备同步。我并不认为这是不好的体验,因为使用方可以通过扩展自己的调用代码来实现类似通知组的功能。

二. 部署Bark

项目提供公共的Server端,你可以选择直接使用,但是服务稳定性不作任何保证,因为作者本身也是在用爱发电。所以为了服务的可靠性还有敏感数据的安全性,项目作者建议我们部署自己的Bark-Server。部署十分简单,仅仅需要绑定一个域名加上Docker运行环境就足够了。

镜像Hub地址:点击跳转

部署命令:

// 获取镜像
[root@weiney ~]# docker pull finab/bark-server:v2.0.2

// 启动容器
[root@weiney ~]# docker run -itd -p 38080:8080 -v /data/bark:/data -e BARK_SERVER_BASIC_AUTH_USER=user -e BARK_SERVER_BASIC_AUTH_PASSWORD=123456 bark-server:v2.0.2 bark

//  启动完成之后你可以使用以下命令检测服务可用性
[root@weiney ~]# curl localhost:38080/ping

启动服务本身并没有难点,关键在于项目启动的配置参数全部是通过环境变量来读取的,项目文档并没有对服务的启动参数做出说明。在查阅服务源码之后我将所有的启动参数都罗列在下表当中,如果你有修改默认启动参数的需求可以查阅以下参数表做对照修改。

 

配置名称 参数说明
BARK_SERVER_DATA_DIR 服务地址⭐
BARK_SERVER_BASIC_AUTH_USER Basic 验证用户名⭐
BARK_SERVER_BASIC_AUTH_USER Basic 验证密码⭐
BARK_SERVER_DATA_DIR 存储地址(默认为/data路径,为了数据的安全性需要将此路径通过挂载方式挂载到持久化目录)⭐
BARK_SERVER_CERT SSL证书
BARK_SERVER_KEY SSL证书密钥
BARK_SERVER_CASE_SENSITIVE 区分URL路径大小写
BARK_SERVER_STRICT_ROUTING 启用严格路由区分
BARK_SERVER_REDUCE_MEMORY_USAGE 是否以高的 CPU 使用率为代价来减少内存使用量
BARK_SERVER_PROXY_HEADER Bark服务器http头使用的远程IP地址
BARK_SERVER_CONCURRENCY 最大并发连接数
BARK_SERVER_READ_TIMEOUT 允许读取完整请求的时间,包括正文
BARK_SERVER_WRITE_TIMEOUT 响应写入超时前的最长持续时间
BARK_SERVER_IDLE_TIMEOUT 启用 keep-alive 时等待下一个请求的最长时间

容器启动成功之后,给服务绑定一个域名并配置好SSL证书,至此Bark-Server的搭建就完成了。

三. Bark的使用

在你的iOS设备上下载Bark的客户端(App Store即可下载),填入刚刚搭建好Bark-Server的调用地址你将会得到一个专属于这台设备的DeviceID,有了这个DeviceID你就可以向这台设备发送消息推送了。

接口文档:点击跳转

文档内已经对大部分参数做了说明,但是由于文档未及时更新,所以少部分参数选项需要通过扒源码的方式获取,包括提示音的常量参数等。本文回对相关参数进行罗列总结,并给出一个基于Go实现的请求封装示例。

1.铃声常量

package bark

type Sound string // 通知铃声

const (
  SoundAlarm              Sound = "alarm.caf"
  SoundBell               Sound = "bell.caf"
  SoundBirdsong           Sound = "birdsong.caf"
  SoundCalypso            Sound = "calypso.caf"
  SoundGlass              Sound = "glass.caf"
  SoundHealthnotification Sound = "healthnotification.caf"
  SoundMailsent           Sound = "mailsent.caf"
  SoundNewmail            Sound = "newmail.caf"
  SoundPaymentsuccess     Sound = "paymentsuccess.caf"
  SoundShake              Sound = "shake.caf"
  SoundTelegraph          Sound = "telegraph.caf"
  // 以上铃声为常用铃声
  SoundAnticipate         Sound = "anticipate.caf"
  SoundBloom              Sound = "bloom.caf"
  SoundChime              Sound = "chime.caf"
  SoundChoo               Sound = "choo.caf"
  SoundDescent            Sound = "descent.caf"
  SoundElectronic         Sound = "electronic.caf"
  SoundFanfare            Sound = "fanfare.caf"
  SoundGotosleep          Sound = "gotosleep.caf"
  SoundHorn               Sound = "horn.caf"
  SoundLadder             Sound = "ladder.caf"
  SoundMinuet             Sound = "minuet.caf"
  SoundMultiwayinvitation Sound = "multiwayinvitation.caf"
  SoundNewsflash          Sound = "newsflash.caf"
  SoundNoir               Sound = "noir.caf"
  SoundSherwoodforest     Sound = "sherwoodforest.caf"
  SoundSilence            Sound = "silence.caf"
  SoundSpell              Sound = "spell.caf"
  SoundSuspense           Sound = "suspense.caf"
  SoundTiptoes            Sound = "tiptoes.caf"
  SoundTypewriters        Sound = "typewriters.caf"
  SoundUpdate             Sound = "update.caf"
)

2.接口定义

package bark

type BarkInfo struct {
  Body      string       `json:"body,omitempty"`       // 推送内容
  DeviceKey string       `json:"device_key,omitempty"` // 设备ID
  Title     string       `json:"title,omitempty"`      // 推送标题
  ExtParams BarkExtParam `json:"ext_params,omitempty"` // 附加参数
  // Deprecated: 我暂时用不到并不代表无效
  Category string `json:"category,omitempty"` // Category 是用于对推送进行分类的,下拉推送时可以有不同的操作。现在只有一个默认的category就是下拉复制推送。
  Sound    Sound  `json:"sound,omitempty"`    // 铃声
}

type BarkExtParam struct {
  Url       string `json:"url,omitempty"`       // 跳转链接
  IsArchive int    `json:"isArchive,omitempty"` // 是否保存
  Copy      string `json:"copy,omitempty"`      // 复制内容
  AuthCopy  string `json:"authCopy,omitempty"`  // 自动复制
  Group     string `json:"group,omitempty"`     // 分组
}

type BarkResponse struct {
  Code      int64  `json:"code"`
  Message   string `json:"message"`
  Timestamp int64  `json:"timestamp"`
}

type Bark interface {
  Barking(barkInfo BarkInfo) (BarkResponse, error)
}

func NewBark(host, user, password string) Bark {
  return &bark{
    host:     host,
    user:     user,
    password: password,
  }
}

3.接口实现

package bark

import (
  "bytes"
  "encoding/json"
  "io/ioutil"
  "net/http"
)

type bark struct {
  host     string
  user     string
  password string
}

func (b *bark) Barking(barkInfo BarkInfo) (BarkResponse, error) {
  data, err := json.Marshal(barkInfo)
  if err != nil {
    return BarkResponse{}, err
  }

  request, err := http.NewRequest(http.MethodPost, b.host, bytes.NewReader(data))
  if err != nil {
    return BarkResponse{}, err
  }
  // 鉴权
  request.SetBasicAuth(b.user, b.password)
  // 设置请求头
  request.Header.Add("Content-Type", "application/json; charset=utf-8")
  response, err := http.DefaultClient.Do(request)
  if err != nil {
    return BarkResponse{}, err
  }
  defer func() {
    _ = response.Body.Close()
  }()
  respBody, err := ioutil.ReadAll(response.Body)
  if err != nil {
    return BarkResponse{}, err
  }

  result := BarkResponse{}
  if err := json.Unmarshal(respBody, &result); err != nil {
    return BarkResponse{}, err
  }
  return result, nil
}

四. 注意事项

  • 自建的Bark-Server需要做好权限校验,以免接口地址泄露造成滥用
  • 如果你不想丢失已经成功注册到Bark-Server的设备信息的话请务必将Bark的缓存路径通过外部挂载的方式挂载出来
  • 为了方便通知的管理,可以对所有类型的通知进行分组,对于无需保存的通知可以设置服务器不保存

Bark-Server对于个人开发者来说已经完全足够了,如果你想实现消息通知回调等功能的话建议使用钉钉或者Telegram,相信后期Bark也会加上这些比较实用的功能。


共有 0 条评论

Top