HarmonyOS APP开发---“股动力“实时行情App,需要用到这个库

HarmonyOS APP开发---"股动力"实时行情App,需要用到这个库

想做一个股票行情 App,K 线、分时、盘口数据要实时推送?REST 轮询延迟高又费流量,WebSocket 又缺强类型契约------@ohos/grpc 的双向流式 RPC 才是正解。
📦 仓库地址:https://gitcode.com/CPF-ApplicationTPC/ohos_grpc_node | 安装:ohpm install @ohos/grpc


写在前面

"股动力"的核心难点是"实时性"。股票行情每秒可能产生几十条 tick,用户打开个股详情页,期望看到的是毫秒级刷新的分时线和盘口。

传统方案的问题:

  • REST 轮询:每秒请求一次,延迟最高 1 秒,流量浪费严重
  • WebSocket:能推送,但没有强类型契约,前后端字段对不上全靠口口相传
  • HTTP/1.1 长连接:单连接不能多路复用,并发请求互相阻塞

gRPC 基于 HTTP/2,天然支持流式通信 + Protobuf 强类型。服务端持续推 tick,客户端持续发订阅,单连接多路复用------这就是"股动力"选它的原因。

这篇文章聊什么

  1. Unary RPC------拉取股票基本信息(一次性请求)
  2. Server Streaming------服务端持续推送实时 tick
  3. 双向流------客户端发订阅,服务端推行情

#mermaid-svg-55sxZ1mWubHcDOMd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-55sxZ1mWubHcDOMd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-55sxZ1mWubHcDOMd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-55sxZ1mWubHcDOMd .error-icon{fill:#552222;}#mermaid-svg-55sxZ1mWubHcDOMd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-55sxZ1mWubHcDOMd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-55sxZ1mWubHcDOMd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-55sxZ1mWubHcDOMd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-55sxZ1mWubHcDOMd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-55sxZ1mWubHcDOMd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-55sxZ1mWubHcDOMd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-55sxZ1mWubHcDOMd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-55sxZ1mWubHcDOMd .marker.cross{stroke:#333333;}#mermaid-svg-55sxZ1mWubHcDOMd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-55sxZ1mWubHcDOMd p{margin:0;}#mermaid-svg-55sxZ1mWubHcDOMd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-55sxZ1mWubHcDOMd .cluster-label text{fill:#333;}#mermaid-svg-55sxZ1mWubHcDOMd .cluster-label span{color:#333;}#mermaid-svg-55sxZ1mWubHcDOMd .cluster-label span p{background-color:transparent;}#mermaid-svg-55sxZ1mWubHcDOMd .label text,#mermaid-svg-55sxZ1mWubHcDOMd span{fill:#333;color:#333;}#mermaid-svg-55sxZ1mWubHcDOMd .node rect,#mermaid-svg-55sxZ1mWubHcDOMd .node circle,#mermaid-svg-55sxZ1mWubHcDOMd .node ellipse,#mermaid-svg-55sxZ1mWubHcDOMd .node polygon,#mermaid-svg-55sxZ1mWubHcDOMd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-55sxZ1mWubHcDOMd .rough-node .label text,#mermaid-svg-55sxZ1mWubHcDOMd .node .label text,#mermaid-svg-55sxZ1mWubHcDOMd .image-shape .label,#mermaid-svg-55sxZ1mWubHcDOMd .icon-shape .label{text-anchor:middle;}#mermaid-svg-55sxZ1mWubHcDOMd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-55sxZ1mWubHcDOMd .rough-node .label,#mermaid-svg-55sxZ1mWubHcDOMd .node .label,#mermaid-svg-55sxZ1mWubHcDOMd .image-shape .label,#mermaid-svg-55sxZ1mWubHcDOMd .icon-shape .label{text-align:center;}#mermaid-svg-55sxZ1mWubHcDOMd .node.clickable{cursor:pointer;}#mermaid-svg-55sxZ1mWubHcDOMd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-55sxZ1mWubHcDOMd .arrowheadPath{fill:#333333;}#mermaid-svg-55sxZ1mWubHcDOMd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-55sxZ1mWubHcDOMd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-55sxZ1mWubHcDOMd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-55sxZ1mWubHcDOMd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-55sxZ1mWubHcDOMd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-55sxZ1mWubHcDOMd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-55sxZ1mWubHcDOMd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-55sxZ1mWubHcDOMd .cluster text{fill:#333;}#mermaid-svg-55sxZ1mWubHcDOMd .cluster span{color:#333;}#mermaid-svg-55sxZ1mWubHcDOMd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-55sxZ1mWubHcDOMd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-55sxZ1mWubHcDOMd rect.text{fill:none;stroke-width:0;}#mermaid-svg-55sxZ1mWubHcDOMd .icon-shape,#mermaid-svg-55sxZ1mWubHcDOMd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-55sxZ1mWubHcDOMd .icon-shape p,#mermaid-svg-55sxZ1mWubHcDOMd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-55sxZ1mWubHcDOMd .icon-shape .label rect,#mermaid-svg-55sxZ1mWubHcDOMd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-55sxZ1mWubHcDOMd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-55sxZ1mWubHcDOMd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-55sxZ1mWubHcDOMd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

打开个股页
Unary: 拉基本信息
Server Stream: 订阅 tick
服务端持续推送
渲染 K 线/分时
切换股票?
Bidi: 发新订阅


第一步:安装

bash 复制代码
ohpm install @ohos/grpc

安装后需将仓库 protobufjs/index.d.ts 替换到 oh_modules 对应路径,确保 Protobuf 类型完整。


