17.4. 开发 PTP 事件消费者应用程序
在裸机集群节点上开发使用 Precision Time Protocol (PTP) 事件的消费者应用程序时,您需要在单独的应用程序 pod 中部署消费者应用程序和 cloud-event-proxy
容器。cloud-event-proxy
容器从 PTP Operator pod 接收事件,并将其传递给消费者应用程序。消费者应用使用 REST API 订阅 cloud-event-proxy
容器中发布的事件。
有关部署 PTP 事件通知的更多信息,请参阅关于 PTP 快速事件通知框架。
以下信息提供了开发使用 PTP 事件的消费者应用程序的一般指导。完整的事件消费者应用示例超出了此信息的范围。
17.4.1. PTP 事件消费者应用程序参考
PTP 事件消费者应用程序需要以下功能:
-
使用
POST
处理程序运行的 Web 服务,以接收云原生 PTP 事件 JSON 有效负载 -
订阅 PTP 事件制作者的
createSubscription
功能 -
getCurrentState
功能轮询 PTP 事件制作者的当前状态
以下示例 Go 片断演示了这些要求:
Go 中的 PTP 事件消费者服务器功能示例
func server() { http.HandleFunc("/event", getEvent) http.ListenAndServe("localhost:8989", nil) } func getEvent(w http.ResponseWriter, req *http.Request) { defer req.Body.Close() bodyBytes, err := io.ReadAll(req.Body) if err != nil { log.Errorf("error reading event %v", err) } e := string(bodyBytes) if e != "" { processEvent(bodyBytes) log.Infof("received event %s", string(bodyBytes)) } else { w.WriteHeader(http.StatusNoContent) } }
Go 中的 PTP 事件 createSubscription 功能示例
import (
"github.com/redhat-cne/sdk-go/pkg/pubsub"
"github.com/redhat-cne/sdk-go/pkg/types"
v1pubsub "github.com/redhat-cne/sdk-go/v1/pubsub"
)
// Subscribe to PTP events using REST API
s1,_:=createsubscription("/cluster/node/<node_name>/sync/sync-status/os-clock-sync-state") 1
s2,_:=createsubscription("/cluster/node/<node_name>/sync/ptp-status/ptp-clock-class-change")
s3,_:=createsubscription("/cluster/node/<node_name>/sync/ptp-status/lock-state")
// Create PTP event subscriptions POST
func createSubscription(resourceAddress string) (sub pubsub.PubSub, err error) {
var status int
apiPath:= "/api/ocloudNotifications/v1/"
localAPIAddr:=localhost:8989 // vDU service API address
apiAddr:= "localhost:8089" // event framework API address
subURL := &types.URI{URL: url.URL{Scheme: "http",
Host: apiAddr
Path: fmt.Sprintf("%s%s", apiPath, "subscriptions")}}
endpointURL := &types.URI{URL: url.URL{Scheme: "http",
Host: localAPIAddr,
Path: "event"}}
sub = v1pubsub.NewPubSub(endpointURL, resourceAddress)
var subB []byte
if subB, err = json.Marshal(&sub); err == nil {
rc := restclient.New()
if status, subB = rc.PostWithReturn(subURL, subB); status != http.StatusCreated {
err = fmt.Errorf("error in subscription creation api at %s, returned status %d", subURL, status)
} else {
err = json.Unmarshal(subB, &sub)
}
} else {
err = fmt.Errorf("failed to marshal subscription for %s", resourceAddress)
}
return
}
- 1
- 将
<node_name>
替换为正在生成 PTP 事件的节点 FQDN。例如,compute-1.example.com
。
Go 中的 PTP 事件消费者 getCurrentState 功能示例
//Get PTP event state for the resource func getCurrentState(resource string) { //Create publisher url := &types.URI{URL: url.URL{Scheme: "http", Host: localhost:8989, Path: fmt.SPrintf("/api/ocloudNotifications/v1/%s/CurrentState",resource}} rc := restclient.New() status, event := rc.Get(url) if status != http.StatusOK { log.Errorf("CurrentState:error %d from url %s, %s", status, url.String(), event) } else { log.Debugf("Got CurrentState: %s ", event) } }
17.4.2. 引用 cloud-event-proxy 部署和服务 CR
在部署 PTP 事件消费者应用程序时,使用 cloud-event-proxy
部署和订阅者服务 CR 示例作为参考。
HTTP 传输是 PTP 和裸机事件的默认传输。在可能的情况下,使用 HTTP 传输而不是 AMQP 用于 PTP 和裸机事件。AMQ Interconnect 于 2024 年 6 月 30 日结束生命周期(EOL)。AMQ Interconnect 的延长生命周期支持 (ELS) 于 2029 年 11 月 29 日结束。如需更多信息,请参阅 Red Hat AMQ Interconnect 支持状态。
使用 HTTP 传输引用 cloud-event-proxy 部署
apiVersion: apps/v1 kind: Deployment metadata: name: event-consumer-deployment namespace: <namespace> labels: app: consumer spec: replicas: 1 selector: matchLabels: app: consumer template: metadata: labels: app: consumer spec: serviceAccountName: sidecar-consumer-sa containers: - name: event-subscriber image: event-subscriber-app - name: cloud-event-proxy-as-sidecar image: openshift4/ose-cloud-event-proxy args: - "--metrics-addr=127.0.0.1:9091" - "--store-path=/store" - "--transport-host=consumer-events-subscription-service.cloud-events.svc.cluster.local:9043" - "--http-event-publishers=ptp-event-publisher-service-NODE_NAME.openshift-ptp.svc.cluster.local:9043" - "--api-port=8089" env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName - name: NODE_IP valueFrom: fieldRef: fieldPath: status.hostIP volumeMounts: - name: pubsubstore mountPath: /store ports: - name: metrics-port containerPort: 9091 - name: sub-port containerPort: 9043 volumes: - name: pubsubstore emptyDir: {}
使用 AMQ 传输引用 cloud-event-proxy 部署
apiVersion: apps/v1 kind: Deployment metadata: name: cloud-event-proxy-sidecar namespace: cloud-events labels: app: cloud-event-proxy spec: selector: matchLabels: app: cloud-event-proxy template: metadata: labels: app: cloud-event-proxy spec: nodeSelector: node-role.kubernetes.io/worker: "" containers: - name: cloud-event-sidecar image: openshift4/ose-cloud-event-proxy args: - "--metrics-addr=127.0.0.1:9091" - "--store-path=/store" - "--transport-host=amqp://router.router.svc.cluster.local" - "--api-port=8089" env: - name: <node_name> valueFrom: fieldRef: fieldPath: spec.nodeName - name: <node_ip> valueFrom: fieldRef: fieldPath: status.hostIP volumeMounts: - name: pubsubstore mountPath: /store ports: - name: metrics-port containerPort: 9091 - name: sub-port containerPort: 9043 volumes: - name: pubsubstore emptyDir: {}
参考 cloud-event-proxy 订阅者服务
apiVersion: v1 kind: Service metadata: annotations: prometheus.io/scrape: "true" service.alpha.openshift.io/serving-cert-secret-name: sidecar-consumer-secret name: consumer-events-subscription-service namespace: cloud-events labels: app: consumer-service spec: ports: - name: sub-port port: 9043 selector: app: consumer clusterIP: None sessionAffinity: None type: ClusterIP
17.4.3. cloud-event-proxy sidecar REST API 中的 PTP 事件
PTP 事件消费者应用程序可以为以下 PTP 时间事件轮询 PTP 事件制作者。
资源 URI | 描述 |
---|---|
|
描述 PTP 设备锁定状态的当前状态。可以处于 |
|
描述主机操作系统时钟同步状态。可以是 |
| 描述 PTP 时钟类的当前状态。 |
17.4.4. 将消费者应用程序订阅到 PTP 事件
在 PTP 事件消费者应用程序可以轮询事件前,您需要将应用程序订阅到事件制作者。
17.4.4.1. 订阅 PTP 锁定状态事件
要为 PTP lock-state
事件创建一个订阅,使用以下 payload 向云事件 API(位于 http://localhost:8081/api/ocloudNotifications/v1/subscriptions
)发送一个 POST
操作:
{ "endpointUri": "http://localhost:8989/event", "resource": "/cluster/node/<node_name>/sync/ptp-status/lock-state", }
响应示例
{ "id": "e23473d9-ba18-4f78-946e-401a0caeff90", "endpointUri": "http://localhost:8989/event", "uriLocation": "http://localhost:8089/api/ocloudNotifications/v1/subscriptions/e23473d9-ba18-4f78-946e-401a0caeff90", "resource": "/cluster/node/<node_name>/sync/ptp-status/lock-state", }
17.4.4.2. 订阅 PTP os-clock-sync-state 事件
要为 PTP os-clock-sync-state
事件创建一个订阅,使用以下 payload 向云事件 API(位于 http://localhost:8081/api/ocloudNotifications/v1/subscriptions
)发送一个 POST
操作:
{ "endpointUri": "http://localhost:8989/event", "resource": "/cluster/node/<node_name>/sync/sync-status/os-clock-sync-state", }
响应示例
{ "id": "e23473d9-ba18-4f78-946e-401a0caeff90", "endpointUri": "http://localhost:8989/event", "uriLocation": "http://localhost:8089/api/ocloudNotifications/v1/subscriptions/e23473d9-ba18-4f78-946e-401a0caeff90", "resource": "/cluster/node/<node_name>/sync/sync-status/os-clock-sync-state", }
17.4.4.3. 订阅 PTP ptp-clock-class-change 事件
要为 PTP ptp-clock-class-change
事件创建一个订阅,使用以下 payload 向云事件 API(位于 http://localhost:8081/api/ocloudNotifications/v1/subscriptions
)发送一个 POST
操作:
{ "endpointUri": "http://localhost:8989/event", "resource": "/cluster/node/<node_name>/sync/ptp-status/ptp-clock-class-change", }
响应示例
{ "id": "e23473d9-ba18-4f78-946e-401a0caeff90", "endpointUri": "http://localhost:8989/event", "uriLocation": "http://localhost:8089/api/ocloudNotifications/v1/subscriptions/e23473d9-ba18-4f78-946e-401a0caeff90", "resource": "/cluster/node/<node_name>/sync/ptp-status/ptp-clock-class-change", }
17.4.5. 获取当前的 PTP 时钟状态
要获取节点的当前 PTP 状态,请发送 GET
操作到以下事件 REST API 之一:
-
http://localhost:8081/api/ocloudNotifications/v1/cluster/node/<node_name>/sync/ptp-status/lock-state/CurrentState
-
http://localhost:8081/api/ocloudNotifications/v1/cluster/node/<node_name>/sync/sync-status/os-clock-sync-state/CurrentState
-
http://localhost:8081/api/ocloudNotifications/v1/cluster/node/<node_name>/sync/ptp-status/ptp-clock-class-change/CurrentState
响应是一个云原生事件 JSON 对象。例如:
lock-state API 响应示例
{ "id": "c1ac3aa5-1195-4786-84f8-da0ea4462921", "type": "event.sync.ptp-status.ptp-state-change", "source": "/cluster/node/compute-1.example.com/sync/ptp-status/lock-state", "dataContentType": "application/json", "time": "2023-01-10T02:41:57.094981478Z", "data": { "version": "v1", "values": [ { "resource": "/cluster/node/compute-1.example.com/ens5fx/master", "dataType": "notification", "valueType": "enumeration", "value": "LOCKED" }, { "resource": "/cluster/node/compute-1.example.com/ens5fx/master", "dataType": "metric", "valueType": "decimal64.3", "value": "29" } ] } }
17.4.6. 验证 PTP 事件消费者应用程序是否收到事件
验证应用程序 pod 中的 cloud-event-proxy
容器是否接收 PTP 事件。
先决条件
-
已安装 OpenShift CLI(
oc
)。 -
您已以具有
cluster-admin
权限的用户身份登录。 - 已安装并配置了 PTP Operator。
流程
获取活跃的
linuxptp-daemon
pod 列表。运行以下命令:$ oc get pods -n openshift-ptp
输出示例
NAME READY STATUS RESTARTS AGE linuxptp-daemon-2t78p 3/3 Running 0 8h linuxptp-daemon-k8n88 3/3 Running 0 8h
运行以下命令,访问所需的消费者端的
cloud-event-proxy
容器的指标:$ oc exec -it <linuxptp-daemon> -n openshift-ptp -c cloud-event-proxy -- curl 127.0.0.1:9091/metrics
其中:
- <linuxptp-daemon>
指定您要查询的 pod,例如
linuxptp-daemon-2t78p
。输出示例
# HELP cne_transport_connections_resets Metric to get number of connection resets # TYPE cne_transport_connections_resets gauge cne_transport_connection_reset 1 # HELP cne_transport_receiver Metric to get number of receiver created # TYPE cne_transport_receiver gauge cne_transport_receiver{address="/cluster/node/compute-1.example.com/ptp",status="active"} 2 cne_transport_receiver{address="/cluster/node/compute-1.example.com/redfish/event",status="active"} 2 # HELP cne_transport_sender Metric to get number of sender created # TYPE cne_transport_sender gauge cne_transport_sender{address="/cluster/node/compute-1.example.com/ptp",status="active"} 1 cne_transport_sender{address="/cluster/node/compute-1.example.com/redfish/event",status="active"} 1 # HELP cne_events_ack Metric to get number of events produced # TYPE cne_events_ack gauge cne_events_ack{status="success",type="/cluster/node/compute-1.example.com/ptp"} 18 cne_events_ack{status="success",type="/cluster/node/compute-1.example.com/redfish/event"} 18 # HELP cne_events_transport_published Metric to get number of events published by the transport # TYPE cne_events_transport_published gauge cne_events_transport_published{address="/cluster/node/compute-1.example.com/ptp",status="failed"} 1 cne_events_transport_published{address="/cluster/node/compute-1.example.com/ptp",status="success"} 18 cne_events_transport_published{address="/cluster/node/compute-1.example.com/redfish/event",status="failed"} 1 cne_events_transport_published{address="/cluster/node/compute-1.example.com/redfish/event",status="success"} 18 # HELP cne_events_transport_received Metric to get number of events received by the transport # TYPE cne_events_transport_received gauge cne_events_transport_received{address="/cluster/node/compute-1.example.com/ptp",status="success"} 18 cne_events_transport_received{address="/cluster/node/compute-1.example.com/redfish/event",status="success"} 18 # HELP cne_events_api_published Metric to get number of events published by the rest api # TYPE cne_events_api_published gauge cne_events_api_published{address="/cluster/node/compute-1.example.com/ptp",status="success"} 19 cne_events_api_published{address="/cluster/node/compute-1.example.com/redfish/event",status="success"} 19 # HELP cne_events_received Metric to get number of events received # TYPE cne_events_received gauge cne_events_received{status="success",type="/cluster/node/compute-1.example.com/ptp"} 18 cne_events_received{status="success",type="/cluster/node/compute-1.example.com/redfish/event"} 18 # HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served. # TYPE promhttp_metric_handler_requests_in_flight gauge promhttp_metric_handler_requests_in_flight 1 # HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code. # TYPE promhttp_metric_handler_requests_total counter promhttp_metric_handler_requests_total{code="200"} 4 promhttp_metric_handler_requests_total{code="500"} 0 promhttp_metric_handler_requests_total{code="503"} 0