Compare commits
26 Commits
v3.4.0
...
sweep/user
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9153afc38 | ||
|
|
7a13284b2e | ||
|
|
75375adf62 | ||
|
|
b17c6ec924 | ||
|
|
fb74453c18 | ||
|
|
82d238afbe | ||
|
|
2c9a2239d8 | ||
|
|
56fd78653c | ||
|
|
872dcae27a | ||
|
|
6ba0d618e4 | ||
|
|
a19f0e534f | ||
|
|
eeb16d4116 | ||
|
|
ae048417ee | ||
|
|
7502b4ac0f | ||
|
|
05ab3fcd06 | ||
|
|
7698368957 | ||
|
|
0d5fe4e6d6 | ||
|
|
2ac54e09a6 | ||
|
|
7153eeb178 | ||
|
|
fd42c6dced | ||
|
|
69eb24f702 | ||
|
|
65c1c412da | ||
|
|
2496a16a88 | ||
|
|
d1af343b13 | ||
|
|
a580c15f6a | ||
|
|
8e0cb6dc47 |
@@ -1,79 +0,0 @@
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#---------------Infrastructure configuration---------------------#
|
||||
etcd:
|
||||
etcdSchema: openim #默认即可
|
||||
etcdAddr: [ 127.0.0.1:2379 ] #单机部署时,默认即可
|
||||
userName:
|
||||
password:
|
||||
secret: openIM123
|
||||
|
||||
mysql:
|
||||
dbMysqlDatabaseName: admin_chat # 数据库名字 默认即可
|
||||
|
||||
# 默认管理员账号
|
||||
admin:
|
||||
defaultAccount:
|
||||
account: [ "admin1", "admin2" ]
|
||||
defaultPassword: [ "password1", "password2" ]
|
||||
openIMUserID: [ "openIM123456", "openIMAdmin" ]
|
||||
faceURL: [ "", "" ]
|
||||
nickname: [ "admin1", "admin2" ]
|
||||
level: [ 1, 100 ]
|
||||
|
||||
|
||||
adminapi:
|
||||
openImAdminApiPort: [ 10009 ] #管理后台api服务端口,默认即可,需要开放此端口或做nginx转发
|
||||
listenIP: 0.0.0.0
|
||||
|
||||
chatapi:
|
||||
openImChatApiPort: [ 10008 ] #登录注册,默认即可,需要开放此端口或做nginx转发
|
||||
listenIP: 0.0.0.0
|
||||
|
||||
rpcport: # rpc服务端口 默认即可
|
||||
openImAdminPort: [ 30200 ]
|
||||
openImChatPort: [ 30300 ]
|
||||
|
||||
|
||||
rpcregistername: #rpc注册服务名,默认即可
|
||||
openImChatName: Chat
|
||||
openImAdminCMSName: Admin
|
||||
|
||||
chat:
|
||||
codeTTL: 300 #短信验证码有效时间(秒)
|
||||
superVerificationCode: 666666 # 超级验证码
|
||||
alismsverify: #阿里云短信配置,在阿里云申请成功后修改以下四项
|
||||
accessKeyId:
|
||||
accessKeySecret:
|
||||
signName:
|
||||
verificationCodeTemplateCode:
|
||||
|
||||
|
||||
oss:
|
||||
tempDir: enterprise-temp # 临时密钥上传的目录
|
||||
dataDir: enterprise-data # 最终存放目录
|
||||
aliyun:
|
||||
endpoint: https://oss-cn-chengdu.aliyuncs.com
|
||||
accessKeyID: ""
|
||||
accessKeySecret: ""
|
||||
bucket: ""
|
||||
tencent:
|
||||
BucketURL: ""
|
||||
serviceURL: https://cos.COS_REGION.myqcloud.com
|
||||
secretID: ""
|
||||
secretKey: ""
|
||||
sessionToken: ""
|
||||
bucket: ""
|
||||
use: "minio"
|
||||
@@ -1,27 +0,0 @@
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#more datasource-compose.yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
orgId: 1
|
||||
url: http://127.0.0.1:9091
|
||||
basicAuth: false
|
||||
isDefault: true
|
||||
version: 1
|
||||
editable: true
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,85 +0,0 @@
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#more prometheus-compose.yml
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
external_labels:
|
||||
monitor: 'openIM-monitor'
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9091']
|
||||
|
||||
- job_name: 'openIM-server'
|
||||
metrics_path: /metrics
|
||||
static_configs:
|
||||
- targets: ['localhost:10002']
|
||||
labels:
|
||||
group: 'api'
|
||||
|
||||
- targets: ['localhost:20110']
|
||||
labels:
|
||||
group: 'user'
|
||||
|
||||
- targets: ['localhost:20120']
|
||||
labels:
|
||||
group: 'friend'
|
||||
|
||||
- targets: ['localhost:20130']
|
||||
labels:
|
||||
group: 'message'
|
||||
|
||||
- targets: ['localhost:20140']
|
||||
labels:
|
||||
group: 'msg-gateway'
|
||||
|
||||
- targets: ['localhost:20150']
|
||||
labels:
|
||||
group: 'group'
|
||||
|
||||
- targets: ['localhost:20160']
|
||||
labels:
|
||||
group: 'auth'
|
||||
|
||||
- targets: ['localhost:20170']
|
||||
labels:
|
||||
group: 'push'
|
||||
|
||||
- targets: ['localhost:20120']
|
||||
labels:
|
||||
group: 'friend'
|
||||
|
||||
|
||||
- targets: ['localhost:20230']
|
||||
labels:
|
||||
group: 'conversation'
|
||||
|
||||
|
||||
- targets: ['localhost:21400', 'localhost:21401', 'localhost:21402', 'localhost:21403']
|
||||
labels:
|
||||
group: 'msg-transfer'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- job_name: 'node'
|
||||
scrape_interval: 8s
|
||||
static_configs:
|
||||
- targets: ['localhost:9100']
|
||||
|
||||
29
.env
29
.env
@@ -29,8 +29,8 @@ PASSWORD=openIM123
|
||||
MINIO_ENDPOINT=http://172.28.0.1:10005
|
||||
|
||||
# Base URL for the application programming interface (API).
|
||||
# Default: API_URL=http://172.0.0.1:10002
|
||||
API_URL=http://172.0.0.1:10002
|
||||
# Default: API_URL=http://172.28.0.1:10002
|
||||
API_URL=http://172.28.0.1:10002
|
||||
|
||||
# Directory path for storing data files or related information.
|
||||
# Default: DATA_DIR=./
|
||||
@@ -98,7 +98,14 @@ PROMETHEUS_NETWORK_ADDRESS=172.28.0.11
|
||||
# Address or hostname for the Grafana network.
|
||||
# Default: GRAFANA_NETWORK_ADDRESS=172.28.0.12
|
||||
GRAFANA_NETWORK_ADDRESS=172.28.0.12
|
||||
|
||||
|
||||
# Address or hostname for the node_exporter network.
|
||||
# Default: NODE_EXPORTER_NETWORK_ADDRESS=172.28.0.13
|
||||
NODE_EXPORTER_NETWORK_ADDRESS=172.28.0.13
|
||||
|
||||
# Address or hostname for the OpenIM admin network.
|
||||
# Default: OPENIM_ADMIN_NETWORK_ADDRESS=172.28.0.14
|
||||
OPENIM_ADMIN_FRONT_NETWORK_ADDRESS=172.28.0.14
|
||||
|
||||
# ===============================================
|
||||
# = Component Extension Configuration =
|
||||
@@ -283,3 +290,19 @@ SERVER_BRANCH=main
|
||||
# Port for the OpenIM admin API.
|
||||
# Default: OPENIM_ADMIN_API_PORT=10009
|
||||
OPENIM_ADMIN_API_PORT=10009
|
||||
|
||||
# Port for the node exporter.
|
||||
# Default: NODE_EXPORTER_PORT=19100
|
||||
NODE_EXPORTER_PORT=19100
|
||||
|
||||
# Port for the prometheus.
|
||||
# Default: PROMETHEUS_PORT=19090
|
||||
PROMETHEUS_PORT=19090
|
||||
|
||||
# Port for the grafana.
|
||||
# Default: GRAFANA_PORT=3000
|
||||
GRAFANA_PORT=3000
|
||||
|
||||
# Port for the admin front.
|
||||
# Default: OPENIM_ADMIN_FRONT_PORT=11002
|
||||
OPENIM_ADMIN_FRONT_PORT=11002
|
||||
|
||||
90
.github/workflows/api-test.yml
vendored
90
.github/workflows/api-test.yml
vendored
@@ -1,90 +0,0 @@
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: OpenIM API TEST
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- "README.md"
|
||||
- "README_zh-CN.md"
|
||||
- "CONTRIBUTING.md"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- "README.md"
|
||||
- "README_zh-CN.md"
|
||||
- "CONTRIBUTING.md"
|
||||
- "docs/**"
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.19"
|
||||
GOLANGCI_VERSION: "v1.50.1"
|
||||
|
||||
jobs:
|
||||
execute-linux-systemd-scripts:
|
||||
name: Execute OpenIM script on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
environment:
|
||||
name: openim
|
||||
strategy:
|
||||
matrix:
|
||||
go_version: ["1.20"]
|
||||
os: ["ubuntu-latest"]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go ${{ matrix.go_version }}
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go_version }}
|
||||
id: go
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
version: '3.x' # If available, use the latest major version that's compatible
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Docker Operations
|
||||
run: |
|
||||
curl -o docker-compose.yml https://raw.githubusercontent.com/OpenIMSDK/openim-docker/main/example/basic-openim-server-dependency.yml
|
||||
sudo docker compose up -d
|
||||
sudo sleep 60
|
||||
|
||||
- name: Module Operations
|
||||
run: |
|
||||
sudo make tidy
|
||||
sudo make tools.verify.go-gitlint
|
||||
|
||||
- name: Build, Start, Check Services and Print Logs
|
||||
run: |
|
||||
sudo ./scripts/install/install.sh -i && \
|
||||
sudo ./scripts/install/install.sh -s && \
|
||||
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
|
||||
|
||||
- name: Run Test
|
||||
run: |
|
||||
sudo make test-api && \
|
||||
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
|
||||
|
||||
- name: Stop Services
|
||||
run: |
|
||||
sudo ./scripts/install/install.sh -u && \
|
||||
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
|
||||
12
.github/workflows/build-docker-image.yml
vendored
12
.github/workflows/build-docker-image.yml
vendored
@@ -52,6 +52,7 @@ jobs:
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern=v{{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
@@ -87,6 +88,16 @@ jobs:
|
||||
uses: docker/metadata-action@v5.0.0
|
||||
with:
|
||||
images: registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=schedule
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern=v{{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
|
||||
- name: Log in to AliYun Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
@@ -126,6 +137,7 @@ jobs:
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern=v{{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
|
||||
6
.github/workflows/build-openim-web-image.yml
vendored
6
.github/workflows/build-openim-web-image.yml
vendored
@@ -15,11 +15,11 @@
|
||||
name: Build OpenIM Web Docker image
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 3 * * *'
|
||||
# schedule:
|
||||
# - cron: '30 3 * * *'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
# - main
|
||||
- release-*
|
||||
tags:
|
||||
- v*
|
||||
|
||||
13
.github/workflows/e2e-test.yml
vendored
13
.github/workflows/e2e-test.yml
vendored
@@ -97,4 +97,15 @@ jobs:
|
||||
|
||||
- name: Exec OpenIM System uninstall
|
||||
run: |
|
||||
sudo ./scripts/install/install.sh -u
|
||||
sudo ./scripts/install/install.sh -u
|
||||
|
||||
- name: gobenchdata publish
|
||||
uses: bobheadxi/gobenchdata@v1
|
||||
with:
|
||||
PRUNE_COUNT: 30
|
||||
GO_TEST_FLAGS: -cpu 1,2
|
||||
PUBLISH: true
|
||||
PUBLISH_BRANCH: gh-pages
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
continue-on-error: true
|
||||
2
.github/workflows/link-pr.yml
vendored
2
.github/workflows/link-pr.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
# ./*.md all markdown files in the root directory
|
||||
args: --verbose -E -i --no-progress --exclude-path './CHANGELOG' './**/*.md'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||
GITHUB_TOKEN: ${{secrets.BOT_GITHUB_TOKEN}}
|
||||
|
||||
- name: Create Issue From File
|
||||
if: env.lychee_exit_code != 0
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</p>
|
||||
|
||||
## 🟢 扫描微信进群交流
|
||||
<img src="https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg" width="300">
|
||||
<img src="./docs/images/Wechat.jpg" width="300">
|
||||
|
||||
|
||||
## Ⓜ️ 关于 OpenIM
|
||||
|
||||
@@ -231,7 +231,7 @@ Before you start, please make sure your changes are in demand. The best for that
|
||||
- [OpenIM Makefile Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md)
|
||||
- [OpenIM Script Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md)
|
||||
- [OpenIM Versioning](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md)
|
||||
|
||||
- [Manage backend and monitor deployment](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md)
|
||||
|
||||
## :busts_in_silhouette: Community
|
||||
|
||||
|
||||
32
config/alertmanager.yml
Normal file
32
config/alertmanager.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
###################### AlertManager Configuration ######################
|
||||
# AlertManager configuration using environment variables
|
||||
#
|
||||
# Resolve timeout
|
||||
# SMTP configuration for sending alerts
|
||||
# Templates for email notifications
|
||||
# Routing configurations for alerts
|
||||
# Receiver configurations
|
||||
global:
|
||||
resolve_timeout: 5m
|
||||
smtp_from: alert@openim.io
|
||||
smtp_smarthost: smtp.163.com:465
|
||||
smtp_auth_username: alert@openim.io
|
||||
smtp_auth_password: YOURAUTHPASSWORD
|
||||
smtp_require_tls: false
|
||||
smtp_hello: xxx监控告警
|
||||
|
||||
templates:
|
||||
- /etc/alertmanager/email.tmpl
|
||||
|
||||
route:
|
||||
group_wait: 5s
|
||||
group_interval: 5s
|
||||
repeat_interval: 5m
|
||||
receiver: email
|
||||
receivers:
|
||||
- name: email
|
||||
email_configs:
|
||||
- to: {EMAIL_TO:-'alert@example.com'}
|
||||
html: '{{ template "email.to.html" . }}'
|
||||
headers: { Subject: "[OPENIM-SERVER]Alarm" }
|
||||
send_resolved: true
|
||||
@@ -198,7 +198,7 @@ rpcRegisterName:
|
||||
# Whether to output in json format
|
||||
# Whether to include stack trace in logs
|
||||
log:
|
||||
storageLocation: ./logs/
|
||||
storageLocation: ../logs/
|
||||
rotationTime: 24
|
||||
remainRotationCount: 2
|
||||
remainLogLevel: 6
|
||||
@@ -382,7 +382,7 @@ callback:
|
||||
# The number of Prometheus ports per service needs to correspond to rpcPort
|
||||
# The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh
|
||||
prometheus:
|
||||
enable: true
|
||||
enable: false
|
||||
prometheusUrl: "https://openim.prometheus"
|
||||
apiPrometheusPort: [20100]
|
||||
userPrometheusPort: [ 20110 ]
|
||||
|
||||
16
config/email.tmpl
Normal file
16
config/email.tmpl
Normal file
@@ -0,0 +1,16 @@
|
||||
{{ define "email.to.html" }}
|
||||
{{ range .Alerts }}
|
||||
<!-- Begin of OpenIM Alert -->
|
||||
<div style="border:1px solid #ccc; padding:10px; margin-bottom:10px;">
|
||||
<h3>OpenIM Alert</h3>
|
||||
<p><strong>Alert Program:</strong> Prometheus Alert</p>
|
||||
<p><strong>Severity Level:</strong> {{ .Labels.severity }}</p>
|
||||
<p><strong>Alert Type:</strong> {{ .Labels.alertname }}</p>
|
||||
<p><strong>Affected Host:</strong> {{ .Labels.instance }}</p>
|
||||
<p><strong>Affected Service:</strong> {{ .Labels.job }}</p>
|
||||
<p><strong>Alert Subject:</strong> {{ .Annotations.summary }}</p>
|
||||
<p><strong>Trigger Time:</strong> {{ .StartsAt.Format "2006-01-02 15:04:05" }}</p>
|
||||
</div>
|
||||
<!-- End of OpenIM Alert -->
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
11
config/instance-down-rules.yml
Normal file
11
config/instance-down-rules.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
groups:
|
||||
- name: instance_down
|
||||
rules:
|
||||
- alert: InstanceDown
|
||||
expr: up == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Instance {{ $labels.instance }} down"
|
||||
description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 1 minutes."
|
||||
1460
config/prometheus-dashboard.yaml
Normal file
1460
config/prometheus-dashboard.yaml
Normal file
File diff suppressed because it is too large
Load Diff
85
config/prometheus.yml
Normal file
85
config/prometheus.yml
Normal file
@@ -0,0 +1,85 @@
|
||||
# my global config
|
||||
global:
|
||||
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
|
||||
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
|
||||
# scrape_timeout is set to the global default (10s).
|
||||
|
||||
# Alertmanager configuration
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets: ['172.28.0.1:19093']
|
||||
|
||||
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
|
||||
rule_files:
|
||||
- "instance-down-rules.yml"
|
||||
# - "first_rules.yml"
|
||||
# - "second_rules.yml"
|
||||
|
||||
# A scrape configuration containing exactly one endpoint to scrape:
|
||||
# Here it's Prometheus itself.
|
||||
scrape_configs:
|
||||
# The job name is added as a label "job='job_name'"" to any timeseries scraped from this config.
|
||||
# Monitored information captured by prometheus
|
||||
- job_name: 'node-exporter'
|
||||
static_configs:
|
||||
- targets: [ '172.28.0.1:19100' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
|
||||
# prometheus fetches application services
|
||||
- job_name: 'openimserver-openim-api'
|
||||
static_configs:
|
||||
- targets: [ '172.28.0.1:20100' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-msggateway'
|
||||
static_configs:
|
||||
- targets: [ '172.28.0.1:20140' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-msgtransfer'
|
||||
static_configs:
|
||||
- targets: [ 172.28.0.1:21400, 172.28.0.1:21401, 172.28.0.1:21402, 172.28.0.1:21403 ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-push'
|
||||
static_configs:
|
||||
- targets: [ '172.28.0.1:20170' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-auth'
|
||||
static_configs:
|
||||
- targets: [ '172.28.0.1:20160' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-conversation'
|
||||
static_configs:
|
||||
- targets: [ '172.28.0.1:20230' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-friend'
|
||||
static_configs:
|
||||
- targets: [ '172.28.0.1:20120' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-group'
|
||||
static_configs:
|
||||
- targets: [ '172.28.0.1:20150' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-msg'
|
||||
static_configs:
|
||||
- targets: [ '172.28.0.1:20130' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-third'
|
||||
static_configs:
|
||||
- targets: [ '172.28.0.1:21301' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-user'
|
||||
static_configs:
|
||||
- targets: [ '172.28.0.1:20110' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
@@ -123,6 +123,30 @@ Explore our Helm-Charts repository and read through: [Helm-Charts Repository](ht
|
||||
|
||||
Using the helm charts repository, you can ignore the following configuration, but if you want to just use the server and scale on top of it, you can go ahead:
|
||||
|
||||
**Use the Helm template to generate the deployment yaml file: `openim-charts.yaml`**
|
||||
|
||||
**Gen Image:**
|
||||
|
||||
```bash
|
||||
../scripts/genconfig.sh ../scripts/install/environment.sh ./templates/helm-image.yaml > ./charts/generated-configs/helm-image.yaml
|
||||
```
|
||||
|
||||
**Gen Charts:**
|
||||
|
||||
```bash
|
||||
for chart in ./charts/*/; do
|
||||
if [[ "$chart" == *"generated-configs"* || "$chart" == *"helmfile.yaml"* ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -f "${chart}values.yaml" ]; then
|
||||
helm template "$chart" -f "./charts/generated-configs/helm-image.yaml" -f "./charts/generated-configs/config.yaml" -f "./charts/generated-configs/notification.yaml" >> openim-charts.yaml
|
||||
else
|
||||
helm template "$chart" >> openim-charts.yaml
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
**Use Helmfile:**
|
||||
|
||||
```bash
|
||||
|
||||
1262
deployments/openim-charts.yaml
Normal file
1262
deployments/openim-charts.yaml
Normal file
File diff suppressed because it is too large
Load Diff
33
deployments/templates/alertmanager.yml
Normal file
33
deployments/templates/alertmanager.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
###################### AlertManager Configuration ######################
|
||||
# AlertManager configuration using environment variables
|
||||
#
|
||||
# Resolve timeout
|
||||
# SMTP configuration for sending alerts
|
||||
# Templates for email notifications
|
||||
# Routing configurations for alerts
|
||||
# Receiver configurations
|
||||
global:
|
||||
resolve_timeout: ${ALERTMANAGER_RESOLVE_TIMEOUT}
|
||||
smtp_from: ${ALERTMANAGER_SMTP_FROM}
|
||||
smtp_smarthost: ${ALERTMANAGER_SMTP_SMARTHOST}
|
||||
smtp_auth_username: ${ALERTMANAGER_SMTP_AUTH_USERNAME}
|
||||
smtp_auth_password: ${ALERTMANAGER_SMTP_AUTH_PASSWORD}
|
||||
smtp_require_tls: ${ALERTMANAGER_SMTP_REQUIRE_TLS}
|
||||
smtp_hello: ${ALERTMANAGER_SMTP_HELLO}
|
||||
|
||||
templates:
|
||||
- /etc/alertmanager/email.tmpl
|
||||
|
||||
route:
|
||||
group_by: ['alertname']
|
||||
group_wait: 5s
|
||||
group_interval: 5s
|
||||
repeat_interval: 5m
|
||||
receiver: email
|
||||
receivers:
|
||||
- name: email
|
||||
email_configs:
|
||||
- to: ${ALERTMANAGER_EMAIL_TO}
|
||||
html: '{{ template "email.to.html" . }}'
|
||||
headers: { Subject: "[OPENIM-SERVER]Alarm" }
|
||||
send_resolved: true
|
||||
@@ -94,12 +94,22 @@ OPENIM_CHAT_NETWORK_ADDRESS=${OPENIM_CHAT_NETWORK_ADDRESS}
|
||||
# Address or hostname for the Prometheus network.
|
||||
# Default: PROMETHEUS_NETWORK_ADDRESS=172.28.0.11
|
||||
PROMETHEUS_NETWORK_ADDRESS=${PROMETHEUS_NETWORK_ADDRESS}
|
||||
|
||||
|
||||
# Address or hostname for the Grafana network.
|
||||
# Default: GRAFANA_NETWORK_ADDRESS=172.28.0.12
|
||||
GRAFANA_NETWORK_ADDRESS=${GRAFANA_NETWORK_ADDRESS}
|
||||
|
||||
|
||||
|
||||
# Address or hostname for the node_exporter network.
|
||||
# Default: NODE_EXPORTER_NETWORK_ADDRESS=172.28.0.13
|
||||
NODE_EXPORTER_NETWORK_ADDRESS=${NODE_EXPORTER_NETWORK_ADDRESS}
|
||||
|
||||
# Address or hostname for the OpenIM admin network.
|
||||
# Default: OPENIM_ADMIN_NETWORK_ADDRESS=172.28.0.14
|
||||
OPENIM_ADMIN_FRONT_NETWORK_ADDRESS=${OPENIM_ADMIN_FRONT_NETWORK_ADDRESS}
|
||||
|
||||
# Address or hostname for the alertmanager network.
|
||||
# Default: ALERT_MANAGER_NETWORK_ADDRESS=172.28.0.15
|
||||
ALERT_MANAGER_NETWORK_ADDRESS=${ALERT_MANAGER_NETWORK_ADDRESS}
|
||||
# ===============================================
|
||||
# = Component Extension Configuration =
|
||||
# ===============================================
|
||||
@@ -215,7 +225,7 @@ PROMETHEUS_PORT=${PROMETHEUS_PORT}
|
||||
GRAFANA_ADDRESS=${GRAFANA_NETWORK_ADDRESS}
|
||||
|
||||
# Port on which Grafana service is running.
|
||||
# Default: GRAFANA_PORT=3000
|
||||
# Default: GRAFANA_PORT=13000
|
||||
GRAFANA_PORT=${GRAFANA_PORT}
|
||||
|
||||
# ======================================
|
||||
@@ -283,3 +293,23 @@ SERVER_BRANCH=${SERVER_BRANCH}
|
||||
# Port for the OpenIM admin API.
|
||||
# Default: OPENIM_ADMIN_API_PORT=10009
|
||||
OPENIM_ADMIN_API_PORT=${OPENIM_ADMIN_API_PORT}
|
||||
|
||||
# Port for the node exporter.
|
||||
# Default: NODE_EXPORTER_PORT=19100
|
||||
NODE_EXPORTER_PORT=${NODE_EXPORTER_PORT}
|
||||
|
||||
# Port for the prometheus.
|
||||
# Default: PROMETHEUS_PORT=19090
|
||||
PROMETHEUS_PORT=${PROMETHEUS_PORT}
|
||||
|
||||
# Port for the grafana.
|
||||
# Default: GRAFANA_PORT=13000
|
||||
GRAFANA_PORT=${GRAFANA_PORT}
|
||||
|
||||
# Port for the admin front.
|
||||
# Default: OPENIM_ADMIN_FRONT_PORT=11002
|
||||
OPENIM_ADMIN_FRONT_PORT=${OPENIM_ADMIN_FRONT_PORT}
|
||||
|
||||
# Port for the alertmanager.
|
||||
# Default: ALERT_MANAGER_PORT=19093
|
||||
ALERT_MANAGER_PORT=${ALERT_MANAGER_PORT}
|
||||
85
deployments/templates/prometheus.yml
Normal file
85
deployments/templates/prometheus.yml
Normal file
@@ -0,0 +1,85 @@
|
||||
# my global config
|
||||
global:
|
||||
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
|
||||
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
|
||||
# scrape_timeout is set to the global default (10s).
|
||||
|
||||
# Alertmanager configuration
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets: ['${ALERT_MANAGER_ADDRESS}:${ALERT_MANAGER_PORT}']
|
||||
|
||||
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
|
||||
rule_files:
|
||||
- "instance-down-rules.yml"
|
||||
# - "first_rules.yml"
|
||||
# - "second_rules.yml"
|
||||
|
||||
# A scrape configuration containing exactly one endpoint to scrape:
|
||||
# Here it's Prometheus itself.
|
||||
scrape_configs:
|
||||
# The job name is added as a label "job='job_name'"" to any timeseries scraped from this config.
|
||||
# Monitored information captured by prometheus
|
||||
- job_name: 'node-exporter'
|
||||
static_configs:
|
||||
- targets: [ '${NODE_EXPORTER_ADDRESS}:${NODE_EXPORTER_PORT}' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
|
||||
# prometheus fetches application services
|
||||
- job_name: 'openimserver-openim-api'
|
||||
static_configs:
|
||||
- targets: [ '${OPENIM_SERVER_ADDRESS}:${API_PROM_PORT}' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-msggateway'
|
||||
static_configs:
|
||||
- targets: [ '${OPENIM_SERVER_ADDRESS}:${MSG_GATEWAY_PROM_PORT}' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-msgtransfer'
|
||||
static_configs:
|
||||
- targets: [ ${MSG_TRANSFER_PROM_ADDRESS_PORT} ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-push'
|
||||
static_configs:
|
||||
- targets: [ '${OPENIM_SERVER_ADDRESS}:${PUSH_PROM_PORT}' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-auth'
|
||||
static_configs:
|
||||
- targets: [ '${OPENIM_SERVER_ADDRESS}:${AUTH_PROM_PORT}' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-conversation'
|
||||
static_configs:
|
||||
- targets: [ '${OPENIM_SERVER_ADDRESS}:${CONVERSATION_PROM_PORT}' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-friend'
|
||||
static_configs:
|
||||
- targets: [ '${OPENIM_SERVER_ADDRESS}:${FRIEND_PROM_PORT}' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-group'
|
||||
static_configs:
|
||||
- targets: [ '${OPENIM_SERVER_ADDRESS}:${GROUP_PROM_PORT}' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-msg'
|
||||
static_configs:
|
||||
- targets: [ '${OPENIM_SERVER_ADDRESS}:${MESSAGE_PROM_PORT}' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-third'
|
||||
static_configs:
|
||||
- targets: [ '${OPENIM_SERVER_ADDRESS}:${THIRD_PROM_PORT}' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
- job_name: 'openimserver-openim-rpc-user'
|
||||
static_configs:
|
||||
- targets: [ '${OPENIM_SERVER_ADDRESS}:${USER_PROM_PORT}' ]
|
||||
labels:
|
||||
namespace: 'default'
|
||||
@@ -67,17 +67,17 @@ services:
|
||||
ipv4_address: ${REDIS_NETWORK_ADDRESS}
|
||||
|
||||
zookeeper:
|
||||
image: bitnami/zookeeper:3.8
|
||||
container_name: zookeeper
|
||||
ports:
|
||||
- "${ZOOKEEPER_PORT}:2181"
|
||||
volumes:
|
||||
- "/etc/localtime:/etc/localtime"
|
||||
environment:
|
||||
- ALLOW_ANONYMOUS_LOGIN=yes
|
||||
- TZ="Asia/Shanghai"
|
||||
restart: always
|
||||
networks:
|
||||
image: bitnami/zookeeper:3.8
|
||||
container_name: zookeeper
|
||||
ports:
|
||||
- "${ZOOKEEPER_PORT}:2181"
|
||||
volumes:
|
||||
- "/etc/localtime:/etc/localtime"
|
||||
environment:
|
||||
- ALLOW_ANONYMOUS_LOGIN=yes
|
||||
- TZ="Asia/Shanghai"
|
||||
restart: always
|
||||
networks:
|
||||
server:
|
||||
ipv4_address: ${ZOOKEEPER_NETWORK_ADDRESS}
|
||||
|
||||
@@ -142,3 +142,68 @@ services:
|
||||
server:
|
||||
ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS}
|
||||
|
||||
openim-admin:
|
||||
image: ${IMAGE_REGISTRY}/openim-admin-front:v3.4.0
|
||||
# image: ghcr.io/openimsdk/openim-admin-front:v3.4.0
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-admin-front:v3.4.0
|
||||
# image: openim/openim-admin-front:v3.4.0
|
||||
container_name: openim-admin
|
||||
restart: always
|
||||
ports:
|
||||
- "${OPENIM_ADMIN_FRONT_PORT}:80"
|
||||
networks:
|
||||
server:
|
||||
ipv4_address: ${OPENIM_ADMIN_FRONT_NETWORK_ADDRESS}
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus
|
||||
container_name: prometheus
|
||||
hostname: prometheus
|
||||
restart: always
|
||||
volumes:
|
||||
- ./config/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- ./config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml
|
||||
ports:
|
||||
- "${PROMETHEUS_PORT}:9090"
|
||||
networks:
|
||||
server:
|
||||
ipv4_address: ${PROMETHEUS_NETWORK_ADDRESS}
|
||||
|
||||
alertmanager:
|
||||
image: prom/alertmanager
|
||||
container_name: alertmanager
|
||||
hostname: alertmanager
|
||||
restart: always
|
||||
volumes:
|
||||
- ./config/alertmanager.yml:/etc/alertmanager/alertmanager.yml
|
||||
- ./config/email.tmpl:/etc/alertmanager/email.tmpl
|
||||
ports:
|
||||
- "${ALERT_MANAGER_PORT}:9093"
|
||||
networks:
|
||||
server:
|
||||
ipv4_address: ${ALERT_MANAGER_NETWORK_ADDRESS}
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana
|
||||
container_name: grafana
|
||||
hostname: grafana
|
||||
user: root
|
||||
restart: always
|
||||
ports:
|
||||
- "${GRAFANA_PORT}:3000"
|
||||
volumes:
|
||||
- ${DATA_DIR}/components/grafana:/var/lib/grafana
|
||||
networks:
|
||||
server:
|
||||
ipv4_address: ${GRAFANA_NETWORK_ADDRESS}
|
||||
|
||||
node-exporter:
|
||||
image: quay.io/prometheus/node-exporter
|
||||
container_name: node-exporter
|
||||
hostname: node-exporter
|
||||
restart: always
|
||||
ports:
|
||||
- "${NODE_EXPORTER_PORT}:9100"
|
||||
networks:
|
||||
server:
|
||||
ipv4_address: ${NODE_EXPORTER_NETWORK_ADDRESS}
|
||||
|
||||
@@ -150,7 +150,7 @@ For convenience, configuration through modifying environment variables is recomm
|
||||
+ **Description**: API address.
|
||||
+ **Note**: If the server has an external IP, it will be automatically obtained. For internal networks, set this variable to the IP serving internally.
|
||||
|
||||
```
|
||||
```bash
|
||||
export API_URL="http://ip:10002"
|
||||
```
|
||||
|
||||
@@ -412,7 +412,7 @@ Configuration for Grafana, including its port and address.
|
||||
|
||||
| Parameter | Example Value | Description |
|
||||
| --------------- | -------------------------- | --------------------- |
|
||||
| GRAFANA_PORT | "3000" | Port used by Grafana. |
|
||||
| GRAFANA_PORT | "13000" | Port used by Grafana. |
|
||||
| GRAFANA_ADDRESS | "${DOCKER_BRIDGE_GATEWAY}" | Address for Grafana. |
|
||||
|
||||
### 2.16. <a name='RPCPortConfigurationVariables'></a>RPC Port Configuration Variables
|
||||
|
||||
323
docs/contrib/prometheus-grafana.md
Normal file
323
docs/contrib/prometheus-grafana.md
Normal file
@@ -0,0 +1,323 @@
|
||||
# Deployment and Design of OpenIM's Management Backend and Monitoring
|
||||
|
||||
<!-- vscode-markdown-toc -->
|
||||
* 1. [Source Code & Docker](#SourceCodeDocker)
|
||||
* 1.1. [Deployment](#Deployment)
|
||||
* 1.2. [Configuration](#Configuration)
|
||||
* 1.3. [Monitoring Running in Docker Guide](#MonitoringRunninginDockerGuide)
|
||||
* 1.3.1. [Introduction](#Introduction)
|
||||
* 1.3.2. [Prerequisites](#Prerequisites)
|
||||
* 1.3.3. [Step 1: Clone the Repository](#Step1:ClonetheRepository)
|
||||
* 1.3.4. [Step 2: Start Docker Compose](#Step2:StartDockerCompose)
|
||||
* 1.3.5. [Step 3: Use the OpenIM Web Interface](#Step3:UsetheOpenIMWebInterface)
|
||||
* 1.3.6. [Running Effect](#RunningEffect)
|
||||
* 1.3.7. [Step 4: Access the Admin Panel](#Step4:AccesstheAdminPanel)
|
||||
* 1.3.8. [Step 5: Access the Monitoring Interface](#Step5:AccesstheMonitoringInterface)
|
||||
* 1.3.9. [Next Steps](#NextSteps)
|
||||
* 1.3.10. [Troubleshooting](#Troubleshooting)
|
||||
* 2. [Kubernetes](#Kubernetes)
|
||||
* 2.1. [Middleware Monitoring](#MiddlewareMonitoring)
|
||||
* 2.2. [Custom OpenIM Metrics](#CustomOpenIMMetrics)
|
||||
* 2.3. [Node Exporter](#NodeExporter)
|
||||
* 3. [Setting Up and Configuring AlertManager Using Environment Variables and `make init`](#SettingUpandConfiguringAlertManagerUsingEnvironmentVariablesandmakeinit)
|
||||
* 3.1. [Introduction](#Introduction-1)
|
||||
* 3.2. [Prerequisites](#Prerequisites-1)
|
||||
* 3.3. [Configuration Steps](#ConfigurationSteps)
|
||||
* 3.3.1. [Exporting Environment Variables](#ExportingEnvironmentVariables)
|
||||
* 3.3.2. [Initializing AlertManager](#InitializingAlertManager)
|
||||
* 3.3.3. [Key Configuration Fields](#KeyConfigurationFields)
|
||||
* 3.3.4. [Configuring SMTP Authentication Password](#ConfiguringSMTPAuthenticationPassword)
|
||||
* 3.3.5. [Useful Links for Common Email Servers](#UsefulLinksforCommonEmailServers)
|
||||
* 3.4. [Conclusion](#Conclusion)
|
||||
|
||||
<!-- vscode-markdown-toc-config
|
||||
numbering=true
|
||||
autoSave=true
|
||||
/vscode-markdown-toc-config -->
|
||||
<!-- /vscode-markdown-toc -->
|
||||
|
||||
OpenIM offers various flexible deployment options to suit different environments and requirements. Here is a simplified and optimized description of these deployment options:
|
||||
|
||||
1. Source Code Deployment:
|
||||
+ **Regular Source Code Deployment**: Deployment using the `nohup` method. This is a basic deployment method suitable for development and testing environments. For details, refer to the [Regular Source Code Deployment Guide](https://docs.openim.io/).
|
||||
+ **Production-Level Deployment**: Deployment using the `system` method, more suitable for production environments. This method provides higher stability and reliability. For details, refer to the [Production-Level Deployment Guide](https://docs.openim.io/guides/gettingStarted/install-openim-linux-system).
|
||||
2. Cluster Deployment:
|
||||
+ **Kubernetes Deployment**: Provides two deployment methods, including deployment through Helm and sealos. This is suitable for environments that require high availability and scalability. Specific methods can be found in the [Kubernetes Deployment Guide](https://docs.openim.io/guides/gettingStarted/k8s-deployment).
|
||||
3. Docker Deployment:
|
||||
+ **Regular Docker Deployment**: Suitable for quick deployments and small projects. For detailed information, refer to the [Docker Deployment Guide](https://docs.openim.io/guides/gettingStarted/dockerCompose).
|
||||
+ **Docker Compose Deployment**: Provides more convenient service management and configuration, suitable for complex multi-container applications.
|
||||
|
||||
Next, we will introduce the specific steps, monitoring, and management backend configuration for each of these deployment methods, as well as usage tips to help you choose the most suitable deployment option according to your needs.
|
||||
|
||||
## 1. <a name='SourceCodeDocker'></a>Source Code & Docker
|
||||
|
||||
### 1.1. <a name='Deployment'></a>Deployment
|
||||
|
||||
OpenIM deploys openim-server and openim-chat from source code, while other components are deployed via Docker.
|
||||
|
||||
For Docker deployment, you can deploy all components with a single command using the [openimsdk/openim-docker](https://github.com/openimsdk/openim-docker) repository. The deployment configuration can be found in the [environment.sh](https://github.com/openimsdk/open-im-server/blob/main/scripts/install/environment.sh) document, which provides information on how to learn and familiarize yourself with various environment variables.
|
||||
|
||||
For Prometheus, it is not enabled by default. To enable it, set the environment variable before executing `make init`:
|
||||
|
||||
```bash
|
||||
export PROMETHEUS_ENABLE=true # Default is false
|
||||
```
|
||||
|
||||
Then, execute:
|
||||
|
||||
```bash
|
||||
make init
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 1.2. <a name='Configuration'></a>Configuration
|
||||
|
||||
To configure Prometheus data sources in Grafana, follow these steps:
|
||||
|
||||
1. **Log in to Grafana**: First, open your web browser and access the Grafana URL. If you haven't changed the port, the address is typically [http://localhost:13000](http://localhost:13000/).
|
||||
|
||||
2. **Log in with default credentials**: Grafana's default username and password are both `admin`. You will be prompted to change the password on your first login.
|
||||
|
||||
3. **Access Data Sources Settings**:
|
||||
|
||||
+ In the left menu of Grafana, look for and click the "gear" icon representing "Configuration."
|
||||
+ In the configuration menu, select "Data Sources."
|
||||
|
||||
4. **Add a New Data Source**:
|
||||
|
||||
+ On the Data Sources page, click the "Add data source" button.
|
||||
+ In the list, find and select "Prometheus."
|
||||
|
||||

|
||||
|
||||
Click `Add New connection` to add more data sources, such as Loki (responsible for log storage and query processing).
|
||||
|
||||
5. **Configure the Prometheus Data Source**:
|
||||
|
||||
+ On the configuration page, fill in the details of the Prometheus server. This typically includes the URL of the Prometheus service (e.g., if Prometheus is running on the same machine as OpenIM, the URL might be `http://172.28.0.1:19090`, with the address matching the `DOCKER_BRIDGE_GATEWAY` variable address). OpenIM and the components are linked via a gateway. The default port used by OpenIM is `19090`.
|
||||
+ Adjust other settings as needed, such as authentication and TLS settings.
|
||||
|
||||

|
||||
|
||||
6. **Save and Test**:
|
||||
|
||||
+ After completing the configuration, click the "Save & Test" button to ensure that Grafana can successfully connect to Prometheus.
|
||||
|
||||
**Importing Dashboards in Grafana**
|
||||
|
||||
Importing Grafana Dashboards is a straightforward process and is applicable to OpenIM Server application services and Node Exporter. Here are detailed steps and necessary considerations:
|
||||
|
||||
**Key Metrics Overview and Deployment Steps**
|
||||
|
||||
To monitor OpenIM in Grafana, you need to focus on three categories of key metrics, each with its specific deployment and configuration steps:
|
||||
|
||||
1. **OpenIM Metrics (`prometheus-dashboard.yaml`)**:
|
||||
+ **Configuration File Path**: Located at `config/prometheus-dashboard.yaml`.
|
||||
+ **Enabling Monitoring**: Set the environment variable `export PROMETHEUS_ENABLE=true` to enable Prometheus monitoring.
|
||||
+ **More Information**: Refer to the [OpenIM Configuration Guide](https://docs.openim.io/configurations/prometheus-integration).
|
||||
2. **Node Exporter**:
|
||||
+ **Container Deployment**: Deploy the `quay.io/prometheus/node-exporter` container for node monitoring.
|
||||
+ **Get Dashboard**: Access the [Node Exporter Full Feature Dashboard](https://grafana.com/grafana/dashboards/1860-node-exporter-full/) and import it using YAML file download or ID import.
|
||||
+ **Deployment Guide**: Refer to the [Node Exporter Deployment Documentation](https://prometheus.io/docs/guides/node-exporter/).
|
||||
3. **Middleware Metrics**: Each middleware requires specific steps and configurations to enable monitoring. Here is a list of common middleware and links to their respective setup guides:
|
||||
+ MySQL:
|
||||
+ **Configuration**: Ensure MySQL has performance monitoring enabled.
|
||||
+ **Link**: Refer to the [MySQL Monitoring Configuration Guide](https://grafana.com/docs/grafana/latest/datasources/mysql/).
|
||||
+ Redis:
|
||||
+ **Configuration**: Configure Redis to allow monitoring data export.
|
||||
+ **Link**: Refer to the [Redis Monitoring Guide](https://grafana.com/docs/grafana/latest/datasources/redis/).
|
||||
+ MongoDB:
|
||||
+ **Configuration**: Set up monitoring metrics for MongoDB.
|
||||
+ **Link**: Refer to the [MongoDB Monitoring Guide](https://grafana.com/grafana/plugins/grafana-mongodb-datasource/).
|
||||
+ Kafka:
|
||||
+ **Configuration**: Integrate Kafka with Prometheus monitoring.
|
||||
+ **Link**: Refer to the [Kafka Monitoring Guide](https://grafana.com/grafana/plugins/grafana-kafka-datasource/).
|
||||
+ Zookeeper:
|
||||
+ **Configuration**: Ensure Zookeeper can be monitored by Prometheus.
|
||||
+ **Link**: Refer to the [Zookeeper Monitoring Configuration](https://grafana.com/docs/grafana/latest/datasources/zookeeper/).
|
||||
|
||||
|
||||
|
||||
**Importing Steps**:
|
||||
|
||||
1. Access the Dashboard Import Interface:
|
||||
|
||||
+ Click the `+` icon on the left menu or in the top right corner of Grafana, then select "Create."
|
||||
+ Choose "Import" to access the dashboard import interface.
|
||||
|
||||
2. **Perform Dashboard Import**:
|
||||
+ **Upload via File**: Directly upload your YAML file.
|
||||
+ **Paste Content**: Open the YAML file, copy its content, and paste it into the import interface.
|
||||
+ **Import via Grafana.com Dashboard**: Visit [Grafana Dashboards](https://grafana.com/grafana/dashboards/), search for the desired dashboard, and import it using its ID.
|
||||
3. **Configure the Dashboard**:
|
||||
+ Select the appropriate data source, such as the previously configured Prometheus.
|
||||
+ Adjust other settings, such as the dashboard name or folder.
|
||||
4. **Save and View the Dashboard**:
|
||||
+ After configuring, click "Import" to complete the process.
|
||||
+ Immediately view the new dashboard after successful import.
|
||||
|
||||
**Graph Examples:**
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 1.3. <a name='MonitoringRunninginDockerGuide'></a>Monitoring Running in Docker Guide
|
||||
|
||||
#### 1.3.1. <a name='Introduction'></a>Introduction
|
||||
|
||||
This guide provides the steps to run OpenIM using Docker. OpenIM is an open-source instant messaging solution that can be quickly deployed using Docker. For more information, please refer to the [OpenIM Docker GitHub](https://github.com/openimsdk/openim-docker).
|
||||
|
||||
#### 1.3.2. <a name='Prerequisites'></a>Prerequisites
|
||||
|
||||
+ Ensure that Docker and Docker Compose are installed.
|
||||
+ Basic understanding of Docker and containerization technology.
|
||||
|
||||
#### 1.3.3. <a name='Step1:ClonetheRepository'></a>Step 1: Clone the Repository
|
||||
|
||||
First, clone the OpenIM Docker repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/openimsdk/openim-docker.git
|
||||
```
|
||||
|
||||
Navigate to the repository directory and check the `README` file for more information and configuration options.
|
||||
|
||||
#### 1.3.4. <a name='Step2:StartDockerCompose'></a>Step 2: Start Docker Compose
|
||||
|
||||
In the repository directory, run the following command to start the service:
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
This will download the required Docker images and start the OpenIM service.
|
||||
|
||||
#### 1.3.5. <a name='Step3:UsetheOpenIMWebInterface'></a>Step 3: Use the OpenIM Web Interface
|
||||
|
||||
+ Open a browser in private mode and access [OpenIM Web](http://localhost:11001/).
|
||||
+ Register two users and try adding friends.
|
||||
+ Test sending messages and pictures.
|
||||
|
||||
#### 1.3.6. <a name='RunningEffect'></a>Running Effect
|
||||
|
||||

|
||||
|
||||
#### 1.3.7. <a name='Step4:AccesstheAdminPanel'></a>Step 4: Access the Admin Panel
|
||||
|
||||
+ Access the [OpenIM Admin Panel](http://localhost:11002/).
|
||||
+ Log in using the default username and password (`admin1:admin1`).
|
||||
|
||||
Running Effect Image:
|
||||
|
||||

|
||||
|
||||
#### 1.3.8. <a name='Step5:AccesstheMonitoringInterface'></a>Step 5: Access the Monitoring Interface
|
||||
|
||||
+ Log in to the [Monitoring Interface](http://localhost:3000/login) using the credentials (`admin:admin`).
|
||||
|
||||
#### 1.3.9. <a name='NextSteps'></a>Next Steps
|
||||
|
||||
+ Configure and manage the services following the steps provided in the OpenIM source code.
|
||||
+ Refer to the `README` file for advanced configuration and management.
|
||||
|
||||
#### 1.3.10. <a name='Troubleshooting'></a>Troubleshooting
|
||||
|
||||
+ If you encounter any issues, please check the documentation on [OpenIM Docker GitHub](https://github.com/openimsdk/openim-docker) or search for related issues in the Issues section.
|
||||
+ If the problem persists, you can create an issue on the [openim-docker](https://github.com/openimsdk/openim-docker/issues/new/choose) repository or the [openim-server](https://github.com/openimsdk/open-im-server/issues/new/choose) repository.
|
||||
|
||||
|
||||
|
||||
## 2. <a name='Kubernetes'></a>Kubernetes
|
||||
|
||||
Refer to [openimsdk/helm-charts](https://github.com/openimsdk/helm-charts).
|
||||
|
||||
When deploying and monitoring OpenIM in a Kubernetes environment, you will focus on three main metrics: middleware, custom OpenIM metrics, and Node Exporter. Here are detailed steps and guidelines:
|
||||
|
||||
### 2.1. <a name='MiddlewareMonitoring'></a>Middleware Monitoring
|
||||
|
||||
Middleware monitoring is crucial to ensure the overall system's stability. Typically, this includes monitoring the following components:
|
||||
|
||||
+ **MySQL**: Monitor database performance, query latency, and more.
|
||||
+ **Redis**: Track operation latency, memory usage, and more.
|
||||
+ **MongoDB**: Observe database operations, resource usage, and more.
|
||||
+ **Kafka**: Monitor message throughput, latency, and more.
|
||||
+ **Zookeeper**: Keep an eye on cluster status, performance metrics, and more.
|
||||
|
||||
For Kubernetes environments, you can use the corresponding Prometheus Exporters to collect monitoring data for these middleware components.
|
||||
|
||||
### 2.2. <a name='CustomOpenIMMetrics'></a>Custom OpenIM Metrics
|
||||
|
||||
Custom OpenIM metrics provide essential information about the OpenIM application itself, such as user activity, message traffic, system performance, and more. To monitor these metrics in Kubernetes:
|
||||
|
||||
+ Ensure OpenIM application configurations expose Prometheus metrics.
|
||||
+ When deploying using Helm charts (refer to [OpenIM Helm Charts](https://github.com/openimsdk/helm-charts)), pay attention to configuring relevant monitoring settings.
|
||||
|
||||
### 2.3. <a name='NodeExporter'></a>Node Exporter
|
||||
|
||||
Node Exporter is used to collect hardware and operating system-level metrics for Kubernetes nodes, such as CPU, memory, disk usage, and more. To integrate Node Exporter in Kubernetes:
|
||||
|
||||
+ Deploy Node Exporter using the appropriate Helm chart. You can find information and guides on [Prometheus Community](https://prometheus.io/docs/guides/node-exporter/).
|
||||
+ Ensure Node Exporter's data is collected by Prometheus instances within your cluster.
|
||||
|
||||
|
||||
|
||||
## 3. <a name='SettingUpandConfiguringAlertManagerUsingEnvironmentVariablesandmakeinit'></a>Setting Up and Configuring AlertManager Using Environment Variables and `make init`
|
||||
|
||||
### 3.1. <a name='Introduction-1'></a>Introduction
|
||||
|
||||
AlertManager, a component of the Prometheus monitoring system, handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver. This document outlines how to set up and configure AlertManager using environment variables and the `make init` command. We will focus on configuring key fields like the sender's email, SMTP settings, and SMTP authentication password.
|
||||
|
||||
### 3.2. <a name='Prerequisites-1'></a>Prerequisites
|
||||
|
||||
+ Basic knowledge of terminal and command-line operations.
|
||||
+ AlertManager installed on your system.
|
||||
+ Access to an SMTP server for sending emails.
|
||||
|
||||
### 3.3. <a name='ConfigurationSteps'></a>Configuration Steps
|
||||
|
||||
#### 3.3.1. <a name='ExportingEnvironmentVariables'></a>Exporting Environment Variables
|
||||
|
||||
Before initializing AlertManager, you need to set environment variables. These variables are used to configure the AlertManager settings without altering the code. Use the `export` command in your terminal. Here are some key variables you might set:
|
||||
|
||||
+ `export ALERTMANAGER_RESOLVE_TIMEOUT='5m'`
|
||||
+ `export ALERTMANAGER_SMTP_FROM='alert@example.com'`
|
||||
+ `export ALERTMANAGER_SMTP_SMARTHOST='smtp.example.com:465'`
|
||||
+ `export ALERTMANAGER_SMTP_AUTH_USERNAME='alert@example.com'`
|
||||
+ `export ALERTMANAGER_SMTP_AUTH_PASSWORD='your_password'`
|
||||
+ `export ALERTMANAGER_SMTP_REQUIRE_TLS='false'`
|
||||
|
||||
#### 3.3.2. <a name='InitializingAlertManager'></a>Initializing AlertManager
|
||||
|
||||
After setting the necessary environment variables, you can initialize AlertManager by running the `make init` command. This command typically runs a script that prepares AlertManager with the provided configuration.
|
||||
|
||||
#### 3.3.3. <a name='KeyConfigurationFields'></a>Key Configuration Fields
|
||||
|
||||
##### a. Sender's Email (`ALERTMANAGER_SMTP_FROM`)
|
||||
|
||||
This variable sets the email address that will appear as the sender in the notifications sent by AlertManager.
|
||||
|
||||
##### b. SMTP Configuration
|
||||
|
||||
+ **SMTP Server (`ALERTMANAGER_SMTP_SMARTHOST`):** Specifies the address and port of the SMTP server used for sending emails.
|
||||
+ **SMTP Authentication Username (`ALERTMANAGER_SMTP_AUTH_USERNAME`):** The username for authenticating with the SMTP server.
|
||||
+ **SMTP Authentication Password (`ALERTMANAGER_SMTP_AUTH_PASSWORD`):** The password for SMTP server authentication. It's crucial to keep this value secure.
|
||||
|
||||
#### 3.3.4. <a name='ConfiguringSMTPAuthenticationPassword'></a>Configuring SMTP Authentication Password
|
||||
|
||||
The SMTP authentication password can be set using the `ALERTMANAGER_SMTP_AUTH_PASSWORD` environment variable. It's recommended to use a secure method to set this variable to avoid exposing sensitive information. For instance, you might read the password from a secure file or a secret management tool.
|
||||
|
||||
#### 3.3.5. <a name='UsefulLinksforCommonEmailServers'></a>Useful Links for Common Email Servers
|
||||
|
||||
For specific configurations related to common email servers, you may refer to their respective documentation:
|
||||
|
||||
+ Gmail SMTP Settings:
|
||||
+ [Gmail SMTP Configuration](https://support.google.com/mail/answer/7126229?hl=en)
|
||||
+ Microsoft Outlook SMTP Settings:
|
||||
+ [Outlook Email Settings](https://support.microsoft.com/en-us/office/pop-imap-and-smtp-settings-8361e398-8af4-4e97-b147-6c6c4ac95353)
|
||||
+ Yahoo Mail SMTP Settings:
|
||||
+ [Yahoo SMTP Configuration](https://help.yahoo.com/kb/SLN4724.html)
|
||||
|
||||
### 3.4. <a name='Conclusion'></a>Conclusion
|
||||
|
||||
Setting up and configuring AlertManager with environment variables provides a flexible and secure way to manage alert settings. By following the above steps, you can easily configure AlertManager for your monitoring needs. Always ensure to secure sensitive information, especially when dealing with SMTP authentication credentials.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 118 KiB |
2
go.mod
2
go.mod
@@ -45,6 +45,7 @@ require (
|
||||
github.com/redis/go-redis/v9 v9.2.1
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.45
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
golang.org/x/sync v0.4.0
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gotest.tools v2.2.0+incompatible
|
||||
)
|
||||
@@ -132,7 +133,6 @@ require (
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.13.0 // indirect
|
||||
golang.org/x/sync v0.4.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
|
||||
@@ -167,7 +167,7 @@ func (c *Client) readMessage() {
|
||||
func (c *Client) handleMessage(message []byte) error {
|
||||
if c.IsCompress {
|
||||
var err error
|
||||
message, err = c.longConnServer.DeCompress(message)
|
||||
message, err = c.longConnServer.DecompressWithPool(message)
|
||||
if err != nil {
|
||||
return utils.Wrap(err, "")
|
||||
}
|
||||
@@ -317,7 +317,7 @@ func (c *Client) writeBinaryMsg(resp Resp) error {
|
||||
|
||||
_ = c.conn.SetWriteDeadline(writeWait)
|
||||
if c.IsCompress {
|
||||
resultBuf, compressErr := c.longConnServer.Compress(encodedBuf)
|
||||
resultBuf, compressErr := c.longConnServer.CompressWithPool(encodedBuf)
|
||||
if compressErr != nil {
|
||||
return utils.Wrap(compressErr, "")
|
||||
}
|
||||
|
||||
@@ -17,14 +17,23 @@ package msggateway
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
gzipWriterPool = sync.Pool{New: func() any { return gzip.NewWriter(nil) }}
|
||||
gzipReaderPool = sync.Pool{New: func() any { return new(gzip.Reader) }}
|
||||
)
|
||||
|
||||
type Compressor interface {
|
||||
Compress(rawData []byte) ([]byte, error)
|
||||
CompressWithPool(rawData []byte) ([]byte, error)
|
||||
DeCompress(compressedData []byte) ([]byte, error)
|
||||
DecompressWithPool(compressedData []byte) ([]byte, error)
|
||||
}
|
||||
type GzipCompressor struct {
|
||||
compressProtocol string
|
||||
@@ -46,6 +55,22 @@ func (g *GzipCompressor) Compress(rawData []byte) ([]byte, error) {
|
||||
return gzipBuffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (g *GzipCompressor) CompressWithPool(rawData []byte) ([]byte, error) {
|
||||
gz := gzipWriterPool.Get().(*gzip.Writer)
|
||||
defer gzipWriterPool.Put(gz)
|
||||
|
||||
gzipBuffer := bytes.Buffer{}
|
||||
gz.Reset(&gzipBuffer)
|
||||
|
||||
if _, err := gz.Write(rawData); err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
}
|
||||
if err := gz.Close(); err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
}
|
||||
return gzipBuffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) {
|
||||
buff := bytes.NewBuffer(compressedData)
|
||||
reader, err := gzip.NewReader(buff)
|
||||
@@ -59,3 +84,23 @@ func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) {
|
||||
_ = reader.Close()
|
||||
return compressedData, nil
|
||||
}
|
||||
|
||||
func (g *GzipCompressor) DecompressWithPool(compressedData []byte) ([]byte, error) {
|
||||
reader := gzipReaderPool.Get().(*gzip.Reader)
|
||||
if reader == nil {
|
||||
return nil, errors.New("NewReader failed")
|
||||
}
|
||||
defer gzipReaderPool.Put(reader)
|
||||
|
||||
err := reader.Reset(bytes.NewReader(compressedData))
|
||||
if err != nil {
|
||||
return nil, utils.Wrap(err, "NewReader failed")
|
||||
}
|
||||
|
||||
compressedData, err = io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, utils.Wrap(err, "ReadAll failed")
|
||||
}
|
||||
_ = reader.Close()
|
||||
return compressedData, nil
|
||||
}
|
||||
|
||||
107
internal/msggateway/compressor_test.go
Normal file
107
internal/msggateway/compressor_test.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package msggateway
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func mockRandom() []byte {
|
||||
bs := make([]byte, 50)
|
||||
rand.Read(bs)
|
||||
return bs
|
||||
}
|
||||
|
||||
func TestCompressDecompress(t *testing.T) {
|
||||
|
||||
compressor := NewGzipCompressor()
|
||||
|
||||
for i := 0; i < 2000; i++ {
|
||||
src := mockRandom()
|
||||
|
||||
// compress
|
||||
dest, err := compressor.CompressWithPool(src)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
// decompress
|
||||
res, err := compressor.DecompressWithPool(dest)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
// check
|
||||
assert.EqualValues(t, src, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressDecompressWithConcurrency(t *testing.T) {
|
||||
wg := sync.WaitGroup{}
|
||||
compressor := NewGzipCompressor()
|
||||
|
||||
for i := 0; i < 200; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
src := mockRandom()
|
||||
|
||||
// compress
|
||||
dest, err := compressor.CompressWithPool(src)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
// decompress
|
||||
res, err := compressor.DecompressWithPool(dest)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
// check
|
||||
assert.EqualValues(t, src, res)
|
||||
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkCompress(b *testing.B) {
|
||||
src := mockRandom()
|
||||
compressor := NewGzipCompressor()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := compressor.Compress(src)
|
||||
assert.Equal(b, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompressWithSyncPool(b *testing.B) {
|
||||
src := mockRandom()
|
||||
|
||||
compressor := NewGzipCompressor()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := compressor.CompressWithPool(src)
|
||||
assert.Equal(b, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecompress(b *testing.B) {
|
||||
src := mockRandom()
|
||||
|
||||
compressor := NewGzipCompressor()
|
||||
comdata, err := compressor.Compress(src)
|
||||
assert.Equal(b, nil, err)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := compressor.DeCompress(comdata)
|
||||
assert.Equal(b, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecompressWithSyncPool(b *testing.B) {
|
||||
src := mockRandom()
|
||||
|
||||
compressor := NewGzipCompressor()
|
||||
comdata, err := compressor.Compress(src)
|
||||
assert.Equal(b, nil, err)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := compressor.DecompressWithPool(comdata)
|
||||
assert.Equal(b, nil, err)
|
||||
}
|
||||
}
|
||||
@@ -17,22 +17,19 @@ package msggateway
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
|
||||
)
|
||||
|
||||
@@ -41,6 +38,7 @@ func (s *Server) InitServer(disCov discoveryregistry.SvcDiscoveryRegistry, serve
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgModel := cache.NewMsgCacheModel(rdb)
|
||||
s.LongConnServer.SetDiscoveryRegistry(disCov)
|
||||
s.LongConnServer.SetCacheHandler(msgModel)
|
||||
@@ -97,22 +95,25 @@ func (s *Server) GetUsersOnlineStatus(
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
temp := new(msggateway.GetUsersOnlineStatusResp_SuccessResult)
|
||||
temp.UserID = userID
|
||||
|
||||
uresp := new(msggateway.GetUsersOnlineStatusResp_SuccessResult)
|
||||
uresp.UserID = userID
|
||||
for _, client := range clients {
|
||||
if client != nil {
|
||||
ps := new(msggateway.GetUsersOnlineStatusResp_SuccessDetail)
|
||||
ps.Platform = constant.PlatformIDToName(client.PlatformID)
|
||||
ps.Status = constant.OnlineStatus
|
||||
ps.ConnID = client.ctx.GetConnID()
|
||||
ps.Token = client.token
|
||||
ps.IsBackground = client.IsBackground
|
||||
temp.Status = constant.OnlineStatus
|
||||
temp.DetailPlatformStatus = append(temp.DetailPlatformStatus, ps)
|
||||
if client == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ps := new(msggateway.GetUsersOnlineStatusResp_SuccessDetail)
|
||||
ps.Platform = constant.PlatformIDToName(client.PlatformID)
|
||||
ps.Status = constant.OnlineStatus
|
||||
ps.ConnID = client.ctx.GetConnID()
|
||||
ps.Token = client.token
|
||||
ps.IsBackground = client.IsBackground
|
||||
uresp.Status = constant.OnlineStatus
|
||||
uresp.DetailPlatformStatus = append(uresp.DetailPlatformStatus, ps)
|
||||
}
|
||||
if temp.Status == constant.OnlineStatus {
|
||||
resp.SuccessResult = append(resp.SuccessResult, temp)
|
||||
if uresp.Status == constant.OnlineStatus {
|
||||
resp.SuccessResult = append(resp.SuccessResult, uresp)
|
||||
}
|
||||
}
|
||||
return &resp, nil
|
||||
@@ -129,50 +130,55 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(
|
||||
ctx context.Context,
|
||||
req *msggateway.OnlineBatchPushOneMsgReq,
|
||||
) (*msggateway.OnlineBatchPushOneMsgResp, error) {
|
||||
var singleUserResult []*msggateway.SingleMsgToUserResults
|
||||
|
||||
var singleUserResults []*msggateway.SingleMsgToUserResults
|
||||
|
||||
for _, v := range req.PushToUserIDs {
|
||||
var resp []*msggateway.SingleMsgToUserPlatform
|
||||
tempT := &msggateway.SingleMsgToUserResults{
|
||||
results := &msggateway.SingleMsgToUserResults{
|
||||
UserID: v,
|
||||
}
|
||||
clients, ok := s.LongConnServer.GetUserAllCons(v)
|
||||
if !ok {
|
||||
log.ZDebug(ctx, "push user not online", "userID", v)
|
||||
tempT.Resp = resp
|
||||
singleUserResult = append(singleUserResult, tempT)
|
||||
results.Resp = resp
|
||||
singleUserResults = append(singleUserResults, results)
|
||||
continue
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "push user online", "clients", clients, "userID", v)
|
||||
for _, client := range clients {
|
||||
if client != nil {
|
||||
temp := &msggateway.SingleMsgToUserPlatform{
|
||||
RecvID: v,
|
||||
RecvPlatFormID: int32(client.PlatformID),
|
||||
}
|
||||
if !client.IsBackground ||
|
||||
(client.IsBackground == true && client.PlatformID != constant.IOSPlatformID) {
|
||||
err := client.PushMessage(ctx, req.MsgData)
|
||||
if err != nil {
|
||||
temp.ResultCode = -2
|
||||
resp = append(resp, temp)
|
||||
} else {
|
||||
if utils.IsContainInt(client.PlatformID, s.pushTerminal) {
|
||||
tempT.OnlinePush = true
|
||||
resp = append(resp, temp)
|
||||
}
|
||||
}
|
||||
if client == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
userPlatform := &msggateway.SingleMsgToUserPlatform{
|
||||
RecvID: v,
|
||||
RecvPlatFormID: int32(client.PlatformID),
|
||||
}
|
||||
if !client.IsBackground ||
|
||||
(client.IsBackground && client.PlatformID != constant.IOSPlatformID) {
|
||||
err := client.PushMessage(ctx, req.MsgData)
|
||||
if err != nil {
|
||||
userPlatform.ResultCode = -2
|
||||
resp = append(resp, userPlatform)
|
||||
} else {
|
||||
temp.ResultCode = -3
|
||||
resp = append(resp, temp)
|
||||
if utils.IsContainInt(client.PlatformID, s.pushTerminal) {
|
||||
results.OnlinePush = true
|
||||
resp = append(resp, userPlatform)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userPlatform.ResultCode = -3
|
||||
resp = append(resp, userPlatform)
|
||||
}
|
||||
}
|
||||
tempT.Resp = resp
|
||||
singleUserResult = append(singleUserResult, tempT)
|
||||
results.Resp = resp
|
||||
singleUserResults = append(singleUserResults, results)
|
||||
}
|
||||
|
||||
return &msggateway.OnlineBatchPushOneMsgResp{
|
||||
SinglePushResult: singleUserResult,
|
||||
SinglePushResult: singleUserResults,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -181,17 +187,21 @@ func (s *Server) KickUserOffline(
|
||||
req *msggateway.KickUserOfflineReq,
|
||||
) (*msggateway.KickUserOfflineResp, error) {
|
||||
for _, v := range req.KickUserIDList {
|
||||
if clients, _, ok := s.LongConnServer.GetUserPlatformCons(v, int(req.PlatformID)); ok {
|
||||
for _, client := range clients {
|
||||
log.ZDebug(ctx, "kick user offline", "userID", v, "platformID", req.PlatformID, "client", client)
|
||||
if err := client.longConnServer.KickUserConn(client); err != nil {
|
||||
log.ZWarn(ctx, "kick user offline failed", err, "userID", v, "platformID", req.PlatformID)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clients, _, ok := s.LongConnServer.GetUserPlatformCons(v, int(req.PlatformID))
|
||||
if !ok {
|
||||
log.ZInfo(ctx, "conn not exist", "userID", v, "platformID", req.PlatformID)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, client := range clients {
|
||||
log.ZDebug(ctx, "kick user offline", "userID", v, "platformID", req.PlatformID, "client", client)
|
||||
if err := client.longConnServer.KickUserConn(client); err != nil {
|
||||
log.ZWarn(ctx, "kick user offline failed", err, "userID", v, "platformID", req.PlatformID)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
return &msggateway.KickUserOfflineResp{}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
)
|
||||
|
||||
// RunWsAndServer run ws server
|
||||
// RunWsAndServer run ws server.
|
||||
func RunWsAndServer(rpcPort, wsPort, prometheusPort int) error {
|
||||
fmt.Println(
|
||||
"start rpc/msg_gateway server, port: ",
|
||||
@@ -37,7 +37,9 @@ func RunWsAndServer(rpcPort, wsPort, prometheusPort int) error {
|
||||
WithPort(wsPort),
|
||||
WithMaxConnNum(int64(config.Config.LongConnSvr.WebsocketMaxConnNum)),
|
||||
WithHandshakeTimeout(time.Duration(config.Config.LongConnSvr.WebsocketTimeout)*time.Second),
|
||||
WithMessageMaxMsgLength(config.Config.LongConnSvr.WebsocketMaxMsgLen))
|
||||
WithMessageMaxMsgLength(config.Config.LongConnSvr.WebsocketMaxMsgLen),
|
||||
WithWriteBufferSize(config.Config.LongConnSvr.WebsocketWriteBufferSize),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -50,10 +50,11 @@ type GWebSocket struct {
|
||||
protocolType int
|
||||
conn *websocket.Conn
|
||||
handshakeTimeout time.Duration
|
||||
writeBufferSize int
|
||||
}
|
||||
|
||||
func newGWebSocket(protocolType int, handshakeTimeout time.Duration) *GWebSocket {
|
||||
return &GWebSocket{protocolType: protocolType, handshakeTimeout: handshakeTimeout}
|
||||
func newGWebSocket(protocolType int, handshakeTimeout time.Duration, wbs int) *GWebSocket {
|
||||
return &GWebSocket{protocolType: protocolType, handshakeTimeout: handshakeTimeout, writeBufferSize: wbs}
|
||||
}
|
||||
|
||||
func (d *GWebSocket) Close() error {
|
||||
@@ -65,6 +66,10 @@ func (d *GWebSocket) GenerateLongConn(w http.ResponseWriter, r *http.Request) er
|
||||
HandshakeTimeout: d.handshakeTimeout,
|
||||
CheckOrigin: func(r *http.Request) bool { return true },
|
||||
}
|
||||
if d.writeBufferSize > 0 { // default is 4kb.
|
||||
upgrader.WriteBufferSize = d.writeBufferSize
|
||||
}
|
||||
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -23,26 +23,22 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
)
|
||||
|
||||
type LongConnServer interface {
|
||||
@@ -61,12 +57,6 @@ type LongConnServer interface {
|
||||
MessageHandler
|
||||
}
|
||||
|
||||
var bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 1024)
|
||||
},
|
||||
}
|
||||
|
||||
type WsServer struct {
|
||||
port int
|
||||
wsMaxConnNum int64
|
||||
@@ -78,7 +68,7 @@ type WsServer struct {
|
||||
onlineUserNum atomic.Int64
|
||||
onlineUserConnNum atomic.Int64
|
||||
handshakeTimeout time.Duration
|
||||
hubServer *Server
|
||||
writeBufferSize int
|
||||
validate *validator.Validate
|
||||
cache cache.MsgModel
|
||||
userClient *rpcclient.UserRpcClient
|
||||
@@ -148,6 +138,7 @@ func NewWsServer(opts ...Option) (*WsServer, error) {
|
||||
return &WsServer{
|
||||
port: config.port,
|
||||
wsMaxConnNum: config.maxConnNum,
|
||||
writeBufferSize: config.writeBufferSize,
|
||||
handshakeTimeout: config.handshakeTimeout,
|
||||
clientPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
@@ -183,27 +174,39 @@ func (ws *WsServer) Run() error {
|
||||
return http.ListenAndServe(":"+utils.IntToString(ws.port), nil) // Start listening
|
||||
}
|
||||
|
||||
var concurrentRequest = 3
|
||||
|
||||
func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *Client) error {
|
||||
conns, err := ws.disCov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wg := errgroup.Group{}
|
||||
wg.SetLimit(concurrentRequest)
|
||||
|
||||
// Online push user online message to other node
|
||||
for _, v := range conns {
|
||||
v := v // safe closure var
|
||||
if v.Target() == ws.disCov.GetSelfConnTarget() {
|
||||
log.ZDebug(ctx, "Filter out this node", "node", v.Target())
|
||||
continue
|
||||
}
|
||||
msgClient := msggateway.NewMsgGatewayClient(v)
|
||||
_, err := msgClient.MultiTerminalLoginCheck(ctx, &msggateway.MultiTerminalLoginCheckReq{
|
||||
UserID: client.UserID,
|
||||
PlatformID: int32(client.PlatformID), Token: client.token,
|
||||
|
||||
wg.Go(func() error {
|
||||
msgClient := msggateway.NewMsgGatewayClient(v)
|
||||
_, err := msgClient.MultiTerminalLoginCheck(ctx, &msggateway.MultiTerminalLoginCheckReq{
|
||||
UserID: client.UserID,
|
||||
PlatformID: int32(client.PlatformID), Token: client.token,
|
||||
})
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "MultiTerminalLoginCheck err", err, "node", v.Target())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "MultiTerminalLoginCheck err", err, "node", v.Target())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
_ = wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -289,70 +292,72 @@ func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Clien
|
||||
}
|
||||
fallthrough
|
||||
case constant.AllLoginButSameTermKick:
|
||||
if clientOK {
|
||||
isDeleteUser := ws.clients.deleteClients(newClient.UserID, oldClients)
|
||||
if isDeleteUser {
|
||||
ws.onlineUserNum.Add(-1)
|
||||
if !clientOK {
|
||||
return
|
||||
}
|
||||
|
||||
isDeleteUser := ws.clients.deleteClients(newClient.UserID, oldClients)
|
||||
if isDeleteUser {
|
||||
ws.onlineUserNum.Add(-1)
|
||||
}
|
||||
for _, c := range oldClients {
|
||||
err := c.KickOnlineMessage()
|
||||
if err != nil {
|
||||
log.ZWarn(c.ctx, "KickOnlineMessage", err)
|
||||
}
|
||||
for _, c := range oldClients {
|
||||
err := c.KickOnlineMessage()
|
||||
if err != nil {
|
||||
log.ZWarn(c.ctx, "KickOnlineMessage", err)
|
||||
}
|
||||
}
|
||||
m, err := ws.cache.GetTokensWithoutError(
|
||||
}
|
||||
m, err := ws.cache.GetTokensWithoutError(
|
||||
newClient.ctx,
|
||||
newClient.UserID,
|
||||
newClient.PlatformID,
|
||||
)
|
||||
if err != nil && err != redis.Nil {
|
||||
log.ZWarn(
|
||||
newClient.ctx,
|
||||
newClient.UserID,
|
||||
newClient.PlatformID,
|
||||
)
|
||||
if err != nil && err != redis.Nil {
|
||||
log.ZWarn(
|
||||
newClient.ctx,
|
||||
"get token from redis err",
|
||||
err,
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
)
|
||||
return
|
||||
}
|
||||
if m == nil {
|
||||
log.ZWarn(
|
||||
newClient.ctx,
|
||||
"m is nil",
|
||||
errors.New("m is nil"),
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
)
|
||||
return
|
||||
}
|
||||
log.ZDebug(
|
||||
newClient.ctx,
|
||||
"get token from redis",
|
||||
"get token from redis err",
|
||||
err,
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
"tokenMap",
|
||||
m,
|
||||
)
|
||||
return
|
||||
}
|
||||
if m == nil {
|
||||
log.ZWarn(
|
||||
newClient.ctx,
|
||||
"m is nil",
|
||||
errors.New("m is nil"),
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
)
|
||||
return
|
||||
}
|
||||
log.ZDebug(
|
||||
newClient.ctx,
|
||||
"get token from redis",
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
"tokenMap",
|
||||
m,
|
||||
)
|
||||
|
||||
for k := range m {
|
||||
if k != newClient.ctx.GetToken() {
|
||||
m[k] = constant.KickedToken
|
||||
}
|
||||
}
|
||||
log.ZDebug(newClient.ctx, "set token map is ", "token map", m, "userID",
|
||||
newClient.UserID, "token", newClient.ctx.GetToken())
|
||||
err = ws.cache.SetTokenMapByUidPid(newClient.ctx, newClient.UserID, newClient.PlatformID, m)
|
||||
if err != nil {
|
||||
log.ZWarn(newClient.ctx, "SetTokenMapByUidPid err", err, "userID", newClient.UserID, "platformID", newClient.PlatformID)
|
||||
return
|
||||
for k := range m {
|
||||
if k != newClient.ctx.GetToken() {
|
||||
m[k] = constant.KickedToken
|
||||
}
|
||||
}
|
||||
log.ZDebug(newClient.ctx, "set token map is ", "token map", m, "userID",
|
||||
newClient.UserID, "token", newClient.ctx.GetToken())
|
||||
err = ws.cache.SetTokenMapByUidPid(newClient.ctx, newClient.UserID, newClient.PlatformID, m)
|
||||
if err != nil {
|
||||
log.ZWarn(newClient.ctx, "SetTokenMapByUidPid err", err, "userID", newClient.UserID, "platformID", newClient.PlatformID)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +370,7 @@ func (ws *WsServer) unregisterClient(client *Client) {
|
||||
}
|
||||
ws.onlineUserConnNum.Add(-1)
|
||||
ws.SetUserOnlineStatus(client.ctx, client, constant.Offline)
|
||||
log.ZInfo(client.ctx, "user offline", "close reason", client.closedErr, "online user Num", ws.onlineUserNum, "online user conn Num",
|
||||
log.ZInfo(client.ctx, "user offline", "close reason", client.closedErr, "online user Num", ws.onlineUserNum.Load(), "online user conn Num",
|
||||
ws.onlineUserConnNum.Load(),
|
||||
)
|
||||
}
|
||||
@@ -404,7 +409,7 @@ func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpError(connContext, errs.ErrConnArgsErr)
|
||||
return
|
||||
}
|
||||
if err := authverify.WsVerifyToken(token, userID, platformID); err != nil {
|
||||
if err = authverify.WsVerifyToken(token, userID, platformID); err != nil {
|
||||
httpError(connContext, err)
|
||||
return
|
||||
}
|
||||
@@ -427,7 +432,8 @@ func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpError(connContext, errs.ErrTokenNotExist.Wrap())
|
||||
return
|
||||
}
|
||||
wsLongConn := newGWebSocket(WebSocket, ws.handshakeTimeout)
|
||||
|
||||
wsLongConn := newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
|
||||
err = wsLongConn.GenerateLongConn(w, r)
|
||||
if err != nil {
|
||||
httpError(connContext, err)
|
||||
|
||||
@@ -27,6 +27,8 @@ type (
|
||||
handshakeTimeout time.Duration
|
||||
// 允许消息最大长度
|
||||
messageMaxMsgLength int
|
||||
// websocket write buffer, default: 4096, 4kb.
|
||||
writeBufferSize int
|
||||
}
|
||||
)
|
||||
|
||||
@@ -53,3 +55,9 @@ func WithMessageMaxMsgLength(length int) Option {
|
||||
opt.messageMaxMsgLength = length
|
||||
}
|
||||
}
|
||||
|
||||
func WithWriteBufferSize(size int) Option {
|
||||
return func(opt *configs) {
|
||||
opt.writeBufferSize = size
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,13 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/OpenIMSDK/tools/mw"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
"github.com/OpenIMSDK/tools/mw"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
|
||||
@@ -427,49 +427,62 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim(
|
||||
break
|
||||
}
|
||||
}
|
||||
rwLock := new(sync.RWMutex)
|
||||
log.ZDebug(context.Background(), "online new session msg come", "highWaterMarkOffset",
|
||||
claim.HighWaterMarkOffset(), "topic", claim.Topic(), "partition", claim.Partition())
|
||||
cMsg := make([]*sarama.ConsumerMessage, 0, 1000)
|
||||
t := time.NewTicker(time.Millisecond * 100)
|
||||
|
||||
split := 1000
|
||||
rwLock := new(sync.RWMutex)
|
||||
messages := make([]*sarama.ConsumerMessage, 0, 1000)
|
||||
ticker := time.NewTicker(time.Millisecond * 100)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
if len(cMsg) > 0 {
|
||||
rwLock.Lock()
|
||||
ccMsg := make([]*sarama.ConsumerMessage, 0, 1000)
|
||||
for _, v := range cMsg {
|
||||
ccMsg = append(ccMsg, v)
|
||||
}
|
||||
cMsg = make([]*sarama.ConsumerMessage, 0, 1000)
|
||||
rwLock.Unlock()
|
||||
split := 1000
|
||||
ctx := mcontext.WithTriggerIDContext(context.Background(), utils.OperationIDGenerator())
|
||||
log.ZDebug(ctx, "timer trigger msg consumer start", "length", len(ccMsg))
|
||||
for i := 0; i < len(ccMsg)/split; i++ {
|
||||
// log.Debug()
|
||||
och.msgDistributionCh <- Cmd2Value{Cmd: ConsumerMsgs, Value: TriggerChannelValue{
|
||||
ctx: ctx, cMsgList: ccMsg[i*split : (i+1)*split],
|
||||
}}
|
||||
}
|
||||
if (len(ccMsg) % split) > 0 {
|
||||
och.msgDistributionCh <- Cmd2Value{Cmd: ConsumerMsgs, Value: TriggerChannelValue{
|
||||
ctx: ctx, cMsgList: ccMsg[split*(len(ccMsg)/split):],
|
||||
}}
|
||||
}
|
||||
log.ZDebug(ctx, "timer trigger msg consumer end", "length", len(ccMsg))
|
||||
case <-ticker.C:
|
||||
if len(messages) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
rwLock.Lock()
|
||||
buffer := make([]*sarama.ConsumerMessage, 0, len(messages))
|
||||
buffer = append(buffer, messages...)
|
||||
|
||||
// reuse slice, set cap to 0
|
||||
messages = messages[:0]
|
||||
rwLock.Unlock()
|
||||
|
||||
start := time.Now()
|
||||
ctx := mcontext.WithTriggerIDContext(context.Background(), utils.OperationIDGenerator())
|
||||
log.ZDebug(ctx, "timer trigger msg consumer start", "length", len(buffer))
|
||||
for i := 0; i < len(buffer)/split; i++ {
|
||||
och.msgDistributionCh <- Cmd2Value{Cmd: ConsumerMsgs, Value: TriggerChannelValue{
|
||||
ctx: ctx, cMsgList: buffer[i*split : (i+1)*split],
|
||||
}}
|
||||
}
|
||||
if (len(buffer) % split) > 0 {
|
||||
och.msgDistributionCh <- Cmd2Value{Cmd: ConsumerMsgs, Value: TriggerChannelValue{
|
||||
ctx: ctx, cMsgList: buffer[split*(len(buffer)/split):],
|
||||
}}
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "timer trigger msg consumer end",
|
||||
"length", len(buffer), "time_cost", time.Since(start),
|
||||
)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for msg := range claim.Messages() {
|
||||
rwLock.Lock()
|
||||
if len(msg.Value) != 0 {
|
||||
cMsg = append(cMsg, msg)
|
||||
if len(msg.Value) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
rwLock.Lock()
|
||||
messages = append(messages, msg)
|
||||
rwLock.Unlock()
|
||||
|
||||
sess.MarkMessage(msg, "")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -18,8 +18,9 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/conversation"
|
||||
@@ -40,6 +41,7 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
)
|
||||
|
||||
@@ -285,18 +287,44 @@ func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Online push message
|
||||
for _, v := range conns {
|
||||
msgClient := msggateway.NewMsgGatewayClient(v)
|
||||
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs})
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
log.ZDebug(ctx, "push result", "reply", reply)
|
||||
if reply != nil && reply.SinglePushResult != nil {
|
||||
wsResults = append(wsResults, reply.SinglePushResult...)
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
wg = errgroup.Group{}
|
||||
input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}
|
||||
maxWorkers = config.Config.Push.MaxConcurrentWorkers
|
||||
)
|
||||
|
||||
if maxWorkers < 3 {
|
||||
maxWorkers = 3
|
||||
}
|
||||
|
||||
wg.SetLimit(maxWorkers)
|
||||
|
||||
// Online push message
|
||||
for _, conn := range conns {
|
||||
conn := conn // loop var safe
|
||||
wg.Go(func() error {
|
||||
msgClient := msggateway.NewMsgGatewayClient(conn)
|
||||
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "push result", "reply", reply)
|
||||
if reply != nil && reply.SinglePushResult != nil {
|
||||
mu.Lock()
|
||||
wsResults = append(wsResults, reply.SinglePushResult...)
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
_ = wg.Wait()
|
||||
|
||||
// always return nil
|
||||
return wsResults, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -17,13 +17,18 @@ package tools
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
)
|
||||
|
||||
func StartTask() error {
|
||||
@@ -32,23 +37,75 @@ func StartTask() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msgTool.ConvertTools()
|
||||
c := cron.New()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
msgTool.convertTools()
|
||||
|
||||
rdb, err := cache.NewRedis()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// register cron tasks
|
||||
var crontab = cron.New()
|
||||
log.ZInfo(context.Background(), "start chatRecordsClearTime cron task", "cron config", config.Config.ChatRecordsClearTime)
|
||||
_, err = c.AddFunc(config.Config.ChatRecordsClearTime, msgTool.AllConversationClearMsgAndFixSeq)
|
||||
_, err = crontab.AddFunc(config.Config.ChatRecordsClearTime, cronWrapFunc(rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq))
|
||||
if err != nil {
|
||||
log.ZError(context.Background(), "start allConversationClearMsgAndFixSeq cron failed", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.ZInfo(context.Background(), "start msgDestruct cron task", "cron config", config.Config.MsgDestructTime)
|
||||
_, err = c.AddFunc(config.Config.MsgDestructTime, msgTool.ConversationsDestructMsgs)
|
||||
_, err = crontab.AddFunc(config.Config.MsgDestructTime, cronWrapFunc(rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs))
|
||||
if err != nil {
|
||||
log.ZError(context.Background(), "start conversationsDestructMsgs cron failed", err)
|
||||
panic(err)
|
||||
}
|
||||
c.Start()
|
||||
wg.Wait()
|
||||
|
||||
// start crontab
|
||||
crontab.Start()
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
||||
<-sigs
|
||||
|
||||
// stop crontab, Wait for the running task to exit.
|
||||
ctx := crontab.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// graceful exit
|
||||
|
||||
case <-time.After(15 * time.Second):
|
||||
// forced exit on timeout
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// netlock redis lock.
|
||||
func netlock(rdb redis.UniversalClient, key string, ttl time.Duration) bool {
|
||||
value := "used"
|
||||
ok, err := rdb.SetNX(context.Background(), key, value, ttl).Result() // nolint
|
||||
if err != nil {
|
||||
// when err is about redis server, return true.
|
||||
return false
|
||||
}
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func cronWrapFunc(rdb redis.UniversalClient, key string, fn func()) func() {
|
||||
enableCronLocker := config.Config.EnableCronLocker
|
||||
return func() {
|
||||
// if don't enable cron-locker, call fn directly.
|
||||
if !enableCronLocker {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
|
||||
// when acquire redis lock, call fn().
|
||||
if netlock(rdb, key, 5*time.Second) {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
82
internal/tools/cron_task_test.go
Normal file
82
internal/tools/cron_task_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
)
|
||||
|
||||
func TestDisLock(t *testing.T) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
assert.Equal(t, true, netlock(rdb, "cron-1", 1*time.Second))
|
||||
|
||||
// if exists, get false
|
||||
assert.Equal(t, false, netlock(rdb, "cron-1", 1*time.Second))
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// wait for key on timeout, get true
|
||||
assert.Equal(t, true, netlock(rdb, "cron-1", 2*time.Second))
|
||||
|
||||
// set different key
|
||||
assert.Equal(t, true, netlock(rdb, "cron-2", 2*time.Second))
|
||||
}
|
||||
|
||||
func TestCronWrapFunc(t *testing.T) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
once := sync.Once{}
|
||||
done := make(chan struct{}, 1)
|
||||
cb := func() {
|
||||
once.Do(func() {
|
||||
close(done)
|
||||
})
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
key := fmt.Sprintf("cron-%v", rand.Int31())
|
||||
crontab := cron.New(cron.WithSeconds())
|
||||
crontab.AddFunc("*/1 * * * * *", cronWrapFunc(rdb, key, cb))
|
||||
crontab.Start()
|
||||
<-done
|
||||
|
||||
dur := time.Since(start)
|
||||
assert.LessOrEqual(t, dur.Seconds(), float64(2*time.Second))
|
||||
crontab.Stop()
|
||||
}
|
||||
|
||||
func TestCronWrapFuncWithNetlock(t *testing.T) {
|
||||
config.Config.EnableCronLocker = true
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
done := make(chan string, 10)
|
||||
|
||||
crontab := cron.New(cron.WithSeconds())
|
||||
|
||||
key := fmt.Sprintf("cron-%v", rand.Int31())
|
||||
crontab.AddFunc("*/1 * * * * *", cronWrapFunc(rdb, key, func() {
|
||||
done <- "host1"
|
||||
}))
|
||||
crontab.AddFunc("*/1 * * * * *", cronWrapFunc(rdb, key, func() {
|
||||
done <- "host2"
|
||||
}))
|
||||
crontab.Start()
|
||||
|
||||
time.Sleep(12 * time.Second)
|
||||
// the ttl of netlock is 5s, so expected value is 2.
|
||||
assert.Equal(t, len(done), 2)
|
||||
|
||||
crontab.Stop()
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
)
|
||||
|
||||
func (c *MsgTool) ConvertTools() {
|
||||
func (c *MsgTool) convertTools() {
|
||||
ctx := mcontext.NewCtx("convert")
|
||||
conversationIDs, err := c.conversationDatabase.GetAllConversationIDs(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -78,17 +78,20 @@ type configStruct struct {
|
||||
} `yaml:"mongo"`
|
||||
|
||||
Redis struct {
|
||||
ClusterMode bool `yaml:"clusterMode"`
|
||||
Address []string `yaml:"address"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
ClusterMode bool `yaml:"clusterMode"`
|
||||
Address []string `yaml:"address"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
EnablePipeline bool `yaml:"enablePipeline"`
|
||||
} `yaml:"redis"`
|
||||
|
||||
Kafka struct {
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
Addr []string `yaml:"addr"`
|
||||
TLS *struct {
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
ProducerAck string `yaml:"producerAck"`
|
||||
CompressType string `yaml:"compressType"`
|
||||
Addr []string `yaml:"addr"`
|
||||
TLS *struct {
|
||||
CACrt string `yaml:"caCrt"`
|
||||
ClientCrt string `yaml:"clientCrt"`
|
||||
ClientKey string `yaml:"clientKey"`
|
||||
@@ -193,11 +196,13 @@ type configStruct struct {
|
||||
WebsocketMaxConnNum int `yaml:"websocketMaxConnNum"`
|
||||
WebsocketMaxMsgLen int `yaml:"websocketMaxMsgLen"`
|
||||
WebsocketTimeout int `yaml:"websocketTimeout"`
|
||||
WebsocketWriteBufferSize int `yaml:"websocketWriteBufferSize"`
|
||||
} `yaml:"longConnSvr"`
|
||||
|
||||
Push struct {
|
||||
Enable string `yaml:"enable"`
|
||||
GeTui struct {
|
||||
MaxConcurrentWorkers int `yaml:"maxConcurrentWorkers"`
|
||||
Enable string `yaml:"enable"`
|
||||
GeTui struct {
|
||||
PushUrl string `yaml:"pushUrl"`
|
||||
AppKey string `yaml:"appKey"`
|
||||
Intent string `yaml:"intent"`
|
||||
@@ -229,6 +234,7 @@ type configStruct struct {
|
||||
ChatRecordsClearTime string `yaml:"chatRecordsClearTime"`
|
||||
MsgDestructTime string `yaml:"msgDestructTime"`
|
||||
Secret string `yaml:"secret"`
|
||||
EnableCronLocker bool `yaml:"enableCronLocker"`
|
||||
TokenPolicy struct {
|
||||
Expire int64 `yaml:"expire"`
|
||||
} `yaml:"tokenPolicy"`
|
||||
|
||||
10
pkg/common/db/cache/init_redis.go
vendored
10
pkg/common/db/cache/init_redis.go
vendored
@@ -28,12 +28,21 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
)
|
||||
|
||||
var (
|
||||
// singleton pattern.
|
||||
redisClient redis.UniversalClient
|
||||
)
|
||||
|
||||
const (
|
||||
maxRetry = 10 // number of retries
|
||||
)
|
||||
|
||||
// NewRedis Initialize redis connection.
|
||||
func NewRedis() (redis.UniversalClient, error) {
|
||||
if redisClient != nil {
|
||||
return redisClient, nil
|
||||
}
|
||||
|
||||
if len(config.Config.Redis.Address) == 0 {
|
||||
return nil, errors.New("redis address is empty")
|
||||
}
|
||||
@@ -66,5 +75,6 @@ func NewRedis() (redis.UniversalClient, error) {
|
||||
return nil, fmt.Errorf("redis ping %w", err)
|
||||
}
|
||||
|
||||
redisClient = rdb
|
||||
return rdb, err
|
||||
}
|
||||
|
||||
278
pkg/common/db/cache/msg.go
vendored
278
pkg/common/db/cache/msg.go
vendored
@@ -20,9 +20,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
|
||||
unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
|
||||
@@ -62,6 +60,8 @@ const (
|
||||
uidPidToken = "UID_PID_TOKEN_STATUS:"
|
||||
)
|
||||
|
||||
var concurrentLimit = 3
|
||||
|
||||
type SeqCache interface {
|
||||
SetMaxSeq(ctx context.Context, conversationID string, maxSeq int64) error
|
||||
GetMaxSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error)
|
||||
@@ -133,10 +133,7 @@ func NewMsgCacheModel(client redis.UniversalClient) MsgModel {
|
||||
|
||||
type msgCache struct {
|
||||
metaCache
|
||||
rdb redis.UniversalClient
|
||||
expireTime time.Duration
|
||||
rcClient *rockscache.Client
|
||||
msgDocDatabase unrelationtb.MsgDocModelInterface
|
||||
rdb redis.UniversalClient
|
||||
}
|
||||
|
||||
func (c *msgCache) getMaxSeqKey(conversationID string) string {
|
||||
@@ -173,29 +170,6 @@ func (c *msgCache) getSeqs(ctx context.Context, items []string, getkey func(s st
|
||||
}
|
||||
|
||||
return m, nil
|
||||
|
||||
//pipe := c.rdb.Pipeline()
|
||||
//for _, v := range items {
|
||||
// if err := pipe.Get(ctx, getkey(v)).Err(); err != nil && err != redis.Nil {
|
||||
// return nil, errs.Wrap(err)
|
||||
// }
|
||||
//}
|
||||
//result, err := pipe.Exec(ctx)
|
||||
//if err != nil && err != redis.Nil {
|
||||
// return nil, errs.Wrap(err)
|
||||
//}
|
||||
//m = make(map[string]int64, len(items))
|
||||
//for i, v := range result {
|
||||
// seq := v.(*redis.StringCmd)
|
||||
// if seq.Err() != nil && seq.Err() != redis.Nil {
|
||||
// return nil, errs.Wrap(v.Err())
|
||||
// }
|
||||
// val := utils.StringToInt64(seq.Val())
|
||||
// if val != 0 {
|
||||
// m[items[i]] = val
|
||||
// }
|
||||
//}
|
||||
//return m, nil
|
||||
}
|
||||
|
||||
func (c *msgCache) SetMaxSeq(ctx context.Context, conversationID string, maxSeq int64) error {
|
||||
@@ -221,15 +195,6 @@ func (c *msgCache) setSeqs(ctx context.Context, seqs map[string]int64, getkey fu
|
||||
}
|
||||
}
|
||||
return nil
|
||||
//pipe := c.rdb.Pipeline()
|
||||
//for k, seq := range seqs {
|
||||
// err := pipe.Set(ctx, getkey(k), seq, 0).Err()
|
||||
// if err != nil {
|
||||
// return errs.Wrap(err)
|
||||
// }
|
||||
//}
|
||||
//_, err := pipe.Exec(ctx)
|
||||
//return err
|
||||
}
|
||||
|
||||
func (c *msgCache) SetMinSeqs(ctx context.Context, seqs map[string]int64) error {
|
||||
@@ -345,85 +310,165 @@ func (c *msgCache) allMessageCacheKey(conversationID string) string {
|
||||
}
|
||||
|
||||
func (c *msgCache) GetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
|
||||
if config.Config.Redis.EnablePipeline {
|
||||
return c.PipeGetMessagesBySeq(ctx, conversationID, seqs)
|
||||
}
|
||||
|
||||
return c.ParallelGetMessagesBySeq(ctx, conversationID, seqs)
|
||||
}
|
||||
|
||||
func (c *msgCache) PipeGetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
|
||||
pipe := c.rdb.Pipeline()
|
||||
|
||||
results := []*redis.StringCmd{}
|
||||
for _, seq := range seqs {
|
||||
res, err := c.rdb.Get(ctx, c.getMessageCacheKey(conversationID, seq)).Result()
|
||||
if err != nil {
|
||||
log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", seq)
|
||||
results = append(results, pipe.Get(ctx, c.getMessageCacheKey(conversationID, seq)))
|
||||
}
|
||||
|
||||
_, err = pipe.Exec(ctx)
|
||||
if err != nil && err != redis.Nil {
|
||||
return seqMsgs, failedSeqs, errs.Wrap(err, "pipe.get")
|
||||
}
|
||||
|
||||
for idx, res := range results {
|
||||
seq := seqs[idx]
|
||||
if res.Err() != nil {
|
||||
log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", seq, "err", res.Err())
|
||||
failedSeqs = append(failedSeqs, seq)
|
||||
continue
|
||||
}
|
||||
|
||||
msg := sdkws.MsgData{}
|
||||
if err = msgprocessor.String2Pb(res, &msg); err != nil {
|
||||
if err = msgprocessor.String2Pb(res.Val(), &msg); err != nil {
|
||||
log.ZError(ctx, "GetMessagesBySeq Unmarshal failed", err, "res", res, "conversationID", conversationID, "seq", seq)
|
||||
failedSeqs = append(failedSeqs, seq)
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.Status == constant.MsgDeleted {
|
||||
failedSeqs = append(failedSeqs, seq)
|
||||
continue
|
||||
}
|
||||
|
||||
seqMsgs = append(seqMsgs, &msg)
|
||||
}
|
||||
|
||||
return
|
||||
//pipe := c.rdb.Pipeline()
|
||||
//for _, v := range seqs {
|
||||
// // MESSAGE_CACHE:169.254.225.224_reliability1653387820_0_1
|
||||
// key := c.getMessageCacheKey(conversationID, v)
|
||||
// if err := pipe.Get(ctx, key).Err(); err != nil && err != redis.Nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
//}
|
||||
//result, err := pipe.Exec(ctx)
|
||||
//for i, v := range result {
|
||||
// cmd := v.(*redis.StringCmd)
|
||||
// if cmd.Err() != nil {
|
||||
// failedSeqs = append(failedSeqs, seqs[i])
|
||||
// } else {
|
||||
// msg := sdkws.MsgData{}
|
||||
// err = msgprocessor.String2Pb(cmd.Val(), &msg)
|
||||
// if err == nil {
|
||||
// if msg.Status != constant.MsgDeleted {
|
||||
// seqMsgs = append(seqMsgs, &msg)
|
||||
// continue
|
||||
// }
|
||||
// } else {
|
||||
// log.ZWarn(ctx, "UnmarshalString failed", err, "conversationID", conversationID, "seq", seqs[i], "msg", cmd.Val())
|
||||
// }
|
||||
// failedSeqs = append(failedSeqs, seqs[i])
|
||||
// }
|
||||
//}
|
||||
//return seqMsgs, failedSeqs, err
|
||||
}
|
||||
|
||||
func (c *msgCache) ParallelGetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
|
||||
type entry struct {
|
||||
err error
|
||||
msg *sdkws.MsgData
|
||||
}
|
||||
|
||||
wg := errgroup.Group{}
|
||||
wg.SetLimit(concurrentLimit)
|
||||
|
||||
results := make([]entry, len(seqs)) // set slice len/cap to length of seqs.
|
||||
for idx, seq := range seqs {
|
||||
// closure safe var
|
||||
idx := idx
|
||||
seq := seq
|
||||
|
||||
wg.Go(func() error {
|
||||
res, err := c.rdb.Get(ctx, c.getMessageCacheKey(conversationID, seq)).Result()
|
||||
if err != nil {
|
||||
log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", seq)
|
||||
results[idx] = entry{err: err}
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := sdkws.MsgData{}
|
||||
if err = msgprocessor.String2Pb(res, &msg); err != nil {
|
||||
log.ZError(ctx, "GetMessagesBySeq Unmarshal failed", err, "res", res, "conversationID", conversationID, "seq", seq)
|
||||
results[idx] = entry{err: err}
|
||||
return nil
|
||||
}
|
||||
|
||||
if msg.Status == constant.MsgDeleted {
|
||||
results[idx] = entry{err: err}
|
||||
return nil
|
||||
}
|
||||
|
||||
results[idx] = entry{msg: &msg}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
_ = wg.Wait()
|
||||
|
||||
for idx, res := range results {
|
||||
if res.err != nil {
|
||||
failedSeqs = append(failedSeqs, seqs[idx])
|
||||
continue
|
||||
}
|
||||
|
||||
seqMsgs = append(seqMsgs, res.msg)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *msgCache) SetMessageToCache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (int, error) {
|
||||
if config.Config.Redis.EnablePipeline {
|
||||
return c.PipeSetMessageToCache(ctx, conversationID, msgs)
|
||||
}
|
||||
return c.ParallelSetMessageToCache(ctx, conversationID, msgs)
|
||||
}
|
||||
|
||||
func (c *msgCache) PipeSetMessageToCache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (int, error) {
|
||||
pipe := c.rdb.Pipeline()
|
||||
for _, msg := range msgs {
|
||||
s, err := msgprocessor.Pb2String(msg)
|
||||
if err != nil {
|
||||
return 0, errs.Wrap(err)
|
||||
return 0, errs.Wrap(err, "pb.marshal")
|
||||
}
|
||||
|
||||
key := c.getMessageCacheKey(conversationID, msg.Seq)
|
||||
if err := c.rdb.Set(ctx, key, s, time.Duration(config.Config.MsgCacheTimeout)*time.Second).Err(); err != nil {
|
||||
_ = pipe.Set(ctx, key, s, time.Duration(config.Config.MsgCacheTimeout)*time.Second)
|
||||
}
|
||||
|
||||
results, err := pipe.Exec(ctx)
|
||||
if err != nil {
|
||||
return 0, errs.Wrap(err, "pipe.set")
|
||||
}
|
||||
|
||||
for _, res := range results {
|
||||
if res.Err() != nil {
|
||||
return 0, errs.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return len(msgs), nil
|
||||
}
|
||||
|
||||
func (c *msgCache) ParallelSetMessageToCache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (int, error) {
|
||||
wg := errgroup.Group{}
|
||||
wg.SetLimit(concurrentLimit)
|
||||
|
||||
for _, msg := range msgs {
|
||||
msg := msg // closure safe var
|
||||
wg.Go(func() error {
|
||||
s, err := msgprocessor.Pb2String(msg)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
key := c.getMessageCacheKey(conversationID, msg.Seq)
|
||||
if err := c.rdb.Set(ctx, key, s, time.Duration(config.Config.MsgCacheTimeout)*time.Second).Err(); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
err := wg.Wait()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(msgs), nil
|
||||
//pipe := c.rdb.Pipeline()
|
||||
//var failedMsgs []*sdkws.MsgData
|
||||
//for _, msg := range msgs {
|
||||
// key := c.getMessageCacheKey(conversationID, msg.Seq)
|
||||
// s, err := msgprocessor.Pb2String(msg)
|
||||
// if err != nil {
|
||||
// return 0, errs.Wrap(err)
|
||||
// }
|
||||
// err = pipe.Set(ctx, key, s, time.Duration(config.Config.MsgCacheTimeout)*time.Second).Err()
|
||||
// if err != nil {
|
||||
// failedMsgs = append(failedMsgs, msg)
|
||||
// log.ZWarn(ctx, "set msg 2 cache failed", err, "msg", failedMsgs)
|
||||
// }
|
||||
//}
|
||||
//_, err := pipe.Exec(ctx)
|
||||
//return len(failedMsgs), err
|
||||
}
|
||||
|
||||
func (c *msgCache) getMessageDelUserListKey(conversationID string, seq int64) string {
|
||||
@@ -554,20 +599,49 @@ func (c *msgCache) DelUserDeleteMsgsList(ctx context.Context, conversationID str
|
||||
}
|
||||
|
||||
func (c *msgCache) DeleteMessages(ctx context.Context, conversationID string, seqs []int64) error {
|
||||
if config.Config.Redis.EnablePipeline {
|
||||
return c.PipeDeleteMessages(ctx, conversationID, seqs)
|
||||
}
|
||||
|
||||
return c.ParallelDeleteMessages(ctx, conversationID, seqs)
|
||||
}
|
||||
|
||||
func (c *msgCache) ParallelDeleteMessages(ctx context.Context, conversationID string, seqs []int64) error {
|
||||
wg := errgroup.Group{}
|
||||
wg.SetLimit(concurrentLimit)
|
||||
|
||||
for _, seq := range seqs {
|
||||
if err := c.rdb.Del(ctx, c.getMessageCacheKey(conversationID, seq)).Err(); err != nil {
|
||||
seq := seq
|
||||
wg.Go(func() error {
|
||||
err := c.rdb.Del(ctx, c.getMessageCacheKey(conversationID, seq)).Err()
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return wg.Wait()
|
||||
}
|
||||
|
||||
func (c *msgCache) PipeDeleteMessages(ctx context.Context, conversationID string, seqs []int64) error {
|
||||
pipe := c.rdb.Pipeline()
|
||||
for _, seq := range seqs {
|
||||
_ = pipe.Del(ctx, c.getMessageCacheKey(conversationID, seq))
|
||||
}
|
||||
|
||||
results, err := pipe.Exec(ctx)
|
||||
if err != nil {
|
||||
return errs.Wrap(err, "pipe.del")
|
||||
}
|
||||
|
||||
for _, res := range results {
|
||||
if res.Err() != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
//pipe := c.rdb.Pipeline()
|
||||
//for _, seq := range seqs {
|
||||
// if err := pipe.Del(ctx, c.getMessageCacheKey(conversationID, seq)).Err(); err != nil {
|
||||
// return errs.Wrap(err)
|
||||
// }
|
||||
//}
|
||||
//_, err := pipe.Exec(ctx)
|
||||
//return errs.Wrap(err)
|
||||
}
|
||||
|
||||
func (c *msgCache) CleanUpOneConversationAllMsg(ctx context.Context, conversationID string) error {
|
||||
@@ -584,14 +658,6 @@ func (c *msgCache) CleanUpOneConversationAllMsg(ctx context.Context, conversatio
|
||||
}
|
||||
}
|
||||
return nil
|
||||
//pipe := c.rdb.Pipeline()
|
||||
//for _, v := range vals {
|
||||
// if err := pipe.Del(ctx, v).Err(); err != nil {
|
||||
// return errs.Wrap(err)
|
||||
// }
|
||||
//}
|
||||
//_, err = pipe.Exec(ctx)
|
||||
//return errs.Wrap(err)
|
||||
}
|
||||
|
||||
func (c *msgCache) DelMsgFromCache(ctx context.Context, userID string, seqs []int64) error {
|
||||
|
||||
387
pkg/common/db/cache/msg_test.go
vendored
Normal file
387
pkg/common/db/cache/msg_test.go
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParallelSetMessageToCache(t *testing.T) {
|
||||
var (
|
||||
cid = fmt.Sprintf("cid-%v", rand.Int63())
|
||||
seqFirst = rand.Int63()
|
||||
msgs = []*sdkws.MsgData{}
|
||||
)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
msgs = append(msgs, &sdkws.MsgData{
|
||||
Seq: seqFirst + int64(i),
|
||||
})
|
||||
}
|
||||
|
||||
testParallelSetMessageToCache(t, cid, msgs)
|
||||
}
|
||||
|
||||
func testParallelSetMessageToCache(t *testing.T, cid string, msgs []*sdkws.MsgData) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
ret, err := cacher.ParallelSetMessageToCache(context.Background(), cid, msgs)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(msgs), ret)
|
||||
|
||||
// validate
|
||||
for _, msg := range msgs {
|
||||
key := cacher.getMessageCacheKey(cid, msg.Seq)
|
||||
val, err := rdb.Exists(context.Background(), key).Result()
|
||||
assert.Nil(t, err)
|
||||
assert.EqualValues(t, 1, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipeSetMessageToCache(t *testing.T) {
|
||||
var (
|
||||
cid = fmt.Sprintf("cid-%v", rand.Int63())
|
||||
seqFirst = rand.Int63()
|
||||
msgs = []*sdkws.MsgData{}
|
||||
)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
msgs = append(msgs, &sdkws.MsgData{
|
||||
Seq: seqFirst + int64(i),
|
||||
})
|
||||
}
|
||||
|
||||
testPipeSetMessageToCache(t, cid, msgs)
|
||||
}
|
||||
|
||||
func testPipeSetMessageToCache(t *testing.T, cid string, msgs []*sdkws.MsgData) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
ret, err := cacher.PipeSetMessageToCache(context.Background(), cid, msgs)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(msgs), ret)
|
||||
|
||||
// validate
|
||||
for _, msg := range msgs {
|
||||
key := cacher.getMessageCacheKey(cid, msg.Seq)
|
||||
val, err := rdb.Exists(context.Background(), key).Result()
|
||||
assert.Nil(t, err)
|
||||
assert.EqualValues(t, 1, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMessagesBySeq(t *testing.T) {
|
||||
var (
|
||||
cid = fmt.Sprintf("cid-%v", rand.Int63())
|
||||
seqFirst = rand.Int63()
|
||||
msgs = []*sdkws.MsgData{}
|
||||
)
|
||||
|
||||
seqs := []int64{}
|
||||
for i := 0; i < 100; i++ {
|
||||
msgs = append(msgs, &sdkws.MsgData{
|
||||
Seq: seqFirst + int64(i),
|
||||
SendID: fmt.Sprintf("fake-sendid-%v", i),
|
||||
})
|
||||
seqs = append(seqs, seqFirst+int64(i))
|
||||
}
|
||||
|
||||
// set data to cache
|
||||
testPipeSetMessageToCache(t, cid, msgs)
|
||||
|
||||
// get data from cache with parallet mode
|
||||
testParallelGetMessagesBySeq(t, cid, seqs, msgs)
|
||||
|
||||
// get data from cache with pipeline mode
|
||||
testPipeGetMessagesBySeq(t, cid, seqs, msgs)
|
||||
}
|
||||
|
||||
func testParallelGetMessagesBySeq(t *testing.T, cid string, seqs []int64, inputMsgs []*sdkws.MsgData) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
respMsgs, failedSeqs, err := cacher.ParallelGetMessagesBySeq(context.Background(), cid, seqs)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, len(failedSeqs))
|
||||
assert.Equal(t, len(respMsgs), len(seqs))
|
||||
|
||||
// validate
|
||||
for idx, msg := range respMsgs {
|
||||
assert.Equal(t, msg.Seq, inputMsgs[idx].Seq)
|
||||
assert.Equal(t, msg.SendID, inputMsgs[idx].SendID)
|
||||
}
|
||||
}
|
||||
|
||||
func testPipeGetMessagesBySeq(t *testing.T, cid string, seqs []int64, inputMsgs []*sdkws.MsgData) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
respMsgs, failedSeqs, err := cacher.PipeGetMessagesBySeq(context.Background(), cid, seqs)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, len(failedSeqs))
|
||||
assert.Equal(t, len(respMsgs), len(seqs))
|
||||
|
||||
// validate
|
||||
for idx, msg := range respMsgs {
|
||||
assert.Equal(t, msg.Seq, inputMsgs[idx].Seq)
|
||||
assert.Equal(t, msg.SendID, inputMsgs[idx].SendID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMessagesBySeqWithEmptySeqs(t *testing.T) {
|
||||
var (
|
||||
cid = fmt.Sprintf("cid-%v", rand.Int63())
|
||||
seqFirst int64 = 0
|
||||
msgs = []*sdkws.MsgData{}
|
||||
)
|
||||
|
||||
seqs := []int64{}
|
||||
for i := 0; i < 100; i++ {
|
||||
msgs = append(msgs, &sdkws.MsgData{
|
||||
Seq: seqFirst + int64(i),
|
||||
SendID: fmt.Sprintf("fake-sendid-%v", i),
|
||||
})
|
||||
seqs = append(seqs, seqFirst+int64(i))
|
||||
}
|
||||
|
||||
// don't set cache, only get data from cache.
|
||||
|
||||
// get data from cache with parallet mode
|
||||
testParallelGetMessagesBySeqWithEmptry(t, cid, seqs, msgs)
|
||||
|
||||
// get data from cache with pipeline mode
|
||||
testPipeGetMessagesBySeqWithEmptry(t, cid, seqs, msgs)
|
||||
}
|
||||
|
||||
func testParallelGetMessagesBySeqWithEmptry(t *testing.T, cid string, seqs []int64, inputMsgs []*sdkws.MsgData) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
respMsgs, failedSeqs, err := cacher.ParallelGetMessagesBySeq(context.Background(), cid, seqs)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(seqs), len(failedSeqs))
|
||||
assert.Equal(t, 0, len(respMsgs))
|
||||
}
|
||||
|
||||
func testPipeGetMessagesBySeqWithEmptry(t *testing.T, cid string, seqs []int64, inputMsgs []*sdkws.MsgData) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
respMsgs, failedSeqs, err := cacher.PipeGetMessagesBySeq(context.Background(), cid, seqs)
|
||||
assert.Equal(t, err, redis.Nil)
|
||||
assert.Equal(t, len(seqs), len(failedSeqs))
|
||||
assert.Equal(t, 0, len(respMsgs))
|
||||
}
|
||||
|
||||
func TestGetMessagesBySeqWithLostHalfSeqs(t *testing.T) {
|
||||
var (
|
||||
cid = fmt.Sprintf("cid-%v", rand.Int63())
|
||||
seqFirst int64 = 0
|
||||
msgs = []*sdkws.MsgData{}
|
||||
)
|
||||
|
||||
seqs := []int64{}
|
||||
for i := 0; i < 100; i++ {
|
||||
msgs = append(msgs, &sdkws.MsgData{
|
||||
Seq: seqFirst + int64(i),
|
||||
SendID: fmt.Sprintf("fake-sendid-%v", i),
|
||||
})
|
||||
seqs = append(seqs, seqFirst+int64(i))
|
||||
}
|
||||
|
||||
// Only set half the number of messages.
|
||||
testParallelSetMessageToCache(t, cid, msgs[:50])
|
||||
|
||||
// get data from cache with parallet mode
|
||||
testParallelGetMessagesBySeqWithLostHalfSeqs(t, cid, seqs, msgs)
|
||||
|
||||
// get data from cache with pipeline mode
|
||||
testPipeGetMessagesBySeqWithLostHalfSeqs(t, cid, seqs, msgs)
|
||||
}
|
||||
|
||||
func testParallelGetMessagesBySeqWithLostHalfSeqs(t *testing.T, cid string, seqs []int64, inputMsgs []*sdkws.MsgData) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
respMsgs, failedSeqs, err := cacher.ParallelGetMessagesBySeq(context.Background(), cid, seqs)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(seqs)/2, len(failedSeqs))
|
||||
assert.Equal(t, len(seqs)/2, len(respMsgs))
|
||||
|
||||
for idx, msg := range respMsgs {
|
||||
assert.Equal(t, msg.Seq, seqs[idx])
|
||||
}
|
||||
}
|
||||
|
||||
func testPipeGetMessagesBySeqWithLostHalfSeqs(t *testing.T, cid string, seqs []int64, inputMsgs []*sdkws.MsgData) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
respMsgs, failedSeqs, err := cacher.PipeGetMessagesBySeq(context.Background(), cid, seqs)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(seqs)/2, len(failedSeqs))
|
||||
assert.Equal(t, len(seqs)/2, len(respMsgs))
|
||||
|
||||
for idx, msg := range respMsgs {
|
||||
assert.Equal(t, msg.Seq, seqs[idx])
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipeDeleteMessages(t *testing.T) {
|
||||
var (
|
||||
cid = fmt.Sprintf("cid-%v", rand.Int63())
|
||||
seqFirst = rand.Int63()
|
||||
msgs = []*sdkws.MsgData{}
|
||||
)
|
||||
|
||||
var seqs []int64
|
||||
for i := 0; i < 100; i++ {
|
||||
msgs = append(msgs, &sdkws.MsgData{
|
||||
Seq: seqFirst + int64(i),
|
||||
})
|
||||
seqs = append(seqs, msgs[i].Seq)
|
||||
}
|
||||
|
||||
testPipeSetMessageToCache(t, cid, msgs)
|
||||
testPipeDeleteMessagesOK(t, cid, seqs, msgs)
|
||||
|
||||
// set again
|
||||
testPipeSetMessageToCache(t, cid, msgs)
|
||||
testPipeDeleteMessagesMix(t, cid, seqs[:90], msgs)
|
||||
}
|
||||
|
||||
func testPipeDeleteMessagesOK(t *testing.T, cid string, seqs []int64, inputMsgs []*sdkws.MsgData) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
err := cacher.PipeDeleteMessages(context.Background(), cid, seqs)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// validate
|
||||
for _, msg := range inputMsgs {
|
||||
key := cacher.getMessageCacheKey(cid, msg.Seq)
|
||||
val := rdb.Exists(context.Background(), key).Val()
|
||||
assert.EqualValues(t, 0, val)
|
||||
}
|
||||
}
|
||||
|
||||
func testPipeDeleteMessagesMix(t *testing.T, cid string, seqs []int64, inputMsgs []*sdkws.MsgData) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
err := cacher.PipeDeleteMessages(context.Background(), cid, seqs)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// validate
|
||||
for idx, msg := range inputMsgs {
|
||||
key := cacher.getMessageCacheKey(cid, msg.Seq)
|
||||
val, err := rdb.Exists(context.Background(), key).Result()
|
||||
assert.Nil(t, err)
|
||||
if idx < 90 {
|
||||
assert.EqualValues(t, 0, val) // not exists
|
||||
continue
|
||||
}
|
||||
|
||||
assert.EqualValues(t, 1, val) // exists
|
||||
}
|
||||
}
|
||||
|
||||
func TestParallelDeleteMessages(t *testing.T) {
|
||||
var (
|
||||
cid = fmt.Sprintf("cid-%v", rand.Int63())
|
||||
seqFirst = rand.Int63()
|
||||
msgs = []*sdkws.MsgData{}
|
||||
)
|
||||
|
||||
var seqs []int64
|
||||
for i := 0; i < 100; i++ {
|
||||
msgs = append(msgs, &sdkws.MsgData{
|
||||
Seq: seqFirst + int64(i),
|
||||
})
|
||||
seqs = append(seqs, msgs[i].Seq)
|
||||
}
|
||||
|
||||
randSeqs := []int64{}
|
||||
for i := seqFirst + 100; i < seqFirst+200; i++ {
|
||||
randSeqs = append(randSeqs, i)
|
||||
}
|
||||
|
||||
testParallelSetMessageToCache(t, cid, msgs)
|
||||
testParallelDeleteMessagesOK(t, cid, seqs, msgs)
|
||||
|
||||
// set again
|
||||
testParallelSetMessageToCache(t, cid, msgs)
|
||||
testParallelDeleteMessagesMix(t, cid, seqs[:90], msgs, 90)
|
||||
testParallelDeleteMessagesOK(t, cid, seqs[90:], msgs[:90])
|
||||
|
||||
// set again
|
||||
testParallelSetMessageToCache(t, cid, msgs)
|
||||
testParallelDeleteMessagesMix(t, cid, randSeqs, msgs, 0)
|
||||
}
|
||||
|
||||
func testParallelDeleteMessagesOK(t *testing.T, cid string, seqs []int64, inputMsgs []*sdkws.MsgData) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
err := cacher.PipeDeleteMessages(context.Background(), cid, seqs)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// validate
|
||||
for _, msg := range inputMsgs {
|
||||
key := cacher.getMessageCacheKey(cid, msg.Seq)
|
||||
val := rdb.Exists(context.Background(), key).Val()
|
||||
assert.EqualValues(t, 0, val)
|
||||
}
|
||||
}
|
||||
|
||||
func testParallelDeleteMessagesMix(t *testing.T, cid string, seqs []int64, inputMsgs []*sdkws.MsgData, lessValNonExists int) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
cacher := msgCache{rdb: rdb}
|
||||
|
||||
err := cacher.PipeDeleteMessages(context.Background(), cid, seqs)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// validate
|
||||
for idx, msg := range inputMsgs {
|
||||
key := cacher.getMessageCacheKey(cid, msg.Seq)
|
||||
val, err := rdb.Exists(context.Background(), key).Result()
|
||||
assert.Nil(t, err)
|
||||
if idx < lessValNonExists {
|
||||
assert.EqualValues(t, 0, val) // not exists
|
||||
continue
|
||||
}
|
||||
|
||||
assert.EqualValues(t, 1, val) // exists
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,14 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
@@ -32,7 +37,6 @@ import (
|
||||
unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
|
||||
pbmsg "github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
@@ -398,7 +402,7 @@ func (db *commonMsgDatabase) BatchInsertChat2Cache(ctx context.Context, conversa
|
||||
func (db *commonMsgDatabase) getMsgBySeqs(ctx context.Context, userID, conversationID string, seqs []int64) (totalMsgs []*sdkws.MsgData, err error) {
|
||||
for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, seqs) {
|
||||
// log.ZDebug(ctx, "getMsgBySeqs", "docID", docID, "seqs", seqs)
|
||||
msgs, err := db.findMsgInfoBySeq(ctx, userID, docID, seqs)
|
||||
msgs, err := db.findMsgInfoBySeq(ctx, userID, docID, conversationID, seqs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -409,13 +413,71 @@ func (db *commonMsgDatabase) getMsgBySeqs(ctx context.Context, userID, conversat
|
||||
return totalMsgs, nil
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) findMsgInfoBySeq(ctx context.Context, userID, docID string, seqs []int64) (totalMsgs []*unrelationtb.MsgInfoModel, err error) {
|
||||
msgs, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, userID, docID, seqs)
|
||||
for _, msg := range msgs {
|
||||
if msg.IsRead {
|
||||
msg.Msg.IsRead = true
|
||||
func (db *commonMsgDatabase) handlerDBMsg(ctx context.Context, cache map[int64][]*unrelationtb.MsgInfoModel, userID, conversationID string, msg *unrelationtb.MsgInfoModel) {
|
||||
if msg.IsRead {
|
||||
msg.Msg.IsRead = true
|
||||
}
|
||||
if msg.Msg.ContentType != constant.Quote {
|
||||
return
|
||||
}
|
||||
if msg.Msg.Content == "" {
|
||||
return
|
||||
}
|
||||
var quoteMsg struct {
|
||||
Text string `json:"text,omitempty"`
|
||||
QuoteMessage *sdkws.MsgData `json:"quoteMessage,omitempty"`
|
||||
MessageEntityList json.RawMessage `json:"messageEntityList,omitempty"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(msg.Msg.Content), "eMsg); err != nil {
|
||||
log.ZError(ctx, "json.Unmarshal", err)
|
||||
return
|
||||
}
|
||||
if quoteMsg.QuoteMessage == nil || quoteMsg.QuoteMessage.ContentType == constant.MsgRevokeNotification {
|
||||
return
|
||||
}
|
||||
var msgs []*unrelationtb.MsgInfoModel
|
||||
if v, ok := cache[quoteMsg.QuoteMessage.Seq]; ok {
|
||||
msgs = v
|
||||
} else {
|
||||
if quoteMsg.QuoteMessage.Seq > 0 {
|
||||
ms, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, userID, db.msg.GetDocID(conversationID, quoteMsg.QuoteMessage.Seq), []int64{quoteMsg.QuoteMessage.Seq})
|
||||
if err != nil {
|
||||
log.ZError(ctx, "GetMsgBySeqIndexIn1Doc", err, "conversationID", conversationID, "seq", quoteMsg.QuoteMessage.Seq)
|
||||
return
|
||||
}
|
||||
msgs = ms
|
||||
cache[quoteMsg.QuoteMessage.Seq] = ms
|
||||
}
|
||||
}
|
||||
if len(msgs) != 0 && msgs[0].Msg.ContentType != constant.MsgRevokeNotification {
|
||||
return
|
||||
}
|
||||
quoteMsg.QuoteMessage.ContentType = constant.MsgRevokeNotification
|
||||
if len(msgs) > 0 {
|
||||
quoteMsg.QuoteMessage.Content = []byte(msgs[0].Msg.Content)
|
||||
} else {
|
||||
quoteMsg.QuoteMessage.Content = []byte("{}")
|
||||
}
|
||||
data, err := json.Marshal("eMsg)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "json.Marshal", err)
|
||||
return
|
||||
}
|
||||
msg.Msg.Content = string(data)
|
||||
if _, err := db.msgDocDatabase.UpdateMsg(ctx, db.msg.GetDocID(conversationID, msg.Msg.Seq), db.msg.GetMsgIndex(msg.Msg.Seq), "msg", msg.Msg); err != nil {
|
||||
log.ZError(ctx, "UpdateMsgContent", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) findMsgInfoBySeq(ctx context.Context, userID, docID string, conversationID string, seqs []int64) (totalMsgs []*unrelationtb.MsgInfoModel, err error) {
|
||||
msgs, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, userID, docID, seqs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tempCache := make(map[int64][]*unrelationtb.MsgInfoModel)
|
||||
for _, msg := range msgs {
|
||||
db.handlerDBMsg(ctx, tempCache, userID, conversationID, msg)
|
||||
}
|
||||
return msgs, err
|
||||
}
|
||||
|
||||
@@ -423,7 +485,7 @@ func (db *commonMsgDatabase) getMsgBySeqsRange(ctx context.Context, userID strin
|
||||
log.ZDebug(ctx, "getMsgBySeqsRange", "conversationID", conversationID, "allSeqs", allSeqs, "begin", begin, "end", end)
|
||||
for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, allSeqs) {
|
||||
log.ZDebug(ctx, "getMsgBySeqsRange", "docID", docID, "seqs", seqs)
|
||||
msgs, err := db.findMsgInfoBySeq(ctx, userID, docID, seqs)
|
||||
msgs, err := db.findMsgInfoBySeq(ctx, userID, docID, conversationID, seqs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -144,9 +144,9 @@ func Test_BatchInsertChat2DB(t *testing.T) {
|
||||
}
|
||||
|
||||
func GetDB() *commonMsgDatabase {
|
||||
config.Config.Mongo.Address = []string{"192.168.44.128:37017"}
|
||||
config.Config.Mongo.Address = []string{"203.56.175.233:37017"}
|
||||
// config.Config.Mongo.Timeout = 60
|
||||
config.Config.Mongo.Database = "openIM"
|
||||
config.Config.Mongo.Database = "openIM_v3"
|
||||
// config.Config.Mongo.Source = "admin"
|
||||
config.Config.Mongo.Username = "root"
|
||||
config.Config.Mongo.Password = "openIM123"
|
||||
@@ -232,37 +232,17 @@ func Test_FindBySeq(t *testing.T) {
|
||||
// }
|
||||
//}
|
||||
|
||||
//func Test_Delete1(t *testing.T) {
|
||||
// config.Config.Mongo.DBAddress = []string{"192.168.44.128:37017"}
|
||||
// config.Config.Mongo.DBTimeout = 60
|
||||
// config.Config.Mongo.DBDatabase = "openIM"
|
||||
// config.Config.Mongo.DBSource = "admin"
|
||||
// config.Config.Mongo.DBUserName = "root"
|
||||
// config.Config.Mongo.DBPassword = "openIM123"
|
||||
// config.Config.Mongo.DBMaxPoolSize = 100
|
||||
// config.Config.Mongo.DBRetainChatRecords = 3650
|
||||
// config.Config.Mongo.ChatRecordsClearTime = "0 2 * * 3"
|
||||
//
|
||||
// mongo, err := unrelation.NewMongo()
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// err = mongo.GetDatabase().Client().Ping(context.Background(), nil)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//
|
||||
// c := mongo.GetClient().Database("openIM").Collection("msg")
|
||||
//
|
||||
// var o unrelationtb.MsgDocModel
|
||||
//
|
||||
// err = c.FindOne(context.Background(), bson.M{"doc_id": "test:0"}).Decode(&o)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//
|
||||
// for i, model := range o.Msg {
|
||||
// fmt.Println(i, model == nil)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
func TestName(t *testing.T) {
|
||||
db := GetDB()
|
||||
var seqs []int64
|
||||
for i := int64(1); i <= 4; i++ {
|
||||
seqs = append(seqs, i)
|
||||
}
|
||||
msgs, err := db.getMsgBySeqsRange(context.Background(), "4931176757", "si_3866692501_4931176757", seqs, seqs[0], seqs[len(seqs)-1])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(msgs)
|
||||
|
||||
}
|
||||
|
||||
@@ -175,7 +175,6 @@ func (c *Controller) CompleteUpload(ctx context.Context, uploadID string, partHa
|
||||
return nil, err
|
||||
}
|
||||
if md5Sum := md5.Sum([]byte(strings.Join(partHashs, partSeparator))); hex.EncodeToString(md5Sum[:]) != upload.Hash {
|
||||
fmt.Println("CompleteUpload sum:", hex.EncodeToString(md5Sum[:]), "upload hash:", upload.Hash)
|
||||
return nil, errors.New("md5 mismatching")
|
||||
}
|
||||
if info, err := c.StatObject(ctx, c.HashPath(upload.Hash)); err == nil {
|
||||
|
||||
@@ -3,7 +3,7 @@ package ginprometheus
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
@@ -17,40 +17,42 @@ import (
|
||||
var defaultMetricPath = "/metrics"
|
||||
|
||||
// counter, counter_vec, gauge, gauge_vec,
|
||||
// histogram, histogram_vec, summary, summary_vec
|
||||
var reqCnt = &Metric{
|
||||
ID: "reqCnt",
|
||||
Name: "requests_total",
|
||||
Description: "How many HTTP requests processed, partitioned by status code and HTTP method.",
|
||||
Type: "counter_vec",
|
||||
Args: []string{"code", "method", "handler", "host", "url"}}
|
||||
// histogram, histogram_vec, summary, summary_vec.
|
||||
var (
|
||||
reqCounter = &Metric{
|
||||
ID: "reqCnt",
|
||||
Name: "requests_total",
|
||||
Description: "How many HTTP requests processed, partitioned by status code and HTTP method.",
|
||||
Type: "counter_vec",
|
||||
Args: []string{"code", "method", "handler", "host", "url"}}
|
||||
|
||||
var reqDur = &Metric{
|
||||
ID: "reqDur",
|
||||
Name: "request_duration_seconds",
|
||||
Description: "The HTTP request latencies in seconds.",
|
||||
Type: "histogram_vec",
|
||||
Args: []string{"code", "method", "url"},
|
||||
}
|
||||
reqDuration = &Metric{
|
||||
ID: "reqDur",
|
||||
Name: "request_duration_seconds",
|
||||
Description: "The HTTP request latencies in seconds.",
|
||||
Type: "histogram_vec",
|
||||
Args: []string{"code", "method", "url"},
|
||||
}
|
||||
|
||||
var resSz = &Metric{
|
||||
ID: "resSz",
|
||||
Name: "response_size_bytes",
|
||||
Description: "The HTTP response sizes in bytes.",
|
||||
Type: "summary"}
|
||||
resSize = &Metric{
|
||||
ID: "resSz",
|
||||
Name: "response_size_bytes",
|
||||
Description: "The HTTP response sizes in bytes.",
|
||||
Type: "summary"}
|
||||
|
||||
var reqSz = &Metric{
|
||||
ID: "reqSz",
|
||||
Name: "request_size_bytes",
|
||||
Description: "The HTTP request sizes in bytes.",
|
||||
Type: "summary"}
|
||||
reqSize = &Metric{
|
||||
ID: "reqSz",
|
||||
Name: "request_size_bytes",
|
||||
Description: "The HTTP request sizes in bytes.",
|
||||
Type: "summary"}
|
||||
|
||||
var standardMetrics = []*Metric{
|
||||
reqCnt,
|
||||
reqDur,
|
||||
resSz,
|
||||
reqSz,
|
||||
}
|
||||
standardMetrics = []*Metric{
|
||||
reqCounter,
|
||||
reqDuration,
|
||||
resSize,
|
||||
reqSize,
|
||||
}
|
||||
)
|
||||
|
||||
/*
|
||||
RequestCounterURLLabelMappingFn is a function which can be supplied to the middleware to control
|
||||
@@ -74,7 +76,7 @@ which would map "/customer/alice" and "/customer/bob" to their template "/custom
|
||||
type RequestCounterURLLabelMappingFn func(c *gin.Context) string
|
||||
|
||||
// Metric is a definition for the name, description, type, ID, and
|
||||
// prometheus.Collector type (i.e. CounterVec, Summary, etc) of each metric
|
||||
// prometheus.Collector type (i.e. CounterVec, Summary, etc) of each metric.
|
||||
type Metric struct {
|
||||
MetricCollector prometheus.Collector
|
||||
ID string
|
||||
@@ -84,7 +86,7 @@ type Metric struct {
|
||||
Args []string
|
||||
}
|
||||
|
||||
// Prometheus contains the metrics gathered by the instance and its path
|
||||
// Prometheus contains the metrics gathered by the instance and its path.
|
||||
type Prometheus struct {
|
||||
reqCnt *prometheus.CounterVec
|
||||
reqDur *prometheus.HistogramVec
|
||||
@@ -102,7 +104,7 @@ type Prometheus struct {
|
||||
URLLabelFromContext string
|
||||
}
|
||||
|
||||
// PrometheusPushGateway contains the configuration for pushing to a Prometheus pushgateway (optional)
|
||||
// PrometheusPushGateway contains the configuration for pushing to a Prometheus pushgateway (optional).
|
||||
type PrometheusPushGateway struct {
|
||||
|
||||
// Push interval in seconds
|
||||
@@ -112,7 +114,7 @@ type PrometheusPushGateway struct {
|
||||
// where JOBNAME can be any string of your choice
|
||||
PushGatewayURL string
|
||||
|
||||
// Local metrics URL where metrics are fetched from, this could be ommited in the future
|
||||
// Local metrics URL where metrics are fetched from, this could be omitted in the future
|
||||
// if implemented using prometheus common/expfmt instead
|
||||
MetricsURL string
|
||||
|
||||
@@ -120,9 +122,11 @@ type PrometheusPushGateway struct {
|
||||
Job string
|
||||
}
|
||||
|
||||
// NewPrometheus generates a new set of metrics with a certain subsystem name
|
||||
// NewPrometheus generates a new set of metrics with a certain subsystem name.
|
||||
func NewPrometheus(subsystem string, customMetricsList ...[]*Metric) *Prometheus {
|
||||
subsystem = "app"
|
||||
if subsystem == "" {
|
||||
subsystem = "app"
|
||||
}
|
||||
|
||||
var metricsList []*Metric
|
||||
|
||||
@@ -131,16 +135,13 @@ func NewPrometheus(subsystem string, customMetricsList ...[]*Metric) *Prometheus
|
||||
} else if len(customMetricsList) == 1 {
|
||||
metricsList = customMetricsList[0]
|
||||
}
|
||||
|
||||
for _, metric := range standardMetrics {
|
||||
metricsList = append(metricsList, metric)
|
||||
}
|
||||
metricsList = append(metricsList, standardMetrics...)
|
||||
|
||||
p := &Prometheus{
|
||||
MetricsList: metricsList,
|
||||
MetricsPath: defaultMetricPath,
|
||||
ReqCntURLLabelMappingFn: func(c *gin.Context) string {
|
||||
return c.Request.URL.Path
|
||||
return c.FullPath() // e.g. /user/:id , /user/:id/info
|
||||
},
|
||||
}
|
||||
|
||||
@@ -150,7 +151,7 @@ func NewPrometheus(subsystem string, customMetricsList ...[]*Metric) *Prometheus
|
||||
}
|
||||
|
||||
// SetPushGateway sends metrics to a remote pushgateway exposed on pushGatewayURL
|
||||
// every pushIntervalSeconds. Metrics are fetched from metricsURL
|
||||
// every pushIntervalSeconds. Metrics are fetched from metricsURL.
|
||||
func (p *Prometheus) SetPushGateway(pushGatewayURL, metricsURL string, pushIntervalSeconds time.Duration) {
|
||||
p.Ppg.PushGatewayURL = pushGatewayURL
|
||||
p.Ppg.MetricsURL = metricsURL
|
||||
@@ -158,13 +159,13 @@ func (p *Prometheus) SetPushGateway(pushGatewayURL, metricsURL string, pushInter
|
||||
p.startPushTicker()
|
||||
}
|
||||
|
||||
// SetPushGatewayJob job name, defaults to "gin"
|
||||
// SetPushGatewayJob job name, defaults to "gin".
|
||||
func (p *Prometheus) SetPushGatewayJob(j string) {
|
||||
p.Ppg.Job = j
|
||||
}
|
||||
|
||||
// SetListenAddress for exposing metrics on address. If not set, it will be exposed at the
|
||||
// same address of the gin engine that is being used
|
||||
// same address of the gin engine that is being used.
|
||||
func (p *Prometheus) SetListenAddress(address string) {
|
||||
p.listenAddress = address
|
||||
if p.listenAddress != "" {
|
||||
@@ -181,7 +182,7 @@ func (p *Prometheus) SetListenAddressWithRouter(listenAddress string, r *gin.Eng
|
||||
}
|
||||
}
|
||||
|
||||
// SetMetricsPath set metrics paths
|
||||
// SetMetricsPath set metrics paths.
|
||||
func (p *Prometheus) SetMetricsPath(e *gin.Engine) {
|
||||
|
||||
if p.listenAddress != "" {
|
||||
@@ -192,7 +193,7 @@ func (p *Prometheus) SetMetricsPath(e *gin.Engine) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetMetricsPathWithAuth set metrics paths with authentication
|
||||
// SetMetricsPathWithAuth set metrics paths with authentication.
|
||||
func (p *Prometheus) SetMetricsPathWithAuth(e *gin.Engine, accounts gin.Accounts) {
|
||||
|
||||
if p.listenAddress != "" {
|
||||
@@ -205,34 +206,43 @@ func (p *Prometheus) SetMetricsPathWithAuth(e *gin.Engine, accounts gin.Accounts
|
||||
}
|
||||
|
||||
func (p *Prometheus) runServer() {
|
||||
if p.listenAddress != "" {
|
||||
go p.router.Run(p.listenAddress)
|
||||
}
|
||||
go p.router.Run(p.listenAddress)
|
||||
}
|
||||
|
||||
func (p *Prometheus) getMetrics() []byte {
|
||||
response, _ := http.Get(p.Ppg.MetricsURL)
|
||||
response, err := http.Get(p.Ppg.MetricsURL)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
body, _ := ioutil.ReadAll(response.Body)
|
||||
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return body
|
||||
}
|
||||
|
||||
var hostname, _ = os.Hostname()
|
||||
|
||||
func (p *Prometheus) getPushGatewayURL() string {
|
||||
h, _ := os.Hostname()
|
||||
if p.Ppg.Job == "" {
|
||||
p.Ppg.Job = "gin"
|
||||
}
|
||||
return p.Ppg.PushGatewayURL + "/metrics/job/" + p.Ppg.Job + "/instance/" + h
|
||||
return p.Ppg.PushGatewayURL + "/metrics/job/" + p.Ppg.Job + "/instance/" + hostname
|
||||
}
|
||||
|
||||
func (p *Prometheus) sendMetricsToPushGateway(metrics []byte) {
|
||||
req, err := http.NewRequest("POST", p.getPushGatewayURL(), bytes.NewBuffer(metrics))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
if _, err = client.Do(req); err != nil {
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Println("Error sending to push gateway error:", err.Error())
|
||||
}
|
||||
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
func (p *Prometheus) startPushTicker() {
|
||||
@@ -244,7 +254,7 @@ func (p *Prometheus) startPushTicker() {
|
||||
}()
|
||||
}
|
||||
|
||||
// NewMetric associates prometheus.Collector based on Metric.Type
|
||||
// NewMetric associates prometheus.Collector based on Metric.Type.
|
||||
func NewMetric(m *Metric, subsystem string) prometheus.Collector {
|
||||
var metric prometheus.Collector
|
||||
switch m.Type {
|
||||
@@ -321,20 +331,20 @@ func NewMetric(m *Metric, subsystem string) prometheus.Collector {
|
||||
}
|
||||
|
||||
func (p *Prometheus) registerMetrics(subsystem string) {
|
||||
|
||||
for _, metricDef := range p.MetricsList {
|
||||
metric := NewMetric(metricDef, subsystem)
|
||||
if err := prometheus.Register(metric); err != nil {
|
||||
fmt.Println("could not be registered in Prometheus,metricDef.Name:", metricDef.Name, " error:", err.Error())
|
||||
}
|
||||
|
||||
switch metricDef {
|
||||
case reqCnt:
|
||||
case reqCounter:
|
||||
p.reqCnt = metric.(*prometheus.CounterVec)
|
||||
case reqDur:
|
||||
case reqDuration:
|
||||
p.reqDur = metric.(*prometheus.HistogramVec)
|
||||
case resSz:
|
||||
case resSize:
|
||||
p.resSz = metric.(prometheus.Summary)
|
||||
case reqSz:
|
||||
case reqSize:
|
||||
p.reqSz = metric.(prometheus.Summary)
|
||||
}
|
||||
metricDef.MetricCollector = metric
|
||||
@@ -353,7 +363,7 @@ func (p *Prometheus) UseWithAuth(e *gin.Engine, accounts gin.Accounts) {
|
||||
p.SetMetricsPathWithAuth(e, accounts)
|
||||
}
|
||||
|
||||
// HandlerFunc defines handler function for middleware
|
||||
// HandlerFunc defines handler function for middleware.
|
||||
func (p *Prometheus) HandlerFunc() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if c.Request.URL.Path == p.MetricsPath {
|
||||
@@ -393,7 +403,7 @@ func prometheusHandler() gin.HandlerFunc {
|
||||
}
|
||||
|
||||
func computeApproximateRequestSize(r *http.Request) int {
|
||||
s := 0
|
||||
var s int
|
||||
if r.URL != nil {
|
||||
s = len(r.URL.Path)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
package kafka
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
@@ -49,8 +51,23 @@ func NewKafkaProducer(addr []string, topic string) *Producer {
|
||||
p.config = sarama.NewConfig() // Instantiate a sarama Config
|
||||
p.config.Producer.Return.Successes = true // Whether to enable the successes channel to be notified after the message is sent successfully
|
||||
p.config.Producer.Return.Errors = true
|
||||
p.config.Producer.RequiredAcks = sarama.WaitForAll // Set producer Message Reply level 0 1 all
|
||||
p.config.Producer.Partitioner = sarama.NewHashPartitioner // Set the hash-key automatic hash partition. When sending a message, you must specify the key value of the message. If there is no key, the partition will be selected randomly
|
||||
|
||||
var producerAck = sarama.WaitForAll // default: WaitForAll
|
||||
switch strings.ToLower(config.Config.Kafka.ProducerAck) {
|
||||
case "no_response":
|
||||
producerAck = sarama.NoResponse
|
||||
case "wait_for_local":
|
||||
producerAck = sarama.WaitForLocal
|
||||
case "wait_for_all":
|
||||
producerAck = sarama.WaitForAll
|
||||
}
|
||||
p.config.Producer.RequiredAcks = producerAck
|
||||
|
||||
var compress = sarama.CompressionNone // default: no compress
|
||||
_ = compress.UnmarshalText(bytes.ToLower([]byte(config.Config.Kafka.CompressType)))
|
||||
p.config.Producer.Compression = compress
|
||||
|
||||
if config.Config.Kafka.Username != "" && config.Config.Kafka.Password != "" {
|
||||
p.config.Net.SASL.Enable = true
|
||||
p.config.Net.SASL.User = config.Config.Kafka.Username
|
||||
|
||||
@@ -118,8 +118,9 @@ func GetNotificationConversationIDByConversationID(conversationID string) string
|
||||
l := strings.Split(conversationID, "_")
|
||||
if len(l) > 1 {
|
||||
l[0] = "n"
|
||||
return conversationID
|
||||
return strings.Join(l, "_")
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ scripts/
|
||||
├── demo.sh # Demonstration or example script.
|
||||
├── docker-check-service.sh # Docker script to check services' status.
|
||||
├── docker-start-all.sh # Docker script to start all containers/services.
|
||||
├── ensure_tag.sh # Ensure correct tags or labeling.
|
||||
├── ensure-tag.sh # Ensure correct tags or labeling.
|
||||
├── env_check.sh # Environment verification and checking.
|
||||
├── gen-swagger-docs.sh # Script to generate Swagger documentation.
|
||||
├── genconfig.sh # Generate configuration files.
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#Include shell font styles and some basic information
|
||||
SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
OPENIM_ROOT=$(dirname "${SCRIPTS_ROOT}")/..
|
||||
|
||||
#Include shell font styles and some basic information
|
||||
source $SCRIPTS_ROOT/path_info.sh
|
||||
source $SCRIPTS_ROOT/lib/init.sh
|
||||
|
||||
cd $SCRIPTS_ROOT
|
||||
|
||||
echo -e "check time synchronize.................................."
|
||||
t=`curl http://time.akamai.com/?iso -s`
|
||||
t1=`date -d $t +%s`
|
||||
t2=`date +%s`
|
||||
let between=t2-t1
|
||||
if [[ $between -gt 10 ]] || [[ $between -lt -10 ]]; then
|
||||
echo -e ${RED_PREFIX}"Warning: The difference between the iso time and the server's time is too large: "$between"s" ${COLOR_SUFFIX}
|
||||
else
|
||||
echo -e ${GREEN_PREFIX} "ok: Server time is synchronized " ${COLOR_SUFFIX}
|
||||
fi
|
||||
|
||||
|
||||
echo -e "check login user........................................"
|
||||
user=`whoami`
|
||||
if [ $user == "root" ] ; then
|
||||
echo -e ${GREEN_PREFIX} "ok: login user is root" ${COLOR_SUFFIX}
|
||||
else
|
||||
echo -e ${RED_PREFIX}"Warning: The current user is not root "${COLOR_SUFFIX}
|
||||
fi
|
||||
|
||||
echo -e "check docker............................................"
|
||||
docker_running=`systemctl status docker | grep running | grep active | wc -l`
|
||||
|
||||
docker_version=`docker-compose -v; docker -v`
|
||||
|
||||
if [ $docker_running -gt 0 ]; then
|
||||
echo -e ${GREEN_PREFIX} "ok: docker is running" ${COLOR_SUFFIX}
|
||||
echo -e ${GREEN_PREFIX} $docker_version ${COLOR_SUFFIX}
|
||||
|
||||
else
|
||||
echo -e ${RED_PREFIX}"docker not running"${COLOR_SUFFIX}
|
||||
fi
|
||||
|
||||
|
||||
echo -e "check environment......................................."
|
||||
SYSTEM=`uname -s`
|
||||
if [ $SYSTEM != "Linux" ] ; then
|
||||
echo -e ${RED_PREFIX}"Warning: Currently only Linux is supported"${COLOR_SUFFIX}
|
||||
else
|
||||
echo -e ${GREEN_PREFIX} "ok: system is linux"${COLOR_SUFFIX}
|
||||
fi
|
||||
|
||||
echo -e "check memory............................................"
|
||||
available=`free -m | grep Mem | awk '{print $NF}'`
|
||||
if [ $available -lt 2000 ] ; then
|
||||
echo -e ${RED_PREFIX}"Warning: Your memory not enough, available is: " "$available"m${COLOR_SUFFIX}"\c"
|
||||
echo -e ${RED_PREFIX}", must be greater than 2000m"${COLOR_SUFFIX}
|
||||
else
|
||||
echo -e ${GREEN_PREFIX} "ok: available memory is: "$available"m${COLOR_SUFFIX}"
|
||||
fi
|
||||
@@ -13,8 +13,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# 本脚本功能:根据 scripts/environment.sh 配置,生成 OPENIM 组件 YAML 配置文件。
|
||||
# 示例:./scripts/genconfig.sh scripts/install/environment.sh scripts/template/config.yaml
|
||||
# Function of this script: Generate the OPENIM component YAML configuration file according to the scripts/environment.sh configuration.
|
||||
# eg:./scripts/genconfig.sh scripts/install/environment.sh scripts/template/config.yaml
|
||||
# Read: https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md
|
||||
|
||||
env_file="$1"
|
||||
@@ -29,8 +29,17 @@ if [ $# -ne 2 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if the required commands exist
|
||||
openim::util::require-dig
|
||||
result=$?
|
||||
if [ $result -ne 0 ]; then
|
||||
openim::log::info "Please install 'dig' to use this feature."
|
||||
openim::log::info "Installation instructions:"
|
||||
openim::log::info " For Ubuntu/Debian: sudo apt-get install dnsutils"
|
||||
openim::log::info " For CentOS/RedHat: sudo yum install bind-utils"
|
||||
openim::log::info " For macOS: 'dig' should be preinstalled. If missing, try: brew install bind"
|
||||
openim::log::info " For Windows: Install BIND9 tools from https://www.isc.org/download/"
|
||||
openim::log::error_exit "Error: 'dig' command is required but not installed."
|
||||
fi
|
||||
|
||||
source "${env_file}"
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ readonly ENV_FILE=${ENV_FILE:-"${OPENIM_ROOT}/scripts/install/environment.sh"}
|
||||
declare -A TEMPLATES=(
|
||||
["${OPENIM_ROOT}/deployments/templates/env_template.yaml"]="${OPENIM_ROOT}/.env"
|
||||
["${OPENIM_ROOT}/deployments/templates/openim.yaml"]="${OPENIM_ROOT}/config/config.yaml"
|
||||
["${OPENIM_ROOT}/deployments/templates/prometheus.yml"]="${OPENIM_ROOT}/config/prometheus.yml"
|
||||
["${OPENIM_ROOT}/deployments/templates/alertmanager.yml"]="${OPENIM_ROOT}/config/alertmanager.yml"
|
||||
)
|
||||
|
||||
for template in "${!TEMPLATES[@]}"; do
|
||||
@@ -45,6 +47,7 @@ for template in "${!TEMPLATES[@]}"; do
|
||||
openim::log::error "Error processing template file ${template}"
|
||||
exit 1
|
||||
}
|
||||
sleep 0.5
|
||||
done
|
||||
done
|
||||
|
||||
|
||||
@@ -116,7 +116,12 @@ LAST_OCTET=$((LAST_OCTET + 1))
|
||||
PROMETHEUS_NETWORK_ADDRESS=$(generate_ip)
|
||||
LAST_OCTET=$((LAST_OCTET + 1))
|
||||
GRAFANA_NETWORK_ADDRESS=$(generate_ip)
|
||||
|
||||
LAST_OCTET=$((LAST_OCTET + 1))
|
||||
NODE_EXPORTER_NETWORK_ADDRESS=$(generate_ip)
|
||||
LAST_OCTET=$((LAST_OCTET + 1))
|
||||
OPENIM_ADMIN_FRONT_NETWORK_ADDRESS=$(generate_ip)
|
||||
LAST_OCTET=$((LAST_OCTET + 1))
|
||||
ALERT_MANAGER_NETWORK_ADDRESS=$(generate_ip)
|
||||
###################### openim 配置 ######################
|
||||
# read: https://github.com/openimsdk/open-im-server/blob/main/deployment/README.md
|
||||
def "OPENIM_DATA_DIR" "/data/openim"
|
||||
@@ -242,6 +247,9 @@ def "OPENIM_WEB_PORT" "11001" # openim-web的端口
|
||||
def "OPENIM_WEB_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # openim-web的地址
|
||||
def "OPENIM_WEB_DIST_PATH" "/app/dist" # openim-web的dist路径
|
||||
|
||||
###################### openim-admin-front 配置信息 ######################
|
||||
def "OPENIM_ADMIN_FRONT_PORT" "11002" # openim-admin-front的端口
|
||||
|
||||
###################### RPC 配置信息 ######################
|
||||
def "RPC_REGISTER_IP" # RPC的注册IP
|
||||
def "RPC_LISTEN_IP" "0.0.0.0" # RPC的监听IP
|
||||
@@ -250,8 +258,38 @@ def "RPC_LISTEN_IP" "0.0.0.0" # RPC的监听IP
|
||||
def "PROMETHEUS_PORT" "19090" # Prometheus的端口
|
||||
def "PROMETHEUS_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # Prometheus的地址
|
||||
|
||||
###################### node-exporter 配置 ######################
|
||||
def "NODE_EXPORTER_PORT" "19100" # node-exporter的端口
|
||||
def "NODE_EXPORTER_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # node-exporter的地址
|
||||
|
||||
###################### alertmanagerS 配置 ######################
|
||||
def "ALERT_MANAGER_PORT" "19093" # node-exporter的端口
|
||||
def "ALERT_MANAGER_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # node-exporter的地址
|
||||
|
||||
###################### AlertManager Configuration Script ######################
|
||||
# 解析超时
|
||||
readonly ALERTMANAGER_RESOLVE_TIMEOUT=${ALERTMANAGER_RESOLVE_TIMEOUT:-'5m'}
|
||||
# 发件人邮箱
|
||||
readonly ALERTMANAGER_SMTP_FROM=${ALERTMANAGER_SMTP_FROM:-'alert@openim.io'}
|
||||
# SMTP服务器地址和端口
|
||||
readonly ALERTMANAGER_SMTP_SMARTHOST=${ALERTMANAGER_SMTP_SMARTHOST:-'smtp.163.com:465'}
|
||||
# SMTP认证用户名
|
||||
readonly ALERTMANAGER_SMTP_AUTH_USERNAME=${SMTP_USERNAME:-"alert@openim.io"}
|
||||
# SMTP认证密码
|
||||
readonly ALERTMANAGER_SMTP_AUTH_PASSWORD=${SMTP_PASSWORD:-"YOURAUTHPASSWORD"}
|
||||
# SMTP是否需要TLS
|
||||
readonly ALERTMANAGER_SMTP_REQUIRE_TLS=${ALERTMANAGER_SMTP_REQUIRE_TLS:-"false"}
|
||||
# SMTP HELO/EHLO标识符
|
||||
readonly ALERTMANAGER_SMTP_HELLO=${ALERTMANAGER_SMTP_HELLO:-"xxx监控告警"}
|
||||
# 邮箱接收人
|
||||
readonly ALERTMANAGER_EMAIL_TO=${ALERTMANAGER_EMAIL_TO:-"alert@example.com"}
|
||||
# 邮箱主题
|
||||
readonly ALERTMANAGER_EMAIL_SUBJECT=${ALERTMANAGER_EMAIL_SUBJECT:-"{EMAIL_SUBJECT:-'[Alert] Notification'}"}
|
||||
# 是否发送已解决的告警
|
||||
readonly ALERTMANAGER_SEND_RESOLVED=${ALERTMANAGER_SEND_RESOLVED:-"{SEND_RESOLVED:-'true'}"}
|
||||
|
||||
###################### Grafana 配置信息 ######################
|
||||
def "GRAFANA_PORT" "3000" # Grafana的端口
|
||||
def "GRAFANA_PORT" "13000" # Grafana的端口
|
||||
def "GRAFANA_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # Grafana的地址
|
||||
|
||||
###################### RPC Port Configuration Variables ######################
|
||||
@@ -340,8 +378,9 @@ def "IOS_BADGE_COUNT" "true" # IOS徽章计数
|
||||
def "IOS_PRODUCTION" "false" # IOS生产
|
||||
|
||||
###################### Prometheus 配置信息 ######################
|
||||
def "PROMETHEUS_ENABLE" "false" # 是否启用 Prometheus
|
||||
def "PROMETHEUS_URL" "/prometheus"
|
||||
# 是否启用 Prometheus
|
||||
readonly PROMETHEUS_ENABLE=${PROMETHEUS_ENABLE:-'false'}
|
||||
def "PROMETHEUS_URL" "${GRAFANA_ADDRESS}:${GRAFANA_PORT}"
|
||||
# Api 服务的 Prometheus 端口
|
||||
readonly API_PROM_PORT=${API_PROM_PORT:-'20100'}
|
||||
# User 服务的 Prometheus 端口
|
||||
@@ -367,6 +406,7 @@ readonly THIRD_PROM_PORT=${THIRD_PROM_PORT:-'21301'}
|
||||
|
||||
# Message Transfer 服务的 Prometheus 端口列表
|
||||
readonly MSG_TRANSFER_PROM_PORT=${MSG_TRANSFER_PROM_PORT:-'21400, 21401, 21402, 21403'}
|
||||
readonly MSG_TRANSFER_PROM_ADDRESS_PORT=${MSG_TRANSFER_PROM_ADDRESS_PORT:-"${DOCKER_BRIDGE_GATEWAY}:21400, ${DOCKER_BRIDGE_GATEWAY}:21401, ${DOCKER_BRIDGE_GATEWAY}:21402, ${DOCKER_BRIDGE_GATEWAY}:21403"}
|
||||
|
||||
###################### OpenIM openim-api ######################
|
||||
def "OPENIM_API_HOST" "127.0.0.1"
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
# It can be downloaded from the following link:
|
||||
# https://github.com/OpenIMSDK/Open-IM-Protoc/releases/tag/v1.0.0
|
||||
#
|
||||
# About the tool:
|
||||
# https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md
|
||||
# Download link (Windows): https://github.com/OpenIMSDK/Open-IM-Protoc/releases/download/v1.0.0/windows.zip
|
||||
# Download link (Linux): https://github.com/OpenIMSDK/Open-IM-Protoc/releases/download/v1.0.0/linux.zip
|
||||
#
|
||||
|
||||
@@ -76,8 +76,8 @@ function openim::install::install_openim() {
|
||||
openim::log::info "check openim dependency"
|
||||
openim::common::sudo "cp -r ${OPENIM_ROOT}/config/* ${OPENIM_CONFIG_DIR}/"
|
||||
|
||||
echo ${LINUX_PASSWORD} | sudo -S bash -c \
|
||||
"${OPENIM_ROOT}/scripts/genconfig.sh ${ENV_FILE} deployments/templates/openim.yaml > ${OPENIM_CONFIG_DIR}/config.yaml"
|
||||
${OPENIM_ROOT}/scripts/genconfig.sh ${ENV_FILE} ${OPENIM_ROOT}/deployments/templates/openim.yaml > ${OPENIM_CONFIG_DIR}/config.yaml
|
||||
${OPENIM_ROOT}/scripts/genconfig.sh ${ENV_FILE} ${OPENIM_ROOT}/deployments/templates/prometheus.yml > ${OPENIM_CONFIG_DIR}/prometheus.yml
|
||||
|
||||
openim::util::check_ports ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]}
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ function openim::msggateway::status() {
|
||||
# Check the running status of the ${SERVER_NAME}. If active (running) is displayed, the ${SERVER_NAME} is started successfully.
|
||||
systemctl status ${SERVER_NAME}|grep -q 'active' || {
|
||||
openim::log::error "${SERVER_NAME} failed to start, maybe not installed properly"
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -38,9 +38,8 @@ IAM_ROOT=$(dirname "${BASH_SOURCE[0]}")/../..
|
||||
[[ -z ${COMMON_SOURCED} ]] && source ${IAM_ROOT}/scripts/install/common.sh
|
||||
|
||||
# API Server API Address:Port
|
||||
INSECURE_OPENIMAPI=${IAM_APISERVER_HOST}:${API_OPENIM_PORT}
|
||||
INSECURE_OPENIMAPI="http://${OPENIM_API_HOST}:${API_OPENIM_PORT}"
|
||||
INSECURE_OPENIMAUTO=${OPENIM_RPC_AUTH_HOST}:${OPENIM_AUTH_PORT}
|
||||
|
||||
CCURL="curl -f -s -XPOST" # Create
|
||||
UCURL="curl -f -s -XPUT" # Update
|
||||
RCURL="curl -f -s -XGET" # Retrieve
|
||||
@@ -73,7 +72,7 @@ function openim::test::auth() {
|
||||
|
||||
# Define a function to get a token (Admin Token)
|
||||
openim::test::get_token() {
|
||||
token_response=$(${CCURL} "${OperationID}" "${Header}" http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/auth/user_token \
|
||||
token_response=$(${CCURL} "${OperationID}" "${Header}" ${INSECURE_OPENIMAPI}/auth/user_token \
|
||||
-d'{"secret": "'"$SECRET"'","platformID": 1,"userID": "openIM123456"}')
|
||||
token=$(echo $token_response | grep -Po 'token[" :]+\K[^"]+')
|
||||
echo "$token"
|
||||
@@ -94,7 +93,7 @@ EOF
|
||||
)
|
||||
echo "Requesting force logout for user: $request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/auth/force_logout" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/auth/force_logout" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -127,7 +126,7 @@ EOF
|
||||
echo "Request body for user registration: $request_body"
|
||||
|
||||
# Send the registration request
|
||||
local user_register_response=$(${CCURL} "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/user/user_register" -d "${request_body}")
|
||||
local user_register_response=$(${CCURL} "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/user/user_register" -d "${request_body}")
|
||||
|
||||
# Check for errors in the response
|
||||
openim::test::check_error "$user_register_response"
|
||||
@@ -148,7 +147,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/user/account_check" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/user/account_check" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -166,7 +165,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/user/get_users" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/user/get_users" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -184,7 +183,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/user/get_users_info" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/user/get_users_info" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -204,7 +203,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/user/get_users_online_status" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/user/get_users_online_status" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -223,7 +222,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/user/update_user_info" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/user/update_user_info" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -238,7 +237,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/user/get_subscribe_users_status" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/user/get_subscribe_users_status" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -259,7 +258,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/user/subscribe_users_status" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/user/subscribe_users_status" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -275,7 +274,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/user/set_global_msg_recv_opt" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/user/set_global_msg_recv_opt" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -330,7 +329,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/is_friend" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/is_friend" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -346,7 +345,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/delete_friend" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/delete_friend" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -365,7 +364,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/get_friend_apply_list" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/get_friend_apply_list" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -384,7 +383,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/get_friend_list" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/get_friend_list" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -401,7 +400,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/set_friend_remark" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/set_friend_remark" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -419,7 +418,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/add_friend" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/add_friend" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -437,7 +436,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/import_friend" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/import_friend" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -456,7 +455,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/add_friend_response" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/add_friend_response" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -475,7 +474,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/get_self_friend_apply_list" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/get_self_friend_apply_list" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -491,7 +490,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/add_black" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/add_black" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -507,7 +506,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/remove_black" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/remove_black" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -526,7 +525,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/friend/get_black_list" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/friend/get_black_list" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -619,7 +618,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/create_group" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/create_group" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -639,7 +638,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/invite_user_to_group" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/invite_user_to_group" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -656,7 +655,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/transfer_group" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/transfer_group" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -671,7 +670,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/get_groups_info" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/get_groups_info" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -690,7 +689,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/kick_group" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/kick_group" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -706,7 +705,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/get_group_members_info" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/get_group_members_info" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -725,7 +724,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/get_group_member_list" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/get_group_member_list" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -744,7 +743,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/get_joined_group_list" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/get_joined_group_list" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -769,7 +768,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/set_group_member_info" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/set_group_member_info" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -784,7 +783,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/mute_group" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/mute_group" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -799,7 +798,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/cancel_mute_group" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/cancel_mute_group" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -814,7 +813,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/dismiss_group" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/dismiss_group" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -830,7 +829,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/cancel_mute_group_member" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/cancel_mute_group_member" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -848,7 +847,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/join_group" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/join_group" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -873,7 +872,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/set_group_info" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/set_group_info" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -890,7 +889,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/quit_group" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/quit_group" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -909,7 +908,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/get_recv_group_applicationList" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/get_recv_group_applicationList" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -927,7 +926,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/group_application_response" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/group_application_response" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -946,7 +945,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/get_user_req_group_applicationList" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/get_user_req_group_applicationList" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -963,7 +962,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/mute_group_member" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/mute_group_member" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -981,7 +980,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/group/get_group_users_req_application_list" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/group/get_group_users_req_application_list" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -1078,7 +1077,7 @@ function openim::test::group() {
|
||||
|
||||
# Define a function to register a user
|
||||
openim::register_user() {
|
||||
user_register_response=$(${CCURL} "${Header}" http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/user/user_register \
|
||||
user_register_response=$(${CCURL} "${Header}" ${INSECURE_OPENIMAPI}/user/user_register \
|
||||
-d'{
|
||||
"secret": "openIM123",
|
||||
"users": [{"userID": "11111112","nickname": "yourNickname","faceURL": "yourFaceURL"}]
|
||||
@@ -1090,7 +1089,7 @@ openim::register_user() {
|
||||
# Define a function to check the account
|
||||
openim::test::check_account() {
|
||||
local token=$1
|
||||
account_check_response=$(${CCURL} "${Header}" -H"operationID: 1646445464564" -H"token: ${token}" http://localhost:${API_OPENIM_PORT}/user/account_check \
|
||||
account_check_response=$(${CCURL} "${Header}" -H"operationID: 1646445464564" -H"token: ${token}" ${INSECURE_OPENIMAPI}/user/account_check \
|
||||
-d'{
|
||||
"checkUserIDs": ["11111111","11111112"]
|
||||
}')
|
||||
@@ -1164,7 +1163,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/msg/send_msg" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/msg/send_msg" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -1185,7 +1184,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/msg/revoke_msg" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/msg/revoke_msg" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
@@ -1203,7 +1202,7 @@ EOF
|
||||
)
|
||||
echo "$request_body"
|
||||
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "http://${OPENIM_API_HOST}:${API_OPENIM_PORT}/msg/user_clear_all_msg" -d "${request_body}")
|
||||
local response=$(${CCURL} "${Token}" "${OperationID}" "${Header}" "${INSECURE_OPENIMAPI}/msg/user_clear_all_msg" -d "${request_body}")
|
||||
|
||||
openim::test::check_error "$response"
|
||||
}
|
||||
|
||||
@@ -1121,8 +1121,7 @@ function openim::util::check-file-in-alphabetical-order {
|
||||
# Checks whether jq is installed.
|
||||
function openim::util::require-jq {
|
||||
if ! command -v jq &>/dev/null; then
|
||||
echo "jq not found. Please install." 1>&2
|
||||
return 1
|
||||
openim::log::errexit "jq not found. Please install." 1>&2
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1130,15 +1129,10 @@ function openim::util::require-jq {
|
||||
# Checks whether dig is installed and provides installation instructions if it is not.
|
||||
function openim::util::require-dig {
|
||||
if ! command -v dig &>/dev/null; then
|
||||
echo "dig command not found."
|
||||
echo "Please install 'dig' to use this feature."
|
||||
echo "Installation instructions:"
|
||||
echo " For Ubuntu/Debian: sudo apt-get install dnsutils"
|
||||
echo " For CentOS/RedHat: sudo yum install bind-utils"
|
||||
echo " For macOS: 'dig' should be preinstalled. If missing, try: brew install bind"
|
||||
echo " For Windows: Install BIND9 tools from https://www.isc.org/download/"
|
||||
openim::log::error "dig command not found."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# outputs md5 hash of $1, works on macOS and Linux
|
||||
|
||||
@@ -34,7 +34,7 @@ release.tag: tools.verify.gsemver release.ensure-tag
|
||||
## release.ensure-tag: ensure tag
|
||||
.PHONY: release.ensure-tag
|
||||
release.ensure-tag: tools.verify.gsemver
|
||||
@scripts/ensure_tag.sh
|
||||
@scripts/ensure-tag.sh
|
||||
|
||||
## release.help: Display help information about the release package
|
||||
.PHONY: release.help
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.keep
|
||||
@@ -1,7 +1,10 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
gettoken "github.com/openimsdk/open-im-server/v3/test/e2e/api/token"
|
||||
"github.com/openimsdk/open-im-server/v3/test/e2e/framework/config"
|
||||
)
|
||||
|
||||
// UserInfoRequest represents a request to get or update user information
|
||||
@@ -17,14 +20,20 @@ type GetUsersOnlineStatusRequest struct {
|
||||
|
||||
// GetUsersInfo retrieves detailed information for a list of user IDs
|
||||
func GetUsersInfo(token string, userIDs []string) error {
|
||||
|
||||
url := fmt.Sprintf("http://%s:%s/user/get_users_info", config.LoadConfig().APIHost, config.LoadConfig().APIPort)
|
||||
|
||||
requestBody := UserInfoRequest{
|
||||
UserIDs: userIDs,
|
||||
}
|
||||
return sendPostRequestWithToken("http://your-api-host:port/user/get_users_info", token, requestBody)
|
||||
return sendPostRequestWithToken(url, token, requestBody)
|
||||
}
|
||||
|
||||
// UpdateUserInfo updates the information for a user
|
||||
func UpdateUserInfo(token, userID, nickname, faceURL string) error {
|
||||
|
||||
url := fmt.Sprintf("http://%s:%s/user/update_user_info", config.LoadConfig().APIHost, config.LoadConfig().APIPort)
|
||||
|
||||
requestBody := UserInfoRequest{
|
||||
UserInfo: &gettoken.User{
|
||||
UserID: userID,
|
||||
@@ -32,13 +41,17 @@ func UpdateUserInfo(token, userID, nickname, faceURL string) error {
|
||||
FaceURL: faceURL,
|
||||
},
|
||||
}
|
||||
return sendPostRequestWithToken("http://your-api-host:port/user/update_user_info", token, requestBody)
|
||||
return sendPostRequestWithToken(url, token, requestBody)
|
||||
}
|
||||
|
||||
// GetUsersOnlineStatus retrieves the online status for a list of user IDs
|
||||
func GetUsersOnlineStatus(token string, userIDs []string) error {
|
||||
|
||||
url := fmt.Sprintf("http://%s:%s/user/get_users_online_status", config.LoadConfig().APIHost, config.LoadConfig().APIPort)
|
||||
|
||||
requestBody := GetUsersOnlineStatusRequest{
|
||||
UserIDs: userIDs,
|
||||
}
|
||||
return sendPostRequestWithToken("http://your-api-host:port/user/get_users_online_status", token, requestBody)
|
||||
|
||||
return sendPostRequestWithToken(url, token, requestBody)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
gettoken "github.com/openimsdk/open-im-server/v3/test/e2e/api/token"
|
||||
"github.com/openimsdk/open-im-server/v3/test/e2e/framework/config"
|
||||
)
|
||||
|
||||
// ForceLogoutRequest represents a request to force a user logout
|
||||
@@ -34,30 +35,39 @@ type Pagination struct {
|
||||
|
||||
// ForceLogout forces a user to log out
|
||||
func ForceLogout(token, userID string, platformID int) error {
|
||||
|
||||
url := fmt.Sprintf("http://%s:%s/auth/force_logout", config.LoadConfig().APIHost, config.LoadConfig().APIPort)
|
||||
|
||||
requestBody := ForceLogoutRequest{
|
||||
PlatformID: platformID,
|
||||
UserID: userID,
|
||||
}
|
||||
return sendPostRequestWithToken("http://your-api-host:port/auth/force_logout", token, requestBody)
|
||||
return sendPostRequestWithToken(url, token, requestBody)
|
||||
}
|
||||
|
||||
// CheckUserAccount checks if the user accounts exist
|
||||
func CheckUserAccount(token string, userIDs []string) error {
|
||||
|
||||
url := fmt.Sprintf("http://%s:%s/user/account_check", config.LoadConfig().APIHost, config.LoadConfig().APIPort)
|
||||
|
||||
requestBody := CheckUserAccountRequest{
|
||||
CheckUserIDs: userIDs,
|
||||
}
|
||||
return sendPostRequestWithToken("http://your-api-host:port/user/account_check", token, requestBody)
|
||||
return sendPostRequestWithToken(url, token, requestBody)
|
||||
}
|
||||
|
||||
// GetUsers retrieves a list of users with pagination
|
||||
func GetUsers(token string, pageNumber, showNumber int) error {
|
||||
|
||||
url := fmt.Sprintf("http://%s:%s/user/account_check", config.LoadConfig().APIHost, config.LoadConfig().APIPort)
|
||||
|
||||
requestBody := GetUsersRequest{
|
||||
Pagination: Pagination{
|
||||
PageNumber: pageNumber,
|
||||
ShowNumber: showNumber,
|
||||
},
|
||||
}
|
||||
return sendPostRequestWithToken("http://your-api-host:port/user/get_users", token, requestBody)
|
||||
return sendPostRequestWithToken(url, token, requestBody)
|
||||
}
|
||||
|
||||
// sendPostRequestWithToken sends a POST request with a token in the header
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.keep
|
||||
@@ -1,6 +1,9 @@
|
||||
package config
|
||||
|
||||
import "flag"
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Flags is the flag set that AddOptions adds to. Test authors should
|
||||
// also use it instead of directly adding to the global command line.
|
||||
@@ -19,3 +22,49 @@ func CopyFlags(source *flag.FlagSet, target *flag.FlagSet) {
|
||||
target.Var(flag.Value, flag.Name, flag.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
// Config defines the configuration structure for the OpenIM components.
|
||||
type Config struct {
|
||||
APIHost string
|
||||
APIPort string
|
||||
MsgGatewayHost string
|
||||
MsgTransferHost string
|
||||
PushHost string
|
||||
RPCAuthHost string
|
||||
RPCConversationHost string
|
||||
RPCFriendHost string
|
||||
RPCGroupHost string
|
||||
RPCMsgHost string
|
||||
RPCThirdHost string
|
||||
RPCUserHost string
|
||||
// Add other configuration fields as needed
|
||||
}
|
||||
|
||||
// LoadConfig loads the configurations from environment variables or default values.
|
||||
func LoadConfig() *Config {
|
||||
return &Config{
|
||||
APIHost: getEnv("OPENIM_API_HOST", "127.0.0.1"),
|
||||
APIPort: getEnv("API_OPENIM_PORT", "10002"),
|
||||
|
||||
// TODO: Set default variable
|
||||
MsgGatewayHost: getEnv("OPENIM_MSGGATEWAY_HOST", "default-msggateway-host"),
|
||||
MsgTransferHost: getEnv("OPENIM_MSGTRANSFER_HOST", "default-msgtransfer-host"),
|
||||
PushHost: getEnv("OPENIM_PUSH_HOST", "default-push-host"),
|
||||
RPCAuthHost: getEnv("OPENIM_RPC_AUTH_HOST", "default-rpc-auth-host"),
|
||||
RPCConversationHost: getEnv("OPENIM_RPC_CONVERSATION_HOST", "default-rpc-conversation-host"),
|
||||
RPCFriendHost: getEnv("OPENIM_RPC_FRIEND_HOST", "default-rpc-friend-host"),
|
||||
RPCGroupHost: getEnv("OPENIM_RPC_GROUP_HOST", "default-rpc-group-host"),
|
||||
RPCMsgHost: getEnv("OPENIM_RPC_MSG_HOST", "default-rpc-msg-host"),
|
||||
RPCThirdHost: getEnv("OPENIM_RPC_THIRD_HOST", "default-rpc-third-host"),
|
||||
RPCUserHost: getEnv("OPENIM_RPC_USER_HOST", "default-rpc-user-host"),
|
||||
}
|
||||
}
|
||||
|
||||
// getEnv is a helper function to read an environment variable or return a default value.
|
||||
func getEnv(key, defaultValue string) string {
|
||||
value, exists := os.LookupEnv(key)
|
||||
if !exists {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user