第二步:Unary RPC------拉取股票基本信息

打开个股页,先拉一次静态信息(名称、昨收、总股本等):

typescript 复制代码
import { grpc } from '@ohos/grpc'

const client = new grpc.Client('https://grpc.market-server.com', {
  credentials: grpc.credentials.createInsecure()
})

// Unary:一次请求,一次响应
client.makeUnaryRequest(
  '/stock.StockService/GetInfo',
  (req) => req.serializeBinary(),
  (buf) => StockInfo.deserializeBinary(buf),
  StockRequest.encode({ code: '600519' }).finish(),
  (err, response) => {
    if (err) { console.error(err); return }
    console.info(`${response.getName()} 昨收 ${response.getPreClose()}`)
    // 渲染基本信息
  }
)

Unary 模式和普通 HTTP 请求很像------一问一答。适合拉取不频繁变更的静态数据。


第三步:Server Streaming------持续接收实时 tick

订阅某只股票后,服务端持续推送 tick 数据:

typescript 复制代码
// Server Streaming:一次请求,持续接收
const stream = client.makeServerStreamRequest(
  '/stock.StockService/SubscribeTick',
  (req) => req.serializeBinary(),
  (buf) => Tick.deserializeBinary(buf),
  SubscribeRequest.encode({ code: '600519', level: 1 }).finish()
)

stream.on('data', (tick) => {
  // 每收到一个 tick,更新 K 线和盘口
  console.info(`价格 ${tick.getPrice()} 量 ${tick.getVolume()}`)
  updateChart(tick)
})

stream.on('end', () => {
  console.info('行情推送结束(收盘)')
})

stream.on('error', (err) => {
  console.error('行情异常: ' + err)
})

这就是 gRPC 流式的核心优势------一个请求建立流,服务端持续推,无需反复建连。比 REST 轮询省流量、低延迟,比 WebSocket 多了 Protobuf 强类型。


第四步:双向流------动态切换订阅

用户从 A 股切到 B 股,不需要断开重连,直接在同一个流里发新订阅:

typescript 复制代码
const bidiStream = client.makeBidiStreamRequest(
  '/stock.StockService/WatchMulti',
  serializer,
  deserializer,
  metadata
)

// 收到行情
bidiStream.on('data', (tick) => {
  updateChart(tick)
})

// 动态发订阅:先订阅 A,再切到 B
bidiStream.write(Subscribe.encode({ code: '600519' }).finish())

// 用户切换股票
setTimeout(() => {
  bidiStream.write(Subscribe.encode({ code: '000858' }).finish())
}, 5000)

// 取消订阅
bidiStream.write(Unsubscribe.encode({ code: '600519' }).finish())

双向流是 gRPC 最强大的模式------一个连接管多只股票的订阅/取消,互不干扰 。这在 REST 里要维护一堆连接状态,在 gRPC 里就是几行 write


为什么"股动力"选了 gRPC?

需求 REST 轮询 WebSocket gRPC
实时推送 ❌ 轮询延迟高 ✅ 可推送 ✅ 流式推送
强类型契约 ❌ JSON 无约束 ❌ 无 ✅ Protobuf
多路复用 ❌ 多连接 ⚠️ 单流 ✅ HTTP/2
动态订阅 ❌ 难管理 ⚠️ 手写协议 ✅ 双向流
跨语言 ✅ Go/Java/Python 互通

总结

"股动力"这个场景里,@ohos/grpc 解决了三件事:

  1. 静态信息------Unary RPC 一问一答,拉股票基本信息
  2. 实时行情------Server Streaming 持续推送 tick,毫秒级触达
  3. 动态订阅------双向流,切股票不断连,发个 write 就行

如果你也在做实时行情、在线协作、IoT 网关双向通信等场景,@ohos/grpc 的流式能力会让你彻底忘掉 REST 轮询的痛。

相关推荐
论文复现现场5 小时前
ComfyUI部署MiniMax H3:8G本地卡、RTX 4090和RTX 5090怎么选?
人工智能·云计算·音视频·gpu算力
aqi005 小时前
鸿蒙版本的JSBridge兼容与安卓配套的H5啦
android·华为·harmonyos·鸿蒙·移动应用
一颗小行星!6 小时前
免费在线 MP3、录音、视频转文本工具推荐对比
音视频
熊猫钓鱼>_>6 小时前
Flutter app_settings 鸿蒙适配实战:Intent 体系到 Want 的跨越
flutter·华为·harmonyos·openharmony·intent·want
天天进步20157 小时前
Pixelle-Video 源码解析 #18:声音克隆功能:参考音频如何影响解说效果?
数据库·音视频
林墨聊AIGC9 小时前
动漫AI视频创作工具在哪找到的?2026年最新动漫AI视频平台与软件指南
大数据·人工智能·ai作画·aigc·音视频
贾伟康9 小时前
【句匠|20】HarmonyOS ArkTS AppGallery 发布复查实战:核对包名、版本、设备、素材和离线声明
harmonyos·arkts·应用上架·appgallery·发布审核
贾伟康10 小时前
【句匠|15】HarmonyOS ArkTS 本地状态持久化实战:让保存、删除和页面返回后的数据即时一致
harmonyos·arkts·数据持久化·appstorage·preferences
FlightYe10 小时前
音视频修炼之基础理论(五):AAC格式与解析
android·linux·c++·音视频·aac
cnnews11 小时前
Ubuntu 编译 postmarketOS
linux·运维·arm开发·ubuntu·github·音视频