## [è°! 就硬è°](/gdb-without-symbol-table)
é®äžèªå·±, è°è¿ä¹?
æèäžç§.
ååéè°æ, çšåºåè°æçšåº, ç®ç§°è°è¯. è°è¯çœç»çšåº, ç®ç§°çœè°.
åŠè¿çŒè¯åççéœç¥é, çšåºçŒè¯çæ¶å, äŒæåæ°éæ©æ¯åŠçºŠè°. çºŠè°æçæ¯åšæ£åŒååžåä¿ççšåºç笊å·è¡šäžºäºåæ¶éªè¯çšåºçžå
³é®é¢.
- go `go build -ldflags "-w -s" xxx` å é€è°è¯è¡š, äžçºŠè°
- gcc `gcc -g xxx` ä¿ç笊å·è¡š, 纊è°
è¿éåªé坹坿§è¡çšåºçè°è¯ä»¥åè°è¯åšåŠ`gdb`/`lldb`.
èææºè¯èšä¹æèªå·±çè°è¯åšåŠ`jdb`, è¿éäžæ¶å.
---
### æŸè°è°
åŸä¹
åŸä¹
以å, åäºåç°è¿çšæ¥å¿ééå°è¯·æ±æ²¡æååºååæç»æäœ, èæ¯ä¿çäºå笊䞲ç圢åŒ.
```json
"request": { | |
"hello": "world" | ==> | "request": "{ \\"hello\\": \\"world\\" }"
} | |
```
忥å¯ä»¥`request.hello: world`çæ¥è¯¢, ç°åšç«ç¶åæäº`request: {"hello": "world"}`, è¿æŸç¶æ æ³æ¥å.
äžå¡çæ¥å¿æ¶ææ¯éå
žåç`elk/efk`, å
¶äžfilebeat被åæäž€å±, èŽèŽ£ç产è
æ¶è޹è
.
ç产è
åºäº[hints](https://www.elastic.co/guide/en/beats/filebeat/8.15/configuration-autodiscover-hints.html)䞺æ¯äžªpodæäŸäžäžªäžªæ§åçå€çæºå¶.
- æå¡æ³šå
¥(`kubernetes`)
- æ¥å¿æ ŒåŒ(`json`/`multiline`)
- æ¥å¿åæµ(`dataset`)
- æ¥å¿è¿æ»€(`dropevent`)
æ¶è޹è
对æ¥kafka, äž»èŠèŽèŽ£ç»äžäºæ¬¡å€ç, ç®çæ¯äžä¿¡ä»»ç产è
鲿¢ç产è
é误ç䞪æ§åæäœé æesåå
¥éè¯, ä»èæåesåå.
- ç»æåå€ç(`decode_json_fields`)
- äžäžæå€ç(`script`)
- æ»ä¿¡éå(`dlq`)
çŽè§äžåºè¯¥æ¯åšæ¶è޹è
çç»æåå€çäžåºç°çé®é¢, ç»æåé
眮åŸç®å, éŸéæ¯`max_depth`ç®éäº?
```yaml
...
processors:
- decode_json_fields:
fields: ["message"]
max_depth: 2
...
```
æŸå°åºç°é®é¢çæ¥å¿ç`message`åæ®µ, åškafkaäžéæ°åéåå€å°è¯å€ç°è¿äžªé®é¢.
ç产è
:
```json
"message": "{ \"request\": \"{ \"hello\": \"world\" }\" }"
```
æ¶è޹è
:
```json
"message": "{ \"json\": { \"request\": \"{ \"hello\": \"world\" }\" } }"
```
å¯ä»¥çå°æ·±åºŠç¡®å®äžº2, äžè¿æŽ»é©¬å»å
è°å€§å°10éæ°å€ç°, 坿é®é¢ä»»ç¶ååš.
åšäœ¿çš`gdb`å, ç¡®ä¿äœ å
æŸå°äœ æççå°æ¹, è¿éå¯ä»¥ç¡®å®æ¯æ¶è޹è
å 䞺æç§åå æ²¡æè§£åºæ¥.
æå¡æ¯å®¹åšåéšçœ²ç, æ¶è޹è
çéå䞺
```yaml
image: docker.elastic.co/beats/filebeat:8.15.1
```
对åºçæºç 䞺 [v8.15.1](https://github.com/elastic/beats/blob/v8.15.1/)
### äžèœè°?
忥æçæ¯äžæ¯ç产è
åºååå垊äžäžäºäžå¯è§åç¬Šæ¥æ è®°å·²ç»è¢«åºååäº, æºç äžå¯ä»¥æŸå°å€æ[æ¯åŠéèŠååºåè¯çå€æåœæ°](https://github.com/elastic/beats/blob/v8.15.1/libbeat/processors/actions/decode_json_fields.go#L263).
```go
package actions
...
func isStructured(s string) bool { // line:263
s = strings.TrimSpace(s)
end := len(s) - 1
return end > 0 && ((s[0] == '[' && s[end] == ']') ||
(s[0] == '{' && s[end] == '}'))
}
```
éŠå
éèŠåŒå¯filebeat容åšç`shareProcessNamespace`, ä¹åçŽæ¥æäžäžªç¹ædebug容åšå°è¿äžªfilebeatäž.
```bash
kubectl debug -it filebeat --image=alpine --target=filebeat --profile=sysadmin
```
è¿å
¥å°debug容åšäž, å¯ä»¥å
æç
§gdb, ç¶åæŸå°filebeatçpid.
```bash
# apk add gdb
# ps | grep filebeat
```
è¿å»gdb, attachå°å¯¹åºpid.
```bash
# gdb
(gdb) attach 7
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0xxx in runtime/internal/syscall.Syscall6 ()
```
çŽæ¥éä¹åå®äœå°çåœæ°`isStructured`, åŸå¥æªæç¹å¹¶æ²¡æè§Šå.
```bash
(gdb) tb github.com/elastic/beats/v7/libbeat/processors/actions[isStructured]
Temporary breakpoint 1 at 0xxx
(gdb) c
Continuing.
```
æè¡ç马äžäŒéäžæ¥æç¹äžå±è°çš`unmarshal`.
```bash
(gdb) tb github.com/elastic/beats/v7/libbeat/processors/actions[unmarshal]
Temporary breakpoint 2 at 0xxx
(gdb) c
Continuing.
```
è¿éåŸå¿«å°±è§Šåäº, è¿éèŠæå°äžå¯¹åºçåé, äœæ¯æ²¡æå¯¹åºç笊å·è¡š, çäžå°ä»»äœæ¬å°åéååæ°.
```bash
[Switching to Thread 0xxx (LWP 5547)]
Thread 10 "filebeat" hit Temporary breakpoint 2, 0xxx in github.com/elastic/beats/v7/libbeat/processors/actions[unmarshal] ()
(gdb) p text
No symbol "text" in current context.
(gdb) i args
No symbol table info available.
```
### 硬è°
åŠæè¿éçäžå°ç¬Šå·è¡šäŒŒä¹æ²¡å¥åæ³äº.
äœæ¯åŠè¿è®¡ç®æºç»æåçåæäœç³»ç»çå©åæçŠäº, å¯ä»¥å©çšçžå
³çå æ ä¿¡æ¯, ç¶åè¿ååºæ¥å¯¹åºå°åçåŒ, è¿äžªåŒå¥œå没ä»ä¹åŒåžž.
```bash
(gdb) backtrace/frame
...
#0 0xxx in github.com/elastic/beats/v7/libbeat/processors/actions[unmarshal] (text=0xxx)
(gdb) x/32sb 0xxx
0xxx: "{\"json\":{\"request\":\"{\\\"hello\\\":\\\"world\\\"}\"}}"
...
```
åŠæäžèµ°`isStructured`, åªèœæ¯è¿é[`!isString`](https://github.com/elastic/beats/blob/v8.15.1/libbeat/processors/actions/decode_json_fields.go#L198)è¿å. åç`"{\"json\":{\"request\":\"{\\\"hello\\\":\\\"world\\\"}\"}}"`, æç¶éé¢äžæ¯å笊䞲. æä»¥è¿äžªéåœåªäŒéåœå笊䞲, åŠæéå笊䞲并äžäŒæç
§`max_depth`å»éå`decode`.
è³æ€è§£å³æ¹æ³åŸç®å, åšåé¢éæ°åºååè¿äžªå¯¹è±¡å³å¯.
```yaml
processors:
- script:
when:
has_fields: ["json"]
lang: javascript
source: function process(event) { event.Put('json', JSON.stringify(event.Get('json'))) }
```
ä¹åéæ°èµ°äžéäžè¿°çæµçš, `tb3`被觊åäº, éå`tb1`ä¹è§Šåäº.
```bash
0xxx: "{\"json\":\"{\\\"request\\\":\\\"{\\\\\\\"hello\\\\\\\":\\\\\\\"world\\\\\\\"}\\\"}\"}" // tb3
0xxx: "{\"request\":\"{\\\"hello\\\":\\\"world\\\"}\"}" // tb1
```
### 蜯硬å
Œæœ
filebeatæç
§åè¡ççŒè¯, äŒå 逿è°è¯ä¿¡æ¯ç, ç®åä»»å¡è¿å¥œ, åŠæå€æä»»å¡è¿æ¯éèŠç¥éåŠäœ[éæ°çŒè¯](https://github.com/elastic/beats/blob/v8.16.1/dev-tools/mage/settings.go#L126)åºæ¥è¿äžªçšåº.
`DEV`ç¯å¢åé䞺`true`廿§è¡makeå³å¯çŒè¯åºæ¥åžŠè°è¯ä¿¡æ¯çfilebeat, æå
Žè¶£å¯ä»¥èªå·±çŒè¯.
èææºè¯èšå¯èœå¹¶æ²¡æçå®çå°å, äœæ¯æ¯äžªå¯¹è±¡éœäŒæå¯¹åºçID, åšç¹å®çè°è¯åšäžåŠææ²¡æè°è¯ä¿¡æ¯ä¹å¯ä»¥æ ¹æ®IDè¿ååºæ¥å¯¹åºçåŒ.
## [让æçç!](/grafana-cloud)
诎å®è¯, è§å¯è¿äœ æå¡åšçç¶æä¹?
æèäžç§.
æå€æå°, éœè§å¯è¿äžäº.
- top
- watch
- tail -f
ææ¶è§å¯éœæ¯æç®çç, äœæ¯24å°æ¶æ äž»è§æä¹çè§å¯è¿ä¹? å¯è§æµæ§èŠæ±äºè¿äžç¹, èç®ååšmetricsé¢åæ¥åçåŒæºå®ç°äž, prometheusågrafanaè¿å¥ä¿©ç¡®å®åŸçå, äžèµ·æ¥ççå§.
---
### ä»ä¹? è¿ç§æ ŒåŒ
å讟æä»¬æäŸäžäžªrpcçæ¹æ³(çšsleep代æ¿), éèŠå
³æ³šä»çè¯·æ±æ¬¡æ°.
```go
go func() {
for {
time.Sleep(time.Duration(rand.Intn(5)+1) * time.Second)
// requestCount.WithLabelValues(fmt.Sprintf("%d", rand.Intn(5)+1)).Inc()
}
}()
```
åå§å`requestCount`èæ¶çŽæ¹åŸ, 并泚åå°`prometheus client`.
```go
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
requestCount = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests.",
},
[]string{"rand"},
)
)
func init() {
prometheus.MustRegister(requestCount)
}
```
åšæºåšäžåŒå¯å¯¹åºçæ°æ®ç«¯å£.
```go
func main() {
...
http.Handle("/metrics", promhttp.Handler())
...
}
```
对äºå
¬çœè¢«åšæåçæç¹æä»¬è¿éèŠæäŸbasic讀è¯.
```go
func BasicAuthMiddleware(next http.Handler, user, pass string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if auth := r.Header.Get("Authorization"); auth == "" || !strings.HasPrefix(auth, "Basic "+base64.StdEncoding.EncodeToString([]byte(user+":"+pass))) { http.Error(w, "Unauthorized", http.StatusUnauthorized); return }
next.ServeHTTP(w, r)
})
}
func main() {
...
http.Handle("/metrics", BasicAuthMiddleware(promhttp.Handler(), "username", "password"))
...
}
```
10ç§å, è¿æ¡äžæ¥çæ°æ®ææ»æŸç€ºåŠäž:
```
# HELP Total number of HTTP requests.
# TYPE http_requests_total counter
http_requests_total{rand="2"} 2
http_requests_total{rand="3"} 1
```
åœç¶, åžžè§çæ
åµå·²ç»æäººåž®æä»¬æå®äº, æ¯åŠäž»æºçä¿¡æ¯å°±åš[node_expoter](https://github.com/prometheus/node_exporter/tree/v1.8.2)äž, åž®æç»è®¡åºæ¥äº, å
¶äžé»è®€å¯åšçæ¶éåšäŒééæåŸ[äž°å¯çäž»æºæ°æ®](https://github.com/prometheus/node_exporter/tree/v1.8.2?tab=readme-ov-file#enabled-by-default).
以ç£çå©äœç©ºéŽäžºäŸ, ä»çæ¶åºæ°æ®æ ŒåŒçèµ·æ¥æ¯è¿æ ·ç.
```sql
# HELP node_filesystem_free_bytes Filesystem free space in bytes.
# TYPE node_filesystem_free_bytes gauge
node_filesystem_free_bytes{device="/dev/vda",device_error="",fstype="ext4",mountpoint="/"} 1.7243516928e+10
```
### åé~ 纵暪亀æ¿
åºåç±äžéšåç»æ:
- åºåå
- æ çŸäžæ çŸåŒ
- æ¶éŽäžæ¶åºåŒ
åé䞺åºåçéå.
æ¶åºæ°æ®åºåºè¯¥æäŸäž€äžªåºç¡èœå:
- è¿æ»€åé éè¿æ çŸååºåå
- å€çåé éè¿å
建é»èŸè¿ç®
ææ¡ä»¶æ¥è¯¢åºåæ¯æ¶åºæ°æ®åºæåºç¡çèœå, æ¯åŠæèŠæ¥è¯¢Cççå©äœç©ºéŽ.
å
建é»èŸè¿ç®å€çç¬æ¶åéåèåŽåé, æ¯åŠç°åšCççå©äœç©ºéŽæ¯å€å°, åæ¶ä¹èŠæ¯æä»å€©Cççå©äœç©ºé޿倧æ¯å€å°.
æä»¥äœ çæ°æ®åšé»èŸçèµ·æ¥æ¯è¿æ ·ç:
```
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â åºåå â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â æ çŸæ° n â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ†â
â â ââââââââââââââââââââââââââââââââââââââââââââââ â â
â â â æ çŸåç§° â â â
â â âââââââââââââââââââââââââââââââââââââââââââââ†â â
â â â æ çŸåŒ â â â
â â ââââââââââââââââââââââââââââââââââââââââââââââ â â
â â ... â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ†â
â â åæ° n â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ†â
â â ââââââââââââââââââââââââââââââââââââââââââââââ â â
â â â æ¶éŽç¹ 1 â â â
â â âââââââââââââââââââââââââââââââââââââââââââââ†â â
â â â åŒ 1 â â â
â â ââââââââââââââââââââââââââââââââââââââââââââââ â â
â â ââââââââââââââââââââââââââââââââââââââââââââââ â â
â â â æ¶éŽç¹ 2 â â â
â â âââââââââââââââââââââââââââââââââââââââââââââ†â â
â â â åŒ 2 â â â
â â ââââââââââââââââââââââââââââââââââââââââââââââ â â
â â ... â â
â ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
...
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
```
以çéšç`prometheus`ç[å
å»ºçæ¶åºæ°æ®åº](https://github.com/prometheus/prometheus/blob/release-2.18/tsdb/docs/format/index.md)æ¥è¯Ž, å®äžä»
ä¿åååšæ ŒåŒ, ä¹å
æ¬è¿äºæ ŒåŒåŠäœå©çšçŽ¢åŒå»å猩, æå
Žè¶£çæååŠä¹ äž.
åæ¶åšå
¶ç©çå±é¢äž, è¿éèŠææ çŸååºååç¬ä»¥åå对åºçèµ·æ¢æ¶éŽåæå°æä»¶æ¹äŸ¿å 蜜æ¥è¯¢.
```
./data
ââ 01BKGTZQ1SYQJTR4PB43C8PD98
âââ chunks
â âââ 000001
âââ meta.json
âââ index
âââ tombstones
```
ä»äžé¢ä»ç»çèµ·æ¥, æ¶åºæ°æ®åºéèŠçèµæºå¯äžå°, æšèæå°éèŠ0.5c0.5g.
æªæ¢ä»æ¥, [bitnami-chart](https://github.com/bitnami/charts/blob/kube-prometheus/10.2.3/bitnami/kube-prometheus/Chart.yaml)äžè¿æ¯äœ¿çš`prometheus:2.55.1`, èœç¶`prometheus:3`å·²ç»åºæ¥å¿«3䞪æäº, æä»¥è¿äžèèªå»ºæ¹æ¡, æä»¬éæ©`prometheus:2.55.1`.
```yaml
# prometheus-value.yaml
prometheus:
resources:
limits:
cpu: 500m
memory: 512Mi
additionalArgs:
- name: enable-feature
value: auto-gomemlimit
```
æ¥äžæ¥åšéçŸ€äžæ§è¡å³å¯, å°äŒåšmonitoräžæç
§äžäžªåå®äŸçprometheuså®äŸ, å€é矀åé«å¯çšä»¥ååç鿬æéç¹, æ¬æäžæ¶å, 请å
³æ³šå
¶ä»æç« .
```bash
helm -n monitor upgrade -i prometheus oci://registry-1.docker.io/bitnamicharts/kube-prometheus -f prometheus-value.yaml
```
### ç°èº«å§! æªå
œ
奜åšå€§éšåååæäŸäºäžªäººå
莹çå®äŸç»å€§å®¶çš:
- è
Ÿè®¯äº - [10亿æ¡/æå
莹åå
¥](https://cloud.tencent.com/product/tmp)
- é¿éäº - [50GBå
莹åå
¥](https://www.aliyun.com/product/developerservices/prometheus)
- grafanaäº - [1äžæ¡è®¡èŽ¹ææ ](https://grafana.com/products/cloud/)
å 䞺é€äºéèŠåå
¥æ°æ®, è¿éèŠå°æ°æ®å±ç€ºåºæ¥, é¿éåè
Ÿè®¯çgrafanaäžå
莹éèŠèŽä¹°æèªå»º. ææ¶è§åŸäžæ¯ç»äžèµ·, èæ¯äœ èŠåçšgrafanaäŒåè¿äºååèŠé±.
æä»¬è¿é䜿çšgrafanaäº, æ¯ç«åäžæ²¡æä»»äœäžå°æºåš(1vcpu/0.5g)èœæäŸè¿äºèœå:
- 泚å https://grafana.com/auth/sign-up/create-user
æ¥äžæ¥å¯é, äžç§è¢«åšæåæšçæ°æ®, åŠäžç§äž»åšäžæ¥æšçæ°æ®.
- è¢«åšæå(æšè) https://{name}.grafana.net/connections/add-new-connection/metrics-endpoint
éåæååäžååå¯ä»¥è®¿é®å°æå¡åšççšæ·.
1. å¯åš`node_expoter`, äžèŠå¿äºé
眮basic/jwt讀è¯å³å¯!
2. é
眮æå https://{name}.grafana.net/connections/add-new-connection/metrics-endpoint

- äž»åšäžæ¥ - https://{name}.grafana.net/connections/add-new-connection/http-metrics
éåæ²¡æååççšæ·.
1. å¯åš`node_expoter`
2. æ°æ®èœ¬æ¢influxdb line protocol
3. curl垊çapiå°å¯¹åºç端ç¹
4. crontab宿¶æ§è¡
蜬åè®®èæ¬.
```bash
line='node_filesystem_free_bytes{device="/dev/vda",device_error="",fstype="ext4",mountpoint="/"} 1.7243516928e+10'
measurement=$(echo "$line" | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\).*$/\1/p')
tags=$(echo "$line" | sed -n 's/^[a-zA-Z_][a-zAZ0-9_]*{\(.*\)} .*/\1/p' | tr ',' '\n' | sed 's/"//g' | sed 's/^/,/' | sed '/=\s*$/d' | sed 's/ /\\ /g' | tr -d '\n')
field_value=$(echo "$line" | sed -n 's/.* \(.*\)$/\1/p')
line_protocol="${measurement}${tags},_instance_=your_ins value=${field_value}"
echo "$line_protocol" # node_filesystem_free_bytes,device=/dev/vda,fstype=ext4,mountpoint=/,_instance_=your_ins value=1.7243516928e+10
```
crontab宿¶æ§è¡ä»»å¡.
```bash
* * * * * bash /root/.metrics
* * * * * sleep 20s; bash /root/.metrics
* * * * * sleep 40s; bash /root/.metrics
```
äžæ¥å®ææ°æ®å, è¿éèŠå°æ°æ®å±ç€º.
以boot_time_seconds䞺äŸ, æ¥è¯ŽæåŠäœç»å¶äžäžªé¢æ¿.
`node_boot_time_seconds` æ¯äžäžªç¬æ¶åºååŒ(éåé), ç± `node_exporter` æäŸ, å®è¡šç€ºèç¹å¯åšæ¶éŽïŒåäœæ¯ç§.
`time()` æ¯ Prometheus äžçäžäžªå
çœ®åœæ°, å®è¿ååœåçæ¶éŽæ³, åäœæ¯ç§.
```
time() - node_boot_time_seconds{_instance_="$_instance_"}
```
计ç®åœåæ¶éŽåèç¹å¯åšæ¶éŽä¹éŽçå·®åŒ, ä¹å°±æ¯èç¹ä»å¯åšå°ç°åšå·²ç»ç»è¿äºå€å°ç§.
è¿éèŠè°æŽè²åœ©çæ ·åŒ. åœç¶grafana labsæäŸäºäž°å¯æ°æ®å±ç€º, node exporterçåŸæ 乿åŸå€, æ¯åŠ[ID1860](https://grafana.com/grafana/dashboards/1860-node-exporter-full/).
ð æ¥äžæ¥, å¯ä»¥äº«åäº


## [dockeræå
äžçšdocker](/docker-build-github)
讲éç, dockeræå
éèŠdockerä¹?
æèäžç§.
åŸäžå欢çšdockeræå
. äž»èŠåå æ¯æçåŒåæ¬èº«å°±åšå®¹åšäž, ççäžæ³åšdockeräžè£
äžäžªdockeräº.
åŠææ²¡ædocker, æ¯åŠååšåŸå¿«çæ¹æ³å¯ä»¥æå
äžäžªåºçšå¢?
---
### Action
è¿çæ, é£å°±æ¯çš[Github Action](https://docs.github.com/en/actions).
Action倧家éœçšè¿, åŸå€åŒæºèœ¯ä»¶ä¹éè¿Actionæå
, çè³çå°æäžäºäžå讟å€çåºä»¶ä¹åšçšActionæå
, çç倪çäº.
è¯æ³äžäž, æå
å°ååžåºçšæä»¬éèŠåªäºåæ°.
- githubä»åºå°å:忝
- githubç§æå¯é¥(åŠææ¯ç§æä»åº)
- dockerhubç莊å·å¯ç
- dockerfileçè·¯åŸ
- dockerhubçååžå°å
### actions/checkout@v4
[actions/checkout@v4](https://github.com/actions/checkout/tree/v4.2.2)æ¯github宿¹åŒæºçaction, çšäºægithubçå
¬åŒæè
ç§æä»åºcloneå°æ¬å°, 并忢å°å¯¹åºç忝.
- githubä»åºå°å:忝
åœç¶æéèŠçæ¯, åŠææä»¬æ³åšA项ç®äžäœ¿çšB项ç®çå°å, é£å°±éèŠææ¡è¿éçéšéšéé.
```yaml
steps:
-
uses: actions/checkout@v4
with:
repository: ${{ inputs.repository }}
ref: ${{ inputs.ref }}
```
- githubç§æå¯é¥(åŠææ¯ç§æä»åº)
é£åп项ç®äžåžæè¢«å
¶ä»äººçå°, è¿éèŠå¯¹è¿äžªactionææ. åš[New personal access token](https://github.com/settings/tokens/new)äž, 䜿çšgithubçŸåäžäžªæ»¡repoæéçtokenå³å¯.
```yaml
steps:
-
uses: actions/checkout@v4
with:
...
token: ${{ inputs.token }}
```
### docker/build-push-action
[docker/build-push-action](https://github.com/docker/build-push-action/tree/v6.10.0)æ¯dockerèªå·±åŒæºçaction, å®å
æ¬äº`actions/checkout`, é»è®€äœ¿çšåœåactionæåšçä»åºå°å, è¿æŸç¶äžæ¯æä»¬éèŠç.
- dockerhubç莊å·å¯ç
dockeræäžäžªactionäžéšæäŸç»åœçaction, ç®çŽè¯å¿, ç»äºäžçšçštokenäº!
```yaml
steps:
-
uses: docker/login-action@v3
with:
username: ${{ inputs.username }}
password: ${{ inputs.password }}
```
- `Dockerfile`çè·¯åŸ
- dockerhubçååžå°å
contextæ¯`Dockerfile`æåšçç®åœ, æ ¹ç®åœäœ¿çš`.`, åªæ¯æçžå¯¹è·¯åŸ!
tagsçè§å䞺`{docker-repo}/{docker-app}:{docker-tag}`.
```yaml
steps:
-
uses: docker/build-push-action@v6
with:
context: ${{ inputs.context }}
push: true
tags: ${{ inputs.tags }}
```
æåéèŠé¢å€éæ°æä»çé»è®€`ctions/checkout@v4`éæ°æ·»å åå».
倧ååæ
## [ååºåŒ: åœæ°åŒååŒäœ](/functional-to-reactive)
æªå¿èªé®, ååºåŒæ¯ä»ä¹?
æèäžç§.
ææ©å¬å°å端çååºåŒæ¯åš[Spring WebFlux](https://docs.spring.io/spring-framework/reference/6.2/web/webflux/new-framework.html)äž, 飿¶åè¿åšäžåŠçæå¯¹è¿äºæŠå¿µæµæµææ, åªæ¯çå°ååå°±æäžç§è«åç髿§èœå²åš, å äžºåœæ¶åŠè¯æµ
èäžçŽæ²¡ææ·±æ.
ä»å€©åçå¹¶æ¢è®šäžäž
è²äŒŒæ§èœåŸé«çååºåŒ, 䞺ä»ä¹çŽè³ä»æ¥ä»ç¶äžæž©äžç«?
---
### åœæ°åŒæ¥ç»æå€Žéš
æ¿æç®åçå€æå¯¹è±¡æ¯åŠäžºç©ºäžŸäŸ, éåœæ°åŒéèŠèªå·±ç»åæµçšæ§å¶è¯å¥çŒå
```java
var obj = new Object();
if (obj != null)
System.out.println(obj); // java.lang.Object@659e0bfd
```
åœæ°åŒéèŠçš[Optional](https://github.com/openjdk/jdk/blob/jdk8-b87/jdk/src/share/classes/java/util/Optional.java#L134-L137)çåœæ°åŒå
è£
```java
var opt = Optional.of(obj);
opt.ifPresent(o -> {
System.out.println(o); // java.lang.Object@659e0bfd
});
```
åæ ·äžæŽå€æçforæ§å¶è¯å¥, 乿坹åºç[Iterable](https://github.com/openjdk/jdk/blob/jdk8-b87/jdk/src/share/classes/java/lang/Iterable.java#L69-L74)æäŸçåœæ°åŒå
è£
.
æ§å¶è¯å¥æ¯æéç, äœæ¯åžžè§çæ§å¶è¯å¥ç»åæ¯æ éç, å æ€åœæ°åŒåšè¿éåæ¥äºæå€§çäœçš,.
**çšæéçæ§å¶è¯å¥ç»ææ éçæ§å¶åœæ°.**
æåºåçæ åŒäº[Stream Package](https://github.com/openjdk/jdk/tree/jdk8-b89/jdk/src/share/classes/java/util/stream), æäŸäºäžäºåæµåŒæäœç®ååŒå, åŒå
¥çææ¬åæ¯éååæµççžäºèœ¬å. æŽå€å
³äºæµäžæ³è¯Žäº, èªè¡ç[APIææ¡£](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html)å³å¯.
äžç¥éä¹åæèäžç§çååºåŒ, åäœæåªäºçæ¡. åå°æ é¢, æè®€äžºååºåŒæ¯åœæ°åŒçç¹æ®åœ¢åŒ.
### ååºåŒæ¥åé
èç³
åŸå¥œçè§£, åŠæåœæ°åŒäžçæäœå¯¹è±¡æ¯è¯·æ±çååº, å¯ä»¥è®€äžºæ¯ååºåŒ.
åšçœç»åºçšäž, ååºåŒå䞺客æ·ç«¯åæå¡ç«¯. è¿åäžäžªå¯ä»¥è¢«è®¢é
ç对象, å¯ä»¥è®€äžºæ¯ååºåŒçæå¡ç«¯.
æ¿æç®åçå声æå¡äžŸäŸæå¡ç«¯åŠäž, è¿é泚æè¿ååŒäžºäžäžªå¯è®¢é
çå笊䞲对象.
```java
@GetMapping
public Mono ping() {
return Mono.just("pong");
}
```
客æ·ç«¯éèŠè®¢é
è¿äžªå¯è®¢é
ç对象, å¹¶åšååºååºçæ¶åååºè¡äžº.
```java
WebClient.create("localhost").get().uri("/ping").accept(MediaType.TEXT_PLAIN)
.retrieve().bodyToMono(String.class)
.subscribe(rsp -> { // ååºåŒ åœæ°åŒäžå€çç对象æ¯çœç»ååº
System.out.println(rsp); // pong
});
```
è¿éæå äžªæææçç¹
- æå¡ç«¯è¿åååºåŒå¯¹è±¡
- 客æ·ç«¯éèŠæ¯æååºåŒ
ä¹å°±æ¯æå¡æ¬èº«ä»¥åäžæžžéèŠåæ¶æ¯æè¿äžªæžžææèœç©äžå». åŠåè¿äžªååºåŒæš¡åäŒéåæé»å¡åŒ. é£çŽæ¥çäžéåžžæä¹äžçœç»åºçšç䞀端æ¯ä»ä¹?
æµè§åšåæ°æ®åº, åŸéæŸ.
é€äºäž€ç«¯å€, å©çšååºåŒåšç¬Šåæ¡ä»¶äžäŒæå éçææ, è¿ç§ææå¯ä»¥äœç°åšææ äž, 倧å乿äžéšååè¿ç§æåå»ç蟩.
è¿é诎çååºåŒçå¿«, **å
¶å®è§£å³çäžæ¯æ»éé®é¢, èæ¯åé
é®é¢.**
æ¢å¥è¯è¯Ž, åŠæäž€ç«¯äžæ¯æé£ä¹èç³å°±è¿ä¹å€§, äœ çæå¡è¶å¿«, å
¶ä»æå¡å°±è¶æ
¢.
åŠæäž€ç«¯æ¯æ, å°ææè®¡ç®èµæºäžç§»å°å®¢æ·ç«¯æµè§åš, ç¡®å®å¯ä»¥å€§å¹
æåæŽäœæ§èœ, *äœæ¯*
- 客æ·ç«¯å¯ä¿¡ä¹?
- æ°æ®åºèœèŽæ
çäºå€å°å¹¶å?
åŠæäœ æ³è§£å³æ»éé®é¢, 请åšå¯ä¿¡å®¢æ·ç«¯åé«å¹¶åæ°æ®åºæ¹åäžåå!!!
## [markdowné䜿å²å¡](/html-in-md)
æäžªé®é¢, åŠäœåŸmarkdownäžå¡äžæ®µåžŠæ ·åŒçhtml?
æèäžç§.
æ»æåšç¥, mdé颿²¡æåæ³çŽæ¥æž²æcss, ä¹åªèœæž²æç®åçhtmlæ çŸ.
åºäºäžäžå 䞪ç»è®º:
- svgé颿¯å¯ä»¥å¡åžŠæ ·åŒçhtmlç
- mdéé¢å¯ä»¥å¡svgç
mdé颿¯å¯ä»¥èµäžäžªåžŠæ ·åŒçhtmlç产çsvgç, æ¯åŠäžé¢
---
### foreignObject
svgäžçforeignObjectéé¢å¯ä»¥åhtml+css, æè§äžäºå äœåŸåœ¢å¯ä»¥çŽæ¥çšhtml衚蟟èäžéèŠçå®çåŸåœ¢, çŽæ¥[éå®ææ¡£](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject).